Add space Between Columns in Bar chart. chartjs - chart.js

I'm creating graphs using chart js V 2.9.3.
When I create the graph with a small amount of data it renders data perfectly but when the amount of data is increasing, the chart becomes Crowded.
A graph has two columns in single label.
I'm also not able to set the labels without rotation.
var config = {
type: 'bar',
data: {
labels: _datesForLabel,
datasets: _chartDataWithOptions,
},
options: {
tooltips: {
},
plugins: {
colorschemes: {
scheme: 'office.Waveform6'
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
}
}],
xAxes: [{
barThickness: 40,
maxBarThickness: 40,
barPercentage: 1.0,
categoryPercentage: 1.0,
ticks: {
min: 0,
},
}]
}
}
};
myBarChart = new Chart(ctx, config);
These are the options I used.
given is the screenshot of the output
output Image
can anyone help me with this.
thank you

Remove this barThickness: 40, (40 in pixels). In your case "No space/room" for such width = overlaps & broken layout.
https://www.chartjs.org/docs/latest/charts/bar.html#barthickness
Basic snippet (Base on your code) (change barThickness barPercentage barPercentage):
https://www.chartjs.org/docs/latest/charts/bar.html#barpercentage-vs-categorypercentage
var canvas = document.getElementById("myChart");
var ctx = canvas.getContext("2d");
var _datesForLabel = ["2020-02-10",
"2020-02-13",
"2020-02-17",
"2020-02-18",
"2020-02-19",
"2020-02-20",
"2020-02-21",
"2020-02-22",
"2020-02-23",
"2020-02-24",
"2020-02-25",
"2020-02-26",
"2020-02-27",
"2020-02-28",
"2020-02-29",
"2020-03-01",
"2020-03-02",
"2020-03-03",
"2020-03-04",
"2020-03-05",
"2020-03-07",
"2020-03-08",
"2020-03-09",
"2020-03-10","2020-02-10",
"2020-02-13",
"2020-02-17",
"2020-02-18",
"2020-02-19",
"2020-02-20",
"2020-02-21",
"2020-02-22",
"2020-02-23",
"2020-02-24",
"2020-02-25",
"2020-02-26",
"2020-02-27",
"2020-02-28",
"2020-02-29",
"2020-03-01",
"2020-03-02",
"2020-03-03",
"2020-03-04",
"2020-03-05",
"2020-03-07",
"2020-03-08",
"2020-03-09",
"2020-03-10"]
var _chartDataWithOptions =[];
_chartDataWithOptions.push({
label:"dataseries1",
data:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],
backgroundColor:"blue"
})
_chartDataWithOptions.push({
label:"dataseries2",
data:[2,3,4,5,6,7,8,9,10,12,13,11,10,19,14,12,11,18,26,23,21,28,24,2,3,4,6,9,1,2,1,11,12,13,14,15,16,17,18,19,20,21,22,23,11,22,4,6,3,6],
backgroundColor:"red"
})
var config = {
type: 'bar',
data: {
labels: _datesForLabel,
datasets: _chartDataWithOptions,
borderSkipped: 'top'
},
options: {
// responsive: true,
tooltips: {
// mode: ''
},
plugins: {
colorschemes: {
scheme: 'office.Waveform6'
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
}
}],
xAxes: [{
// barThickness: 40, // number (pixels) or 'flex'
maxBarThickness: 40,
barPercentage: 1,/* change this */
categoryPercentage: 0.5,/* change this */
ticks: {
min: 0,
},
}]
}
}
};
myBarChart = new Chart(ctx, config);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js" ></script>
<div style="height: 500px; width: 100%;">
<canvas id="myChart" ></canvas>
</div>
About "set labels without rotation" - again "no room" - by maxRotation: 0, - full answer + example her: Chart Js Change Label orientation on x-Axis for Line Charts
"To much points/data" issue:
For now "no way" to auto group data - one idea is to use
stacked: true ("save room") - or manually filter your data (Show fewer points - related StackOverflow Q: Chartjs 2 scaling lots of data points).
Related Github feature request: https://github.com/chartjs/Chart.js/issues/4053

Related

How to use Chartjs to plot a single row of colored bars with a time based x axis

