Dojo Chart issues in column chart with positive and negative values - javascript

I am creating Column chart with positive and negative values using dojo chart.Here i need my starting values starts from 100 instead of zero .Suppose My values are above 100 then it shows above 100 of bar and similary if my values is below 100 then it shows below of 100 like negatives values shows. Below is my code for
index.php
dojo.require("dojox.charting.Chart");
dojo.require("dojox.charting.axis2d.Default");
dojo.require("dojox.charting.plot2d.ClusteredColumns");
dojo.require("dojox.charting.themes.Tufte");
dojo.require("dojox.dtl");
dojo.require("dojox.dtl.Context");
charts = [
{
description: "Clustered columns with positive and negative values, readable theme, 1-second animate-in.",
makeChart: function(node){
(new dojox.charting.Chart(node)).
setTheme(dojox.charting.themes.Tufte).
addAxis("x", { fixLower: "minor", fixUpper: "minor", natural: true }).
addAxis("y", {
majorTickStep: 10,
max: 150,
vertical: true, fixLower: "major", fixUpper: "major" }).
addPlot("default", { type: "ClusteredColumns", gap: 10, animate: { duration: 1000 } }).
addSeries("Series A", [ 120, 110, 107, 115, 121 ] ).
addSeries("Series B", [ 80, 90, 75, 85, 62 ] ).
render();
}
},
];
var now = function(){
return (new Date()).getTime();
};
dojo.addOnLoad(function(){
var defaultStyle = { width: "400px", height: "200px" };
var tmpl = new dojox.dtl.Template(dojo.byId("template").value);
var context = new dojox.dtl.Context({ charts: charts });
dojo.byId("charts").innerHTML = tmpl.render(context);
dojo.forEach(charts, function(item, idx){
var start = now();
var n = dojo.byId("chart_"+idx);
dojo.style(n, item.style||defaultStyle);
item.makeChart(n);
console.debug((now()-start), "ms to create:", (idx+1)+":", item.description);
});
So request to you please suggest how can i set my axis label form 100 instead of 0.So that i can configure my charts

In your code u can add this parameters.
.addAxis("y", {
dropLabels : true,
min : 10,
max : 20,
from: 0,
to: 15
});

Related

How to customise distance between tickers on the 2nd y-axis in BokehJS

So I'm trying to create a plot with BokehJS. This plot needs 3 axes (left, right, bottom):
The plot looks like this
As you can see, the right y-axis is pretty ugly, you can't read any data from towards the bottom as the tickers are bunched up.
This is how im creating my plot and lines
function zip(arr1, arr2, floatCast){
var out = {};
if (floatCast){
arr1.map( (val,idx)=>{ out[parseFloat(val)] = arr2[idx]; } );
}
else{
arr1.map( (val,idx)=>{ out[val] = arr2[idx]; } );
}
return out;
}
function createBokehPlot(PNVibe, staticPN, transX, transY){
//empty the previous plot
$( "#graph-div" ).empty();
//Data Source for PN under vibration vs. offset frequency
const source = new Bokeh.ColumnDataSource({
data: { x: Object.keys(PNVibe), y: Object.values(PNVibe) }
});
//Data source for Static PN vs offset frequency
const source1 = new Bokeh.ColumnDataSource({
data: { x: Object.keys(staticPN), y: Object.values(staticPN) }
});
//Data source for Transmissibility line
const source2 = new Bokeh.ColumnDataSource({
data: { x: transX, y: transY}
});
//Set plot x/y ranges
var max_offset = Math.max(Object.keys(PNVibe));
const xdr = new Bokeh.Range1d({ start: 1, end: 100000 });
const ydr = new Bokeh.Range1d({ start: -180, end: -50 });
const y2_range = new Bokeh.Range1d({start: 0.001, end: 10});
// make a plot with some tools
const plot = Bokeh.Plotting.figure({
title: 'Example of random data',
tools: "pan,wheel_zoom,box_zoom,reset,save",
toolbar_location: "right",
toolbar_sticky: false,
height: 600,
width: 700,
outerWidth: 800,
legend_location: "top_left",
x_range: xdr,
y_range: ydr,
x_axis_type:"log",
x_axis_label: "Offset Frequency (Hz)",
y_axis_type: "linear",
y_axis_label: "Phase Noise (dBc/Hz)",
extra_y_ranges: {y2_range},
major_label_standoff: 1
});
//Add the second y axis on the right
const second_y_axis = new Bokeh.LogAxis({y_range_name:"y2_range", axis_label:'Vibration Profile (g^2/Hz)', x_range: xdr, bounds:[0.0001, 10]});
second_y_axis.ticker = new Bokeh.FixedTicker({ticks: [0.0001, 0.001, 0.01, 0.1, 1, 10]})
plot.add_layout(second_y_axis, "right");
// add line for vibraiton phase noise
plot.line({ field: "x" }, { field: "y" }, {
source: source,
line_width: 2,
line_color: "red",
legend_label: "Phase Noise under Vibrations"
});
//add line for static phase noise
plot.line({ field: "x" }, { field: "y" }, {
source: source1,
line_width: 2,
line_color: "blue",
legend_label: "Static Phase Noise"
});
plot.line({ field: "x" }, { field: "y" }, {
source: source2,
line_width: 2,
line_color: "green",
y_range_name:"y2_range",
legend_label: "Transmissibillity"
});
// show the plot, appending it to the end of the current section
Bokeh.Plotting.show(plot, "#graph-div");
return;
}
//Call function
var PNVibe = zip([10, 100, 1000], [-95, -100, -105], false);
var staticPN = zip([10, 100, 1000], [-90, -105, -110], false);
var transX = [10, 100, 1000];
var transY = [0.0005, 0.003, 0.05];
createBokehPlot(PNVibe, staticPN, transX, transY);
My question is, how would I be able to make it so that the right y-axis displays better? Preferably I want each tick to be the same distance from each other (ie. space between 10^0 and 10^1 is the same as space between 10^1 and 10^2)
Thanks
I also posted this on the bokeh forums:Here
Fixed my problem:
I believe I may have had some invalid javascript in my original code which is why it wasnt correctly rendering. After hours of messing around with whatever I could think of, it's fixed.
//Define range for y2
const y2_range = new Bokeh.Range1d({start: 0.0001, end: 10}); //Cannot use //array for this
// make a plot with some tools
const plot = Bokeh.Plotting.figure({
title: 'Example of random data',
tools: "pan,wheel_zoom,box_zoom,reset,save,hover",
toolbar_location: "right",
toolbar_sticky: false,
height: 600,
width: 700,
outerWidth: 800,
legend_location: "top_left",
x_range: [1, 1000000],
y_range: [-180, -70],
x_axis_type:"log",
x_axis_label: "Offset Frequency (Hz)",
y_axis_label: "Phase Noise (dBc/Hz)",
extra_y_ranges: {"y2_range": y2_range}, //This was incorrect
extra_y_scales: {"y2_range": new Bokeh.LogScale()}, //This was incorrect
major_label_standoff: 5,
});
//Add the second y axis on the right
const second_y_axis = new Bokeh.LogAxis({
y_range_name:"y2_range",
axis_label:'Vibration Profile (g^2/Hz)',
x_range: [1, 1000000],
bounds:[0.0001, 10],
});
plot.add_layout(second_y_axis, "right");

C3 js: Is it possible to align 2 points from end to end?

this is my first question here in stack overflow.
I'm working with C3 and I'm wondering if is possible to align 2 points from end to end.
for example if in my chart I have 100 and another point -100, but I want to pass through the columns that appears in the image.
let me show you what I want:
I want to join/align the two-point that appears in the circle highlighted:
here is my code:
var chart = c3.generate({
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", -100],
["data3", null, null, null, null, null, 100],
],
axis: {
y: {
max: 365,
min: 335,
},
},
type: "bar",
types: {
data2: "line",
data3: "line",
},
colors: {
data1: "#f567",
data2: "blue",
},
bar: {
width: {
ratio: 0.5, // this makes bar width 50% of length between ticks
},
},
labels: {
format: function (v, id, i, j) {
return v;
},
},
// or
//width: 100 // this makes bar width 100px
},
});
thank you for your help! :)
I just added this part and it works
line: {
connectNull: true
},

