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
Related
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
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).
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
I'm creating multiple line-graphs that are stacked by "symbol" from a csv-file.
This is how the csv looks-like:
symbol,date,price
Agency,2015/5/6,33
Agency,2015/5/7,29
Agency,2015/5/8,32
test,2015/5/6,23
test,2015/5/7,19
test,2015/5/8,22
example,2015/5/6,43
example,2015/5/7,49
example,2015/5/8,42
So for each symbol contained in the csv-file a separate graph gets drawn. This all works fine so far.
But I'm struggling to add dots to each line.
This is the part that's creating the dots (line 142 - 148 in my fiddle):
var circles = svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 2)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.price); })
.style("fill", "black");
But this code creates the dots populated by the values of the last "symbol" off the csv and not separate for each "symbol".
I researched a lot here on stackoverflow, but none of the solutions or hints I found worked for me.
In this example http://jsbin.com/isuris/1/edit?html,output I found this part:
// add circles
pathContainers.selectAll('circle')
.data(function (d) { return d; })
.enter().append('circle')
.attr('cx', function (d) { return xScale(d.x); })
.attr('cy', function (d) { return yScale(d.y); })
.attr('r', 3);
So I tried to change line 143 of my fiddle http://fiddle.jshell.net/doyL1L0p/1/ like this:
.data(function (d) { return d; })
But this didn't work, the "wrong" dots disappear.
So I tried this:
.data(function (d) { return d.key; })
because the separate "symbol"-value gets displayed by using "d.key". But know I get this error in the console:
Unexpected value NaN for attribute cx
Unexpected value NaN for attribute cy
Then I tried to alter lines 146 and 147:
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.price); })
but without success.
I tried many other posted "solutions" off stackoverflow and others I found in the web, but all without success again.
Can anyone please give me a hint on how to solve this?
Heres the fiddle again:
http://fiddle.jshell.net/doyL1L0p/1/
Based on some editing of your fiddle, it seems that they ARE actually being displayed... but they're out of range of the canvas.
If you look at the outcome here, you can see that the newly added point near the top is displayed, on all of the graphs.
I'll see if I can work on a proper fix, because obviously this is not the desired behaviour.
UPDATE
It looks like the problem is that you aren't updating your domain when you display your dots, so they are using the last domain which was set (hence why only last show normally). In addition, you are displaying dots for the entire data set on each graph (although most of them are outside the domain).
I would actually recommend trying to parse your data into an array for each unique symbol, it make things much more straightforward for you. For example, you could simply set the domain using d3.extent.
SUMMARY
A couple of solutions spring to mind:
Setting up separate axes/domains per graph.
Putting your dots into a g element per symbol, then adding data to them via a nested selection.
This works, but you really should read up a bit more on (nested) selections. Ideally, you would probably want to separately generate each graph, out of scope of the others. This solution is badly designed, but hopefully it should help to give you an idea of how your code is working.
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