d3 data manipulation before plotting - javascript

Good evening everybody,
I am working for the first time on a visualization using D3. Basically I want to plot a bar chart that plots means of groups.
For example:
d3.csv("sample.csv", function(d) {
return {
"currency" : +d["year"],
"month" : +d.month,
"spendings" : +d.spendings
}
})
.then(function(data) {
chart_group.selectAll(".bar")
.data(sample.filter(function(d) { return d["currency"] === temp_curr; }))
.enter()
.append("rect")
.attr("class", "bar")
It is easily possible to create a new object containing the dynamic data i want. Group by curreny and mean the spending.
Is it possible to replace the .data(sample) by another object I created. Somehow the plot is always empty changing it. If it is not. How can I get the mean as y value?
.attr("y", function(d) { return y(d.spendings); })
I tried many things including nesting and d3.mean but there are always errors messages complaining about lists instead of single values. I totally get that but don't know how to do the trick.
Can someone help me?

Try .data(data.filter since data is the var name provided to the function containing your d3 code

Related

Getting color as a JSON input to a d3 bullet chart

I am following the following D3 bullet chart example, trying to modify it a bit so that the different colors of the ranges are also included in the JSON: http://www.d3noob.org/2013/07/introduction-to-bullet-charts-in-d3js.html. The reason for the change is that I need the colors to be dynamic and depend on various things.
This exists one other place in the forum, but old and unsolved. I should add that I am a total newbie to d3, and don't have a lot of JavaScript experience in general.
Here is the JSON I use. "rangecolor" will in the future be an array of different colors, as there are several ranges, but for simplicity I attempt only with one color to begin with.
{
"title":"Memory Used",
"subtitle":"MBytes",
"ranges":[256,512,1024],
"rangecolor": "red",
"measures":[768],
"markers":[900]
}
Now, getting an idea of how to use it, I looked at the working example for title:
var title = svg.append("g")
.style("text-anchor", "end")
.attr("transform", "translate(-6," + height / 2 + ")");
title.append("text")
.attr("class", "title")
.text(function(d) { return d.title; });
The problem is that I cannot get the following to work:
d3.selectAll(".bullet .range.s0")
.style("fill", function(d) { return d.rangecolor; });
The following does work:
d3.selectAll(".bullet .range.s0")
.style("fill", function(d) { return "red"; });
And I can also extract the rangecolor value to the title:
title.append("text")
.attr("class", "title")
.text(function(d) { return d.rangecolor; }); //works - title is now "red"
My approach might be misguided, so any help on how to best include color ranges to the JSON and using it would be much appreciated.
The problem is that when you select all bullets, there is no data bound to them so d is undefined here:
d3.selectAll(".bullet .range.s0")
.style("fill", function(d) { return d.rangecolor; });
Why? You did not perform a data join like this:
d3.selectAll('.something').data(somethingData)
.style('fill', function (d) { // d is defined });
You should wonder why it works for the title on the contrary. This is because when you do this:
var title = svg.append("g");
title inherits data from the svg selection. See Mike Bostock explanation. In fact I was myself not aware of this behaviour, I prefer performing data joins explicitly.
I don't know the overall structure of your code, but you might apply you rangecolor properties with data inheritance (as for title) or refactor to use explicit data joins.
I have talked with some experienced developers that state that the tutorial I am following is not a good one. It is a bit messy, and I am trying to find something cleaner. Troubleshooting has been difficult in this regard, and
The exact issue presented here was solved by using d3.selectAll(".bullet .range.s0").data(data).

How to add an array of annotations to a d3 barchart?

I've managed to draw a barchart (it's inverted I know :)) for each year of data by reading in the CSV data and then using d3.nest() to group the data for each date per year, see block here or blockbuilder here.
However I'm am now trying to append notes from my annotations array to each chart and I'm stuck.
One approach I'm trying is to selectAll(".charts") and to append my nested annotations array i.e. annotationsPerYear. But I'm finding it difficult to join my annotationsPerYear key with my charts and then to iterate and append the notes for each year onto the correct chart. Any ideas?
You can rely on nested selections for this. You're already using nested selection with this bit:
svg.selectAll(".bar")
.data(function(d) {return d.values;})
The above binds different data to each of the 3 SVGs created earlier. It does this by calling the function(d) 3 times, and returns a different d.values each time.
You can do a similar thing to bind and create a different set of annotations in each SVG. You need a function (passed to data()) that returns the applicable annotations per chart, but this time you don't have something pre-computed like d.values. Instead, you have to extract the applicable annotations out of annotations array, using filter():
svg.selectAll(".annotation")
.data(function(d) {
// return annotations whose key matches d's key
return annotations.filter(function(a) { return a.key == d.key; });
})
Then you can use enter() and append() as you've done for the bars to create the text and position it. I'm not sure how you intend to lay it out, but altogether you want something like this:
svg.selectAll(".annotation")
.data(function(d) {
return annotations.filter(function(a) { return a.key == d.key; });
})
.enter()
.append("text")
.attr("class", "annotation")
.attr("x", function(d) { return x(d.xPos); })
.attr("y", function(d) { return d.yPos; })
.text(function(d) { return d.note; })
See:
Updated block
Updated blockbuilder

Lines not showing in d3.js multi-line-chart with time axis

Hello nice people of SO,
I'm pretty new to javascript, JSON and D3.js and I'm trying to build a multi-line time series chart. My data comes as JSON from my server like this:
{
"t": ["2014-08-16T16:15:00", "2014-08-16T16:20:00", "2014-08-16T16:25:00", ...],
"todd": [0,0,1,2,3,2,1,0,0, ...],
"pete": [3,2,1,0,4,4,0,0,0, ...]
}
This I store in an array called 'dataset'. The datetimes are subsequently parsed. 't', 'todd' and 'pete' arrays are of equal length.
Ultimately I want the time (t) values to be used for the x-axis, the other two as y values over time. Parsing the data works, however I can't get it to display properly. What works so far is:
A: display my values one after the other without a reference to time, so I get two nice, colored lines but the x-axis holds no information, as it just displays 0 to N.
OR
B: have the x-axis show as a timeline based on the "t"-values (this tells me that the parsing works as it should). However, I can't get my two lines to show.
In Variant A I was simply returning the index as x value like so:
var line = d3.svg.
.x(function(d,i) {
return i;
})
...
Now I'm trying to return the datetime from my "t"-array:
var line = d3.svg.
.x(function(d,i) {
return dataset.t[i];
})
...
However, nothing is displayed. I get no errors at all in the console. I tried console logging the value of 'dataset.t[i]' but nothing shows. It's as if the code isn't even running so here is the for loop that calls the above code:
for (var key in dataset) {
if (dataset.hasOwnProperty(key) && key!="t") {
data=dataset[key];
svg.append("svg:path").attr("d", line(data))
.style("stroke", color(key))
.style("stroke-width", 2);
//BELOW IS IRRELEVANT TO MY QUESTION, JUST FOR COMPLETENESS...
svg.append("rect")
.attr("x", w - 65)
.attr("y", n*25)
.attr("width", 10)
.attr("height", 10)
.style("fill", color(key));
svg.append("text")
.attr("x", w - 50)
.attr("y", n * 25 + 10)
.attr("height",30)
.attr("width",100)
.style("fill", color(key))
.text(key);
n+=1; // counter up
}
};
I can tell this is running as the 'rect' and 'text' elements are added to the chart.
The full code is here: http://pastebin.com/tbS7rD7p
By now I can't see straight anymore so I'm really looking forward to your ideas. Many thanks in advance!
Cheers, Ben
Thanks, in code veritas, for your input. I'm in the process of optimizing the code in the way you suggested. However, it seems you do not need the .data() method with lines as you allocate the data via .x() and .y() methods for these (if I understand correctly).
Anyway, in the end the solution was to change:
.x(function(d,i) {return dataset.t[i];})
to
.x(function(d,i) {return x(dataset.t[i]);})
which is mentioned in pretty much every tutorial but which I, wisely ^^, chose to ignore because it seemed pointless to me. If anybody can comment on that and enlighten me, I'd be grateful.
Cheers!

Using translate function on more than data

At the moment I'm using .filter to specific the type of data I would like in my data set. However, when it comes to positioning that specific, it comes through overlapping of course. For example:
.filter(function(d) {
return d.Country === 'Russia';
})
.attr("transform", function(d,i) {
return "translate(-180,50)";
});
This of course just makes all that specific data go in one place over one another. I was therefore wondering if I could make multiple transform specified by the filter that I have. I attempted to do something like this:
.attr("transform", function(d,i) {
return if (d.name === 'Amy') { "translate(-180,50)"; }
})
Heres an example of my json etc.. http://jsfiddle.net/xwZjN/38/
If anyone could point me into the right direction that would be great

d3.js: How to add a data key?

I'm learning D3.js and trying to get my head around data keys used with streamgraphs. I would like to adapt the official streamgraph example:
...so that each path has an explicit data key, and so that the mouseover logs the data key.
The official example adds paths as follows:
var area = d3.svg.area()
.x(function(d) { console.log('x', d.data); return d.x * w / mx; })
.y0(function(d) { return h - d.y0 * h / my; })
.y1(function(d) { return h - (d.y + d.y0) * h / my; });
vis.selectAll("path")
.data(data0)
.enter().append("path")
.style("fill", function() { return color(Math.random()); })
.attr("d", area);
I tried adapting the code as follows, but I'm not sure how to change the structure of data0 (currently an array of arrays) to achieve what I want:
vis.selectAll("path")
.data(data0, function(d) { return d.name }) // Add key function
.enter().append("path")
.style("fill", function() { return color(Math.random()); })
.attr("d", area)
.on("mouseover", function (d,i) {
console.log("mouseover", d.name); // Log name property on mouseover
});
As it stands, without my having made any changes to the structure of data0, it unsurprisingly does not work. How can I add a name property to data0 without also messing up the area and .data() functions?
UPDATE: To be a bit clearer: the D3 docs say that the area function is expecting a two-dimensional array. So if I change data0 from a two-dimensional array, to an array of objects, each with a name key and a data key, how can I also change what I pass to area?
The data in the example doesn't have a "name" property, so you would need to add that to the data to use it. The data keys you refer to are used when merging/updating data, i.e. you have drawn some paths already and then update (some of them). The .data() function will try to figure out what data is updated and what data is new. If that doesn't work for you, you can use the data key to help it, i.e. in your case tell it that things with the same name are the same data.
If what you mean by data keys are "data legends", then you might want to take a look at the following examples where I've completely separated the placement of magnitudes, legend bullets and legend text in different areas of the charts.
Multiple D3 Pie Charts Mixed In With HTML Layout Constructs
Multiple D3 Horizontal Bar Charts Mixed In With HTML Layout Constructs
In each of the examples, you'll clearly see how the data is labeled, structured, passed in, and used.
I also tied them together through mouseover and mouseout events so that mousing over or out of any element causes all elements in a set to change color simultaneously.
I hope this helps.
Frank

Categories