D3 time scale from array index - javascript

I am attempting to map an array index to a time range in d3.js as I do not have individual dates for the array - I just know the values are between 1900 and 2000.
var data = [45,678,490...]; // length 820
var x = d3.time.scale().range([0, width]).domain([new Date(1900, 0, 0), new Date(2000, 0, 0)]);
// line function
var line = d3.svg.line()
.x(function(d,i) {
// here map i to date between domain min / max
return x(i);
})
.y(function(d) {
return y(d);
});
Is there a smarter way of doing this than manually calculating a date for each array value? Perhaps calculating the step and incrementing?

Use another scale to map from the index value to date:
var arrayScale = d3.time.scale()
.domain([0, data.length])
.range([new Date(1900, 0, 0), new Date(2000, 0, 0)]);

Related

D3 put times in X axis instead of array value

This question is a bit tricky. I have some example code working with the following data. It plots it on a line graph.
var dataset = [{
y: 0.1
},
{
y: 0.6
},
{
y: 0.6
},
{
y: 0.7
}
];
D3 uses the following code to put this on the X axis:
var xScale = d3.scaleLinear()
.domain([0, n - 1]) // input
.range([0, width]); // output
However I'm really looking to get dat
var dataset = [{
"1pm": 0.1
},
{
"2pm": 0.6
},
{
"3pm": 0.6
},
{
"4pm": 0.7
}
];
How do I put these values into the graph X axis instead of it just being based on the array numbers? When I try the code as it is above it breaks the graph it says:
Error: <path> attribute d: Expected number, "M0,NaNC17.222222222…".
This is the full code:
http://jsfiddle.net/spadez/e6hv9x8m/17/
Here is your modified fiddle.
First off, you can include an additional value in your dataset objects to represent the x value, I've called this date:
var dataset = [{
y: 0.1,
date: new Date(2012,0,1)
},
{
y: 0.6,
date: new Date(2012,0,2)
}
];
Next, the x scale needs to be changed from linear to time:
var minDate = new Date(2012,0,1);
var maxDate = new Date(2012,0,2);
var xScale = d3.scaleTime()
.domain([minDate, maxDate])
.range([0, width]);
And finally, you must update the callback here to pull the date field from the dataset object, rather than just it's position in the array:
var line = d3.line()
.x(function(d, i) {
return xScale(d.date);
}) // set the x values for the line generator
.y(function(d) {
return yScale(d.y);
}) // set the y values for the line generator
.curve(d3.curveMonotoneX) // apply smoothing to the line

dc.js x-axis will not display ticks as months, shows decimals instead

