Scenario: I need to show a bar chart about payment details, how much the customers paid for a product using Card, Cheque or Cash. And also need to display the total value in a stacked bar chart.
var chart = c3.generate({
data: {
columns: [
['Cash', 30, 200, 100, 400, 150, 250],
['Card', 130, 100, 140, 200, 150, 50],
['Total', 160,300,240,600,300,300]
],
groups:[['Cash','Card']],
type: 'bar'
}
});
Output:
I need to show the Total value but not the bar and the legend. How can do this? Any advice would be helpful. Thank you.
You don't need the total dataset, you can alter the tooltip contents to calculate it on the fly (adapting the technique found in the answer here -> https://stackoverflow.com/a/36789099/368214)
var chart = c3.generate({
data: {
columns: [
['Cash', 30, 200, 100, 400, 150, 250],
['Card', 130, 100, 140, 200, 150, 50],
],
groups:[['Cash','Card']],
type: 'bar'
},
tooltip : {
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
var total = d.reduce (function(subTotal,b) { return subTotal + b.value; }, 0);
d.push ({value: total, id: "Total", name: "Total", x:d[0].x , index:d[0].index});
return this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color);
}
}
});
The total value is calculated using .reduce to sum all the other values at that point. Then make a new data point for the tooltip called 'Total' using that value and pass it to the default renderer for drawing (this.getTooltipContent)
http://jsfiddle.net/vz1rwvwn/
Related
When hovering over a line in billboardjs you can see a marker which follows the mouse (a tall vertical line). Is there a function for putting a marker on the x-line which can be used without triggering an automatic marker via onmousemove/hovering over data-points?
var chart = bb.generate({
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 50, 20, 10, 40, 15, 25]
],
type: "line", // for ESM specify as: line()
},
bindto: "#lineChart"
});
https://naver.github.io/billboard.js/demo/#Chart.LineChart
So to exemplify. I use an onclick (in the data object) in the chart which defocuses the view and I still want the marker to remain.
So the code would look something like:
var chart = bb.generate({
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 50, 20, 10, 40, 15, 25]
],
type: "line", // for ESM specify as: line()
onclick: function (d) {
focusElsewhere()
showMarker(d.x)
}
},
bindto: "#lineChart"
});
So the question is if there is a function for this, or an obvious fix?
I have looked through https://naver.github.io/billboard.js/release/latest/doc/Chart.html but I may of course have missed something.
I found that using xgrids did the trick. I don't think that the documentation gives a good example of how to use it. But basically you can use the "value" field to give which point the line should be on and add a class to show different kinds of lines.
var chart = bb.generate({
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 50, 20, 10, 40, 15, 25]
],
type: "line", // for ESM specify as: line()
onclick: function (d) {
focusElsewhere()
this.xgrids.add({ value: d.x, class: "hover-line" }); //showMarker(d.x)
}
},
bindto: "#lineChart"
});
To remove the line or reset the billboard for continued use so to say, you can use
xgrids․remove({}) and add an object with some parameters of what kind of lines you want to remove.
I need to visualize the bar chart with Javascript. So I'm used C3 JS to visualize the chart. Simply I'm using this Bar chart as follows,
<div id="chart"></div>
<script>
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
],
axes: {
data2: 'y2'
},
types: {
data2: 'bar'
}
},
axis: {
y: {
label: {
text: 'Y Label',
position: 'outer-middle'
},
tick: {
format: d3.format("$,") // ADD
}
},
y2: {
show: true,
label: {
text: 'Y2 Label',
position: 'outer-middle'
}
}
}
});
</script>
But the problem is when X-axis consists of the large data set Chart getting cramped. I have more than 700 data.
Have any possible way to avoid this? can I add scroll bar between primary and secondary X-axis?
Google, such a wonderfull thing ;)
https://c3js.org/samples/interaction_zoom.html
or this
C3 / D3 bar chart with horizontal scroll
Using point show as false hides all the data points.
But what to do if I want to hide all but one datapoint.
For Example,
var chart = c3.generate({
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
});
In the above line chart, how to highlight the data point where value is 100 and hide every other data point?
In C3, those circles have a class named c3-circle. So, using a D3 selection, you can set the opacity based on the bound datum:
var circles = d3.selectAll(".c3-circle")
.style("opacity", function(d){
return d.value === 100 ? 1 : 0;
})
Thus, only the circle corresponding to 100 is visible.
Here is the demo:
var chart = c3.generate({
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
});
var circles = d3.selectAll(".c3-circle").style("opacity", function(d){
return d.value === 100 ? 1 : 0;
})
<script src="https://d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://rawgit.com/masayuki0812/c3/master/c3.css">
<script src="https://rawgit.com/masayuki0812/c3/master/c3.js"></script>
<div id="chart"></div>
I'm using C3.js library to create a stacked bar chart (my current code is in jsfiddle on the bottom). The problem is that by default the columns are... stacked. Since I need to create columns with min, average and max values, I'd like the values to rather contain each other, not stack. E.g. if I have min = 10, average = 50 and max = 100, I'd like the bar to be of the height 100, not 160. Is there any built in way to support such behavior?
My current code:
<div id="chart"></div>
<script>
var chart = c3.generate({
bindTo: '#chart',
data: {
columns: [
['min', 10, 25, 15],
['avg', 50, 33, 51],
['max', 100, 75, 200]
],
type: 'bar',
groups: [
['min', 'avg', 'max']
]
}
});
</script>
Here is a jsfiddle with my code:
https://jsfiddle.net/gguej6n0/
Right, I'm adding this as another answer as it's completely different plus if I changed my original answer the comments would make no sense..
This time I'm taking advantage of err... feature (bug?) found in c3 that caused another user to unintentionally get the effect that you wanted.
c3.js generate a stacked bar from JSON payload
Basically, if you supply the data as json you can get the effect you want if you supply each datum as a separate point
e.g. doing this will overplot min and max on the same column even if they are meant to be stacked
[{ "x-axis": "0",
"min": 30
},
{ "x-axis": "0",
"max": 40
}],
Whereas, this way will stack them:
[{ "x-axis": "0",
"min": 30,
"max": 40
}],
So what we need is a routine to turn the original column-based data into a json object where every datum is parcelled up separately:
http://jsfiddle.net/gvn3y0q6/5/
var data = [
['min', 10, 25, 15, 12],
['avg', 50, 33, 51, 24],
['max', 100, 75, 200, 76]
];
var json = [];
data.forEach (function (datum) {
var series = datum[0];
for (var i = 1; i < datum.length; i++) {
var jdatum = {"x-axis": i-1};
jdatum[series] = datum[i];
json.push (jdatum);
}
});
var chart = c3.generate({
data: {
x: "x-axis",
json:json,
keys: {
x: "x-axis",
value: ["max", "avg", "min"]
},
groups: [
['max', 'avg', 'min']
],
type: 'bar',
},
bar: {
width: {
ratio: 0.95
}
},
axis: {
x: {
padding: {
left: 0.5,
right: 0.5,
}
}
},
tooltip: {
grouped: true,
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
var data = this.api.data.shown().map (function(series) {
var matchArr = series.values.filter (function (datum) {
return datum.value != undefined && datum.x === d[0].x;
});
matchArr[0].name = series.id;
return matchArr[0];
});
return this.getTooltipContent(data, defaultTitleFormat, defaultValueFormat, color);
}
}
});
This time, hiding a series doesn't affect the other series. There's still some tooltip jiggery-pokery needed though otherwise we only get one of the values reported in the tooltip when hovering over one of the 'stacks'. It looks like the data includes a lot of empty value points, which leads me to think this is a bug of some sort I'm taking advantage of here..
So bearing in mind this might get fixed at some point in the future (or maybe left in, if someone points out it's useful for doing this sort of plot) - then this seems to do what you want
I'm not aware of anything built-in that will make the bars merge into one another as you describe, but could a side-by-side view help? If so, remove the grouping.
See https://jsfiddle.net/gguej6n0/1/
var chart = c3.generate({
bindTo: '#chart',
data: {
columns: [
['min', 10, 25, 15],
['avg', 50, 33, 51],
['max', 100, 75, 200]
],
type: 'bar'
},
bar: {
width: {
ratio: 0.3
}
}
});
I don't think c3 has an 'overplot' bars option, but you could massage your data...
Basically process the data beforehand so max is actually max - avg, and avg is actually avg - min
Then the tooltip routine restores the correct totals for showing to a user
Just be careful if you pass the data onto anything else and remember the data has been changed and restore it (or keep a copy)
https://jsfiddle.net/gguej6n0/5/
var data = [
['min', 10, 25, 15],
['avg', 50, 33, 51],
['max', 100, 75, 200]
];
for (var n = data.length-1; n > 0; n--) {
for (var m = 1; m < data[n].length; m++) {
data[n][m] -= data[n-1][m];
}
}
var chart = c3.generate({
bindTo: '#chart',
data: {
columns: data,
type: 'bar',
groups: [
['min', 'avg', 'max']
]
},
tooltip: {
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
var dc = d.map (function (dd) {
return {value: dd.value, x: dd.x, id: dd.id, name: dd.name, index: dd.index};
})
for (var n= 1; n < dc.length; n++) {
dc[n].value += dc[n-1].value;
}
return this.getTooltipContent(dc, defaultTitleFormat, defaultValueFormat, color);
}
}
});
If I have a C3JS grouped bar chart defined like the following, how can I get the segments to stay in the order I've defined them instead of in ascending order by value? By default C3 will order them as 5, 10, 40, but I want it to remain as 10, 40, 5.
c3.generate({
bindto: '.active-loads',
data: {
columns: [
['Picking up future', 10],
['Enroute', 40],
['Delivered', 5]
],
type: 'bar',
groups: [
['Picking up future', 'Enroute', 'Delivered']
],
onclick: function(d) {
console.debug(d);
}
},
axis: {
rotated: true,
x: {
show: false
}
}
});
EDIT
Turns out it's as easy as specifying order: null in the data property.
C3js documentation has page for this : http://c3js.org/samples/data_order.html
You can order your data in following way :
var chart = c3.generate({
data: {
columns: [
['data1', 130, 200, 320, 400, 530, 750],
['data2', -130, 10, 130, 200, 150, 250],
['data3', -130, -50, -10, -200, -250, -150]
],
type: 'bar',
groups: [
['data1', 'data2', 'data3']
],
order: 'desc' // stack order by sum of values descendantly.
// order: 'asc' // stack order by sum of values ascendantly.
// order: null // stack order by data definition.
},
grid: {
y: {
lines: [{value:0}]
}
}
});
Also detailed explanation here too : http://c3js.org/reference.html#data-order
You can specify your function too :)