I am working on a project using chart.js. I want to display data in a Radar chart form. We have 5 different data sources. To make our radar chart more readable we have divided some of these data sources (some having much higher values than others), and our chart is looking good.
However the only problem now is that in our legend the data is displayed with a decimal place (as we have divided it).
Does anyone know how to edit the legend template in chart.js so that we can multiply the results shown for some of our data sets (so users do not see decimal point data)?
This the function in our app.js file that is creating the dataset for chart.js (notice some values divided):
return largestStations.reduce(function(previousValue, currentValue, index, array) {
previousValue.datasets[index] = {
label: currentValue.commonName.replace(' Underground Station', ''),
// add chart formatting (needs to be made dynamic)
fillColor: "rgba(255, 0, 0, 0.2)",
strokeColor: "rgba(255, 0, 0, 1)",
pointColor: "rgba(255, 0, 0, 1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
// end of chart formatting
data: [
app.getValueFromStation(currentValue, 'Gates') / 10,
app.getValueFromStation(currentValue, 'Lifts'),
app.getValueFromStation(currentValue, 'Payphones'),
app.getValueFromStation(currentValue, 'Escalators') / 3,
app.getValueFromStation(currentValue, 'Cash Machines')
]
};
return previousValue; }
We are currently using the default legend template in chart.js which looks like this:
<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>
Is editing the legend template the best solution for this problem? If anyone knows a better way to display this data (which has a big difference in range) using chart.js that would be very much welcomed also.
this is from github:
You could add a personalized callback to the tooltip labels, where you round the values
e.g.
1 - tooltipItem.yLabel.toFixed(2) would return a value with 2 decimal places.
2.123.toFixed(2)
>> "2.12"
2.0001.toFixed(2)
>> "2.00"
2- Math.round(tooltipItem.yLabel * 100) / 100 would return a value rounded to the nearest 2nd decimal place.
Math.round(2.123 * 100) / 100
>> 2.12
Math.round(2.00001 * 100) / 100
>> 2
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var label = data.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
label += tooltipItem.yLabel.toFixed(2);
return label;
}
}
}
Related
The pie chart I created with Apexcharts shows relative (percentage) numbers at the circle (like the "99.9%" in the screenshot below).
Instead of the relative number I'd like to show the absolute value like what's in the tooltip (see example: "6752").
I tried a formatter function with signature function(value, {seriesIndex,dataPointIndex,w}), but value is the relative value (e.g. 99.9), dataPointIndex is undefined, seriesIndex is always 1 and w contains the whole chart config without being specific to this slice of the pie.
How can I show absolute numbers?
You have the right strategy, but probably not the right formatter. The one you need is dataLabels.formatter:
let options = {
series: [10, 20, 15],
chart: {
width: 350,
type: 'pie'
},
labels: ['Label 1', 'Label 2', 'Label 3'],
dataLabels: {
formatter: (val, { seriesIndex, w }) => w.config.series[seriesIndex] // <--- HERE
}
};
let chart = new ApexCharts(document.querySelector('#chart'), options);
chart.render();
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<div id="chart"></div>
I'm enjoying the great Material chart lib from Google. I'm wondering if there is a way to set a max value on one of the series when using two independent x-axis?
var options7 = {
bars: 'horizontal',
series: {
0: {axis: 'percent', maxValue: 100}, // Bind series 0 to an axis named 'percent'.
1: {axis: 'total'} // Bind series 1 to an axis named 'total'.
},
axes: {
x: {
percent: {label: 'Percent Answered', maxValue: 100}, // Bottom x-axis.
total: {side: 'top', label: 'Total Questions'} // Top x-axis.
}
},
hAxis: {title: 'Questions Answered for the Division', maxValue: 100}
};
data7.sort({column: 2});
chart7 = new google.charts.Bar(document.getElementById("deptQuestionsAnswered"));
chart7.draw(data7, options7);
I've added the maxValue field in all the places I thought it might help one at a time, as well as combined (as in above code), but I can't find any documentation on how to do this for multi-series.
Any thoughts?
I've found numerous posts on how to gradient fill the area beneath the chart, but I'd like to do this:
Is that doable with ChartJS?
It is somehow doable. A simple approach presented below assumes one dataset only (it should be easy to extend the approach for handling more datasets, though). The idea is as follows. We will create a plugin that will override the beforeUpdate method (which is called at the start of every update). At the start of every update, the exact Y pixels of the min and max values of the dataset are calculated. A vertical linear gradient is then created from the context of the canvas using createLinearGradient, with a kind of red for the Y pixel that corresponds to the min value of the dataset and a jazzy kind of blue for the Y pixel that corresponds to the max value of the dataset. Look at the commented code for more information. There may be some glitches regarding hovering over points and legend coloring, which I am not very keen on looking into. A working fiddle is here and the code is also available below.
var gradientLinePlugin = {
// Called at start of update.
beforeUpdate: function(chartInstance) {
if (chartInstance.options.linearGradientLine) {
// The context, needed for the creation of the linear gradient.
var ctx = chartInstance.chart.ctx;
// The first (and, assuming, only) dataset.
var dataset = chartInstance.data.datasets[0];
// Calculate min and max values of the dataset.
var minValue = Number.MAX_VALUE;
var maxValue = Number.MIN_VALUE;
for (var i = 0; i < dataset.data.length; ++i) {
if (minValue > dataset.data[i])
minValue = dataset.data[i];
if (maxValue < dataset.data[i])
maxValue = dataset.data[i];
}
// Calculate Y pixels for min and max values.
var yAxis = chartInstance.scales['y-axis-0'];
var minValueYPixel = yAxis.getPixelForValue(minValue);
var maxValueYPixel = yAxis.getPixelForValue(maxValue);
// Create the gradient.
var gradient = ctx.createLinearGradient(0, minValueYPixel, 0, maxValueYPixel);
// A kind of red for min.
gradient.addColorStop(0, 'rgba(231, 18, 143, 1.0)');
// A kind of blue for max.
gradient.addColorStop(1, 'rgba(0, 173, 238, 1.0)');
// Assign the gradient to the dataset's border color.
dataset.borderColor = gradient;
// Uncomment this for some effects, especially together with commenting the `fill: false` option below.
// dataset.backgroundColor = gradient;
}
}
};
Chart.pluginService.register(gradientLinePlugin);
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["First", "Second", "Third", "Fourth", "Fifth"],
datasets: [{
label: 'My Sample Dataset',
data: [20, 30, 50, 10, 40],
// No curves.
tension: 0,
// No fill under the line.
fill: false
}],
},
options: {
// Option for coloring the line with a gradient.
linearGradientLine: true,
scales: {
yAxes: [{
ticks: {
min: 0,
max: 100,
stepSize: 20
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js"></script>
<canvas id="myChart" width="400" height="200"></canvas>
There is also a pluginless method, mentioned here, but that method is lacking. According to that method, one would have to set the borderColor to a gradient that should have been created before the creation of the chart. The gradient is calculated statically and will never fit an arbitrary range or respond to resizing as is.
I have a chartjs linechart diagram to show the sales of different products on a range of dates. The user can select a date range (for example from 2015-12-01 to 2015-12-10) to view the sales per day and thats fine and its working.
But if the user selects only one day (range from for example 2015-12-01 to 2015-12-01), he gets the correct diagram, but it doesn't look good:
As you can see, the points are stick to the y-axis. Is there a possibility, to center the points on the diagram?
Thats how it should look like:
Instead of hardcoding the labels and values with blank parameters, use the offset property.
const options = {
scales: {
x: {
offset: true
}
}
}
Documentation: https://www.chartjs.org/docs/latest/axes/cartesian/linear.html#common-options-to-all-cartesian-axes
You can check the length of your labels (or data) arrays and add dummy non-renderable points to the left and right by using empty string labels and null value, like so
var chartData = {
labels: ['', "A", ''],
datasets: [
{
fillColor: "rgba(255, 52, 21, 0.2)",
pointColor: "#da3e2f",
strokeColor: "#da3e2f",
data: [null, 20, null]
},
{
fillColor: "rgba(52, 21, 255, 0.2)",
strokeColor: "#1C57A8",
pointColor: "#1C57A8",
data: [null, 30, null]
},
]
}
Fiddle - https://jsfiddle.net/pf24vg16/
Wanted to add to the above answer and say that I got a similar effect on a time series scatter plot using this:
if (values.length === 1) {
const arrCopy = Object.assign({}, values);
values.unshift({x: arrCopy[0].x - 86400000, y: null});
values.push({x: arrCopy[0].x + 2 * 86400000, y: null});
}
That only handles for a single point, however. To add in functionality for multiple points, I did the following:
const whether = (array) => {
const len = array.length;
let isSame = false;
for (let i = 1; i < len; i++) {
if (array[0].x - array[i].x >= 43200000) {
isSame = false;
break;
} else {
isSame = true;
}
}
return isSame;
}
if (values.length === 1 || whether(arr[0])) {
const arrCopy = Object.assign({}, values);
values.unshift({x: arrCopy[0].x - 86400000, y: null});
values.push({x: arrCopy[0].x + 2 * 86400000, y: null});
}
You might notice I'm just subtracting/adding a day in milliseconds into the x values. To be honest, I was just having the worst of times with moment.js and gave up haha. Hope this helps someone else!
Note: my code has a tolerance of 43200000, or 12 hours, on the time. You could use moment.js to compare days if you have better luck with it than I did tonight :)
For your specific problem, try to modify the options->scales->xAxes option like so:
options: {
title: {
display: true,
text: 'mytitle1'
},
scales: {
xAxes: [{
type: 'linear',
ticks: {
suggestedMin: 0,
suggestedMax: (11.12*2),
stepSize: 1 //interval between ticks
}
}],
More info at: Chart JS: Ignoring x values and putting point data on first available labels
How do I adjust the column width on a google combo chart? Below is my code, but I can't figure out how to set the column width. Depending on the data I enter, the api makes the columns different widths. I'd like them all 10px. I've been trying to set the with with bar.groupWidth but cannot. Any ideas?
<script type="text/javascript">
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart);
function getValueAt(column, dataTable, row) {
return dataTable.getFormattedValue(row, column);
}
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Time', 'Boluses', 'Total Volume', '30 mL/kg', { role: 'annotation' }], [0,0,0,1769.1, null],[9, 500, 500, 1769.1, null],[29, 250, 750, 1769.1, null],[44, 250, 1000, 1769.1, null],[114, 2000, 3000, 1769.1, null],[238, 0, 3000, 1769.1, null],[238, 0, 3000, 1769.1, null],[288, 85, 3085, 1769.1, null],[288, 6.8, 3091.8, 1769.1, null],[348, 100, 3191.8, 1769.1, null],[348, 8, 3199.8, 1769.1, null],[408, 100, 3299.8, 1769.1, null],[408, 8, 3307.8, 1769.1, null],[360, 0, 3307.8, 1769.1, null]
]);
var view = new google.visualization.DataView(data);
var options = {
title: 'sepsis treatment summary',
fontName: 'Lato',
titleTextStyle: {fontSize: 18},
annotation: {},
vAxis: {title: 'total fluids received (mL)', minValue: 0, gridlines: {count: 6}},
hAxis: {title: 'time after alert (minutes)', viewWindow: {min: 0, max: 360}, gridlines: {count: 6}},
seriesType: "bars",
series: {
1: {color: '#99CCFF', type: "area"},
2: {color: 'red', type: "line", lineDashStyle: [10, 2]},
3: {role: "annotation"}
},
annotations: {style: 'line'},
};
var chart = new google.visualization.ColumnChart(document.getElementById('chart'));
chart.draw(view, options);
}
This code creates the following chart:
The API calculates a maximum width for each bar group that is roughly:
var chartWidth, // chart area width in pixels
axisRange, // axis max value - axis min value
data, // DataTable, assume axis values are in column 0 for this exercise, and that data is sorted ascending
minSeparation = axisRange, // will hold the minimum separation between daat points
barGroupWidth;
// calculate the minimum distance between any two adjacent points
for (var i = 1; i < data.getNumberOfRows(); i++) {
if (data.getValue(i, 0) - data.getValue(i - 1, 0) < minSeparation) {
minSeparation = data.getValue(i, 0) - data.getValue(i - 1, 0);
}
}
// calculate the maximum width of a bar group
barGroupWidth = chartWidth * minSeparation / axisRange;
Pleaase note that this function is a rough approximation of what the API does based on what I was able to reverse engineer.
So, if you have a chart that has a chart area 300 pixels wide with an axis range of 100 and a minimum separation between adjacent points of 10, your maximum bar group width will be 30 pixels. If you try to set the bar group width above this value, your setting will be ignored.
In your case, you have adjacent points with a separation of 0 (rows 5 and 6, 7 and 8, 9 and 10, 11 and 12), which would result in a bar group width of 0 by my rough approximation. The actual algorithm is more generous, and is likely giving you 1 pixel wide groups. There is no setting you can change to make the bar groups wider, your only recourse is to change your data set to space the values out more. This may not be easy to do, but I would suggest starting by thinking about what it means to have two events at the same time with different values, and how you might be able to represent that differently.