Chartjs, Bubble Chart, positioning problem with duplicate value in data labels - javascript

With a duplicate value in the data labels - Is there a way to draw the bubble on the first or second duplicate value in the bubble chart?
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
var ctx = document.getElementById("myChart");
var options = {responsive: true,
maintainAspectRatio: false,
};
var mixedChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["1", "2", "1", "4"], //Same Value on First and Third Position
datasets: [
//Lines
{
label: "First_Line",
type: "line",
borderColor: "#8e5ea2",
data: [5,10,7,12],
fill: false
}, {
label: "Second_Line",
type: "line",
borderColor: "#3e95cd",
data: [1,4,15,6],
fill: false
},
//Bubbles
{
label: "Bubble_One",
type: "bubble",
backgroundColor: "#8e5ea2",
data: [{ x: "2", y: 10, r: 15}],
},
{
label: "Bubble_Two",
type: "bubble",
backgroundColor: "#3e95cd",
backgroundColorHover: "#3e95cd",
data: [{x: ???, y: 6, r: 15}] //First "1" or Second "1" possible?
}
]
},
options: options
});
</script>
Something like "1"[0] unfortunately does not work.

You can not do it directly afaik but you can put an extra part in the label and filter it out of there with the callbacks for the tooltip and the axis:
<canvas id="myChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
var ctx = document.getElementById("myChart");
var options = {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
offset: false,
ticks: {
callback: function(val) {
return this.getLabelForValue(val).split('-')[0]
}
}
}
},
plugins: {
tooltip: {
callbacks: {
label: (ttItem) => {
if (ttItem.dataset.type === "bubble") {
return `${ttItem.label}: (${ttItem.raw.x.split('-')[0]},${ttItem.raw.y})`
} else if (ttItem.dataset.type === "line") {
return `${ttItem.dataset.label}: ${ttItem.parsed.y}`
}
},
title: (ttItem) => (ttItem[0].label.split('-')[0])
}
}
}
};
var mixedChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["1-1", "2", "1-2", "4"], //Same Value on First and Third Position
datasets: [
//Lines
{
label: "First_Line",
type: "line",
borderColor: "#8e5ea2",
data: [5, 10, 7, 12],
fill: false
}, {
label: "Second_Line",
type: "line",
borderColor: "#3e95cd",
data: [1, 4, 15, 6],
fill: false
},
//Bubbles
{
label: "Bubble_One",
type: "bubble",
backgroundColor: "#8e5ea2",
data: [{
x: "2",
y: 10,
r: 15
}],
},
{
label: "Bubble_Two",
type: "bubble",
backgroundColor: "#3e95cd",
backgroundColorHover: "#3e95cd",
data: [{
x: "1-2",
y: 6,
r: 15
}] //First "1" or Second "1" possible?
}
]
},
options: options
});
</script>

Related

How to adjust spaces between points in chart js?

