function to calculate the parent size from children's size - javascript

I'm doing something like this, http://bl.ocks.org/mbostock/1283663
But I want that the parent's size was the average of children's size. I found a solution, but It works only for the last parent. My proposition is to implement a new function average
function average(d) {
if (d.children !=null){
return d3.sum(d.children, function(d) {return d.value;})/d.children.length;}
else{
return d.value;
}
}
Then I replace all occurence of
.attr("width", function(d) { return x(d.value); })
with
.attr("width", function(d) { return x(average(d)); })
But it calculate the average only for the lastest parents and their children. Have you any idea how can I correct this function to calculate the average for all parents ?
It's the third time i asked this question, but please understand me, it's very interesting for me, and I know that i will find solution if someone helps me
Thanks a lot

First of all, you don't need to invent a new function to find the average of a certain property for each element in an array of objects. You can use d3.mean for that.
You could just use a recursive function to add a new property to each node that has children, calling it something like averageChildValue.
For example, you could do this:
function recurse(node) {
// IF A NODE HAS CHILDREN...
if (node.children) {
// ADD A PROPERTY REPRESENTING THE AVG VALUE OF ITS CHILDREN
node.averageChildValue = d3.mean(node.children, function(d) {
return d.value;
});
// RECURSIVELY CALL THE FUNCTION ON THE CHILDREN
node.children.forEach(function(d) { recurse(d); });
}
}
d3.json("readme.json", function(error, root) {
// PARTITION THE DATA
partition.nodes(root);
// USE THE RECURSIVE FUNCTION TO CALCULATE THE AVERAGES
recurse(root)
// DO SOMETHING WITH YOUR NEW-AND-IMPROVED DATA
console.log(root)
});
Check your console output and each node with children should now have an extra property called averageChildValue.

Related

D3.js method chaining command dosn't work when I split it, it works

I am new in D3.js,
when i use this code it doesn't work,(it is part of the redraw, when running for first time it works good when calling redraw again it works unexpextedly)
var rows=tbody.selectAll('tr').data(roster);
rows.enter().append('tr');
rows.exit().remove();
rows.selectAll('td').data(function(row) { return columns.map(function(col) {
return row[col];
});}).enter().append('td').text(function(d) {return d;} );
when I break the chain down into smaller it works.
var rows=tbody.selectAll('tr').data(roster);
rows.enter().append('tr');
rows.exit().remove();
var cells = rows.selectAll("td")
.data(function(row) { return columns.map(function(col) {
return row[col];
});});
cells.enter().append("td");
cells.text(function(d) { return d; });
any reason or any rule govern this.
In the first case you are only updating the text on the new cells, not the old ones. When you chain .enter() like that, all of the following methods chained apply to the object returned by .enter() and that is the enter selection : added cells in other words.
Read this

Dynamic returned values in append function

I'm using d3 library and I want to create different elements by checking some values. If I do:
elements.append("rect").attr(...);
What happens if I want to create different elements? I tried:
elements.append(function (d) {
if (d.foo) {
return "rect";
}
return "circle";
});
This seems not to work.
What's the alternative to this?
As you've already pointed out, .append() accepts a function that returns the DOM element to append. This looks as follows.
var data = ["circle", "rect"];
d3.select("svg").selectAll(".shape").data(data)
.enter()
.append(function(d) {
return document.createElementNS(d3.ns.prefix.svg, d);
});
Of course you also need to set all the attributes correctly, which is the crux of the method -- in principle you don't need lots of if/switch statements to handle each element type, but in practice you do because the attributes you need to set differ (unless you want to set everything for everything).
Complete demo here.
Even append function accepts a function, I'm not sure how that should work (maybe someone brings the light here). However the following solution is human readable and easy to implement:
elements.each(function (d) {
var el = d3.select(this);
var type = "circle";
if (el.foo) {
type = "rect";
}
var aType = el.append(type);
if (type === "rect") {
aType.attr("rx", 5)
.attr("ry", 5)
.attr("height", ...)
.attr("width", ...);
} else if (type === "circle") {
aType.attr("r", ...);
}
});

Return D3 key of Selected