I'm building a bar chart using dc.js, and want the x-axis to list the 12 months of the year.
So far, I have it rendering showing the first tick as a time of day (06 PM), and all subsequent ticks as decimals (thousandths).
I saw a similar question (https://stackoverflow.com/questions/21392997/dc-js-x-axis-hour-labels-appear-as-thousandths#=), but the answer involved converting milliseconds to larger units, which isn't possible with months (that I know of).
Here is my code. Thanks in advance for the help.
var parseDate = d3.time.format("%m/%d/%Y").parse;
data.forEach(function(d) {
d.date = parseDate(d.weekStart);
d.month = d.date.getMonth();
});
var monthDim = ndx.dimension(function(d) {return d.month;});
var milesByMonth = monthDim.group().reduceSum(dc.pluck('miles'));
//set range for x-axis
var minDate = dateDim.bottom(1)[0].date;
var maxDate = dateDim.top(1)[0].date;
var barChart = dc.barChart("#myBarChart");
barChart
.width(800).height(200)
.margins({top: 10, right: 10, bottom: 20, left: 60})
.dimension(monthDim)
.group(milesByMonth)
.gap(40)
.transitionDuration(1500)
.yAxisLabel('Miles Per Month')
.renderHorizontalGridLines(true)
.x(d3.time.scale().domain([minDate,maxDate]))
//.x(d3.scale.ordinal())
//.xUnits(dc.units.ordinal)
//.x(d3.time.scale().domain([new Date(2012, 0, 1), new Date(2012, 11, 31)]))
//.round(d3.time.month.round)
.elasticX(true)
.elasticY(true);
You are using the month number for your dimension key, but dates for your x domain. I would suggest always using dates as your dimension key, but using months as your group key, so that it bins by month but doesn't lose the rest of the information.
var milesByMonth = dateDim.group(function(d) {return d.month;}).reduceSum(dc.pluck('miles'));
barChart.dimension(dateDim)
You should be able to use the axis .tickFormat() with d3.time.format to get the months printing.
Something like
var xAxis = chart.xAxis().tickFormat(d3.time. format('%B');
https://github.com/mbostock/d3/wiki/SVG-Axes#tickFormat
https://github.com/mbostock/d3/wiki/Time-Formatting
You may also need to specify
chart.xUnits(d3.time.months)

Updating Nvd3 chart priodically

I'm working with Nvd3 charts from the examples from their official website. Now I want a line chart to update periodically based on data sent from server but I couldn't found any useful Example for this on internet.
I have created a function which re-draws the chart when new data is arrived but i want to append every new point to the existing chart (like we can do in highcharts) but i'm stuck.
Here is the code I'm using for Updating the chart.
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);

Time intervals with d3.js

I have to draw a set of rect that represent the range of a day in the month.
svg.selectAll(".month")
.data(function() { return getMonths(year); })
.enter().append("path")
.attr("class", "month")
.attr("d", monthPath);
My function is:
function getMonths(year){
var months = d3.time.months(new Date(year, 0, 1), new Date(year + 1, 0, 1));
return months;
}
This function is called for a set of year like [2003,2004,2005 and so on..]
If I call my function for a set of year it doesn't work, instead if I call this function in this way for all years:
function getMonths(year){
var months = d3.time.months(new Date(2012, 0, 1), new Date(2012 + 1, 0, 1));
return months;
}
it works.
Why I have this problem?
Thanks.

Drawing path from 2 dataset

I have been trying to use loop in order to prepare dataset for visualization with d3 but I got some problem when getting data back from loop.
Let's say I have 2 data set,
setX = [1,2 ,3, 4, 5] and setY = [10, 20, 30, 40, 50]
and I create for loop to get array of coordinate x and y from 2 dataset.
var point = new Object();
var coordinate = new Array();
for(var i=0; i < setX.length;i++){
point.x = setX[i];
point.y = setY[i];
coordinate.push(point);
}
and pulling back data from array to draw with
var d3line = d3.svg.line()
.x(function(d){return d.x;})
.y(function(d){return d.y;})
.interpolate("linear");
But the value of x and y data of d3line(coordinate) are always 5 and 50.
Is there a correct way to fix this?
Here is example of code
<div id="path"></div>
<script>
var divElem = d3.select("#path");
canvas = divElem.append("svg:svg")
.attr("width", 200)
.attr("height", 200)
var setX = [1,2 ,3, 4, 5];
var setY = [10, 20, 30, 40, 50];
var point = new Object();
var coordinate = new Array();
for(var i=0; i < setX.length;i++){
point.x = setX[i];
point.y = setY[i];
coordinate.push(point);
}
var d3line = d3.svg.line()
.x(function(d){return d.x;})
.y(function(d){return d.y;})
.interpolate("linear");
canvas.append("svg:path")
.attr("d", d3line(coordinate))
.style("stroke-width", 2)
.style("stroke", "steelblue")
.style("fill", "none");
</script>
You also see my example of this code on http://jsfiddle.net/agadoo/JDtqf/
There are two problems with your code. First (and this causes the behaviour you see), you're overwriting the same object in the loop that creates the points. So you end up with exactly the same object several times in your array and each iteration of the loop changes it. The output you're producing inside the loop prints the correct values because you're printing before the next iteration overwrites the object. This is fixed easily by moving the declaration of point inside the loop.
The second problem isn't really a problem but more of a style issue. You can certainly use d3 in the way that you're using it by simply passing the data in where you need it. A better way is to use d3s selections however where you pass in the data and then tell it what to do with it.
I've updated your js fiddle here with those changes made.

Categories