How to plot time on the X-axis in non-ugly mode? - nvd3.js

This is ugly:
I need something that is not ugly. Time series are very usual, but I not see how to build a "plug and play" chart with ISO dates.
Perhaps an equivalent question is "How to use d3-scalelinear/Non-numeric range/Date with NVD3?" (or d3-scaletime)
Notes
This is the main code:
nv.addGraph(function () {
var chart = nv.models.discreteBarChart()
//.x( d=> d.label ) // very Ugly!
.x( d=> {
let dd = new Date(d.label);
return d3.time.format('%b %d')(dd)
}) // Ugly, because need to jump some days before show next
.y( d=> d.value )
.staggerLabels(true) // not solved
d3.select('#chart svg')
.datum(data)
.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update); // necessary?
return chart;
}); // \nv.add
I try some variations, no one work fine (all ugly).
My data is something as
[ {
key: "ExampleData",
color: "#ff7f0e",
values: [
{ label: "2019-03-28", value: 7.8389242307 },
{ label: "2019-03-29", value: 9.4185632435 },
{ label: "2019-03-30", value: 7.3553138346 },
{ label: "...", value: ... }
]
} ];
​​
The values Array have ~100 items.
Problematic workarounds
This (not elegant) solution loss the tooltip, as illustrated,
var chart = nv.models.discreteBarChart()
.x( d=> d.label )
.y( d=> d.value )
.staggerLabels(true);
chart.xAxis
.showMaxMin(false)
.tickFormat(function(d) {
let dd = new Date(d)
return (dd.getDay()==1)? d3.time.format('%x')(aux): '';
}); // each 7 days format, else empty string
This other solution, http://jsfiddle.net/ee2todev/3Lu46oqg/
seems good and elegant, using d3.scale.ordinal().domain(valuesForXAxis).rangePoints([0,width]), but is not NVD3, is pure D3...
The NVD3 Live Line Plus Bar Chart seems good (!), but the input data is not ISO date, is something very strange (unix time?) that is a ordinal domain and is automatically displayed with non-ugly algorithm.

You can try using historicalBarChart which can have time series on x axis.
var chart = nv.models.historicalBarChart()
.x(function(d) { return d[0]})
.y(function(d) { return d[1]})
.useInteractiveGuideline(true);
...
chart.xAxis
.showMaxMin(false)
.tickFormat(function(d) {
return d3.time.format('%x')(new Date(d))
});
Working example is here.

Related

Line chart using nvd3

I'm new to this d3 and nvd3.I'm trying to build a multi line chart using nvd3 and i have got 2 issues
1) the x axis doesn't seem to show the right values for hour and minutes as in json data which has date in standard utc format.
2) when i put the the first value as non-zero for both memory and cpu it scales down to negative y axis and looks weird.
Ty for the help. here is the plunk
nv.addGraph(function() {
var chart = nv.models.cumulativeLineChart()
.x(function(d) {
console.log(d.time)
return d.time
})
.y(function(d) {
console.log(d.value)
return d.value
})
.color(["#FF0000", "#000000"])
.height(210)
.width(420)
.useInteractiveGuideline(false)
.showControls(false)
.forceY(0)
;
chart.xAxis
.tickFormat(function(d) {
return d3.time.format('%H:%M')(new Date(d))
});
chart.yAxis.tickFormat(d3.format(',.1'));
d3.select('nv-indexLine').fill("black");
d3.select('#chart svg')
.datum(formattedData)
.call(chart);
nv.utils.windowResize(chart.update);
chart.lines.interactive(false);
return chart;
});
One change make your function like this for return x values it should return a Date object.
So instead of doing this
.x(function(d) {
console.log(d.time)
return d.time
})
Do like this to return Date object
.x(function(d) {
console.log(d.time)
return new Date(d.time)
})
working code here

nvd3 - force all xaxis labels to show on line chart

