Related
I'd like to show bar chart with categories on x-axis (say months), multiple series and each bar element containing multiple data points (value shown on y-axis, the rest in tooltip).
It's relatively easy to do it using series.data:
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
// Use axis to trigger tooltip
type: 'shadow' // 'shadow' as default; can also be 'line' or 'shadow'
}
},
xAxis: {
type: 'category'
},
yAxis: {
type: 'value'
},
series: [
{
name: 'Series A',
type: 'bar',
label: {
show: true
},
data: [
['Jul', 320, 2],
['June', 119, 4]],
encode: {
tooltip: [0,1,2]
}
}
,
{
name: 'Series B',
type: 'bar',
label: {
show: true
},
data: [
['Jul', 420, 3],
['June', 123, 5]],
encode: {
tooltip: [0,1,2]
}
}
]
};
I'm wondering how it can be refactored best to use dataset? I have two ideas, I wonder what are pros & cons.. Maybe there is other, cleaner way to express it.
Ideally instead of one big array I'd prefer to have array of objects so dimensions are named.
Solution: One dataset, shifting indices
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
// Use axis to trigger tooltip
type: 'shadow' // 'shadow' as default; can also be 'line' or 'shadow'
}
},
legend: {},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category'
//, data: ['Jul', 'June']
},
yAxis: {
type: 'value'
},
dataset: {
source: [
['Jul', 320, 2, 420, 3],
['June', 119, 4, 123, 5]]
},
series: [
{
name: 'Series A',
type: 'bar',
label: {
show: true
},
encode: {
y: 1,
tooltip: [0,1,2]
}
}
,
{
name: 'Series B',
type: 'bar',
label: {
show: true
},
encode: {
y: 3,
tooltip: [0,3,4]
}
}
]
};
With named dimensions it would look like:
dataset: {
source: [
[month:'Jul', SeriesA_dim1: 320, SeriesA_dim2: 2, SeriesB_dim1: 420, SeriesB_dim2: 3],
[month:'Jun', SeriesA_dim1: 119, SeriesA_dim2: 4, SeriesB_dim1: 123 SeriesB_dim2: 5],
},
....
encode: {
y: 'SeriesB_dim1',
tooltip: ['month','SeriesB_dim1','SeriesB_dim2']
}
Solution 2: Multiple datasets
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
// Use axis to trigger tooltip
type: 'shadow' // 'shadow' as default; can also be 'line' or 'shadow'
}
},
legend: {},
xAxis: {
type: 'category'
},
yAxis: {
type: 'value'
},
dataset: [{
source: [
['Jul', 320, 2],
['June', 119, 4]]
},
{
source: [
['Jul', 420, 3],
['June', 123, 5]]
}],
series: [
{
name: 'Series A',
type: 'bar',
label: {
show: true
},
// data: [
// ['Jul', 320, 2],
// ['June', 119, 4]],
encode: {
tooltip: [0,1,2]
}
}
,
{
name: 'Series B',
type: 'bar',
label: {
show: true
},
// data: [
// ['Jul', 420, 3],
// ['June', 123, 5]],
datasetIndex: 1,
encode: {
tooltip: [0,1,2]
}
}
]
};
With named dimensions it would look like:
dataset: [{
source: [
[month:'Jul', dim1: 320, dim2: 2],
[month:'June', dim1: 119, dim2: 4]]
},
{
source: [
[month:'Jul', dim1: 420, dim2: 3],
[month:'June', dim1: 123, dim2: 5]]
}],
....
encode: {
y: 'dim1', // the default would be likely working as well
tooltip: ['month','dim1','dim2']
}
I am testing out HighCharts and ChartJS to see which to use. For Highcharts, I was able to find a hack to create a bar chart that had double grouping on the x-axis.
This is what I want to look like:
I am new to both charting JS options and wonder if there is a way to do this in ChartJS.
I have the datasets something like this:
xAxis: {
categories: [{
name: "Total",
categories: ["2004", "2008", "2012"]
}, {
name: "Lower than 2.50",
categories: ["2004", "2008", "2012"]
}]
},
yAxis: {
min: 0,
title: {
text: 'Percent (%)'
}
},
series: [{
name: 'Male',
data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4]
}, {
name: 'Female',
data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6]
}]
Essentially I need a nest series on the x-axis and I am open to plugins or code from Github to do this.
You can make use of the Plugin Core API. It offers different hooks that may be used for executing custom code. In below code, I use the afterDraw hook to draw the category labels and the delimiter lines.
new Chart('myChart', {
type: 'bar',
plugins: [{
afterDraw: chart => {
let ctx = chart.chart.ctx;
ctx.save();
let xAxis = chart.scales['x-axis-0'];
let xCenter = (xAxis.left + xAxis.right) / 2;
let yBottom = chart.scales['y-axis-0'].bottom;
ctx.textAlign = 'center';
ctx.font = '12px Arial';
ctx.fillText(chart.data.categories[0], (xAxis.left + xCenter) / 2, yBottom + 40);
ctx.fillText(chart.data.categories[1], (xCenter + xAxis.right) / 2, yBottom + 40);
ctx.strokeStyle = 'lightgray';
[xAxis.left, xCenter, xAxis.right].forEach(x => {
ctx.beginPath();
ctx.moveTo(x, yBottom);
ctx.lineTo(x, yBottom + 40);
ctx.stroke();
});
ctx.restore();
}
}],
data: {
labels: ['2004', '2008', '2012', '2016', '2004', '2008', '2012', '2016'],
categories: ['Total', 'Lower than 2.50'],
datasets: [{
label: 'Male',
data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4, 51.2, 51.8],
backgroundColor: 'rgba(124, 181, 236, 0.9)',
borderColor: 'rgb(124, 181, 236)',
borderWidth: 1
},
{
label: 'Female',
data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6, 53.7, 54.6],
backgroundColor: 'rgba(67, 67, 72, 0.9)',
borderColor: 'rgb(67, 67, 72)',
borderWidth: 1
}
]
},
options: {
legend: {
position: 'bottom',
labels: {
padding: 30,
usePointStyle: true
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
max: 80,
stepSize: 20
},
scaleLabel: {
display: true,
labelString: 'Percent (%)'
}
}],
xAxes: [{
gridLines: {
drawOnChartArea: false
}
}]
}
}
});
canvas {
max-width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>
You have to define a second x-axis and play around with the many ticks and gridLines options.
Please take a look at below runnable code and see how it could be done. This is obviously only a draft and needs to optimized and made more generic.
new Chart(document.getElementById('myChart'), {
type: 'bar',
data: {
labels: ['2004', '2008', '2012', '2004', '2008', '2012'],
datasets: [{
label: 'Male',
data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4],
backgroundColor: 'rgba(124, 181, 236, 0.9)',
borderColor: 'rgb(124, 181, 236)',
borderWidth: 1
},
{
label: 'Female',
data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6],
backgroundColor: 'rgba(67, 67, 72, 0.9)',
borderColor: 'rgb(67, 67, 72)',
borderWidth: 1
}
]
},
options: {
legend: {
position: 'bottom',
labels: {
usePointStyle: true
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
max: 80,
stepSize: 20
},
scaleLabel: {
display: true,
labelString: 'Percent (%)'
}
}],
xAxes: [{
gridLines: {
drawOnChartArea: false
}
},
{
offset: true,
ticks: {
autoSkip: false,
maxRotation: 0,
padding: -15,
callback: (v, i) => {
if (i == 1) {
return 'Total';
} else if (i == 4) {
return 'Lower than 2.50';
} else {
return '';
}
}
},
gridLines: {
drawOnChartArea: false,
offsetGridLines: true,
tickMarkLength: 20,
color: ['white', 'white', 'white', 'lightgray', 'white', 'white', 'lightgray']
}
}
]
}
}
});
canvas {
max-width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>
In echarts-3.6.2, when I set position:'end' for markLine, the lable will display at the end of line
markLine: {
data: [{
symbol:"none",
name: 'GOAL',
yAxis: 3.12 ,
label:{
normal:{
show:true,
position:'end'
}
},
lineStyle: {
normal: {
color: '#5C57FF',
width: 2
}
},
}]
},
However, I want to dislay it above the line at the end of the line? How to make it?
Change position value to insideEndTop(see in docs):
markLine: {
data: [{
symbol: "none",
name: 'GOAL',
yAxis: 3.12,
label: {
normal: {
show: true,
position: 'insideEndTop'
}
},
lineStyle: {
normal: {
color: '#5C57FF',
width: 2
}
},
}]
},
hello,do you have any ideas for not using position: 'insideEndTop', I could not upgrade the echarts plugin
I can't help without a crutch/workaround because it's very old version. You need to update the Echarts immediately, it's only right way. Or you can try to simulate markLine with the graphic component, something like below but it's highway to hell.
var myChart = echarts.init(document.getElementById('main'));
var option = {
color: ['rgba(92, 87, 255, 0.3)'],
grid: {
left: 50,
bottom: 50,
},
graphic: [{
type: 'group',
id: 'markLine',
bounding: 'raw',
children: [{
id: 'lineShape',
$action: 'replace',
type: 'line',
invisible: true,
shape: {
x1: 50,
y1: 300,
x2: 120,
y2: 300,
},
style: {
stroke: '#5C57FF',
lineWidth: 2,
},
zlevel: 10,
}, {
type: 'polygon',
$action: 'replace',
id: 'arrowShape',
invisible: true,
scale: [0.5, 0.3],
position: [90, 292.5],
shape: {
points: [
[16, 5],
[16, 47],
[38, 26]
]
},
style: {
fill: '#5C57FF',
}
}, {
type: 'text',
$action: 'replace',
id: 'labelShape',
invisible: true,
style: {
text: 'GOAL: 3.12',
x: -100,
y: 290,
fill: '#5C57FF',
font: 'bolder 12px sans-serif',
},
zlevel: 10,
}],
}],
xAxis: {
data: ["1", "2", "3", "4", "5", "6"]
},
yAxis: {
type: 'value',
max: 50
},
series: [{
name: 'Series',
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
}]
};
myChart.setOption(option);
function renderMarkLine({ instance, yAxisValue, text, speed }){
var currentStep = 0;
var arrowShape = (val) => {
return {
stopCoord: 710, // 525
opts: {
invisible: false,
id: 'arrowShape',
position: [5 + val, yAxisValue - 7.5] // yAxisValue + 7.5
}
}
};
var lineShape = (val) => {
return {
stopCoord: 680, //540
opts: {
id: 'lineShape',
invisible: false,
shape: {
x1: 50,
y1: yAxisValue, // +0
x2: 50 + val,
y2: yAxisValue
}
}
}
};
var labelShape = (val) => {
return {
stopCoord: 660, // 460
opts: {
id: 'labelShape',
invisible: false,
style: {
x: -10 + val,
y: yAxisValue - 10, // 10
fill: '#5C57FF',
font: 'bolder 12px sans-serif'
}
}
}
};
var interval = setInterval(function(){
var graphicData = [];
[arrowShape, lineShape, labelShape].forEach(el => {
if (el(null).stopCoord > currentStep){
graphicData.push(el(currentStep).opts);
}
});
if (graphicData.length === 0) clearInterval(interval);
instance.setOption({ graphic: graphicData });
currentStep += 10;
}, speed);
};
renderMarkLine({ instance: myChart, yAxisValue: 500, speed: 0 });
<script src="https://cdn.jsdelivr.net/npm/echarts#3.6.2/dist/echarts.min.js"></script>
<div id="main" style="width:800px;height:600px;"></div>
I am using echarts for a scatter plot. Upon hover of a point, I want to show some more data in the tooltip, such as a date and unique ID - this data is not an axis in the plot. So far I have the following below, however I cannot figure out how to display the extra info
option = {
xAxis: {
type: 'value',
name: 'X axis title',
nameLocation: 'middle',
nameGap: 50,
nameTextStyle: {
fontSize: 12
}
},
yAxis: {
type: 'value',
name: 'Y axis title',
nameLocation: 'middle',
nameGap: 70,
nameTextStyle: {
fontSize: 12
}
},
dataZoom: [{
type: 'inside'
}],
legend: {
orient: 'vertical',
left: 10
},
grid: {
top: 80,
containLabel: true,
},
tooltip: {
trigger: "item"
},
series: [{
name: 'Outliers (Forensic Cases)',
type: 'scatter',
data: [[161.2, 51.6], [167.5, 59.0], [159.5, 49.2], [157.0, 63.0], [155.8, 53.6]
]],
}, {
name: 'Inliers (Regular Transaction)',
type: 'scatter',
data: [[172.7, 105.2], [153.4, 42]]
}]
};
;
Thanks for config, it saves time.
The tooltip component has method formatter and you can pass to it function-callback where to make request to addition data store to get and show on tooltip necessary information.
Suppose you have an object that stored additional information that needs to be shown in the tooltip:
var store = {
outliers: ['A', 'B', 'C', 'D', 'E', 'F'],
inliers: ['A', 'B', 'C', 'D', 'E', 'F']
}
Let's add id to the each data series, which will simplify data fetching.
{
id: 'outliers', // <--- this
name: 'Outliers (Forensic Cases)',
...
},
{
id: 'inliers', // <--- this
name: 'Inliers (Regular Transaction)',
...
}
In the callback you will have this default args:
{
$vars: ["seriesName", "name", "value"],
borderColor: undefined,
color: "#c23531",
componentIndex: 0,
componentSubType: "scatter",
componentType: "series",
data: [159.5, 49.2],
dataIndex: 2,
dataType: undefined,
dimensionNames: ["x", "y"],
encode: {
x: [0],
y: [1]
},
marker: "<span style=\"display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:#c23531;\"></span>",
name: "",
seriesId: "outliers",
seriesIndex: 0,
seriesName: "Outliers (Forensic Cases)",
seriesType: "scatter",
value: [circular object Array]
}
Let's make the callback handler for the process series data and shown it on the tooltip:
var callback = (args) => {
var data = store[args.seriesId][args.dataIndex]
return args.marker + ' ' + args.value[1] + '<br />' + args.seriesId + ': ' + data
}
Now you can pass callback to formatter. It's all. See example below:
var myChart = echarts.init(document.getElementById('main'));
var store = {
outliers: ['A', 'B', 'C', 'D', 'E', 'F'],
inliers: ['A', 'B', 'C', 'D', 'E', 'F']
}
var callback = (args) => {
var data = store[args.seriesId][args.dataIndex]
return args.marker + ' ' + args.value[1] + '<br />' + args.seriesId + ': ' + data
}
var option = {
xAxis: {
type: 'value',
name: 'X axis title',
nameLocation: 'middle',
nameGap: 50,
nameTextStyle: {
fontSize: 12
}
},
yAxis: {
type: 'value',
name: 'Y axis title',
nameLocation: 'middle',
nameGap: 70,
nameTextStyle: {
fontSize: 12
}
},
dataZoom: [{
type: 'inside'
}],
legend: {
orient: 'vertical',
left: 10
},
grid: {
top: 80,
containLabel: true,
},
tooltip: {
trigger: "item",
formatter: callback,
},
series: [{
id: 'outliers',
name: 'Outliers (Forensic Cases)',
type: 'scatter',
data: [
[161.2, 51.6],
[167.5, 59.0],
[159.5, 49.2],
[157.0, 63.0],
[155.8, 53.6]
],
},
{
id: 'inliers',
name: 'Inliers (Regular Transaction)',
type: 'scatter',
data: [
[172.7, 105.2],
[153.4, 42]
]
}
]
}
myChart.setOption(option);
<script src="https://cdn.jsdelivr.net/npm/echarts#4.9.0/dist/echarts.min.js"></script>
<div id="main" style="width: 600px;height:400px;"></div>
Hope someone can help, I've search examples of this but can't get them to work on my particular setup. I have a stacked column chart in Highcharts representing 5 sets of data for each day of the week.
I then select a day to see an hourly breakdown of the day, I have the data coming through but I can't get the 5 values I have for each hour to stack on top of each other. (in the same way as the first chart) - ideally this second chart would be an area chart)
Here is my code:
$(function () {
Highcharts.setOptions({
lang: {
drillUpText: 'Reset'
}
});
// Create the chart
$('#chart2').highcharts({
chart: {
type: 'column',
height: 300
},
credits: {
enabled: false
},
title: {
text: null
},
subtitle: {
text: 'Select a day to expand to hourly data'
},
legend: {
enabled: false
},
tooltip: {
formatter: function() {
var point = this.point,
s = 'Day: <b>' + point.name + '</b><br/>Utilisation: <b>' + this.y + '% </b><br/>';
if (point.drilldown) {
s = 'Day: <b>' + point.name + '</b><br/>Utilisation: <b>' + this.y + '% </b><br/>Select to view hours';
} else {
s = 'Time: <b>' + point.name + '</b><br/>Utilisation: <b>' + this.y + '% </b><br/>Reset to return';
}
return s;
}
},
xAxis: {
type: 'category',
//categories: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
//categories: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24']
},
yAxis: {
title: false, // {text: 'Utilisation'}, Y axis title - taken text out
tickPositions: [0, 50, 70, 100], // Y axis labels
labels: {
format: '{value}%' // Y axis labels with % suffix
},
min: 0, // Following lines setting the grids to off adding min max
max: 100,
minorGridLineWidth: 0,
gridLineWidth: 0,
alternateGridColor: null,
plotBands: [{ // Below Avg.
from: 0,
to: 50,
color: 'rgba(255,108,96, 0.5)',
label: {
// text: 'Below Average',
style: {
color: 'rgba(153,194,98, 0.8)'
}
}
}, { // Average
from: 50,
to: 70,
color: 'rgba(248,211,71, 0.5)',
label: {
// text: 'Average',
style: {
color: 'rgba(153,194,98, 0.8)'
}
}
}, { // Above Avg.
from: 70,
to: 100,
color: 'rgba(153,194,98, 0.5)',
label: {
text: 'Above Average',
style: {
color: 'rgba(153,194,98, 0.8)'
}
}
}]
},
plotOptions: {
series: {
stacking: 'normal'
}
},
series: [{
type: 'column',
name: 'Status 5',
color: '#86C9F2',
borderWidth: 0,
shadow: true,
data: [<?php echo $data7a5?>]
}, {
type: 'column',
name: 'Status 4',
color: '#6FB2DB',
borderWidth: 0,
shadow: true,
data: [<?php echo $data7a4?>]
}, {
type: 'column',
name: 'Status 3',
color: '#589BC4',
borderWidth: 0,
shadow: true,
data: [<?php echo $data7a3?>]
}, {
type: 'column',
name: 'Status 2',
color: '#4184AD',
borderWidth: 0,
shadow: true,
data: [<?php echo $data7a2?>]
}, {
type: 'column',
name: 'At the Desk',
color: '#2B6E97', //rgb(43, 110, 151)
borderWidth: 0,
shadow: true,
data: [<?php echo $data7a?>]
}],
drilldown: {
drillUpButton: {
//relativeTo: 'spacingBox',
position: {
y: 10,
x: -10
},
theme: {
fill: 'white',
stroke: 'silver',
r: 2,
states: {
hover: {
fill: '#41739D',
style: {
color: 'white'
}
}
}
}
},
series: [{
type: 'column',
id: 'D2',
data: [['8', 13.77],['8', 2.74],['8', 1.27],['8', 2.64],['8', 2.28],['9', 29.30],['9', 6.44],['9', 3.79],['9', 5.11],['9', 5.32],['10', 36.41],['10', 9.01],['10', 5.47],['10', 7.11],['10', 7.06],['11', 34.12],['11', 7.50],['11', 4.48],['11', 10.02],['11', 8.28],['12', 26.82],['12', 5.03],['12', 5.79],['12', 15.00],['12', 10.27],['13', 30.08],['13', 5.40],['13', 5.34],['13', 11.73],['13', 9.57],['14', 33.90],['14', 7.75],['14', 4.78],['14', 6.41],['14', 9.33],['15', 33.27],['15', 7.73],['15', 4.95],['15', 8.11],['15', 7.09],['16', 31.29],['16', 8.53],['16', 4.51],['16', 6.44],['16', 5.81],['17', 17.36],['17', 3.87],['17', 2.06],['17', 4.47],['17', 3.42],['18', 4.79],['18', .38],['18', .79],['18', 1.44],['18', 2.45]]
}, {
type: 'area',
id: 'D3',
data: [<?php echo $data7b2?>]
}, {
type: 'area',
id: 'D4',
data: [<?php echo $data7b3?>]
}, {
type: 'area',
id: 'D5',
data: [<?php echo $data7b4?>]
}, {
type: 'area',
id: 'D6',
data: [<?php echo $data7b5?>]
}]
}
});
});
I've shown the first drilldown data so you can see the structure. Any help would be appreciated.
Thanks
Rob
You can change the type of drill-down data from ['time', data] to [time, data]:
data: [
['8', 13.77],
['8', 2.74],
['8', 1.27],
['8', 2.64],
['8', 2.28],
['9', 29.30],
['9', 6.44],
['9', 3.79],
['9', 5.11],
['9', 5.32],
...
]
to:
data: [
[8, 13.77],
[8, 2.74],
[8, 1.27],
[8, 2.64],
[8, 2.28],
[9, 29.30],
[9, 6.44],
[9, 3.79],
[9, 5.11],
[9, 5.32],
...
]
And I also changed the tooltip.formatter to show the correct tooltip for drill-downs. Here's the DEMO