I have converted a line chart into a cumulative line chart and its y values are not displayed correctly. The range of the y axis should be 80.00 - 140.00 but instead I get -0.08 - 0.20. Has anyone managed to tweak their normalization code below to make it work with all kinds of ranges?
line.values = line.values.map(function(point, pointIndex) {
point.display = {
'y': (lines.y()(point, pointIndex) - v) / (1 + v)
};
return point;
})
Any help will be greatly appreciated.
I know that this question is somewhat old, but I am convinced that the normalization code for the cumulative line chart is not conceptually correct. Furthermore, the NVD3 cumulative line chart implementation is actually an index chart implementation (see Mike Bostock's example). A cumulative line chart would be more like this, I think. The cumulative chart can be easily achieved using the NVD3 line chart and some quick modifications to the underlying data.
If we take Bostock to be correct, and we really do wish to achieve an indexed line chart, then the indexify function in NVD3 should be changed to:
/* Normalize the data according to an index point. */
function indexify(idx, data) {
if (!indexifyYGetter) indexifyYGetter = lines.y();
return data.map(function(line, i) {
if (!line.values) {
return line;
}
var indexValue = line.values[idx];
if (indexValue == null) {
return line;
}
var v = indexifyYGetter(indexValue, idx);
// TODO: implement check below, and disable series if series
// causes a divide by 0 issue
if ((Math.abs(v) < 1e-6) && !noErrorCheck) {
// P.S. You may have to set a higher threshold (~1e-6?).
// I don't know I didn't run any tests...
line.tempDisabled = true;
return line;
}
line.tempDisabled = false;
line.values = line.values.map(function(point, pointIndex) {
point.display = {
'y': (indexifyYGetter(point, pointIndex) - v) / v
};
return point;
});
return line;
})
}
I asked a related question to the authors of NVD3 and plan to submit a pull request. Note that percentage change charts are really only meaningful when all of the underlying data is positive. When you start throwing negative values into the mix, percentage change loses all of its meaning.
What I found works is to insert another point with a y value of 0 at the beginning of the sequence of points.
Given a list of data points in the form [ [x1,y1], [x2,y2], ... [xn,yn]] ],
something like values.upshift([0,0]) works for me (the x value is arbitrary, but i just use 0 or values[0][0]) to insert to the front of the list.
(I'm getting the same thing with that chart. I'm still looking into it, but I hope this helped.)
Related
I am trying to plot a trajectory in real-time using Javascript and Highcharts. The data will come from external sensors but for the moment I was practicing with this example:
http://jsfiddle.net/0fp1gzw8/1/
As you can see, the JS snippet tries to plot a circle using a cosine and a sine function:
load: function () {
var series = this.series[0];
setInterval(function () {
a = a + 0.1;
x = Math.sin(a),
y = Math.cos(a);
series.addPoint([x, y], true);
}, 100);
}
The problem is that once the point has crossed the x axes, the line segment is no more drawn between two consecutive samples, instead it connects the new sample with one of the old ones already plotted before:
How can I solve this and get a clean x-y plot?
Thanks
Highcharts expects spline/line chart data to always be sorted by the x value. With this expectation, when you call addPoint it looks like it draws the line segment to the previous x-value not the previously added point.
If you switch your code to use setData:
var data = [];
var series = this.series[0];
setInterval(function () {
a = a + 0.1;
x = Math.sin(a),
y = Math.cos(a);
data.push([x,y]);
series.setData(data, true);
}, 100);
it draws the line segments correctly but you get lots of these errors in the console:
Highcharts error #15: www.highcharts.com/errors/15
You might have better luck switching to a scatter chart that doesn't have this limitation. If you need the line segments, you could add them yourself with the Renderer.
I am trying to modify the chart http://bl.ocks.org/gniemetz/4618602 (D3.js).
I want remove the date format of the X axis and get numerical values of 'Gesamt' column (data.txt), example:
214-------220--------234---------255 (x axis)
I've tried to remove all occurrences of "formatDate" code, but does not work. What should I change?
Data import is happening here:
d3.csv("data.txt", function(error, data) {
data.forEach(function(d) {
d.Uhrzeit = parseDate(d.Uhrzeit);
d.Durchschn = +d.Durchschn;
d.Anz = +d.Anz;
});
d is each row or observation. If you want to use Gesamt instead of Uhrzeit, this is where you need to make the adjustments.
I am trying to render a dc.js barChart where my y-axis is percentage 0-100% and my x-axis are numbers, however I want to order the x-axis by date.
My data looks like this:
date,trend,percent
01/01/2014 13:00,238,53.6
01/01/2014 13:15,239,64.2
01/01/2014 13:30,219,43.1
01/01/2014 13:45,219.2,43.1
01/01/2014 14:00,237.4,50.6
...
I am adding the data to crossfilter
data.forEach(function (d) { d.date = parseDate(d.date); });
var ndx = crossfilter(data);
var trendDimension = ndx.dimension(function (d) { return d.trend; });
var trendGroup = trendDimension.group().reduce(
function (p, v) {
p.time = v.date.getTime();
p.trend = +v.trend;
p.percent = +v.percent;
return p;
},
...
).order(function (p) { return p.time; }); // ??? order by time rather than trend
When I graph the dimension and group, my x-axis is sorted by trend as my x domain looks like:
var minTrend = trendDimension.bottom(1)[0].trend;
var maxTrend = trendDimension.top(1)[0].trend;
...
chart.x(d3.scale.linear().domain([minTrend, maxTrend]);
...
chart.render();
Everything plots, however the bars a sorted in order of trend and I would like them sorted in order of date/time.
Is this possible?
EDIT
I also tried:
chart.ordering(function (d) { return d.value.time; });
but that does not seem to have an effect on the ordering...
Do you want to graph percent versus trend or percent versus time?
Right now your dimension is on trend, so it will not be possible to order it by date. Crossfilter will create a bin for each trend value (which may have many dates), and the way you have written your reduce function, it will simply replace the date entry for the bin, with the last date it sees.
If you want to order by date and then use trend to affect some other aesthetic (color for example), you should use a date dimension, group by some quantization of the date, not do anything with the date in your reduce, and use date scale/xUnits.
not sure if I am going about this the right way but here goes...
So i have the this example see fiddle here
using lineplusbarchart and i am building on it from this question i posted here:
SO question
I have edited the lineplusbarchart to show the labels on the xaxis:
chart.xAxis.tickFormat(function(d) {
var dx = testdata[0].values[d] && testdata[0].values[d].x || 0;
return dx;
})
.showMaxMin(false);
but i am still having a couple of issues to get what i want...
1 -> how can i make the y1 and y2 axis be alligned? (ideally it would be good if there was only one axis)
2 -> how do i remove the y2 axis? (soution here but this does not work as I then want the 2 axis aligned)
3 -> how do i make the thickness of the barchart part for label1 and label5 to be the same thickness as the others(lable2,3 and 4)?
hope this helps:
you can use chart.lines.forceY() to set a range. To make it
work with dynamic values I'd suggest to find the overall max value of the attached data
and use it for the bar and the lines. Eg:
var maxValue = d3.max(d3.entries(testdata), function(d) {
return d3.max(d3.entries(d.value.values), function(e) {
return e.value.y;
});
}),
minValue = 0;
chart.bars.forceY([minValue, maxValue]);
chart.lines.forceY([minValue, maxValue]);
Your posted solution is exactly what I would do too.
Remove padData()
I have created a beautiful bubble chart using Google Charts. Here is a shot of the chart:
The numbers along the x-axis represent individual customers. The numbers along the y-axis represent individual products. As you all can see, there are about 23 customers and 7 products.
The problem is that the X and Y axes can only be numeric (as far as I know from the documentation). I wish to be able to display the string values for the customers and products along the axes instead of just representative integers. This will make it easier to understand what we are looking at.
Does anyone know how this can be accomplished?
I do have JS arrays which contain the customer and product strings. Their integer indices correspond to the numbers that show up on the chart. For example:
customers[6] = "Microsoft"
customers[7] = "Dell"
...
But right now just the integers show up.
Any help would be greatly appreciated! Thanks!
Here is the code I used to define the chart:
var options = {
'title':'Customer / Product Grid',
'width': 1000,
'height':500
};
//for customer product grid
var customer_product_grid_data_table = new google.visualization.DataTable();
customer_product_grid_data_table.addColumn('string', 'Customer and Product');
customer_product_grid_data_table.addColumn('number', 'Customer');
customer_product_grid_data_table.addColumn('number', 'Product');
customer_product_grid_data_table.addColumn('number', 'Profit Margin');
customer_product_grid_data_table.addColumn('number', 'Proportion of Sales');
for (var i = 1; i < customer_product_grid_data.length; i ++){
customer_product_grid_data_table.addRow([
'',
customer_product_grid_data[i][0],
customer_product_grid_data[i][1],
(customer_product_grid_data[i][3] - customer_product_grid_data[i][2]) / customer_product_grid_data[i][3] * 100,
customer_product_grid_data[i][3] / qnty_sell_total
]);
}
var chart = new google.visualization.BubbleChart(document.getElementById('customer_product_grid'));
chart.draw(customer_product_grid_data_table, options);
Judging from all the searching I did, and also the answer given here by jmac, I decided the only way to go was a Javascript hack to replace the axes numbers with words. The code I implemented is here:
/*
*
* The following 2 functions are a little hacky, they have to be done after calling the "draw" function
* The bubble chart originally displays only numbers along the x and y axes instead of customer or product names
* These 2 functions replace those numbers with the words for the customers and products
*
*/
for ( var i = -2; i < products.length + 1; i ++ ){
$('#customer_product_grid svg text[text-anchor="start"]:contains("'+i+'")').text(function(j,t){
if (t == i){
if (i >= products.length || i < 0){
return " ";
}
return products[i];
}
});
}
for ( var i = -2; i <= customers.length + 3; i ++ ){
$('#customer_product_grid svg text[text-anchor="end"]:contains("'+i+'")').text(function(j,t){
if (i >= customers.length + 1 || i <= 0){
return " ";
}else if (t == i){
return customers[i-1];
}
});
}
Basically, you just make a for loop that iterates through all the integers that you are showing on the x and y axes. Do some if...else stuff to either replace the integer with an element from the array, or just make it blank.
Keep in mind for the above code to work properly, you need to have the following property in the chart options -> vAxis: { textPosition: 'in' }
Unfortunately, there is no easy way to do this as bubble charts (or anything that uses numerical series for an axis value). You can work around it as explained here.
The general concept is to eliminate your axis labels on the 'main chart' and adjust the gridlines to match the amount of labels you want. Then create an additional dummy chart which shows only the categories, and use that to show the labels.
Unfortunately, this is how it has to be until Google decides to implement the entire ICU pattern set for chart axes...