how can I force all xAxis labels to show up on my graph without defining each tick via .tickvalues ?
I'm totally new to nvd3 and this is my first try.
Maybe there is someone with a good heart who can have a look at my code and help me out.
I did my research but nothing worked for me.
Here is my code:
var data = [
{
"key": "www.WebsiteA.com",
"values": [
{date:"20151221",rank:1},
{date:"20151222",rank:3},
{date:"20151223",rank:2},
{date:"20151224",rank:4},
{date:"20151225",rank:2},
{date:"20151226",rank:5},
{date:"20151227",rank:3},
{date:"20151228",rank:2},
{date:"20151229",rank:2},
{date:"20151230",rank:1},
{date:"20151231",rank:2},
{date:"20160101",rank:4},
{date:"20160102",rank:5},
{date:"20160103",rank:3},
] },
{
"key": "www.WebsiteB.com",
"values": [
{date:"20151221",rank:2},
{date:"20151222",rank:1},
{date:"20151223",rank:3},
{date:"20151224",rank:5},
{date:"20151225",rank:1},
{date:"20151226",rank:4},
{date:"20151227",rank:1},
{date:"20151228",rank:5},
{date:"20151229",rank:3},
{date:"20151230",rank:4},
{date:"20151231",rank:2},
{date:"20160101",rank:1},
{date:"20160102",rank:3},
{date:"20160103",rank:2},
] },
]
nv.addGraph(function() {
var chart = nv.models.lineChart()
.x(function(d) {return d3.time.format("%Y%m%d").parse(d.date) })
.y(function(d) {return d.rank})
.yDomain([6, 1])
.color(d3.scale.category10().range())
.useInteractiveGuideline(true)
.margin({left: 100})
.margin({right: 50})
.margin({bottom: 100})
;
chart.legend.margin({top: 10, right:60, left:80, bottom: 100});
chart.xAxis
.tickFormat(function(d) {return d3.time.format('%Y-%m-%d')(new Date(d)) })
.rotateLabels(-45)
;
chart.xScale(d3.time.scale()); //fixes misalignment of timescale with line graph
chart.yAxis
.axisLabel('Rank')
.tickFormat(d3.format('d'))
;
d3.select('#chart svg')
.datum(data)
.transition().duration(500)
.call(chart)
;
nv.utils.windowResize(chart.update);
return chart;
});
You can find the fiddle here: http://jsfiddle.net/Marei/1azqmx1L/3/
Thank you!
I think (I have only worked directly with d3 before, not nvd3) that you need to specify a list of xValues that you want to display using tickValues().
So the first thing you need to do is to get a list of all your xValues (the order and/or duplicates do not matter):
//Map all xValues for each dataset to an array (tmp)
var tmp = data.map(function(e) {
return e.values.map(function(d) {
return d3.time.format('%Y%m%d').parse(d.date);
});
});
//And flatten out that array, so you have all your xValues in a 1D-array
var xValues = [].concat.apply([], tmp);
Then use this to set that you want to display all xValues:
chart.xAxis
.tickFormat(function(d) {return d3.time.format('%Y-%m-%d')(new Date(d)) })
.rotateLabels(-45)
.tickValues(xValues)
.showMaxMin(false)
;
showMaxMin needs is set to false because otherwise all the end-values must be displayed
I have string values for x,
so, for tickValues() i created an index based array with items length like,
axisLabel: graph.x_label,
tickValues: Array.from({ length: graph.values.length }, (x, i) => i),

D3. Histogram Layout. How do I recalculate the histogram on the fly?

I have a histogram created from datasource.
this.histogramDataSource = d3.layout.histogram()
.value (function(d) { return d.score; })
.bins(binThresholds)
(self.datasource);
That I render thusly
histogramRects = histogramGroup.selectAll('.svg_container rect')
.data(histogram.histogramDataSource)
.enter().append("rect")
.attr({
x: function(d) { return histogram.x(d); },
width: function(d) { return histogram.width(d); },
y: function(d) { return histogram.y(d); },
height: function(d) { return histogram.height(d); }
})
.attr("fill", function(d) { return scarpa.ConnectivityScoreHistogram.fillColor(); });
Based on user input I want to filter the input datasource, recalculate the histogram and re-render. I thought I could simply do something like this:
this.histogramDataSource(filtered_data_source);
But that generates errors. What have I missed here?
I think you need to keep a reference to the original histogram builder:
this.histogramLayout = d3.layout.histogram()
.value (function(d) { return d.score; })
.bins(binThresholds);
this.histogramDataSource = this.histogramLayout(self.datasource);
Then
this.histogramDataSource = this.histogramLayout(filtered_data_source);
However, keep in mind that this is recalculating the entire histogram from scratch so it might get a bit slow with somewhat larger data sets. If you really want interactive filtering you might want to look at Crossfilter.