Is there a way to scale c3.js graph if the data is small?

I have data to display on c3.js graph and there is one piece of data which is smaller than other ones.
For exmaple A can by 140 000, B 20 000 and C 700. C can even be negative.
The problem is that when I display my data on the chart you can't really see the dynamics of C because its' values are too little for scale of the graph. If C is negative it doesn't show on the graph at all.
Is there some way to scale the graph that way so all data displayed properly?
const chartConfig = {
bindto: $element.find('.canvas')[0],
data: {
json: [],
keys: { value: [] },
type: 'line'
},
grid: {
x: {
get: getXGrid
}
},
axis: {
y: { min: 0 },
x: {
tick: {
culling: false,
format: formatXAxis
}
},
y2: {
padding: {top: 0, bottom: 0},
min : 0,
max : 100,
tick : {
format: (value) => `${value}%`
}
}
}
};
Later in one of my methods I set Y max to be the biggest number in data.
chartConfig.axis.y.max = getRoundMax(chartConfig.data);
UPD link
You must be looking for logarithmic scale.
At this moment c3.js does not support for it out-of-the-box.
But there are some workarounds at github:
First, they convert values:
data_test_original = ['data1', 140000, 20000, 700, 10, 100, 1000, 3, 500, 50, 5, 3000]
data_test = ['data1'];
for(var i=1; i<data_test_original.length; i++){
data_test[i] = Math.log(data_test_original[i]) / Math.LN10;
}
Then, they use it alongside with adjusted Y-axis:
c3.generate({
data: {
...
columns: [ data_test ]
},
...
axis : {
y : {
tick: {
format: function (d) { return Math.pow(10,d).toFixed(0); }
}
}
}
});
See full example here or there. Hope this will suit your needs.

