Line Plus Bar with Multi Bars? - d3.js

I'm trying to make an chart using the default line plus bar chart, but I want to use two or more streams in the bars, is it possible?
Currently, when I try to do this, I got some trouble with the effects of the chart, and so I can't show properly the hover balloon of the bars, he always display the data of just one of the streams. But the main problem is the dates of x axis, displaying 1970's dates, when I remove the second stream of bars, the dates display well:
Anyone already tried to do this kind of chart successfully?
EDIT
Adding Fiddles:
Fiddle with two columns stream and messy dates
Fiddle with just one column stream and ok dates
I'm calling this kind of graph:
linePlusBarChart()

The problem with the dates is that your data contains timestamps (i.e. in seconds), but Javascript expects milliseconds. This is easily fixed by multiplying the values by 1000:
series.values = series.values.map(function (d) {
return {
x: d[0]*1000,
y: d[1]
}
});
The tooltip problem is actually a bug in NVD3 -- it's not meant to be used this way. The problem boils down to the mouseover handler assuming that the first item of the data is representative of what you want. You can fix this for your case by selecting the item by data point number modulo 2 (because there're two bars):
.on('mouseover', function(d,i) {
d3.select(this).classed('hover', true);
dispatch.elementMouseover({
point: d,
series: data[i%2],
pos: [x(getX(d,i)), y(getY(d,i))],
pointIndex: i,
seriesIndex: i%2,
e: d3.event
});
})
This will only work for exactly two bar series though. Updated jsfiddle with the modified NVD3 code here.

Related

snapping brush to the next x value? [dc.js]

I have a line chart and data in the form
[{
time: "2016-4-29"
total: 23242
},
{
time: "2016-5-16
total: 3322
}
...
]
I'm trying to filter on the x-axis with the brush, however, since I don't have every single date, if I brush in a small range, the filter handler seems to return an empty array for my filters
I've set up my line chart's x-axis like so:
.x(d3.time.scale().domain([minDate,maxDate]))
is there a way to make it so a user can only filter on dates that are in the dataset?
I would like the brush to snap to dates in the dataset.
it seems like whats happening is that you are able to brush between ticks..so it doesn't know what it selected.
I'm going to answer the easier question: How do I create a brush that will not allow nothing to be selected?
In other words, if the brush contains no data, do not allow it to take.
There are two parts to the solution. First, since any chart with a brush will remove the old filter and then add the new filter, we can set up the addFilterHandler to reject any filter that does not contain non-zero bins:
spendHistChart.addFilterHandler(function(filters, filter) {
var binsIn = spendHistChart.group().all().filter(function(kv) {
return filter.isFiltered(kv.key) && kv.value;
});
console.log('non-empty bins in range', binsIn.length);
return binsIn.length ? [filter] : [];
});
That's the straightforward part, and incidentally I think you could probably modify it to snap the brush to existing data. (I haven't tried it, though.)
The more tricky part is that this won't get rid of the brush, it just doesn't apply the filter. So the chart will end up in an inconsistent state.
We need to detect when the brush action has finished, and if there is no filter at that point, explicitly tell the chart to clear the filter:
spendHistChart.brush().on('brushend.no-empty', function() {
if(!spendHistChart.filters().length)
window.setTimeout(function() {
spendHistChart.filterAll().redraw();
}, 100);
});
We need a brief delay here, because if we respond to brushend synchronously, the chart may still be responding to it, causing bickering and dissatisfaction.
As a bonus, you get kind of a "nah-ah" animation because of the unintentional remove-brush animation.
demo fiddle

DC.js Choropleth filtering Issue

