Highcharts percentage without stacking? - highcharts

Is it possible to create a Highcharts chart with splines in percentages, but WITHOUT them stacking?
I would like to display lines in a relative view, but I would like them to be crossing when their percentages changes.
Basically, like this: http://jsfiddle.net/dp6pjeo9/5/
plotOptions: {
series: {
pointStart: 2010,
stacking: 'percent'
}
},
but without the lines 'stacking' behavior. Stacking is really inconvenient for lines because it is hard to tell whether they are 'absolute' values or 'stacked' values.
See here, the top line should be under then the black line in 2010.

You need to process you data before you put them in the chart config.
Calculate sums:
var data = [3, 5, 10, 15,25]
var data2 = [25,15, 10, 5, 0]
var sum = data.map((v, i) => v + data2[i]) // grab sums
Calculate percentages and set data:
series: [{
name: 'Installation',
data: data.map((v, i) => ({
y: v / sum[i] * 100,
absoluteY: v
}))
}, {
name: 'Manufacturing',
data: data2.map((v, i) => ({
y: v / sum[i] * 100,
absoluteY: v
}))
}]
Change tooltip's pointFormat so it shows absolute y value instead of percentage value
tooltip: {
pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.absoluteY}</b><br/>'
},
example: http://jsfiddle.net/dp6pjeo9/6/

Related

Shade area under the curve to one side of crosshair in highcharts/highcharter

I am looking for a way to shade an area to the left of cursor to visually see the proportion of total area under the density curve where x < X, i.e. a visual feel for a cumulative density function. Is there an easy/elegant way to do this in highcharts?
library(highcharter)
df <- data.frame(x = seq(-3, 3, 0.02),
pdf = dnorm(seq(-3, 3, 0.02)),
cdf = pnorm(seq(-3, 3, 0.02)))
highchart() |>
hc_add_series(df, "line", hcaes(x, pdf)) |>
hc_tooltip(
headerFormat = "",
pointFormat = "{point.cdf}"
) |>
hc_xAxis(crosshair = T)
You can try changing the shading parameters in the SVG attributes, these options are only available from the wrap core Highcharts code.
(function(H) {
H.wrap(H.Axis.prototype, 'drawCrosshair', function(p, e, point) {
p.call(this, e, point)
console.log(this, e, point);
if (this.cross && point) {
this.cross.attr({
stroke: point.series.color,
d: "M 10 50 L 200 50"
})
}
})
}(Highcharts));
Highcharts.chart('container', {
xAxis: {
crosshair: true
},
yAxis: {
crosshair: true
},
series: [{
data: [20, 30, 50]
}, {
data: [10, 20, 15]
}]
});
Demo: http://jsfiddle.net/BlackLabel/xe902c6q/

How do I add a real gap in high stocks so it would be visible on the chart?

