I am trying to prepare a Tornado Chart using the column chart in Highcharts. Here is my fiddle.
My current code is:
$('#container').highcharts({
chart: {
type: 'columnrange',
inverted: true
},
title: {
text: 'Net Sales'
},
subtitle: {
text: 'MM $'
},
xAxis: {
categories: ['Annual Revenue', 'Number of Years', 'Annual Costs']
},
yAxis: {
title: {
text: 'MM $'
}
},
plotOptions: {
columnrange: {
dataLabels: {
enabled: true,
formatter: function () {
return this.y;
}
}
},
scatter:{
marker:{
symbol:'line',
lineWidth:11,
radius:8,
lineColor:'#f00'
}
}
},
legend: {
enabled: false
},
series: [{
name: 'Temperatures',
data: [
[12.15, 46.86],
[15.45, 42.28],
[27.77, 31.24]
]
},
{
name:'Base',type: 'scatter',data:[120],
}]
});
The problem is that the last series (Annual Costs) does not show, as it is in reversed order. Also, I'd like the Tornado Chart to look more like this:
Note that the labels in this chart are different from the actual values plotted. Also note that the bar in the center - in the example code, there would be a vertical line at 29.5. I would also like to support a combined uncertainty bar like the one at the bottom. Any suggestions would be greatly appreciated.
Your last bat is not showing, because first number is lower than second, see: http://jsfiddle.net/kErPt/1/
If you want to display another values at labels, then add that info first. Example:
data: [{
low: 12,
high: 15,
lowLabel: 35,
highLabel: 46
}, {
low: 2,
high: 35,
lowLabel: 15,
highLabel: 26
} ... ]
And then use dataLabels.formatter for series.
To add vertical line use plotLines.
I'm not sure what is the last bar called 'combined uncertainty'.
I've used Highcharts with separate series (thanks jlbriggs) to create a Tornado Chart: http://jsfiddle.net/uRjBp/
var baseValue = 29.5;
var outputTitle = "Net Sales";
var chart = new Highcharts.Chart({
chart: {
renderTo:'container',
//type:'column'
//type:'area'
//type:'scatter'
//type:'bubble'
},
credits: {},
exporting: {},
legend: {},
title: {
text: outputTitle
},
subtitle: {
text: "MM $"
},
tooltip: {
formatter: function() {
var msg = "";
var index = this.series.chart.xAxis[0].categories.indexOf(this.x);
var low = round(this.series.chart.series[0].data[index].y+baseValue);
var high = round(this.series.chart.series[1].data[index].y+baseValue);
if (this.x === "Combined Uncertainty") {
msg = "Combined Uncertainty in "+outputTitle+": "+low+" to "+high;
} else {
var lowLabel = this.series.chart.series[0].data[index].label;
var highLabel = this.series.chart.series[1].data[index].label;
msg = '<b>'+outputTitle+'</b> goes from '+ low +' to '+ high+'<br/> when '+this.x +
' goes from <br/> '+lowLabel+" to "+highLabel;
}
return msg;
}
},
plotOptions: {
series: {
dataLabels: {
enabled: true,
formatter: function () {
var index = this.series.chart.xAxis[0].categories.indexOf(this.x);
if (this.series.userOptions.labels === undefined) {
return this.y+baseValue;
}
return this.key === "Combined Uncertainty" ? "":this.series.userOptions.labels[index];
}
}
}
},
xAxis: {
title: {
text: 'Factor'
},
allowDecimals:false,
categories: ['Annual Revenue', 'Number of Years', 'Annual Costs', 'Combined Uncertainty']
},
yAxis: {
title: {
text: 'MM $'
},
labels: {
formatter:function() {
return this.value+baseValue;
}
}
},
series:[{
name: 'Low',
grouping:false,
type:'bar',
data:[{y:12.15-baseValue, label:10},{y:15.45-baseValue, label:1},{y:31.25-baseValue, label:2},{y:12.15-baseValue, color:'#99CCFF', label: ""}],
labels:[10,1,2,]
},{
name: 'High',
grouping:false,
type:'bar',
data:[{y:46.86-baseValue, label:30},{y:42.28-baseValue, label:3},{y:27.77-baseValue, label:4},{y:46.86-baseValue, color:'#99CCFF', label:""}],
labels:[30,3,4,]
},
{
name: 'Median',
type: 'scatter',
data: [null,null, null,27-baseValue],
marker: {
lineWidth: 2,
lineColor: Highcharts.getOptions().colors[3],
fillColor: 'white'
}
}]
});
function round(num) {
return Math.round(num*100)/100;
}
usually, this kind of chart is done using a separate series for the left and right portions
One way to do this is by setting one set of data as negative numbers, and then using the formatters to make the axis labels, datalabels, and tooltips display the absolute values
example:
http://jsfiddle.net/jlbriggs/yPLVP/68/
UPDATE:
to show a line as in your original chart, you can extend the marker symbols to include a line type, and use a scatter series to draw that point:
http://jsfiddle.net/jlbriggs/yPLVP/69/
If you don't want to have the extra code for the line marker type, you could use any of the other existing marker symbols for the scatter series.
Related
I have this fiddle JSfiddle
Here is the reproduced code:
$(function () {
$('#container').highcharts({
chart: {
type: 'bar'
},
xAxis: {
categories: ['Heroku', 'Ruby','Lisp','Javascript','Python','PHP']
},
yAxis: {
categories: ['low','medium','high'],
title: {
text: 'expertise',
align: 'high'
},
labels: {
overflow: 'justify'
}
},
tooltip: {
valueSuffix: ' millions'
},
plotOptions: {
bar: {
dataLabels: {
enabled: true
}
}
},
series: [{
data: ['low','high','low','medium','medium']
}]
});
});
If you look at the fiddle the yAxis does not render and has a value of for every x category. I've been looking at the highcharts api, but I can't seem to get this right. The code makes sense to me but I'm obviously doing something wrong. Can someone point out why the YAxis is not displaying correctly?
As mentioned in my comment, you need to supply the numeric value of the category, not the category name.
In the case of categories, the numeric value is the array index.
Also, in your case, the way you are trying to plot the values, I would add an empty category at the beginning, otherwise your first category of low gets plotted as 0, which doesn't seem right.
So,
categories: ['low','medium','high']
Becomes
categories: ['','low','medium','high'],
And
data: ['low','high','low','medium','medium']
Becomes
data: [1,3,1,2,2]
Updated fiddle:
http://jsfiddle.net/jlbriggs/k64boexd/3/
Check this fiddle:
http://jsfiddle.net/navjot227/k64boexd/2/
Trick is to utilize the formatter function. You can use a similar formatter function on y-axis labels too if that's desired. Though it seems like you need it for data labels for this problem.
$(function() {
$('#container').highcharts({
chart: {
type: 'bar'
},
xAxis: {
categories: ['Heroku', 'Ruby', 'Lisp', 'Javascript', 'Python', 'PHP']
},
yAxis: {
title: {
text: 'expertise',
align: 'high'
},
labels: {
overflow: 'justify',
}
},
tooltip: {
valueSuffix: ' millions'
},
plotOptions: {
bar: {
dataLabels: {
enabled: true,
formatter: function() {
if (this.y == 0) {
return 'low'
} else if (this.y == 1) {
return 'medium'
} else {
console.log(this.y);
return 'high'
}
}
}
}
},
series: [{
data: [0, 2, 0, 1, 1]
}]
});
});
In my opinion, it is kinda unlikely for a line graph to have a y-axis category, since it speaks more of amount or value. In your case, "low, medium, and high" speaks of ranges, with which a certain value can be assigned to any of it.
Thus, Highcharts accepts series data in numeric form. But you can work around it by setting ['low', 'medium', 'high'] in the category attribute of yAxis, then setting series data as an array of number corresponding to the index of the category, i.e. [0,1,1,2,...] and tweaking the tooltip to display the category instead of the y value using formatter attribute.
Here is the code:
$(function() {
yCategories = ['low', 'medium', 'high'];
$('#container').highcharts({
title: {
text: 'Chart with category axes'
},
xAxis: {
categories: ['Heroku', 'Ruby','Lisp','Javascript','Python','PHP']
},
yAxis: {
categories: yCategories
},
tooltip: {
formatter: function() {
return this.point.category + ': ' + yCategories[this.y];
}
},
series: [{
data: [0, 1, 2, 2, 1]
}]
});
});
Here is a working example : JSFiddle
I have an area spline highchart that seems to incorrectly place data points along the y-axis. I'm using the angular-highcharts directive and delivering the chart options through my service. The data itself is correct, as are the labels, the points are just placed too high along the y-axis. See image and code below:
Service function:
function getReportingChart(quarterlyData, chartType, reportingType) {
// extract top 4 drugs
var top4 = _.sortBy(quarterlyData, 'x2q2').reverse().slice(0, 4);
var dataSeries = [];
var firstDrug = quarterlyData[0];
var seriesNumber = 0;
var quarters = ['Q1 2012', 'Q2 2012', 'Q3 2012', 'Q4 2012', 'Q1 2013', 'Q2 2013', 'Q3 2013', 'Q4 2013', 'Q1 2014', 'Q2 2014'];
_.each(top4, function (drug) {
var name = drug.A;
var dataArray = [];
dataArray.push(drug.x0q1);
dataArray.push(drug.x0q2);
dataArray.push(drug.x0q3);
dataArray.push(drug.x0q4);
dataArray.push(drug.x1q1);
dataArray.push(drug.x1q2);
dataArray.push(drug.x1q3);
dataArray.push(drug.x1q4);
dataArray.push(drug.x2q1);
dataArray.push(drug.x2q2);
var newDataSet = new buildSeries(name, dataArray, seriesNumber);
dataSeries.push(newDataSet);
console.log(newDataSet);
seriesNumber += 1;
});
return {
options: {
chart: {
type: chartType
},
plotOptions: {
series: {
stacking: 'normal'
}
},
},
series: dataSeries,
title: {
text: 'Top 4 Drugs By ' + annotateChartByType(reportingType).titleSuffix
},
credits: {
enabled: false
},
loading: false,
xAxis: {
categories: quarters,
tickmarkPlacement: 'between',
labels: {},
min: 0.5,
max: quarters.length - 1.5,
startOnTick: false,
endOnTick: false
},
yAxis: {
title: {
text: annotateChartByType(reportingType).yAxis.title
},
labels: {
format: annotateChartByType(reportingType).yAxis.unitformat
}
},
size: {}
};
}
Below is the annotateChartByType function:
function annotateChartByType(params){
var defaultOption = {titleSuffix:" Primary Reports", yAxis: {title: "Number of Reports", unitformat: '{value}'}};
switch (params) {
case 'ps-cases':
return defaultOption;
case 'patients':
return {titleSuffix:" Patient Incidence Rate",yAxis: {title: "Incidence Rate", unitformat: '{value:,.2f} %'}};
case 'serious-outcomes':
return {titleSuffix:" Outcome Rate", yAxis: {title: "Incidence Rate", unitformat: '{value:%.2f} %'}};
case 'serious-adverse-events':
return {titleSuffix:" Serious AE Rate", yAxis: {title: "Incidence Rate", unitformat: '{value:%.2f} %'}};
default:
return defaultOption;
}
}
This is because you have:
plotOptions: {
series: {
stacking: 'normal'
}
}
This means that the value you are seeing is indeed correct for the blue series, but it is stacked upon three other series, which makes it go higher on the y-axis. If this is unintentional just remove this code.
If it is intentional you can get the total instead of the series value using the total value in a formatter or pointFormat. See this pointFormat example:
tooltip: {
pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b> ({point.total})<br/>'
}
See this JSFiddle demonstration. This is the default with the parenthesis added to show the total.
I´ve a chart using highcharts, the only problem is that each column has the same column.
What should I do so each column has a different column.
Here is my code:
var charts = [];
$containers = $('#container1');
var datasets = [
{
name: 'Tokyo',
data: [49, 57]
}];
var cat = ['A', 'B'];
console.log(datasets);
$.each(datasets, function(i, dataset) {
console.log(dataset);
charts.push(new Highcharts.Chart({
chart: {
renderTo: $containers[i],
type: 'column',
marginLeft: i === 0 ? 100 : 10
},
title: {
text: dataset.name,
align: 'left',
x: i === 0 ? 90 : 0
},
credits: {
enabled: false
},
xAxis: {
categories: cat,
labels: {
enabled: i === 0
}
},
yAxis: {
allowDecimals: false,
title: {
text: null
}
},
legend: {
enabled: false
},
series: [dataset]
}));
});
Thanks in advance.
To have each column be a different color, all you have to do is set the colorByPoint property to true.
Reference:
http://api.highcharts.com/highcharts#plotOptions.column.colorByPoint
Alternatively you can make each column a separate series, which gives you additional levels of control.
OTOH, in the majority of cases, having each column a separate color serves no purpose except to clutter and confuse the data, and make the user work harder cognitively to interpret the chart.
If you want to highlight a single column for a particular reason, you can do that by adding the fillColor property to the data array:
Something like:
data:[2,4,5,{y:9,fillColor:'rgba(204,0,0,.75)',note:'Wow, look at this one'},4,5,6]
I finally found a way to show more than 1 color for each column:
var charts1 = [];
var $containers1 = $('#container1');
var datasets1 = [{
name: 'Dalias',
data: [29]
},
{
name: 'Lilas',
data: [1]
},
{
name: 'Tulipanes',
data: [15]
}];
$('#container1').highcharts({
chart: {
type: 'column',
backgroundColor: 'transparent'
},
title: {
text: 'Montos pedidos por división'
},
tooltip: {
pointFormat: '<span style="color:{series.color};" />{series.name} </span>:<b>{point.y}</b>',
useHTML: true
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
},
series : {
cursor: 'pointer',
point: {
events: {
/*click: function() {
verDetalle("Especialidades,"+ this.series.name);
}*/
}
}
}
},
credits:{
enabled: false
},
yAxis: {
min: 0,
title: {
text: ''
}
},
xAxis: {
categories: ['División']
},
series: datasets1
});
I was wondering if anyone knew of jsfiddle examples where stacked bar graphs with multiple values on a single point were changed after their creation. I've seen plenty of examples using setData for single points on a series, but none for multiple.
I currently have the following graphs and would like to put multiple values on each point.
window.jQuery(function () {
//var opportunities_by_month_chart;
opportunities_by_month_chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'column'
},
title: {
text: 'Pipeline By Month By Outcome'
},
subtitle: {
text: ''
},
xAxis: {
categories: ['test', 'test2']
},
yAxis: {
min: 0,
title: {
text: 'Opportunity size in £'
}
},
tooltip: {
formatter: function () {
return 'Month: ' + this.x + '<br/>' +
this.series.name + ': £' + this.y + '<br/>' +
'Total: £' + this.point.stackTotal;
}
},
plotOptions: {
series: {
stacking: 'normal'
}
},
credits: {
enabled: false
},
series: [{
name: 'Closed Won',
data: [],
color: 'green'
}, {
name: 'Closed Postponed',
data: [],
color: 'orange'
}, {
name: 'Closed Lost',
data: [],
color: 'red'
}, {
name: 'Other',
data: [],
color: 'blue'
}
]
});
});
http://jsfiddle.net/9FyGX/2/
If someone could point to an example or add an example to the jsfiddle it would make my day :D
As you said
chart.series[0].setData([1, 2, 3, 4], true);
chart.series[1].setData([2, 3, 4], true);
will work. However, the last parameter (true) tells highcharts to redraw the chart. Therefore it's more effecient to call the fist setData with false, and only redraw once you've finished adding the data.
chart.series[0].setData([1, 2, 3, 4], false);
chart.series[1].setData([2, 3, 4], true);
I am working with HighCharts to produce a bar chart. My values can range from as minimal as 0 to as high as 100k (example). Therefore, one bar of the graph can be very small and the other can be very long. HighCharts has introduced the feature of "Logarithmic Scaling". The example of which can be seen HERE
My js code is written in this jsfiddle file. I want to display my horizontal axis (x-Axis) logarithmically. I have inserted the key type as shown in the example but the script goes into an infinite loop which has to be stopped.
What is the flaw in the execution or is logarithmic scaling for HighCharts still not mature?
P.S The commented line in jsfiddle is causing the issue
Since the "official" method is still buggy, you can achieve the log scale more manually by manipulating your input data with a base 10 log and masking your output data raising 10 to the output value. See it in action here http://jsfiddle.net/7J6sc/ code below.
function log10(n) {
return Math.log(n)/Math.log(10);
}
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
defaultSeriesType: 'bar',
marginRight: 200,
marginLeft: 10,
},
title: {
text: 'Negative'
},
xAxis: {
categories: [''],
title: {
text: null
}
},
yAxis: {
min: 0,
title: {
text: '',
align: 'high',
},
labels: {
formatter: function() {
return Math.round(Math.pow(10,this.value));
}
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'top',
x: -50,
y: 100,
floating: true,
borderWidth: 1,
shadow: true
},
tooltip: {
formatter: function() {
return '' + this.series.name + ': ' + Math.round(Math.pow(10,this.y)) + ' millions';
}
},
plotOptions: {
bar: {
dataLabels: {
enabled: true,
formatter: function() {
return Math.round(Math.pow(10,this.y));
}
}
}
},
credits: {
enabled: false
},
series: [{
"data": [log10(4396)],
"name": "A"},
{
"data": [log10(4940)],
"name": "B"},
{
"data": [log10(4440)],
"name": "C"},
{
"data": [log10(2700)],
"name": "D"},
{
"data": [log10(2400)],
"name": "E"},
{
"data": [log10(6000)],
"name": "F"},
{
"data": [log10(3000)],
"name": "G"},
{
"data": [log10(15000)],
"name": "E"}],
});
It is still experimental according to the Official Documentation, so that might be the case:
"The type of axis. Can be one of "linear" or "datetime". In a datetime axis, the numbers are given in milliseconds, and tick marks are placed on appropriate values like full hours or days.
As of 2.1.6, "logarithmic" is added as an experimental feature, but it is not yet fully implemented. Defaults to "linear".
Try it: "linear", "datetime" with regular intervals, "datetime" with irregular intervals, experimental "logarithmic" axis."
For those of you who are still looking for an answer :
JSFiddle : http://jsfiddle.net/TuKWT/76/
Or SO snippet :
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
defaultSeriesType: 'bar'
},
title: {
text: 'Negative'
},
xAxis: {
categories: ['A','B','C','D','E','F','G','H'],
title: {
text: null
}
},
yAxis: {
type: 'logarithmic',
//min: 0, <= THIS WILL CAUSE ISSUE
title: {
text: null,
}
},
legend: {
enabled: false
},
tooltip: {
formatter: function() {
return this.x + ':' + this.y + ' millions';
}
},
plotOptions: {
bar: {
dataLabels: {
enabled: false
}
}
},
credits: {
enabled: false
},
series: [{
"data": [4396,4940,4440,2700,2400,6000,3000,15000],
}],
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<div id="container" style="height: 300px"></div>