I am trying to filter data on my choropleth chart from a bargraph. Strange thing is that it is not showing correct value on selecting a bar from the accompanying bar chart.
Here is the jsfiddle: https://jsfiddle.net/anmolkoul/jk8LammL/
The script code begins from line 4794
If i select WIN004 from the bar chart, it should highlight only five states and the tooltip should reflect the values for the data. Some states are highlighted for whom WIN004 does not exist.
I changed the properties of the choropleth from
.colors(d3.scale.quantize().range(["#F90D00", "#F63F00", "#F36F01", "#F09E01", "#EDCB02", "#DDEA03", "#ADE703", "#7EE404", "#50E104", "#24DE05", "#05DB11"]))
.colorDomain([-1, 1])
To
.colors(d3.scale.linear().range(["green", "white", "red"]))
.colorDomain([-2, 0, 2])
But i get a lot of white states where its hard to discern what has been highlighted. The tool tip for some white-ed-out states show -0.00 :/
Here is the fiddle http://jsfiddle.net/anmolkoul/jk8LammL/1/
So i guess either its a problem with my color range or how my data is getting parsed.
I would ideally like to specify the data ranges in the .colorDomain based on the top and bottom values of the riskIndicator dimension. My functions are not working though. Should i use d3.max or riskIndicator.top here?
EDIT:
I got the color domain dynamic by using the min and max values but still the graph is not performing as expected? Could this be an issue with the geochoropleth chart? I further took a working geochoropleth example and ported my data to it and even that gave me the same issue of representing data incorrectly. I thoughit could be a data problem but i validated using a couple of good BI tools and their map charts displayed data correctly.
Could this be an issue with the dc choropleth?
Thank you.
Anmol
This has the same root cause as the issue in this question:
Crossfilter showing negative numbers on dc.js with no negative numbers in the dataset
In short, floating point numbers don't always cancel out to zero when added and subtracted. This "fake group" will ensure they snap to zero when they get close:
function snap_to_zero(source_group) {
return {
all:function () {
return source_group.all().map(function(d) {
return {key: d.key,
value: (Math.abs(d.value)<1e-6) ? 0 : d.value};
});
}
};
}
Added it to the FAQ!

Simple way to add raw data to dc.js composite chart via Ajax

I have a composite chart of 2 line charts however I need to add a third chart to it.
This third chart will have these unique properties:
The data will come in via an ajax call and be available as a two dimensional array [[timestamp,value],[timestamp,value]...]
Every new ajax call needs to replace the values of the previous one
It does not need to respect any of the filters and will not be used on any other charts
It will however need to use a differently scaled Y axis.. (and labeled so on the right)
This is how the chart currently looks with only two of the charts
This is my code with the start of a third line graph... Assuming I have the array of new data available i'm at a little loss of the best/simplest way to handle this.
timeChart
.width(width).height(width*.333)
.dimension(dim)
.renderHorizontalGridLines(true)
.x(d3.time.scale().domain([minDate,maxDate]))
.xUnits(d3.time.months)
.elasticY(true)
.brushOn(true)
.legend(dc.legend().x(60).y(10).itemHeight(13).gap(5))
.yAxisLabel(displayName)
.compose([
dc.lineChart(timeChart)
.colors(['blue'])
.group(metric, "actual" + displayName)
.valueAccessor (d) -> d.value.avg
.interpolate('basis-open')
.dimension(dim),
dc.lineChart(timeChart)
.colors(['red'])
.group(metric, "Normal " + displayName)
.valueAccessor (d) -> d.value.avg_avg
.interpolate('basis-open'),
dc.lineChart(timeChart)
.colors(['#666'])
.y()#This needs to be scaled and labeled on the right side of the chart
.group() #I just want to feed a simple array of values into here
])
Also side note: I've noticed what I might be a small bug with the legend rendering. As you can see in the legend both have the same label but i've used different strings in the second .group() argument.
I believe you are asking a few questions here. I will try to answer the main question: how do you add data to a dc chart.
I created an example here: http://jsfiddle.net/djmartin_umich/qBr7y/
In this example I simply add random data to the crossfilter, though this could easily be adapted to pull data from the server:
function AddData(){
var q = Math.floor(Math.random() * 6) + 1;
currDate = currDate.add('month', 1);
cf.add( [{date: currDate.clone().toDate(), quantity: q}]);
$("#log").append(q + ", ");
}
I call this method once a second. Once it completes, I reset the x domain and redraw the chart.
window.setInterval(function(){
AddData();
lineChart.x(d3.time.scale().domain([startDate, currDate]));
dc.redrawAll();
}, 1000);
I recommend trying to get this working in isolation before trying to add the complexity of multiple y-axis scales.
Currently your best bet is to create a fake group. Really the .data method on the charts is supposed to do this, but it doesn't work for charts that derive from the stack mixin.
https://github.com/dc-js/dc.js/wiki/FAQ#filter-the-data-before-its-charted