I have this chart
<script src="http://code.highcharts.com/highcharts.js"></script>
<div id="container" style="height: 300px"></div>
var seriesOptions = [],
seriesCounter = 0,
names = ['MSFT', 'AAPL', 'GOOG'];
/**
* Create the chart when all data is loaded
* #returns {undefined}
*/
function createChart() {
Highcharts.stockChart('container', {
plotOptions: {
series: {
gapSize: 5 * 24 * 3600 * 1000,
gapUnit: 'relative'
}
},
rangeSelector: {
selected: 5
},
yAxis: {
labels: {
formatter: function () {
return (this.value > 0 ? ' + ' : '') + this.value + '%';
}
},
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
plotOptions: {
series: {
compare: 'percent',
showInNavigator: true
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
valueDecimals: 2,
split: true
},
series: seriesOptions
});
}
$.each(names, function (i, name) {
$.getJSON('https://www.highcharts.com/samples/data/' + name.toLowerCase() + '-c.json', function (data) {
if (i==0) {
var first = [], last = [];
first.push.apply(first, data.slice(0,1)[0]);
last.push.apply(first, data.slice(0,1)[0]);
first[0] = first[0] - 1900 * 24 * 3600 * 1000;
last[0] = last[0] - 130 * 24 * 3600 * 1000;
data = [];
data.push(first);
data.push(last);
}
seriesOptions[i] = {
name: name,
data: data
};
// As we're loading the data asynchronously, we don't know what order it will arrive. So
// we keep a counter and create the chart when all the data is loaded.
seriesCounter += 1;
if (seriesCounter === names.length) {
createChart();
}
});
});
and as you can see there are three stocks shown. If you hover the chart with the mouse and go to the beginning you'll notice MSFT stock which has only 2 points and that's intentional. After MSFT there should be about 6 year gap, however on the chart it's shown in a few pixels.
How can I configure stockChart to show real gaps? In other words, I want to see the gap of 6 years so from 2005 till 2011 there will be empty space proportional to the whole chart?
The discussion in the comment section of the first answer reveals that OP wants to hide no-data periods only in some cases.
The solution here might be to set ordinal to false (as #ewolden) suggested and use breaks instead:
xAxis: {
breaks: [{
breakSize: 24 * 3600 * 1000,
from: Date.UTC(2017, 0, 6),
to: Date.UTC(2017, 0, 9)
}],
ordinal: false
},
series: [{
data: [
[Date.UTC(2017, 0, 2), 6],
[Date.UTC(2017, 0, 3), 7],
[Date.UTC(2017, 0, 4), 3],
[Date.UTC(2017, 0, 5), 4],
[Date.UTC(2017, 0, 6), 1],
[Date.UTC(2017, 0, 9), 8],
[Date.UTC(2017, 0, 10), 9],
[Date.UTC(2017, 6, 1), 4],
[Date.UTC(2017, 6, 2), 5]
]
Example: http://jsfiddle.net/BlackLabel/ocg0dujg/
In the above demo I was able to hide the weekend (7 and 8 Jan) and maintain the space between January and July.
API reference: https://api.highcharts.com/highstock/xAxis.breaks
What you are after is ordinal.
In an ordinal axis, the points are equally spaced in the chart regardless of the actual time or x distance between them. This means that missing data for nights or weekends will not take up space in the chart.
Setting ordinal to false, like this, will give you the gap you are after:
xAxis: {
type: 'datetime',
ordinal: false,
},
There are some other issues with your code, if you look in console, you are getting error 15 which states that highcharts requires data to be sorted. You get this because of how you add the series data to your MSFT series. You add both the x and the y to a single 1D array, which means highcharts tries to plot both your x and y values on the x axis.
I did a workaround that gives it the right format in this fiddle: http://jsfiddle.net/2cps91ka/91/

Using hours and minutes as data in highcharts donut chart

I got a donut chart and want to display hours and minutes. Using highcharts pie chart with constant visible drilldown data this leads me to a big problem, please see the picture first:
The inner blue slice has the value 10.40, which stands for 10:40 as a time. The two outer slices have the values 5.50 and 4.50, which, summed up as hours and minutes, results in 10.40, but highcharts doesn't know it's a time format, so it sums that up as 10.00 which perfectly shows my problem.
I tried setting the yAxis type to datetime
type: 'datetime',
which does not change anything so far. I found other entries like Pass time values to Highcharts Column chart, which sadly left me clueless.
Did I miss something? Glad for any help.
An minimal example solution, using millisecond values to only display hours and minutes (JSFiddle):
var m = 1000 * 60;
var h = m * 60;
$('#container').highcharts({
chart: {
type: 'pie'
},
tooltip: {
formatter: function() {
return '<b>' + this.series.name +'</b><br/>' +
Highcharts.dateFormat('%H:%M', new Date(this.y));
}
},
series: [{
name: 'Inner',
data: [
{ name: 'A', y: (10 * h) + (40 * m), color: 'blue' },
{ name: 'B', y: (3 * h), color: 'red' },
],
size: '60%'
}, {
name: 'Outer',
data: [
{ name: 'A1', y: (5 * h) + (50 * m), color: 'blue' },
{ name: 'A2', y: (4 * h) + (50 * m), color: 'blue' },
{ name: 'B1', y: (3 * h), color: 'red' },
],
size: '100%',
innerSize: '60%'
}]
});
Here we just operate with time going from 0 and out. The tooltip is used to format the values in a readable format.
See this more elaborate JSFiddle demonstration using some of the pie-donut demo code to color the slices like your example picture.

Highcharts - Stacked Bar Column - Total Value of Stacked Bar not Correct with Negative and Positive Values

I'm trying out Highcharts and I have a situation where I have negative values and positive values within one bucket on the x-axis. The total for the stacked values is not accurately represented on the y-axis and the y-axis stackLabel.
For instance, in this JSFiddle, the Oranges have -30, 20 and 40. The total is showing 60; not 30.
Here's a snippet of the Highcharts object showing the data:
series: [{
name: 'John',
data: [50, -30, 40, 70, 20]
}, {
name: 'Jane',
data: [20, 20, 30, -20, 10]
}, {
name: 'Joe',
data: [30, 40, 40, -20, 50]
}]
In the highcharts stackLabel sum values from negative and positive values separately. Take look at your exampel and you will notice that you have stacklabel -30 and 60, separaterly calculated.
You can solve this by using the yAxis.stackLabels.formatter property as below:
yAxis: {
stackLabels: {
enabled: true,
align: 'center',
formatter: function() {
var sum = 0;
var series = this.axis.series;
for (var i in series) {
if (series[i].visible && series[i].options.stacking == 'normal')
sum += series[i].yData[this.x];
}
if(this.total > 0 ) {
return Highcharts.numberFormat(sum,1);
} else {
return '';
}
}
}
}
From this answer.

Controlling Highcharts bar chart look and layout

I have a bar chart where the data is coming from the database, so I don't know how many categories or series it will contain, but I still wish to display the different charts with the same standard look and feel (same bar width, space between bars, space between categories, etc.).
The chart width has to be 600px and each bar has to be 20px wide. The space between the bars has to be 5px and the space between the categories has to be 20px. The height should be calculated based on these values as I think that Highcharts doesnt do this by itself.
I tried to solve this by setting pointWidth to 20px, and then calculating the height. But I am unsure about the pointPadding and groupPadding values. It says that it's value is given in x-axis units. My x-axis only contains categories (i.e. fruits: lemon, orange, apple, etc.). What is the x-axis unit for such a category?
I have made an example fiddle where I am calculating the chart height based on the number of categories, number of series, pointWidth, pointPadding, groupPadding:
var pointWidthInPx = 20,
spaceBetweenBarsInPx = 5,
groupPaddingInPx = 5,
categories = ['Africa', 'America', 'Asia', 'Europe', 'Oceania'],
series = [{
name: 'Year 1800',
data: [107, 55, 635, 203, 30]
}, {
name: 'Year 1900',
data: [133, 156, 550, 408, 45]
}, {
name: 'Year 2008',
data: [500, 604, 404, 632, 60]
}];
var totalPointWidthsInPx = pointWidthInPx * categories.length * series.length,
totalSpaceBetweenBarsInPx = spaceBetweenBarsInPx * categories.length * (series.length - 1),
totalGroupPaddingsInPx = (groupPaddingInPx * 2) * categories.length,
calculatedHeightInPx = totalPointWidthsInPx + totalSpaceBetweenBarsInPx + totalGroupPaddingsInPx;
var groupPadding = (groupPaddingInPx * categories.length) / calculatedHeightInPx;
http://jsfiddle.net/VpMtD/
Ufortunately, I can't seem to get the correct pixel value of the groupPadding or the pointPadding correctly.
Any help on how to solve this will be greatly appreciated.
You need to calculate height of the chart according to your needs. For example: http://jsfiddle.net/PhVDV/6/
function genChart(n) {
var pointWidth = 20;
var h = (pointWidth + 10) * 2 * n + 50 + 70;
// + 10- distance between bars
// * 2 - number of series
// +50+70 - margins top + bottom
// n - number of points
if ($('#container').highcharts()) {
$('#container').highcharts().destroy();
}
$('#container').highcharts({
chart: {
type: 'bar',
marginTop: 50,
marginBottom: 70,
height: h
},
plotOptions: {
bar: {
pointWidth: pointWidth,
groupPadding:0,
}
},
series: [{
data: randomData(n)
}, {
data: randomData(n)
}]
});
}
function randomData(n) {
var d = [];
while (n--) {
d.push(Math.random());
}
return d;
}
$("button").click(function () {
genChart(Math.round(Math.random() * 20) + 4);
});

Resources