Scale in Bilevel Partition in D3 - javascript

I am trying to make the Bilevel Partition in D3 work with log scale.
However, it doesn't seem to be working properly.
I have specified a log scale for angles:
var angle1 = d3.scale.log()
.base(2.0)
.domain([0, 2 * Math.PI])
.range([root.x, root.x + root.dx]);
However, this applies only when the angles are recalculated on zoom.
I have tried to modify the original partition scale, but with no success.
Any hints appreciated. See example code at the link below.
http://bl.ocks.org/mbostock/5944371

The partition layout in D3 sums up the values of the leaf nodes in order to calculate layout of the the elements with children. By definition of layout it should word like that. Therefore, only leaf nodes values are taken into account when calling layout.nodes(). Hence, only leaf nodes can be scaled (for example as log(count + 1)). However, non-leaf nodes will be represented sums of underlying values. The only option for all nodes to scale logarithmically would be to write a new layout, that would take into account a value at each node, and not sum up the values of the children.

Related

Can't reference data objects in d3js

I can't wrap my head around manipulating the data object in d3js. I'm planning to create a chart composed of horizontal bars to hold data elements. Each data element is a circle. I figured out how to insert circles into the different bars, but I'm stuck on how to equally space the circles in each bar. For example, if the width is 800 and there are 8 circles, the x attribute should be 100*i.
Here's a link to my project: https://plnkr.co/edit/fHrdJsItEqA5qc35iUxG?p=preview
I think the problem is how to reference the data object in this block of code. Anyways, I would like to equally space the circles using scaleBand which I defined as variable x earlier in my code:
var x = d3.scaleBand()
.range([0,width]);
I think the solution would look something like this: .attr("x",x.domain(data.map(function(d,i){return d[i]})); x.bandwidth(), but obviously data is not the right object.
Selecting each bar and inserting circles:
bar.selectAll("rect")
.data(function(d,i){console.log(data_group[i].values.length);return data_group[i].values})
.enter().append("circle")
.attr("class","circle")
.attr("width", width)
//.attr("x",) //how to equally space these circle elements???
.attr("height",20)
.attr("y", y.bandwidth())
console.log(y.bandwidth());
As always, I would really appreciate your help.
There are a number of issues with your code that are preventing it from working, including:
You aren't setting a domain for your x scale.
You are attempting to place <circle>s inside of <rect>s but you cannot nest shapes in SVGs. You should place both inside of a <g>.
A <circle>'s position is set using the cx and cy attributes (and you also need to provide it an r radius attribute).
To address your question, you will need to determine how you want your items laid out. Because you are referencing the index in your question, I will use that.
You are breaking your data into nested groups where each one has a values array. You are rendering a <circle> for each datum in that array, so you will want to determine the length of the longest values array.
var longest = data_group.reduce(function(acc, curr) {
return curr.values.length > acc ? curr.values.length : acc;
}, -Infinity);
Once you have the length of the longest values array, you can set the domain for your x scale.
You are using d3.scaleBand (d3.scalePoint would probably work better here), which is an ordinal scale. Ordinal scales work on discrete domains, which means that you will need to have a domain value for each possible input (the indices). For this, you will need to generate an array of the possible indices from 0 to longest-1.
var domainValues = d3.range(longest);
Now that you have the input domain values, you can set them for the x scale.
x.domain(domainValues);
Then, for each <circle>, you will set its cx value using the index of the circle in its group and the x scale.
.attr('cx', function(d,i) { return x(i); })
As I mentioned in the beginning, there are other errors in your code, so just fixing this won't get it running correctly, but it should push you in the right direction.

Automatically resize d3.js chart y scale based on brush selection

I'm using d3.js v4.
Is there a more convenient way to find the minimum and maximum values of a brush selection. This is meant to resize the y axis when I select a period in my brush area below.
Here is my method (everything is inside my function called when the brush is used) :
I find the extent limits of my selection
extent = d3.event.selection.map(chartComponent.x2().invert))
Then I have to redo an array containing all my selected points: I go on each point and compare it to the extent[0] or extent[1] to see if it is in the limits. I store the beginning and end point indices and then I use data.slice(begin, end) on my original data to get a new array.
Then apply d3.min and d3.max on the new array to find the min and the max level.
Then set the y axis to use theses limits.
chart.y().domain([chartComponent.ymin, chartComponent.ymax]);
chart.yaxisGraph.call(chartComponent.yAxis(true));
Do someone have a better idea ?

d3.js Force layout only applied vertically