I am trying to make a line chart by using chart.js. But I facing a problem. I want to control the space between points, but the chart.js makes it equal.
Here is my code:
<canvas id="myChart"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.js" integrity="sha512-OD9Gn6cAUQezuljS6411uRFr84pkrCtw23Hl5TYzmGyD0YcunJIPSBDzrV8EeCiFxGWWvtJOfVo5pOgB++Jsag==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
var ctx = document.getElementById("myChart");
var points = [1,1,9,9.3];
var Dates = ["Dec 29th", "jan 10th", "Feb 21st", "Feb 25th"];
var myChart = new Chart(ctx, {
type: "line",
data: {
labels: Dates,
datasets: [
{
label: "My chart",
data: points,
backgroundColor: "black",
borderColor: "blue",
borderWidth: 3,
fill: false,
lineTension: 0.4,
}
]
},
options: {
legend: {display: false},
scales: {
yAxes:[{
ticks: {
min:1,
max: 9.9,
callback: function(value, index, values) {
return '$' + value;
}
}
}],
},
elements: {
point:{
radius: 0
}
}
}
});
</script>
Here is the result of my code:
But I want the big space in middle like this:
How can I make the big space between point 3 and point4, please?
Can you use time on the x axis?
Below is an example
// "+" before (new Date(2021,11,29)) return miliseconds from 1970 year
const dataA = [
{x:+new Date(2021,11,29), y:1}, // { x: 1640732400000, y: 1},
{x:+new Date(2022,0,10), y:1}, // { x: 1641769200000, y: 1},
{x:+new Date(2022,1,21), y:9}, // { x: 1645398000000, y: 9},
{x:+new Date(2022,1,25), y:9.3}, // { x: 1645743600000, y: 9.3}
];
const cfgChart = {
type: 'line',
data: {
datasets: [
{
backgroundColor: '#74adf7',
borderColor: '#74adf7',
data: dataA,
label: 'A',
lineTension: 0.4,
},
],
},
options: {
animation: false,
parsing: false,
interaction: {
mode: 'index',
axis: 'x',
intersect: false,
},
scales: {
x: {
type: 'time',
},
},
},
};
const chart = new Chart(document.querySelector('#chart').getContext('2d'), cfgChart);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/2.3.1/luxon.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-adapter-luxon/1.1.0/chartjs-adapter-luxon.min.js"></script>
<div>
<canvas id="chart"></canvas>
</div>

How to show xaxis lable o only data point and hide all others?