Trying to modify Voronoi Map in D3JS

I am trying to modify this D3 example to use with my dataset
http://mbostock.github.io/d3/talk/20111116/airports.html
I think my problem is creating the array of coordinates for calculating the Voronoi polygons, but at this point I'm not sure.
I'm getting a Uncaught TypeError: Cannot read property '0' of undefined error that points to the line where I am calling the array. The code is live, please see here
http://cds.library.brown.edu/projects/mapping-genres/symbol-maps/brown-voronoi-map.html
The map displays just fine, but the data points, the radio button, and the voronoi lines do not appear (I'm not trying to show lines between data points, so that code has been removed).
Any thoughts or suggestions would be most appreciated. Many thanks!
There are a number of small things going on here which can be easily sorted out but some will require a bit of work. The first is that you're using a new version of d3 (v3) and the example you're trying to replicate is an older version. There have been significant changes between the versions on the mapping side of things. So you can either use an old version of d3 (v2 will work I think) or investigate the changes.
The Uncaught TypeError: Cannot read property '0' of undefined error is being generated because d3.geom.voronoi(positions) line is producing NaN. I'm not 100% sure why but you could just filter these out to get a temporary fix. A simple filter would be something like:
g.append("svg:path")
.attr("class", "cell")
.attr("d", function(d, i) {
if (polygons[i][0][0]) {
return "M" + polygons[i].join("L") + "Z";
}
})
with the if true when there is not a false value (NULL, NaN, etc) in the first element of the polygon. Please note that this is temporary fix while you investigate why you're getting NaN and this will not produce the correct voronoi polygons.
The fix for the data points is very simple, you just have to set the radius to something greater than 0 such as 10 (although I imagine that you might want to scale the dots to a property of your data like TotalPublished). For instance see the last line:
circles.selectAll("circle")
.data(locations.rows
.sort(function(a, b) { return b.TotalPublished - a.TotalPublished; }))
.enter().append("svg:circle")
.attr("transform", function(d) { return "translate(" + projection(d.coordinates) + ")"; })
.attr("r", 10);
The radio button (or checkbox) does not show up as it's not generated by the javascript (although it can be). In the example you refer to its generated in the html in this snippet:
<div style="position:absolute;bottom:0;font-size:18px;">
<input type="checkbox" id="voronoi"> <label for="voronoi">show Voronoi</label>
</div>
Once you've worked through this you'll need to get the voronoi lines working. I think with your code as is the voronoi lines won't show up as the checkbox is not checked. So you could comment these lines out and see what happens. A quick tip with paths is that if you want to just show the lines you need to set the fill style to none and the stroke to whatever colour you want. If you don't set the fill to none you'll just get a black screen.

Update multi-line graph D3.js

I'm attempting to update a d3 multi-line graph by pulling data at 5 second intervals from a mySQL database using PHP. At the moment the graph is displaying but not updating - I know that there is something not right with my updateData function but have tried everything can think of and nothing seems to work. Can anyone please help?
https://gist.github.com/Majella/ab32fe0151fd487da3f6
UPDATE:
As you can see the x-axis line is only showing sporadically and some of the lines aren't lined up with the y-axis.
Updated gist:
https://gist.github.com/Majella/ab32fe0151fd487da3f6
UPDATE 2: For some bizarre reason the lines are changing colour - or moving completely not exactly sure. So while on graph above the lines are from top - blue, orange then white - when graph updating the blue might move to bottom with orange on top and white in middle etc - but happening randomly?
In your original drawing of the graph, you correctly use:
var parameter = svg.selectAll(".parameter")
.data(data, function(d) { return d.key; })
.enter().append("g")
.attr("class", "parameter");
which joins the data (data) to the elements (g.parameter)
During your update function, you will need to join the data again in order to perform updates, deletes, and adds of elements. The 3 little circles tutorial is an excellent place to learn more about this.
Anyway, in your update function, you may want something like this (untested):
// re-acquire joined data
var containers = svg.selectAll("g.parameter")
.data( data );
// update existing elements for this data
containers
.select( "path.line" )
.attr( "d", function(d) { return line(d.values); })

Resources