Normalize the Graph axes in NVD3 Line with focus chart - d3.js

I am having two lines in th graph like in the picture having different X axis points. I just want to compare the lines. So I need to bring the second line under the first line (Normalize the graph like the second picture)
I tried to use nvd3 domain and range option and some others too. But its not working. Could you guys tell me how to get those normalized graph. I dont worry about the Tick format as far as the lines are comparable.
PICTURE 1 (the graph I am having)
https://www.dropbox.com/s/sycwppnlachju2s/Screenshot%20from%202014-06-19%2014%3A21%3A40.png
PICTURE 2 (Graph I need)
https://www.dropbox.com/s/eoajf0yyk96w6y7/Screenshot%20from%202014-06-19%2014%3A20%3A21.png
nv.addGraph(function() {
var chart = nv.models.lineWithFocusChart();
chart.xAxis
.tickFormat(d3.format(',f'));
chart.yAxis
.tickFormat(d3.format(',.2f'));
chart.y2Axis
.tickFormat(d3.format(',.2f'));
d3.select('#chart svg')
.datum(data)
.transition().duration(500)
.call(chart)
;
nv.utils.windowResize(chart.update);
return chart;
});
This is my data file. data.json
[{"key":"Memory_FREEmyses1","values":[{"x":"1395426430","y":"200028"},
{"x":"1395426431","y":"199904"},{"x":"1395426432","y":"187620"},
{"x":"1395426434","y":"187504"},{"x":"1395426435","y":"187380"},
{"x":"1395426436","y":"187008"},{"x":"1395426437","y":"186760"},
{"x":"1395426438","y":"186512"},{"x":"1395426439","y":"186388"},
{"x":"1395426440","y":"186264"},{"x":"1395426441","y":"181804"},
{"x":"1395426443","y":"181084"},{"x":"1395426444","y":"181084"}]},
{"key":"Memory_FREEmyses2","values":[{"x":"1395426455","y":"178604"},
{"x":"1395426456","y":"178348"},{"x":"1395426457","y":"178356"},
{"x":"1395426458","y":"178232"},{"x":"1395426460","y":"178108"},
{"x":"1395426461","y":"177860"},{"x":"1395426462","y":"177480"},
{"x":"1395426463","y":"176992"},{"x":"1395426464","y":"176868"},
{"x":"1395426465","y":"176620"},{"x":"1395426466","y":"176612"},
{"x":"1395426467","y":"176620"}]}]

Try to map your data like:
data = data.map(function(series) {
var d0 = series.values[0].x;
var dN = series.values[series.values.length -1].x;
series.values = series.values.map(function(d) {
return {
x: 10*(d.x-d0)/(dN-d0),
y: d.y
}
});
return series;
})
See demo.

Related

Change x-axis range to percent of bullet chart in NVD3

Please help me how can i change x-axis range of bullet chart to percentage?
i have this code of my NVD3.js bullet chart
nv.addGraph(function() {
var chart = nv.models.bulletChart();
chart.tooltip.valueFormatter(function(d){
return ''+ numberWithCommas(d)+''; //on hover show percentage and value
});
d3.select('#chart svg')
.datum(data)
.transition().duration(1000)
.call(chart);
return chart;
});
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
here's my data variable
var data = {
measureLabels: ["Total Obligated"]
measures:[0]
rangeLabels:["Available Amount"]
ranges:[28464000]
subtitle:"CODE"
title:"200000100003000"
}
i have tried to add this code
chart.xAxis.tickFormat(function(d) {
//return ranges * 100 / ranges
});
but it said that 'tickFormat' is not a function.
ok i figure out how to change it LOL!.
i added this code.
d3.selectAll('g.nv-tick').select('text').text(function (t) {
return (100 * t / data.ranges[0]).toFixed();
});
i tooks me half a day to figure it out T_T

Storing ellipse path data in D3js

I've got an application that uses D3js to display ratios as ellipses on a chart. Right now every time the chart is zoomed in and out of, my code recalculates the path of every ellipse. Right now I'm trying to store the projected path data for each ellipse, then just scale that, but I'm having trouble figuring out how to store data like that for each ellipse. Anyone have an idea of how I'd approach it?
The calculations I want to store are in interpolate
var ellipsePath = d3.svg.line()
.x(function (datum) {
return x(datum[0] + d.x);
})
.y(function (datum) {
return y(datum[1] + d.y);
})
.interpolate(function (points) {
var i = 1, path = [points[0][0], ",", points[0][1]];
while (i + 3 <= points.length) {
cubicBezier(path, points[i++], points[i++], points[i++]);
}
return path.join("");
});

Getting graph coordinates from mouse in nvd3

I'm trying to see where in the chart the user clicked. The below code almost works but it is offset by some amount. I suspect I need to handle the click relative to the charting area and not take the axis into account. What is the proper way to do this?
d3.select('#chart1 svg')
.datum(chartData)
.on("click", mouseClick )
.call(chart);
...
function mouseClick()
{
var coordinates = d3.mouse(this);
var x = chart.lines.xScale().invert(coordinates[0]);
var y = chart.lines.yScale().invert(coordinates[1]);
console.log(x+','+y);
}
You just have to subtract the margin:
function mouseClick()
{
var coordinates = d3.mouse(this);
var x = chart.lines.xScale().invert(coordinates[0]-chart.margin().left);
var y = chart.lines.yScale().invert(coordinates[1]-chart.margin().top);
}

nvd3 prevent repeated values on y axis