I have some time based data I want a graphical representation of, and was hoping to use Chartjs to plot this.
The data looks something like the following:
Time State
--------------
7am up
9am down
10.45am out
17.35 up
Also, each "state" will have its own color, so I would use this as a bar color when using a bar graph
up = red
down = yellow
out = green
The end result I am after is a simple one row bar like the following...
I thought I may be able to use a Chartjs horizontal stacked bar chart (https://www.chartjs.org/docs/latest/charts/bar.html#horizontal-bar-chart) to do this somehow, but I just can't work out how to get this working.
Some (not working) experimental code is as follows:
private createChart(): void {
if (this.chart !== undefined) {
return;
}
Chart.register(BarController, PointElement, Tooltip, Legend, TimeScale, LinearScale, CategoryScale, LinearScale, BarElement);
const options: ChartOptions = {
plugins: {
legend: {
display: false,
},
title: {
display: false,
},
},
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
stacked: true,
type: 'time',
display: true,
// position: 'bottom',
time: {
unit: 'minute',
displayFormats: {
minute: 'h:mm a'
}
}
},
x: {
stacked: false,
}
}
};
this.chart = new Chart(this.canvasRef.nativeElement, {
type: 'bar',
data: this.chartData,
options
});
this.chart.config.options.scales.y.min = new Date('2023-02-13T06:19:31.842Z').getTime();
const labels = [
'up',
'down'
// 'Dataset 2'
];
const d1 = new Date('2023-02-13T06:20:32.842Z').getTime();
const d2 = new Date('2023-02-13T06:21:33.842Z').getTime();
this.chartData = {
labels,
datasets: [{
label: 'up',
data: [{x: 10, y: d1}],
backgroundColor: 'red',
},
{
label: 'down',
data: [{x: 20, y: d2}],
backgroundColor: 'green',
}
]
};
this.chart.update();
}
In the above I have tried various combinations of labels, x values, y values, data shapes, but I only even get an empty graph.
Perhaps this is not really possible (I am trying to use the wrong component).
How can I achieve this using chartjs?
Update
Using example from #winner_joiner below, I have put a copy of it at plunkr and have tried to use the time in the x axis, but can see it is still not plotting the bars using the dates as the length
Well your code basically works, here a slightly modified version of your code.
After your comments and updated question, I reworke the example (seen below). Although it is possible to do with chart.js the question is, maybe for this specific task a different library or solution would be better/more convenient.
Update Chart, with some similar values from your question:
(I'm using here momentjs, since it is recommend usually needed form date/time actions in chartjs, as mentioned in the documentation)
const d0 = moment.duration('07:00:00').asMinutes();
const d1 = moment.duration('09:00:00').asMinutes();
const d2 = moment.duration('10:45:00').asMinutes();
const d3 = moment.duration('17:35:00').asMinutes();
const d4 = moment.duration('19:00:00').asMinutes();
let values = [d0, d1, d2, d3, d4];
let data = {
labels: [''],
datasets: [{
label: 'up',
axis: 'y',
data: [d1],
backgroundColor: 'red',
},{
label: 'down',
axis: 'y',
data: [d2],
backgroundColor: 'yellow',
},{
label: 'out',
axis: 'y',
data: [d3],
backgroundColor: 'green',
},{
label: 'up',
axis: 'y',
data: [d4],
backgroundColor: 'red',
}
]
};
const config = {
data,
type: 'bar',
options:{
plugins: {
tooltip: {
mode: 'dataset',
callbacks: {
label: function(item){
return moment().startOf('day').add({ minute: item.raw}).format('HH:mm');
}
}
},
legend: {
display: false,
},
title: {
display: false,
},
},
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
min: d0,
ticks: {
callback: function(value, index, ticks) {
return moment().startOf('day').add({ minute: value}).format('HH:mm');
}
},
afterBuildTicks: axis => axis.ticks = values.map(v => ({ value: v }))
},
y: {
stacked: true
},
}
}};
new Chart(document.getElementById("chart"), config);
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>
<script src="//cdn.jsdelivr.net/npm/moment#^2"></script>
<script src="//cdn.jsdelivr.net/npm/chartjs-adapter-moment#^1"></script>
<div class="chart" style="height:184px; width:350px;">
<canvas id="chart" ></canvas>
</div>

is there a way to do automatic scrolling?