Invert xAxis for NVD3 MultiBar Chart

How can I invert the xAxis for the NVD3 MultiBar chart? The data is the same (except I had to convert an {x: ... , y: ... } object for the values property to a paired array for the stacked area chart. The data is displaying correctly on the lineChart as well.
FYI, the date data is ordered from latest to earliest in the array but that shouldn't matter for a time scale, right?
// Stacked Area Chart
var renderStackedAreaChart = function(data){
var newData = convertData(data);
nv.addGraph(function() {
var chart = nv.models.stackedAreaChart()
.x(function(d) { return d[0]; })
.y(function(d) { return d[1]; })
.clipEdge(true);
chart.xAxis
.tickFormat(function(d) { return d3.time.format('%b')(new Date(d)); });
chart.yAxis
.tickFormat(d3.format('$,.2f'));
d3.select('#graph svg')
.datum(newData)
.transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
};
// Stacked MultiBar Chart
var renderStackedMultiBar = function(data) {
nv.addGraph(function() {
var chart = nv.models.multiBarChart();
chart.xAxis
.tickFormat(function(d) { return d3.time.format('%b')(new Date(d)); });
chart.yAxis
.tickFormat(d3.format('$,.2f'));
d3.select('#graph svg')
.datum(data)
.transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
}
This isn't supported by NVD3 -- you would have to modify the source code. A quick and dirty workaround is (as you've suggested in the comments) to use an ordinal scale instead of a time scale. This way, the order of the values you specify as domain matters and you can simply pass it in in reverse order.
Note that you'll lose the advantages of a time scale this way though, e.g. automatic label format depending on the time range shown.

How to set the domain and scale on an yaxis on a discreteBarChart nvd3.js

I'm using d3.js charts in one of my applications.
Here they are in this image
See Charts
For Y axis on Money chart (See in the Image), I want maximum value rounded to 400, no matter what maximum bar size is here it is $358.72, but I want to keep bar at 358.72 but on Y Axis it would be 400 Limit.
Code for it is here
tradesChart = [
{
key: "Cumulative Return",
values: [
{
"label" : "6E",
"value" : 100 },
{
"label" : "NG",
"value" : 100 },
{
"label" : "TF",
"value" : 67 } ]
}
];
nv.addGraph(function() {
var chart = nv.models.discreteBarChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value/100 })
.staggerLabels(true)
.showValues(true)
.valueFormat(d3.format('.0%'))
chart.yAxis.tickFormat(function(d) { return d3.format('d')(d*100) + '%'; });
d3.select('#chart-trades svg')
.datum(tradesChart)
.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
Kindly Help me to solve this out
You need to modify the domain of the Y-axis scale. Usually it is derived from the maximum value of the data with a statement like the following:
yScale.domain([0, d3.max(data, function (d) { return d.v; }) ]);
In your case, you should modify it to be more like this instead:
yScale.domain([0, 400]);
Alternatively, if you want to set the maximum value from the data or a minimum static value, you could do something like the following:
yScale.domain([0, d3.max(data, function (d) { return d.v > 400 ? d.v : 400; }) ]);
A full example jsfiddle is here.
That's how to do it with D3.js, I'm not familiar with the venerable nvd3.js lib, so I'm not sure how to access the scale, but i'll take a look and see if I can find it.
I use nvd3 and for me this worked instead:
nv.models.discreteBarChart().yDomain([0,400])
I could'nt get it to work with yScale.domain
Force Y
List of numbers to Force into the Y scale (ie. 0, or a max / min, etc.). The numbers tell the d3.js the values to use in the scale, rather than d3.js determining the values.
Datatype: Array of Numbers (i.e. [0, 50]
http://cmaurer.github.io/angularjs-nvd3-directives/line.chart.html

Resources