C3.js - stacked bar chart - can I make values to contain instead of stack each other?

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);
}
}
});

How do you graph 24 hour times (e.g., 23:01:35) , as y-values, in rickshaw?

I have data that lists sunrise and sunset times for a given datetime. I would like to plot these on two different lines. Example data for August 7, 2015 would be:
Sunrise line:
{"x" : 1438967378, "y" : "05:19:00"}
Sunset line:
{"x" : 1438967378, "y" : "20:33:00"}
I know how to graph datetimes in the x-axis. However, it's not clear how you would go about plotting hours of the day (in a military clock style 22:01:34) for your y-values.
Here the code that I am working with:
$(document).ready(function () {
$('[data-toggle="tooltip"]').tooltip();
var sunrise_data = {{ sunrise_report }}; // {{ variable }} data passed in via Django view
var sunset_data = {{ sunset_report }};
var daylight_data = {{ daylight_report }};
sunrise_data.sort(function (a, b) {
return parseFloat(a.x) - parseFloat(b.x);
});
sunset_data.sort(function (a, b) {
return parseFloat(a.x) - parseFloat(b.x);
});
daylight_data.sort(function (a, b) {
return parseFloat(a.x) - parseFloat(b.x);
});
var seriesData = [sunrise_data, sunset_data, daylight_data];
var graph = new Rickshaw.Graph({
element: document.getElementById("sun_chart"),
renderer: 'multi',
width: $("div#sun_chart").width(),
height: 180,
max: 120,
min: -10,
interpolation: "linear",
dotSize: 4,
series: [{
name: 'sunrise',
data: seriesData.shift(),
color: 'rgba(255, 0, 0, 0.4)',
renderer: 'line'
}, {
name: 'sunset',
data: seriesData.shift(),
color: 'rgba(127, 0, 0, 0.3)',
renderer: 'line'
}, {
name: 'daylight',
data: seriesData.shift(),
renderer: 'line',
color: 'rgba(0, 0, 127, 0.25)'
}
]
});
var timeUnit = {
'name': '12 hour',
'seconds': 3600 * 12,
'formatter': function(d)
{
return moment(d).format('h:mm a');
}
};
var x_axis = new Rickshaw.Graph.Axis.Time( { graph: graph, timeUnit: timeUnit } );
//var y_axis = new Rickshaw.Graph.Axis.Time( { graph: graph, timeUnit: timeUnit } );
graph.render();
var detail = new Rickshaw.Graph.HoverDetail({
graph: graph
});
});
On the Y Axis you will have the timestamp of the day, where the tick values are going to be integer. So the Y-Axis will look like (0, 1, 2, ..., 24).
So 16:30 is going to be 16.5. To do that you can see how to convert time to decimal number.
On the X Axis you will have the whole year from 1st of January until December 31.
Your graph will look like this:
Definitely check https://ptaff.ca/soleil/?lang=en_CA
It will help you cross check if your graphs are right.

Categories