I have a chartjs line chart requirement where the client is asking to show labels on x axis only if it has a data point. please find his mockup below.
the x-axis is time and here is what I am getting.
how can I achieve this?
here is my config.
options={{
scales: {
xAxes: [
{
distribution: 'linear',
type: "time",
time: {
min: range_min.toDateString(),
max: range_max.toDateString(),
unit: "day",
stepSize: "1",
},
id: 'xAxis',
ticks: {
autoSkip: true,
callback: function (value, index, values) {
return formatDate(new Date(value))
},
}
},
],
},
pan: {
enabled: true,
mode: "x",
speed: 1,
threshold: 1,
},
zoom: {
enabled: true,
drag: true,
sensitivity: 0.5,
mode: "x",
},
annotation: {
annotations: [{
type: 'line',
mode: 'vertical',
scaleID: 'xAxis',
value: 1582229218219,
endValue: 1582229218219,
borderColor: 'rgb(75, 0, 0)',
borderWidth: 4
}]
},
onClick: (event, item) => {
console.log(item)
}
}}
yes this is possible, you can achieve this by using the tick callback like so:
const data = [{
x: 'Red',
y: 10
}, {
x: 'Yellow',
y: 5
}, {
x: 'Orange',
y: 3
}]
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data,
borderWidth: 1,
backgroundColor: 'red',
borderColor: 'red',
fill: false
}]
},
options: {
scales: {
xAxes: [{
ticks: {
callback: (val, y, z, t) => (
data.map(el => el.x).indexOf(val) >= 0 ? val : null
)
}
}]
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js" integrity="sha512-hZf9Qhp3rlDJBvAKvmiG+goaaKRZA6LKUO35oK6EsM0/kjPK32Yw7URqrq3Q+Nvbbt8Usss+IekL7CRn83dYmw==" crossorigin="anonymous"></script>
</body>

Two chart for the same set of data - Chart.js

Is it possible to get 2 separate types of chart (in this case bar and line) for the same dataset, but under one label?
As an example, this is my chart:
const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());
function getConfig() {
return {
type: 'bar',
data: {
labels: [ "Mar-20", "Apr-20" ],
datasets: [{
type: "line",
label: "en-US",
borderColor: "#8c856f",
data: [ 2, 3 ],
borderWidth: 2,
fill: false
}, {
type: "bar",
label: "en-US",
backgroundColor: "#beb391",
data: [ 2, 3 ]
}, {
type: "line",
label: "sv-SE",
borderColor: "#b3cbaa",
data: [ 1, 2 ],
borderWidth: 2,
fill: false
}, {
type: "bar",
label: "sv-SE",
backgroundColor: "#683e3a",
data: [ 1, 2 ]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
min : 0
}
}]
}
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart-container"></canvas>
As you can see, I have 4 datasets: 2 for each language (en & se). It all works perfectly fine except the legend labels are generated for each dataset (2 x "en-US", 2 x "sv-SE"), while I would like to show only the unique labels, essentially using the values from the main "labels" property, instead of the one inside each dataset.
Is it possible, and how can I do it?
You can filter out the labels for the line series via:
options: {
legend: {
labels: {
filter: function(item, chart) {
return chart.datasets[item.datasetIndex].type === 'bar';
}
},
onClick: function(e, legendItem) {
let chart = this.chart;
let index = legendItem.datasetIndex;
let visible = !chart.getDatasetMeta(index).hidden;
chart.data.datasets.forEach((dataset, i) => {
if (dataset.label === legendItem.text) {
chart.getDatasetMeta(i).hidden = visible;
}
});
chart.update();
}
}
}
This has been adapted from: Is it possible in chartjs to hide certain dataset legends?
For toggling the similar datasets, I followed this example.
Demo
const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());
function getConfig() {
return {
type: 'bar',
data: {
labels: ["Mar-20", "Apr-20"],
datasets: [{
type: "line",
label: "en-US",
borderColor: "#8c856f",
data: [2, 3],
borderWidth: 2,
fill: false
}, {
type: "bar",
label: "en-US",
backgroundColor: "#beb391",
data: [2, 3]
}, {
type: "line",
label: "sv-SE",
borderColor: "#b3cbaa",
data: [1, 2],
borderWidth: 2,
fill: false
}, {
type: "bar",
label: "sv-SE",
backgroundColor: "#683e3a",
data: [1, 2]
}]
},
options: {
legend: {
labels: {
filter: function(item, chart) {
return chart.datasets[item.datasetIndex].type === 'bar';
}
},
// Override hide/show for multiple series with same label.
onClick: function(e, legendItem) {
let chart = this.chart;
let index = legendItem.datasetIndex;
let visible = !chart.getDatasetMeta(index).hidden;
chart.data.datasets.forEach((dataset, i) => {
if (dataset.label === legendItem.text) {
chart.getDatasetMeta(i).hidden = visible;
}
});
chart.update();
}
},
scales: {
yAxes: [{
ticks: {
min: 0
}
}]
}
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>
<canvas id="chart-container"></canvas>
Alternatively, you could create a plugin... This makes it so that you can configure each individual series.
var hideLegendItemsPlugin = {
beforeInit: function(chartInstance) {
if (chartInstance.options.hideLegendItemsPlugin) {
Object.assign(chartInstance.options.legend, {
labels : {
filter : (item, chart) => {
return chart.datasets[item.datasetIndex].hideLegendItem !== true;
}
},
onClick: function(e, legendItem) {
let chart = this.chart;
let index = legendItem.datasetIndex;
let visible = !chart.getDatasetMeta(index).hidden;
chart.data.datasets.forEach((dataset, i) => {
if (dataset.label === legendItem.text) {
chart.getDatasetMeta(i).hidden = visible;
}
});
chart.update();
}
});
}
}
};
Chart.pluginService.register(hideLegendItemsPlugin);
const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());
function getConfig() {
return {
type: 'bar',
data: {
labels: ["Mar-20", "Apr-20"],
datasets: [{
type: "line",
label: "en-US",
borderColor: "#8c856f",
data: [2, 3],
borderWidth: 2,
fill: false,
hideLegendItem: true // Hide it!
}, {
type: "bar",
label: "en-US",
backgroundColor: "#beb391",
data: [2, 3]
}, {
type: "line",
label: "sv-SE",
borderColor: "#b3cbaa",
data: [1, 2],
borderWidth: 2,
fill: false,
hideLegendItem: true // Hide it!
}, {
type: "bar",
label: "sv-SE",
backgroundColor: "#683e3a",
data: [1, 2]
}]
},
options: {
hideLegendItemsPlugin : true, // Enable the plugin
scales: {
yAxes: [{
ticks: {
min: 0
}
}]
}
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart-container"></canvas>
If you want to have the chart figure-out label uniqueness, you can build a set of series names behind the scenes.
I also added an option to specify priority of series type i.e. bar, line, etc... If you do not specify a priority, the last appearance of the label will take priority. This works in your example, because the bar series are after the line series. Obviously this can be tweaked a bit.
var hideLegendItemsPlugin = {
beforeInit: function(chartInstance) {
let pluginOptions = chartInstance.options.hideLegendItemsPlugin;
if (pluginOptions) {
if (pluginOptions.priority) {
const labelMap = chartInstance.data.datasets.reduce((ret, dataset) => {
return Object.assign(ret, {
[dataset.label] : (ret[dataset.label] || []).concat(dataset.type)
});
}, {});
chartInstance.options.legend.labels.filter = (item, chart) => {
let dataset = chart.datasets[item.datasetIndex];
if (dataset.type !== pluginOptions.priority) {
if (labelMap[dataset.label].includes(pluginOptions.priority)) {
return false;
}
}
return true;
};
} else {
// Default prioritization is the last index (appearance) of that label.
const labelMap = chartInstance.data.datasets.reduce((ret, dataset, index) => {
return Object.assign(ret, { [dataset.label] : index });
}, {});
chartInstance.options.legend.labels.filter = (item, chart) => {
let dataset = chart.datasets[item.datasetIndex];
return item.datasetIndex === labelMap[dataset.label];
};
}
chartInstance.options.legend.onClick = function(e, legendItem) {
let chart = this.chart;
let index = legendItem.datasetIndex;
let visible = !chart.getDatasetMeta(index).hidden;
chart.data.datasets.forEach((dataset, i) => {
if (dataset.label === legendItem.text) {
chart.getDatasetMeta(i).hidden = visible;
}
});
chart.update();
};
}
}
};
Chart.pluginService.register(hideLegendItemsPlugin);
const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());
function getConfig() {
return {
type: 'bar',
data: {
labels: ["Mar-20", "Apr-20"],
datasets: [{
type: "line",
label: "en-US",
borderColor: "#8c856f",
data: [2, 3],
borderWidth: 2,
fill: false
}, {
type: "bar",
label: "en-US",
backgroundColor: "#beb391",
data: [2, 3]
}, {
type: "line",
label: "sv-SE",
borderColor: "#b3cbaa",
data: [1, 2],
borderWidth: 2,
fill: false,
hideLegendItem: true
}, {
type: "bar",
label: "sv-SE",
backgroundColor: "#683e3a",
data: [1, 2]
}]
},
options: {
// This could also be simply `true` instead of an object.
hideLegendItemsPlugin : {
priority : 'bar'
},
scales: {
yAxes: [{
ticks: {
min: 0
}
}]
}
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart-container"></canvas>

Bar Chart different Bars in the xAxis

I'm testing some charts libs and now I'm using Chartjs and I want to create A bar chart and on this chart I want to make each bar has your on x position value and a legend to it.
Something like this: https://codepen.io/mmoskorz/pen/maPExb, but in this case the chart left space for the other bars.
var ctx = document.getElementById("mybarChart").getContext("2d");
var mybarChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['1', '2', '3'],
datasets: [{
label: 'Candidate A Votes',
backgroundColor: "#000080",
data: [90,0,0]
}, {
label: 'Candidate B Votes2',
backgroundColor: "#d3d3d3",
data: [0,70,0]
}, {
label: 'Candidate C Votes3',
backgroundColor: "#add8e6",
data: [0,0,45]
}]
},
options: {
legend: {
display: true,
position: 'top',
labels: {
fontColor: "#000080",
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
Only one X position doesn't satisfy my problem: https://codepen.io/mmoskorz/pen/aPNmNx
var ctx = document.getElementById("mybarChart").getContext("2d");
var mybarChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['1'],
datasets: [{
label: 'Candidate A Votes',
backgroundColor: "#000080",
data: [90]
}, {
label: 'Candidate B Votes2',
backgroundColor: "#d3d3d3",
data: [70]
}, {
label: 'Candidate C Votes3',
backgroundColor: "#add8e6",
data: [45]
}]
},
options: {
legend: {
display: true,
position: 'top',
labels: {
fontColor: "#000080",
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
I tried to put values to x and y, but not work as well: https://codepen.io/mmoskorz/pen/OrNRXp
var ctx = document.getElementById("mybarChart").getContext("2d");
var mybarChart = new Chart(ctx, {
type: 'bar',
data: {
datasets: [{
label: 'Candidate A Votes',
backgroundColor: "#000080",
data: [{x:90, y:1}]
}, {
label: 'Candidate B Votes2',
backgroundColor: "#d3d3d3",
data: [{x:70, y:2}]
}, {
label: 'Candidate C Votes3',
backgroundColor: "#add8e6",
data: [{x:45, y:3}]
}]
},
options: {
legend: {
display: true,
position: 'top',
labels: {
fontColor: "#000080",
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});

Mark X value on Chart Js graph

I have this chart:
And I want to mark a given X value for the user. For example I want something like this:
The chart code:
var chart = new Chart(document.getElementById("featureChart"), {
type: 'scatter',
data: {
datasets: [{
label: "Benign_Cross_Entropy",
data: customize_date
}]
},
options: {
responsive: true
}
});
How can I do it ? Thanks
Edit: I'm trying to use annotation and i'm not sure what is wrong.
The example beaver gave in the comments looks good
var ann = [1];
var ann_values = ["your data"];
var annotations_array = ann.map(function(date, index) {
return {
type: 'line',
id: 'vline' + index,
mode: 'vertical',
scaleID: 'x-axis-0',
value: date,
borderColor: 'green',
borderWidth: 1,
label: {
enabled: true,
position: "center",
content: ann_values[index]
}
}
});
var chart = new Chart(document.getElementById("featureChart"), {
type: 'scatter',
data: {
datasets: [{
label: "Benign_Cross_Entropy",
data: customize_date,
backgroundColor: "rgba(75,192,192,0.4)",
borderColor: "rgba(75,192,192,1)",
borderCapStyle: 'butt',
borderJoinStyle: 'miter'
// pointBorderColor: "rgba(75,192,192,1)",
// pointBackgroundColor: "#fff"
}
]
},
options: {
responsive: true,
elements: { point: { radius: 0 } },
annotation: {
drawTime: 'afterDatasetsDraw',
annotations: annotations_array,
}
}
});
Here is the correct usage of annotation plugin:
var ann = [1];
var ann_labels = ["your data"];
var annotations_array = ann.map(function(value, index) {
return {
type: 'line',
id: 'vline' + index,
mode: 'vertical',
scaleID: 'x-axis-0',
value: value,
borderColor: 'red',
borderWidth: 2,
label: {
enabled: true,
position: "center",
content: ann_labels[index]
}
}
});
console.log(annotations_array)
var data = [{
x: 0,
y: 5
}, {
x: 1,
y: 6
}, {
x: 2,
y: 8
}, {
x: 3,
y: 9
}];
var chart = new Chart(document.getElementById("ctx"), {
type: 'scatter',
data: {
datasets: [{
label: "Benign_Cross_Entropy",
data: data,
borderWidth: 2,
showLine: true,
backgroundColor: "rgba(75,192,192,0.4)",
borderColor: "rgba(75,192,192,1)",
//borderCapStyle: 'butt',
//borderJoinStyle: 'miter'
// pointBorderColor: "rgba(75,192,192,1)",
// pointBackgroundColor: "#fff"
}]
},
options: {
responsive: true,
//elements: { point: { radius: 0 } },
annotation: {
drawTime: 'afterDatasetsDraw',
annotations: annotations_array,
},
scales: {
xAxes: [{
type: 'linear',
id: 'x-axis-0',
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-annotation/0.5.5/chartjs-plugin-annotation.min.js"></script>
<canvas id="ctx"></canvas>
Here is a jsfiddle as well: https://jsfiddle.net/beaver71/e3b1Ldms/

Categories

Resources