I would like to set the color of y-axis tick labels in Chart.js bar and line charts based on the numeric label value. Specifically, I'd like negative values to be rendered red. Additionally rather than displaying "-1", "-2", etc., I'd like to override to display "(1)", "(2)", etc.
I've seen examples for changing tick labels based on index / position, but not conditionally based on the label value. Thanks in advance for any guidance.
You can define the scriptable option scales.x.ticks.color as an array of colors that depend on the corresponding data value each. The following definition for example shows red tick labels for every bar of a value less than 10.
scales: {
x: {
ticks: {
color: data.map(v => v < 10 ? 'red' : undefined)
}
}
}
For further information, consult Tick Configuration from the Chart.js documentation.
Please take a look at below runnable code and see how it works.
const data = [4, 12, 5, 13, 15, 8];
new Chart('myChart', {
type: 'bar',
data: {
labels: ['A', 'B', 'C', 'D', 'E', 'F'],
datasets: [{
label: 'Dataset',
data: data,
}]
},
options: {
responsive: false,
scales: {
x: {
ticks: {
color: data.map(v => v < 10 ? 'red' : undefined)
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<canvas id="myChart" height="180"></canvas>
How to align zeros on chart with multi axes, if there are both positive and negative values in dataset?
I want zeroes to be on the same line.
I dont like this:
Graph image
link to jsfiddle
new Chart(canvas, {
type: 'line',
data: {
labels: ['1', '2', '3', '4', '5'],
datasets: [{
label: 'A',
yAxisID: 'A',
data: [-10, 96, 84, 76, 69]
}, {
label: 'B',
yAxisID: 'B',
data: [-2, 3, 5, 2, 3]
}]
},
options: {
scales: {
yAxes: [{
id: 'A',
type: 'linear',
position: 'left',
}, {
id: 'B',
type: 'linear',
position: 'right',
}]
}
}
});
actualy, The example on the official web page has the same problem. Looks so messy.
The answer above is great if you know the ranges in advance and can specify min and max for each ticks object in advance. If, like me, you're trying to produce the effect with generated data, you'll need something that computes these values for you. The following does this without recourse to hard-coded values. It does generate ugly max / min values - but there's another fix for that: Hide min and max values from y Axis in Chart.js. It may also generate less than ideal spacing in which case a simple fudge factor applied to the min and max values may be used.
function scaleDataAxesToUnifyZeroes (datasets, options) {
let axes = options.scales.yAxes
// Determine overall max/min values for each dataset
datasets.forEach(function (line) {
let axis = line.yAxisID ? axes.filter(ax => ax.id === line.yAxisID)[0] : axes[0]
axis.min_value = Math.min(...line.data, axis.min_value || 0)
axis.max_value = Math.max(...line.data, axis.max_value || 0)
})
// Which gives the overall range of each axis
axes.forEach(axis => {
axis.range = axis.max_value - axis.min_value
// Express the min / max values as a fraction of the overall range
axis.min_ratio = axis.min_value / axis.range
axis.max_ratio = axis.max_value / axis.range
})
// Find the largest of these ratios
let largest = axes.reduce((a, b) => ({
min_ratio: Math.min(a.min_ratio, b.min_ratio),
max_ratio: Math.max(a.max_ratio, b.max_ratio)
}))
// Then scale each axis accordingly
axes.forEach(axis => {
axis.ticks = axis.ticks || { }
axis.ticks.min = largest.min_ratio * axis.range
axis.ticks.max = largest.max_ratio * axis.range
})
}
You can do this by setting the ticks option on your axes to control the max, min, and stepSize. To make the zeros align, you need to make the axes symmetric. stepSize is optional if you want the gridlines to be aligned as well.
In your example:
yAxes: [{
id: 'A',
type: 'linear',
position: 'left',
ticks : {
max: 100,
min: -50,
stepSize: 50
}
}, {
id: 'B',
type: 'linear',
position: 'right',
ticks : {
max: 6,
min: -3,
stepSize: 3
}
}]
See updated fiddle: https://jsfiddle.net/x885kpe1/1/
I am trying to use ComboCharts .
In its options variable, it sets a value 5 against series in example Here. What does this 5 mean ?
var options = {
title : 'Monthly Coffee Production by Country',
vAxis: {title: "Cups"},
hAxis: {title: "Month"},
seriesType: "bars",
series: {5: {type: "line"}}
};
Edit: I now realize that this 5 is the value types per value on vAxis
While I experimented, changing 5 to 0,1,2,3 or 4. It only changed the position of the line.
How is it related to position of line in the chart ?
The number there is the zero based index of the series in your data that you are changing the properties of. The seriesType: "bars" part in you options says all of your series will default to being rendered as bars.
When you specifically call out a series like that you are overriding the defaults. In this case you were saying that column 5 should be rendered as a line.
Take a look at this example to see the relationship between the series and the data.
google.load("visualization", "1", {
packages: ["corechart"]
});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
["X", "C1", "C2", "C3", "C4", "C5"],
["A", 1, 2, 3, 4, 5],
["B", 2, 5, 1, 7, 9],
["C", 6, 2, 4, 1, 8],
["D", 7, 1, 2, 3, 6]
]);
var options = {
seriesType: "bars",
series: {
// Make the first column (C1) a blue bar (bar because it is the default)
0: {
color: "blue"
},
// Make the fourth column (C4) a green line (line because we overrode the default)
3: {
type: "line",
color: "green"
}
}
};
var chart = new google.visualization.ComboChart(document.getElementById("chart"));
chart.draw(data, options);
}
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<div id="chart" style="width: 900px; height: 300px;"></div>
I want to draw chart that can set x between two y for example I want to draw a chart with x=100 and y1=200 , y2=500 just vertical line with good detail which chart can do that for me something like this image
Which chart I should use I think it should be line chart but how?
You can use a LineChart to do that. Create a DataTable with columns for x and y values, then enter your points one row at a time, with linked points adjacent to each other, and empty rows in between (which causes the line to break, so you don't connect to the next vertical pair). Here's an example:
function drawChart() {
var data = google.visualization.arrayToDataTable([
['X', 'Y'],
[1, 100], // keep linked points adjacent
[1, 200],
[null, null], // insert blank row in between
[2, 150],
[2, 275],
[null, null],
[3, 75],
[3, 200],
[null, null],
[4, 100],
[4, 300]
]);
var chart = new google.visualization.LineChart(document.querySelector('#chart_div'));
chart.draw(data, {
height: 400,
width: 600,
pointSize: 5
});
}
google.load('visualization', '1', {packages:['corechart'], callback: drawChart});
http://jsfiddle.net/asgallant/mMy27/
I'm trying to create a Google chart that looks like the following:
http://chart.googleapis.com/chart?cht=bvs&chs=200x125&chd=t2:10,50,60,80,40%7C50,60,100,40,20%7C30,70,90,95,45&chco=4d89f900,c6d9fd&chbh=20&chds=0,160&chm=H,336699,2,-1,1:22
Basically, I just want to represent the max, min, and average all on one chart, but I can't seem to figure out how to do this. I know it's possible using markers with the old URL-based charts, but they're being deprecated and it doesn't look like the new API supports markers yet.
I tried using candlesticks, but the only way I got it working was with a skinny line and a horizontal line in the middle, so it looked like a bunch of plus signs rather than floating columns with line markers. I know I could also technically stack a column chart with a stepped area chart, but then the line is continuous across all entries, which I don't want.
Thanks.
EDIT: Using jmac's method and intervals, I came up with this:
function drawVisualization() {
// Create and populate the data table.
var data = new google.visualization.DataTable();
data.addColumn('string', 'label');
data.addColumn('number', 'filler');
data.addColumn('number', 'range');
data.addColumn({type:'number', role:'interval'});
data.addRows([
['A', 3, 4, 2],
['B', 2, 5, 4],
['C', 4, 4, 1],
['D', 5, 2, 1],
['E', 1, 8, 4],
]);
// Create and draw the visualization.
var ac = new google.visualization.ColumnChart(document.getElementById('visualization'));
ac.draw(data, {
width: 600,
isStacked: true,
series: [{color:'transparent'},{color:'silver'},{color:'silver'}],
vAxis: {gridlines: {color: 'transparent'}, textPosition: 'none'},
focusTarget: 'category',
intervals: { 'style': 'bars', 'barWidth': 1.3, 'lineWidth': 2 },
});
}
I don't have enough reputation to post an image of what it looks like yet, but if you paste it in here you can see it: https://code.google.com/apis/ajax/playground/?type=visualization#column_chart
Also, since it still highlights the filler area when you mouse over it, I found a css hack to hide the highlighting on mouse over:
#chart-div {
svg g g g g rect {
stroke-width:0px;
}
}
You can use "box" style intervals to accomplish what you want:
function drawChart () {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Category');
data.addColumn('number', 'Min');
data.addColumn('number', 'Average');
data.addColumn('number', 'Max');
data.addRows([
['Foo', 3, 5, 7],
['Bar', 5, 8, 10],
['Baz', 0, 2, 6],
['Bat', 1, 2, 4]
]);
var view = new google.visualization.DataView(data);
// duplicate 1 column as a dummy data series, and add intervals to it
view.setColumns([0, 1, {
id: 'min',
type: 'number',
role: 'interval',
calc: function (dt, row) {
return dt.getValue(row, 1);
}
}, {
id: 'avg',
type: 'number',
role: 'interval',
calc: function (dt, row) {
return dt.getValue(row, 2);
}
}, {
id: 'max',
type: 'number',
role: 'interval',
calc: function (dt, row) {
return dt.getValue(row, 3);
}
}, 1, 2, 3]);
var chart = new google.visualization.LineChart(document.querySelector('#chart_div'));
chart.draw(view, {
height: 400,
width: 600,
lineWidth: 0,
intervals: {
style: 'boxes'
},
legend: {
position: 'none'
},
series: {
0: {
// dummy data series, controls color of intervals
visibleInLegend: false,
color: 'blue',
enableInteractivity: false
},
1: {
// min series options
},
2: {
// average series options
},
3: {
// max series options
}
}
});
}
google.load('visualization', '1', {packages:['corechart'], callback: drawChart});
See working example: http://jsfiddle.net/asgallant/pvJpx/
If all you care about is how it looks visually, you can recreate this with a bit of finagling to have it look like this:
This is the code:
function drawVisualization() {
// Create and populate the data table.
var data = google.visualization.arrayToDataTable([
['label', 'filler', 'bot half', 'top half'],
['A', 3, 2, 2],
['B', 2, 4, 1],
['C', 4, 1, 3],
['D', 5, 1, 1],
['E', 1, 4, 4],
]);
// Create and draw the visualization.
var ac = new google.visualization.ColumnChart(document.getElementById('visualization'));
ac.draw(data, {
width: 600,
isStacked: true,
series: [{color:'transparent'},{color:'silver'},{color:'silver'}],
vAxis: {gridlines: {color: 'transparent'}, textPosition: 'none'},
focusTarget: 'category',
});
}
This is a dumb workaround, but here are the steps given a min value, a max value, and an avg value:
Create a dummy (transparent) series equal to min
Create a second series for the bottom half of the bar equal to avg - min
Create a third series for the top half of the bar equal to max - avg
Although it looks right, the issue is that interaction with the chart will be real funky, in the sense that it won't show you what you would expect from the chart (you would have separate values that aren't showing min, max, and average, but only two values for the size of points 2) and 3) above). You can get around this with creative use of focusTarget, but that will still get you odd stuff like this:
Now you could theoretically rename your series, and use the {v:, f:} trick to make it look nicer, and that may be a good workaround, but it is very kludgy depending on your application. If you finagle it all nice and right, you would get something like this:
This is done with the following code:
function drawVisualization() {
// Create and populate the data table.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Series Name');
data.addColumn('number', 'Average');
data.addColumn('number', 'Minimum');
data.addColumn('number', 'Maximum');
data.addRows([
['A', {v:3, f:'5'}, {v:2, f:'3'}, {v:2, f:'7'}],
['B', {v:2, f:'6'}, {v:4, f:'2'}, {v:1, f:'7'}],
['C', {v:4, f:'5'}, {v:1, f:'4'}, {v:3, f:'8'}],
['D', {v:5, f:'6'}, {v:1, f:'5'}, {v:1, f:'8'}],
['E', {v:1, f:'5'}, {v:4, f:'1'}, {v:4, f:'9'}],
]);
// Create and draw the visualization.
var ac = new google.visualization.ColumnChart(document.getElementById('visualization'));
ac.draw(data, {
width: 600,
isStacked: true,
series: [{color:'transparent'},{color:'silver'},{color:'silver'}],
vAxis: {gridlines: {color: 'transparent'}, textPosition: 'none'},
focusTarget: 'category',
});
}
Again, this is kludgy and not perfect (see the grey box around the filler series, that can't be helped), but it will display the info, and it can be automated using some fancy javascript and/or formatters with dataviews depending on how often the charts need to be changed and what format you get your data in.