I know I can use d3.keys() to return all keys inside of an object, but I want to return the selected items key I'm targeting inside of a mouseover event.
I'm targeting elements in D3 like so:
var test = something.selectAll('rect')
.data(myData['groupSelection'])
.enter()
.append('rect')
.on('mouseover', function (d) {
console.log(d3.keys(d));
}
This will return that given selections keys though, when I really need a count of that items keys, for instance, if I select the second rect created from the data, it'd be nice for it to return 2.
All callbacks in D3 that get the data as an argument also get the index of the data as an argument. That is, instead of
.attr("foo", function(d) { ... });
you can also write
.attr("foo", function(d, i) { ... });
where d is the data and i the index of d in the array of data that you've passed to .data(). The same goes for .style(), .on(), etc.
For example, assume you have data [2,3] and elements with data 1 and 2 bound to them. Now if you do (note the key function to .data() to match elements by their contents)
var sel = d3.selectAll("element").data([2,3], function(d) { return d; });
you'll get non-empty enter (containing 3), update (containing 2) and exit (containing the element that 1 was bound to) selections. You can operate on each of these selections, e.g.
sel.attr("foo", function(d, i) { ... });
The i refers to the index within the selection. Each selection contains only one element, so you'll get 0 for i -- for each selection. That is, the code
sel.attr("foo", function(d, i) { console.log(i); });
sel.enter().attr("foo", function(d, i) { console.log(i); });
sel.exit().attr("foo", function(d, i) { console.log(i); });
will log 0 to the console three times. If your update selection was of length 3 for example (that is, three elements in the argument to .data() are matched up with DOM elements in the selection), you would get 0, 1, 2 on the console.

Select/detect the changed elements in d3.js?

The example looks like this:
a=[1,2,3,4,5];
svg.selectAll("rect").data(a)
a[1]=10;
a[4]=50;
svg.selectAll("rect").data(a)
The second and the fifth elements of a are changed. And I want to ONLY select these two elements and set their color as red. However, I don't know how to do that, or in other words, select these changed element in d3.js. (As I understand, enter() can't do this job). Does anyone have ideas about this?
There might be a better way of doing this:
//update data array
a[4]=50;
//color update elements
svg.selectAll('rect')
.filter(function(d, i){ return d != a[i]; })
.style('color', 'red')
//bind updated data
svg.selectAll('rect').data(a)
You need a way to store the old data value so that you can compare it against the new one. Maybe add a custom data attribute like this:
a=[1,2,3,4,5];
svg.selectAll("rect").data(a)
.attr("data-currentVal", function(d){return d});
a[1]=10;
a[4]=50;
svg.selectAll("rect").data(a)
.style("fill", function(d) {
if (d3.select(this).attr("data-currentVal") != d) {return red;}
else {return black;}
});
Live example (slightly fancied up so you can see the changes happening):
http://fiddle.jshell.net/5Jm5w/1/
Of course, for the more common example where d is a complex object, you would need to have a way of accessing it's value(s) as a unique string, since the attribute value would always be coerced to string. For example, if you have an (x,y) point, you would need to create a named helper function like dToString = function(d) {return "(" + d.x + "," + d.y + ")";}, and then pass in the name of that function when you set the attribute, and use it again when you compare the old and new.

difference between function(d) and function(d,i)?

Every D3js beginner must be going through this thought, I am pretty much sure about it.
I have been around this thing for few hours now!!!!But I don't know how to use it and what is the difference between them?
function(d){return d}
function(d,i){return d and some more custom code}
for Example--->
var data = [4, 8, 15, 16, 23, 42];
Function(d):::::
chart.selectAll("div")
.data(data)
.enter().append("div")
.style("width", function(d) { return d * 10 + "px"; })
.text(function(d) { return d; });
------------------------------------------------------------------------------------
Function(d*i):::::
chart.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("y", function(d, i) { return i * 20; })
.attr("width", x)
.attr("height", 20);
Your example is a good illustrator of the difference between the two.
In the first example, only d is used. d represents the data associated with a given selection. In this case, an array of selected div elements is created, one for each element in the data array:
chart.selectAll("div")
.data(data)
.enter()
.append("div")
This not only creates an array of div elements, but associates data with each of those elements. This is done on a one-to-one basis, with each div corresponding to a single element in the data array. One is associated with '4', one with '8', and so on.
If I then go on to use .text(function(d){...}) on the array of selections, d will refer to the data associated with each selected div, so if I use the following method on my selections:
.text(function(d) { return d; });
Each of my divs will have text added, the value of which is d, or the data associated with the element.
When an array of selections is created, they are also given an index in the array. In your example, this corresponds to the position of their data in the data array. If your function requests both d and i, then i will correspond to this index. Going back to our divs, the div associated with '4' will have an index of '0', '8' will have an index of '1', and so on.
It's also important to note that the character used in the variable requested doesn't matter. The first variable in the function call is always the data, and the second is the index. If i used a method like
.text(function(cat,moose){ return( "data is: " + cat + " index is: " + moose)})
cat will correspond to the data of the selection, and moose will correspond to the index.
I hope that this example can help you. This is a complete web page where you can start playing:
<!doctype html>
<meta charset="utf-8">
<title>my first d3</title>
<body>
<script>
var data=[10,20,30,40];
var lis = d3.select("body")
.append("ul")
.selectAll("li")
.data(data)
lis.enter()
.append("li")
.text(function(d,i){ return "item n° "+i+" has value: "+d})
</script>
Basically d is the value of the data, and i is his index.
You can take a look of this example here: http://jsfiddle.net/M8nK8/
If you're talking about the callback functions you would pass to methods like .attr(), then the function is called for each item in the current selection, where the i gives you the index of the current item, but depending on what you're doing you might not care about the index. So although D3.js will always call your function with both arguments, if you don't actually need the second argument in a particular case your function need not declare it explicitly.
JavaScript lets you call any function with any number of arguments regardless of how many were explicitly included in the function declaration. If you call a function with fewer arguments than were defined the leftovers will get the value undefined. If you call a function with more arguments than were defined you can still access the additional ones from within the function by using the arguments object - or you can ignore them.
(Note: you should have a lowercase f in function().)

Categories

Resources