This question already has an answer here:
NVD3 - How to refresh the data function to product new data on click
(1 answer)
Closed 7 years ago.
I try to create a living line chart. I always show a fixed number of points adding a new one means removing an old one. To do this I use an interval timer to redraw the chart.
This works quite nice until I run the profiler and have a look at the memory consumption. This chart consumes a lot of memory and more and more for every step. I cannot see an obvious reason because the data is shift() out of the array after a new value is push() in.
var data = [{
"key" : "Long",
"values" : getData()
}];
var chart;
function redraw() {
nv.addGraph(function() {
var chart = nv.models.lineChart().margin({
left : 100
})
//Adjust chart margins to give the x-axis some breathing room.
.useInteractiveGuideline(true) //We want nice looking tooltips and a guideline!
//.transitionDuration(350) //how fast do you want the lines to transition?
.showLegend(true) //Show the legend, allowing users to turn on/off line series.
.showYAxis(true) //Show the y-axis
.showXAxis(true);
//Show the x-axis
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 getData() {
var arr = [];
var theDate = new Date(2012, 01, 01, 0, 0, 0, 0);
for (var x = 0; x < 30; x++) {
arr.push({
x : new Date(theDate.getTime()),
y : Math.random() * 100
});
theDate.setDate(theDate.getDate() + 1);
}
return arr;
}
setInterval(function() {
var long = data[0].values;
var next = new Date(long[long.length - 1].x);
next.setDate(next.getDate() + 1)
long.shift();
long.push({
x : next.getTime(),
y : Math.random() * 100
});
redraw();
}, 1500);
What's wrong?
Thanks to #shabeer90 hint I found the solution. I just had to call the following method after the chart has been constructed.
function update() {
var data = getData();
// Update the SVG with the new data and call chart
chartData.datum(data).transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
};
And that's it!
Related
I need to figure out this bug, my dc.js chart works just fine, except when I add negative values to the stacked bar-chart. Instead of starting from y=0 it starts from the top of the stacked one and messes up the chart.
function grid (selector,data) {
var ndx = crossfilter(data),
all = ndx.groupAll();
var bar_bank = dc.barChart(selector + " .bank");
var bank = ndx.dimension(function(d) {
if (typeof d.bank == "undefined") return "";
return d.bank;
});
var bankGroupBuy = bank.group().reduceSum(function(d) {
return d.buy; });
var bankGroup = bank.group().reduceSum(function(d) {
return d.sell); });
bar_bank
.width(444)
.height(300)
.outerPadding(22)
.gap(1)
.x(d3.scaleBand())
.y(d3.scaleLinear())
.xUnits(dc.units.ordinal)
.brushOn(false)
.elasticY(true)
.yAxisLabel("#BARCHART")
.dimension(bank)
.group(bankGroupBuy)
.stack(bankGroupSell)
;
dc.renderAll();
}
This renders:
but when i
return -d.sell
This happens:
Any ideas how I can fix it? So the stacked negative one starts from 0 and goes down, and does not start from the top of the first one?
Thanks in advance!
This is my csv:
time,bank,buy,sell,AVG_vol_price
2019-02-11,AVA,26378,138177,1.688
2019-02-11,NON,19340,13500,1.683
2019-02-11,SFK,0,43,1.74
2019-02-11,SHB,11300,498,1.692
2019-02-11,SWB,101200,6000,1.689
2019-02-12,AVA,125612,138612,1.683
2019-02-12,ENS,5000,0,1.702
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
I'm using nvd3, but I think this is a general d3.js question about time scale and formatting. I've created a simple example that illustrates the problem (see code below):
If I omit .tickFormat for the xAxis, it works fine without date formatting. With the example below I get the error:
Uncaught TypeError: Object 1326000000000 has no method 'getMonth'
nv.addGraph(function() {
var chart = nv.models.lineChart();
chart.xAxis
.axisLabel('Date')
.rotateLabels(-45)
.tickFormat(d3.time.format('%b %d')) ;
chart.yAxis
.axisLabel('Activity')
.tickFormat(d3.format('d'));
d3.select('#chart svg')
.datum(fakeActivityByDate())
.transition().duration(500)
.call(chart);
nv.utils.windowResize(function() { d3.select('#chart svg').call(chart) });
return chart;
});
function days(num) {
return num*60*60*1000*24
}
/**************************************
* Simple test data generator
*/
function fakeActivityByDate() {
var lineData = [];
var y = 0;
var start_date = new Date() - days(365); // One year ago
for (var i = 0; i < 100; i++) {
lineData.push({x: new Date(start_date + days(i)), y: y});
y = y + Math.floor((Math.random()*10) - 3);
}
return [
{
values: lineData,
key: 'Activity',
color: '#ff7f0e'
}
];
}
The example (now fixed) is in nvd3 with date axis.
Try creating a new Date object before the tick for the x-axis gets passed to the formatter:
.tickFormat(function(d) { return d3.time.format('%b %d')(new Date(d)); })
See the documentation for d3.time.format to see how you can customize the formatting string.
Adding on to seliopou's answer, to correctly align the dates with the x-axis, try this:
chart.xAxis
.tickFormat(function(d) {
return d3.time.format('%d-%m-%y')(new Date(d))
});
chart.xScale(d3.time.scale()); //fixes misalignment of timescale with line graph
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.
I use the Raphael .mouseover() and .mouseout() events to highlight some elements in my SVG.
This works fine, but after I click on an element, I want it to stop highlighting.
In the Raphael documentation I found :
To unbind events use the same method names with “un” prefix, i.e. element.unclick(f);
but I can't get this to work and I also don't understand the 'f' parameter.
This doesn't work , but what does??
obj.click( function() {
this.unmouseover();
});
Ok, what you have to do is pass the handler function to the unmouseover request:
// Creates canvas 320 × 200 at 10, 50
var paper = Raphael(10, 50, 320, 200);
// Creates circle at x = 50, y = 40, with radius 10
var circle = paper.circle(50, 40, 10);
// Sets the fill attribute of the circle to red (#f00)
circle.attr("fill", "#f00");
// Sets the stroke attribute of the circle to white
circle.attr("stroke", "#fff");
var mouseover = function (event) {
this.attr({fill: "yellow"});
}
var mouseout = function (event) {
this.attr({fill: "red"});
}
circle.hover(mouseover, mouseout);
circle.click(function (event) {
this.attr({fill: "blue"});
this.unmouseover(mouseover);
this.unmouseout(mouseout);
});
http://jsfiddle.net/GexHj/1/
That's what f is about. You can also use unhover():
circle.click(function (event) {
this.attr({fill: "blue"});
this.unhover(mouseover, mouseout);
});
http://jsfiddle.net/GexHj/2/