I'm doing a data visualisation with d3. To give you some context,
the graph contains about 400 nodes (all data is loaded from multiple
json files) that are connected to each other
they are all mapped by year in a timeline (x axis)
the position in the y axis is completely randomized
the nodes have all different sizes
Now my question:
How can I distribute the nodes in the y axis so that they don't overlap?
You can checkout the full sourcecode on the GitHub Repository (work in progress - currently on the real-database branch).
This is a screenshot of how it currently looks:
Basically, in the tick() function, reset the nodes array x values to what you want them to be (presumably some scale to do with year), and the node and links will be drawn at those x values, and subsequent force calculations will start again from those values too
force.on("tick", function() {
// Adjust to what you want nodePos to be, here I'm just doing it by index
graph.nodes.forEach (function(nodePos,i) {
nodePos.x = i * 15;
//nodePos.x = xscale (data[i].year); // or whatever
})
// then normal node/link layout
I've forked this standard force-directed example by blt909 to show one way it could be done -->
http://jsfiddle.net/vztydams/
PS If you have a lot of items and very few discrete x values, best to give them a bit of wiggle room at first (i.e. a range in x they're contained to rather than a value), then slowly narrow that range down. Otherwise nodes will get 'stuck' behind each other.
Edit 02/03/16:
Hi Alvaro, essentially the graph.nodes is your linked data, as these are the objects that are attached to the displayed nodes as the data.
So if I set up a scale, and stick in a random year per datum:
var dom = [1994,2014];
var xscale = d3.scale.linear().domain(dom).range([20,400]);
graph.nodes.forEach (function(datum) {
datum.year = dom[0] + Math.floor (Math.random() * (dom[1] - dom[0]));
});
...
We can then restrict the x position of each node's datum like this:
graph.nodes.forEach (function(d,i) {
//d.x = i * 15;
d.x = xscale(d.year);
})
(As I say, if you have a lot of nodes and few years, you'd be better restricting to a range and then narrowing that range down on each subsequent tick)
http://jsfiddle.net/vztydams/2/

d3.js linkStrength influence on linkDistance in a force graph

I'm working on a graph to show relations between different nodes. The closer related the nodes are (according to business logic), the closer together the nodes should be.
I noticed that some links with the linkStrength of .1 are shorter (that is what I wanted to achieve) and some others with the same strength are longer than the ones with the linkStength of 1. From a documentation about the force layout parameters I now found this quote:
The default linkStrength value is 1.0, which maintains the full effect of linkDistance. By setting the value of linkStrength less than 1, though, the distance constraint can be relaxed.
Does that mean that if I was to set linkDistance to 150, the links with linkStrength(1.0) will be closer to 150 than the ones with linkStrength(.1)? And if yes, do they need to be shorter, longer or doesn't that matter at all? Because I was kind of surprised about the layout.
To cut a long story short: when using D3's force layout there is no built-in way to enforce a fixed length of the links. The force layout is inherently dynamic and setting values for force.linkDistance() and force.linkStrength() introduces just another force to the set of calculations carried out on each iteration, i.e. each tick, while the force layout is running.
There are three forces calculated on each tick:
1. Link length. The first force to be calculated is the adjustment for link lengths set via the above mentioned methods. This is done in a loop for each link and, looking at the source, this comes down to essentially one line of code:
l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
With l calculated to be the Euclidean distance between the link's source node and target node, the desired link distance distances[i] and the link's strengths[i] this line determines how to pull both nodes together or push them apart to approximate the link distance set via force.linkDistance(). It's easy to see, that the link strength has a linear effect on the resulting force. Contrary to the API documentation, though, which defines the valid range for the strength to be in the interval [0,1], the source code does not impose any restrictions on the value set by force.linkStrength().
2. Gravity. The second force to be calculated will take into account gravitational forces on each node.
3. Charge. Lastly, the mutual forces of the nodes' charges are calculated.
Because the effects of all the calculated forces are superimposed and will add up to the resulting movement of each node for a given tick, it becomes clear, that the link length is only one part of the entire computation. The other two forces may weaken or even reverse its effect.
As for your question
Does that mean that if I was to set linkDistance to 150, the links with linkStrength(1.0) will be closer to 150 than the ones with linkStrength(.1)?
The outcome depends largely on the setup of the force layout's parameters and the distribution of the nodes, and still no guarantee is made regarding the final lengths of the links.
The link strength sets the rigidity of the links, not the distance between the nodes (force layout doc). The distance of the nodes from each other is controlled by their charge. You can make the charge of a node dynamic like so:
var force = d3.layout.force()
.nodes(nodes)
.theta(0.1)
.charge(function (d) {
return -(d.radius * d.radius * 0.125);
})
.gravity(0.1)
.size([width, height])
.on("tick", tick)
.start();
A working example that uses this technique: vizz.ly

d3.js radial tree layout variable node length / text wrap node description

I am trying to create a radial hierarchy on d3.js based on mbostock's reingold-tilford tree example.
The block on bl.ocks.org is here: http://bl.ocks.org/ratnakarv/43087fb7f373338bf62c
Considering root node is 'Level 0', the text at some nodes at level 2 and some nodes at level 3 is encroaching on child node's text.
I am possible looking at two solutions:
1. Increasing the length of edges as the levels increase (as text length increases with levels in hierarchy)
2. including a word wrap function to give a neater look
or a combination of two.
Appreciate if some one can point me to ideas/code examples to incorporate these
[Note: I have a basic idea of d3.js. I am not a data visualizer and primarily constructing these visualizations to describe my main work]
D3's tree layout populates the x and y attributes on the data when you call tree.nodes(). Now it's just a matter of manipulating the y values to "spread out" the tree so that there's enough space between each level for your text.
A straightforward method of doing this is to simply scale each node's y value based on its depth. After the tree layout is created, do:
nodes.forEach(function(d) { d.y = d.depth * 180; });
Working fiddle here: http://jsfiddle.net/sk7krdrp/1/
You may even want to make the scaling a function of the maximum node text length.
This makes the tree a lot bigger, but it looks like that's what you want, as long as the texts don't overlap. If you want to keep the tree smaller and wrap text so that longer text appears in two or more lines, you could do that by wrapping each "line" in a tspan (example here). Of course, you will have to move around the neighboring nodes so that the wrapped text of a node doesn't overlap its neighbors.

Categories