My chart repeats the values on the y axis. Is there a way I can only have 1,2,3 but nothing between (e.g the problem I have is shown on the chart below on the y axis):
The code im using is something like this:
nv.addGraph(function() {
var chart = nv.models.multiBarChart();
chart.xAxis
.tickFormat(function(d) { return d3.time.format('%x')(new Date(d)) });
chart.yAxis
.tickFormat(d3.format(',.1f'));
d3.select('#errorsFiredByDate')
.datum([{values: output, key: "Count by date"}])
.transition()
.duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
You have a few options to control the ticks in a D3 axis. You can specify the number of ticks explicitly using .ticks(), but this is more of a hint to the layout that may be disregarded. If you want to be absolutely sure about the ticks, use .tickValues() to set the tick values explicitly.
In your case, this would look something like this.
chart.yAxis.tickValues(d3.range(chart.yAxis.scale().domain()[0], chart.yAxis.scale().domain()[1]);
Other methods for figuring out the range may be more suitable, depending on your concrete application.
I was facing the same problem and I did this in chart options to fix
yAxis: {
tickFormat: function(d){
if ( (d * 10) % 10 === 0 ) {
return d3.format('.0f')(d);
}
else {
return '';
}
}
}
A late reply , but might be useful to others. I use a workaround to eliminate duplicate ticks, by maintaining an array of already applied ticks & d3's multi format specifier.
uniqueTicks = [];
xTickFormat = d3.time.format.multi([
["%Y", function (d) {
if (uniqueTicks.indexOf(d.getFullYear()) === -1) {
uniqueTicks.push(d.getFullYear());
return true;
} else {
return false;
}
}],
["", function () {
return true;
}]
]);
d3.svg.axis().tickFormat(xTickFormat );
This trick can be tweaked for any other type of tick format.

NVD3 line chart with realtime data

I have a really simple line chart written using NVD3.js. I've written a simple redraw based on timer, pulled from examples I've seen, but I get the error
Uncaught TypeError: Cannot read property 'y' of undefined
The JS is
var data = [{
"key": "Long",
"values": getData()
}];
var chart;
nv.addGraph(function () {
chart = nv.models.cumulativeLineChart()
.x(function (d) { return d[0] })
.y(function (d) { return d[1] / 100 })
.color(d3.scale.category10().range());
chart.xAxis
.tickFormat(function (d) {
return d3.time.format('%x')(new Date(d))
});
chart.yAxis
.tickFormat(d3.format(',.1%'));
d3.select('#chart svg')
.datum(data)
.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
function redraw() {
d3.select('#chart svg')
.datum(data)
.transition().duration(500)
.call(chart);
}
function getData() {
var arr = [];
var theDate = new Date(2012, 01, 01, 0, 0, 0, 0);
for (var x = 0; x < 30; x++) {
arr.push([theDate.getTime(), Math.random() * 10]);
theDate.setDate(theDate.getDate() + 1);
}
return arr;
}
setInterval(function () {
var long = data[0].values;
var next = new Date(long[long.length - 1][0]);
next.setMonth(next.getMonth() + 1)
long.shift();
long.push([next.getTime(), Math.random() * 100]);
redraw();
}, 1500);
Second Answer (after comment)
I looked at source for cumulativeLineChart. You can see the display.y property get created during chart creation. It relies on a private method: "indexify". If some derivative of that method was made public, then perhaps you could do something like chart.reindexify() before redrawing.
As a temporary workaround, you could recreate the chart from scratch on every update. If you remove the transition, that seems to work okay. Example jsfiddle: http://jsfiddle.net/kaliatech/PGyKF/.
First Answer
I believe there is bug in cumulativeLineChart. It appears that the cumulativeLineChart adds a "display.y" property dynamically to data values in the series. However, it does not regenerate this property when new values are added to the series for a redraw. I don't know of anyway to make it do this, although I'm new to NVD3.
Do you really need a CumulativeLineChart, or would a normal line chart be sufficient? If so, I had to make the following changes to your code:
Change from cumulativeLineChart to lineChart
Change from using 2 dimension arrays of data, to using objects of data (with x,y properties)
(I'm not familiar enough with NVD3 to say what data formats is expects. The 2D array obviously works for initial loads, but I think it fails to work for subsequent redraws. This is likely related to the same issue you are having with cumulativeLineChart. I thought changing to objects would fix cumulativeLineChart as well, but it didn't seem to.)
I also changed the following, although not as important:
Modified your getData function to create a new instance of Date to avoid unexpected consequences of sharing a reference as the date gets incremented.
Modified the update interval function to generate new data in increments of days (not months) with y values in the same range as the getData function.
Here's a working jsfiddle with those changes:
http://jsfiddle.net/kaliatech/4TMMD/
I found what I think is a better solution. The problem occurs because the cumulative chart sets the y function during processing. Whenever your want to refresh the chart, first set it back to a default which returns the correct original y. In your redraw function do this before updating:
chart.y(function (d) { return d.y; });
Even better would be if the cumulative chart could do this for itself (store the original access function before setting the new one, and put it back before re-indexing). If I get a chance, I'll try to push a fix.
I ran into the same issue. I changed the y() function on the lines from
.y(function(d) { return d.display.y })
to
.y(function(d) { return d.display ? d.display.y : d.y })
This gets rid of the error. Obviously it won't be displaying the (non-existent) indexed value in the error case, but in my experience, the chart gets updated again with display defined, and it looks correct.

Resources