I'm trying to put data into chartJS and have it automatically scroll when it reaches the end
so like when it reaches here
it will keep adding data without the user having to scroll, is there any ways that I can do this without the scroll bar at the bottom?
Automatic scrolling without visible scroll bar would means that the user can never see data again that was scrolled out of the visible area. If this is what you want, you can simply remove outdated labels and dataset values once a certain limit is reached. This can be done using Array.shift(), which removes the first element from an array.
chart.data.labels.push(<new label>);
chart.data.datasets[0].data.push(<new value>);
if (chart.data.labels.length > maxValues) {
chart.data.labels.shift();
chart.data.datasets[0].data.shift();
}
chart.update();
Please have a look at the runnable code snippet below that allows up to 10 labels and values. Once this limit is reached, outdated labels and values are removed.
var chart = new Chart('canvas', {
type: "line",
responsive: true,
maintainAspectRatio: false,
data: {
labels: [],
datasets: [{
label: "Data",
data: [],
fill: true,
backgroundColor: "lightblue",
borderColor: "lightblue",
pointRadius: 0
}]
},
options: {
legend: {
display: true,
position: 'bottom'
},
scales: {
yAxes: [{
ticks: {
min: 0,
max: 20,
stepSize: 5
}
}]
}
}
});
var maxValues = 10;
var count = 0;
setInterval(() => {
chart.data.labels.push(++count);
chart.data.datasets[0].data.push(Math.floor((Math.random() * 20) + 1));
if (chart.data.labels.length > maxValues) {
chart.data.labels.shift();
chart.data.datasets[0].data.shift();
}
chart.update();
}, 1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="canvas" height="100"></canvas>

Add buffer to Y axis with chart js

Sometimes chart value is the same height as chart height. For example im my picture red bar is 6, the same as y-axis top number. Can I add some buffer so chart bar never reaches top of y axis? Lets say y axis would go to 7 now (or similar).
Image show my problem (open image in new window for better view)
By Axis Range Settings
https://www.chartjs.org/docs/latest/axes/cartesian/linear.html#axis-range-settings
suggestedMax: 7
1/2. Static max value example
Change min to 10 and max to 90 (For data[30, 40, 50, 60]).
let chart = new Chart(ctx, {
type: 'line',
responsive: true,
data: {
datasets: [{
label: 'First dataset',
data: [30, 40, 50, 60]
}],
labels: ['January', 'February', 'March', 'April']
},
options: {
scales: {
yAxes: [{
ticks: {
suggestedMin: 10,
suggestedMax: 90
}
}]
}
}
});
<canvas id="ctx" width="800" height="350"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
2/2. Dynamic "buffer"
First no one solution for this idea (The max value related to your data structure). For the most basic data structure (Flat), this is one solution:
Get the max value of [20,40,60, 80] ==> 80
updateScaleDefaults Change max y-axis to max + buffer (20 in this example)
updateScaleDefaults - The default configuration for a scale can be easily changed using the
scale service. All you need to do is to pass in a partial
configuration that will be merged with the current scale default
configuration to form the new default. https://www.chartjs.org/docs/latest/axes/#updating-axis-defaults
Example:
For data: [20,40,60, 80]
/* data */
var data = {
labels: ["Africa", "Asia", "Europe", "America"],
datasets: [{
/* data */
label: "Population (millions)",
backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f", '#1d49b8'],
data: [20,40,60, 80]
}]
};
/* buffer trick */
var buffer = 20;
const dataSet = data.datasets[0].data;
console.log("data: " + dataSet);
/* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max */
var maxDATAvalue = Math.max(...dataSet);
var maxValuePlusBuffer = maxDATAvalue + buffer;
console.log("max value(" + maxDATAvalue + ") / Plus Buffer(" + maxValuePlusBuffer + ")");
/* The default configuration for a scale can be easily changed using the scale service. */
/* https://www.chartjs.org/docs/latest/axes/#updating-axis-defaults */
Chart.scaleService.updateScaleDefaults('linear', {
ticks: {
max: maxValuePlusBuffer
}
});
var options = {
responsive: true,
title: {
text: 'Set max value to max(data) + Buffer',
display: true
},
scales: {
xAxes: [{
stacked: true,
ticks: {
},
}],
yAxes: [{
stacked: true,
}]
}
};
var myChart = new Chart(document.getElementById("chart"), {
type: 'bar',
data: data,
options: options
});
<canvas id="chart" width="800" height="350"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
Related Stackoverflow Q:
How to set max and min value for Y axis
ChartJS: How to set fixed Y axis max and min

Chart.js display x axis labels ON ticks in bar chart, not between

I have this chart:
...which is displaying exactly how I want it to with one exception... The data in the bars is for between the two times in the x axis... so all the labels need shifting to lie on the grid lines, not between them as default for a bar chart. So the red and blue bar is data between 8:00 and 9:00. I hope I've explained that clearly enough.
I'm trawling through the Chart.js docs and it just doesn't seem like this is possible! I know I could change my labels to be, for example, 8pm - 9pm, but that seems a much more visually clunky way of doing it. Is there a way anyone know of achieving this? Ideally there would be another '12am' on the last vertical grid line too.
You can draw the tick lables at the desired position directly on to the canvas using the Plugin Core API. It offers number of hooks that may be used for performing custom code. In below code snippet, I use the afterDraw hook to draw my own labels on the xAxis.
const hours = ['00', '01', '02', '03', '04', '05', '06'];
const values = [0, 0, 0, 0, 10, 6, 0];
const chart = new Chart(document.getElementById('myChart'), {
type: 'bar',
plugins: [{
afterDraw: chart => {
var xAxis = chart.scales['x-axis-0'];
var tickDistance = xAxis.width / (xAxis.ticks.length - 1);
xAxis.ticks.forEach((value, index) => {
if (index > 0) {
var x = -tickDistance + tickDistance * 0.66 + tickDistance * index;
var y = chart.height - 10;
chart.ctx.save();
chart.ctx.fillText(value == '0am' ? '12am' : value, x, y);
chart.ctx.restore();
}
});
}
}],
data: {
labels: hours,
datasets: [{
label: 'Dataset 1',
data: values,
categoryPercentage: 0.99,
barPercentage: 0.99,
backgroundColor: 'blue'
}]
},
options: {
responsive: true,
legend: {
display: false
},
scales: {
xAxes: [{
type: 'time',
time: {
parser: 'HH',
unit: 'hour',
displayFormats: {
hour: 'Ha'
},
tooltipFormat: 'Ha'
},
gridLines: {
offsetGridLines: true
},
ticks: {
min: moment(hours[0], 'HH').subtract(1, 'hours'),
fontColor: 'white'
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>

Chartjs 2: Multi level/hierarchical category axis in chartjs

Is it possible to define a bar chart with a multi level/category axis?
For instance I'd like to display the Region/Province categories like in this Excel chart:
I found this example using multiple xAxes.
xAxes:[
{
id:'xAxis1',
type:"category",
ticks:{
callback:function(label){
var month = label.split(";")[0];
var year = label.split(";")[1];
return month;
}
}
},
{
id:'xAxis2',
type:"category",
gridLines: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
ticks:{
callback:function(label){
var month = label.split(";")[0];
var year = label.split(";")[1];
if(month === "February"){
return year;
}else{
return "";
}
}
}
}
]
The problem is it seems that the two axes are not really linked and the alignment of second axis is based on values instead of aligning in middle of lower level category. this case cause issues
Is there a clean way to achieve this in chart.js?
Update:
I ended up creating a feature request on chartjs.
you can provide value separately for different axis via datesets and provide an object with different configuration option (borderColor, pointBackgroundColor, pointBorderColor) etc, i hope It'll help.
here is the link for the with an update (fiddle you shared) Updated Fiddle
data: {
labels: ["January;2015", "February;2015", "March;2015", "January;2016", "February;2016", "March;2016"],
datasets: [{
label: '# of Votes',
xAxisID:'xAxis1',
data: [12, 19, 3, 5, 2, 3]
},
// here i added another data sets for xAxisID 2 and it works just fine
{
label: '# of Potatoes',
xAxisID:'xAxis2',
data: [9, 17, 28, 26, 29, 9]
}]
}
I hope that solves your problem :)
Hope this helps,
I did a bit of research and couldn't find methods to implement your solution in chartjs. Chartjs has grouped bar charts but not subgrouped bar charts like in your case.
Example: http://jsfiddle.net/harshakj89/ax3zxtzw/
Here are some alternatives,
D3js (https://d3js.org/) can be used to create sub grouped bar charts.Data can be loaded from csv or json. D3 is highly configurable, but you may have to put some effort than chartsjs.
https://plnkr.co/edit/qGZ1YuyFZnVtp04bqZki?p=preview
https://stackoverflow.com/questions/37690018/d3-nested-grouped-bar-chart
https://stackoverflow.com/questions/15764698/loading-d3-js-data-from-a-simple-json-string
ZingChart is a commercial tool and can be used to implement bar charts with sub groupes.
https://www.zingchart.com/docs/chart-types/bar-charts/
But I prefer D3 over this library. because D3 comes under BSD License.
This should work as per your requirement http://tobiasahlin.com/blog/chartjs-charts-to-get-you-started/#8-grouped-bar-chart
The best library I could found to have exactly this feature is Highcharts, this is my implementation:
and here http://jsfiddle.net/fieldsure/Lr5sjh5x/2/ you can find out how to implement it.
$(function () {
var chart = new Highcharts.Chart({
chart: {
renderTo: "container",
type: "column",
borderWidth: 5,
borderColor: '#e8eaeb',
borderRadius: 0,
backgroundColor: '#f7f7f7'
},
title: {
style: {
'fontSize': '1em'
},
useHTML: true,
x: -27,
y: 8,
text: '<span class="chart-title"> Grouped Categories with 2 Series<span class="chart-href"> Black Label </span> <span class="chart-subtitle">plugin by </span></span>'
},
yAxis: [{ // Primary yAxis
labels: {
format: '${value}',
style: {
color: Highcharts.getOptions().colors[0]
}
},
title: {
text: 'Daily Tickets',
style: {
color: Highcharts.getOptions().colors[0]
}
}
}, { // Secondary yAxis
title: {
text: 'Invoices',
style: {
color: Highcharts.getOptions().colors[0]
}
},
labels: {
format: '${value}',
style: {
color: Highcharts.getOptions().colors[0]
}
},
opposite: true
}]
,
series: [{
name: 'Daily',
type: 'column',
yAxis: 1,
data: [4, 14, 18, 5, 6, 5, 14, 15, 18],
tooltip: {
valueSuffix: ' mm'
}
}, {
name: 'Invoices',
type: 'column',
data: [4, 17, 18, 8, 9, 5, 13, 15, 18],
tooltip: {
valueSuffix: ' °C'
}
}],
xAxis: {
categories: [{
name: "1/1/2014",
categories: ["Vendor 1", "Vendor 2", "Vendor 3"]
}, {
name: "1/2/2014",
categories: ["Vendor 1", "Vendor 2", "Vendor 3"]
}, {
name: "1/3/2014",
categories: ["Vendor 1", "Vendor 2", "Vendor 3"]
}]
}
});
});
body {
padding: 0px !important;
margin: 8px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://blacklabel.github.io/grouped_categories/grouped-categories.js"></script>
<div id="container" class="chart-container"></div>
But the problem is the library is not free for commercial purposes, and this is the Chartjs implementation, in my case it is look like this:
const data ={"labels":[{"label":"Exams","children":["Wellness Examination"]},{"label":"Surgery","children":["Neuter Surgery"]},{"label":"Vaccines","children":["Bordetella"]},{"label":"Dentistry","children":["Dental Cleaning"]},{"label":"Diagnostics","children":["Other","Pre-Anesthetic","Adult Diagnostics","Pre-Anesthetic Diagnostics","Heartworm & Tick Borne Disease Test"]},{"label":"Treatments/Other","children":["Other","Microchip"]}],"datasets":[{"label":"Consumed","backgroundColor":"red","tree":[{"value":0,"children":["0"]},{"value":0,"children":["0"]},{"value":1,"children":["1"]},{"value":0,"children":["0"]},{"value":15,"children":["0","1","3","11","0"]},{"value":15,"children":["2","13"]}]},{"label":"Purchased","backgroundColor":"blue","tree":[{"value":28,"children":["28"]},{"value":1,"children":["1"]},{"value":24,"children":["24"]},{"value":10,"children":["10"]},{"value":103,"children":["2","16","34","49","2"]},{"value":165,"children":["75","90"]}]}]};
window.onload = () => {
const ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
title: {
display: true,
text: 'Chart.js Hierarchical Bar Chart'
},
layout: {
padding: {
// add more space at the bottom for the hierarchy
bottom: 45
}
},
scales: {
xAxes: [{
type: 'hierarchical',
stacked: false,
// offset settings, for centering the categorical
//axis in the bar chart case
offset: true,
// grid line settings
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
stacked: false,
ticks: {
beginAtZero: true
}
}]
}
}
});
};
canvas {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/chart.js/dist/Chart.bundle.js"></script>
<script src="https://unpkg.com/chartjs-scale-hierarchical"></script>
<div id="container" style="width: 75%;">
<canvas id="canvas"></canvas>
</div>
for each more column just add another dataset.