I need to arrange several plots in a (possibly "incomplete") rectangular array. (By "incomplete" I mean that the last row of the array may have fewer cells than does the first row.)
I implemented an approach for doing this that relies on HTML tables, as shown in this silly example, but it seems to me that this is the kind of thing that should be done entirely within d3.js. Before re-inventing the wheel, is there a built-in way to do this? (I'm hoping to find a way that incorporates refinements, such as axes and tickmarks, and takes care of corner cases, including "incomplete" arrays, as defined above.)
You can do this with the same selections API you used to build the chart itself. So instead of something like d3.select('body').append('svg'), you can do this:
var svg = d3.select("body").selectAll(".pie")
.data(data)
.enter().append("svg")
.attr("class", "pie")
.attr("width", radius * 2)
.attr("height", radius * 2)
Demo using multiple donut charts: http://bl.ocks.org/mbostock/3888852
Related
I would like to create a D3 graph like the image in this post.
Force Layout seem to be the best option, but, my nodes has to have different distances, and each node has to have different size.
So, should I insist on Force Layout?
I couldn't find any example similar to my problem, and it's being very hard to understand how to write down some code to implement those different distances and sizes.
The graph I want to make (it's my first question, so I don't have reputation to put an image directly in this post):
https://i.ibb.co/Tk0hHkv/toaskd3.png
Different link distances and different radii can be achieved in d3js.
You can add a radius property to each node, i.e. your nodes should look something like {r:5, id:1, ...}. Then, when its time to create corresponding svg elements, you can do something like
var circles = svg.append("g").selectAll("circle").data(nodes);
circles.enter().append("circle").attr("r", function(d) { return d.r; });
Similarly, you can add a dist property to each of your links. Then add a link force to your simulation like this:
var sim = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links)
.distance(function(l){
return l.dist;
})
.strength(1)
);
For a working example, you can check this codepen I just created. You can always check the official API reference for detailed information.
In my opinion, D3's force layout is a very flexible option for depicting graphs.
I have been working on a d3 visualization in v4, which is a scatter/bubble plot with data points that have been filled with an image (see JS Bin link).
The problem I have is that a lot of the data points overlap, so I would like to have the overlapping points move until they are next to each other (the exact location of the points isn't too important).
I am new to d3 and have been struggling to get my head around simulation (collision detection, forceCollide etc) and would appreciate some help on how I can achieve this.
My attempts so far result in the initial x and y data points being ignored and the result is one big circle of non-overlapping circles. But what I'm after is the initial x and y values to be preserved and ONLY the overlapping circles be moved (so the outliers should still be outliers).
I've created an example on JS Bin (below) to demonstrate what I have. In particular, the force simulation code (when uncommenting simulation.stop(); ) seems to overwrite the initial x and y values.
I feel like I'm almost there but I'm doing something in the wrong order...
var simulation = d3.forceSimulation(data)
.force('charge', d3.forceManyBody().strength(3))
.force('collision', d3.forceCollide().radius(function(d) { return d.radius + 1 }) )
.on('tick', function() {
svg.selectAll('.node')
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
})
simulation.stop();
JS Bin Example
I have a similar chart issue and was able to get the desired behavior by applying 3 forces: d3.forceX, d3.forceY to maintain the plotted points and then d3.forceCollide which treats each circle not as a point but a circle with a radius so they don't overlap.
You had a couple issues. First, utilizing simulation.stop() will stop the simulation from running and applying your force calculations. I believe simulation.stop() is used if you want to manually control the simulation using simulation.tick() see d3 docs for .stop(). Also, I don't think using d3.forceManyBody is needed, but if there are other posters with more experience on that I'd love see a discussion. Lastly, I positioned the simulation to run after your chart was initialized. The graph needed to be initialized, so you could then reference your circle nodes and apply the force layout(or so I believe, like I said, I'm still new to d3).
You can check out an altered JSbin here
This is an older questions with no answers, but I thought I'd chime in if it helps someone else, and maybe stir up discussion on some of the parts I'm not as clear on either. Hopefully that is what you were looking for.
I've been working with Mike Bostock's stacked bar chart (here: https://bl.ocks.org/mbostock/4679202).
I've successfully made a number of modifications, but what I'm stuck on is trying to add a y axis with ticks and properly scaled values.
I thought it would simply be done by using this:
var yAxisRight = d3.svg.axis().scale(y2) //define ticks
.orient("right").ticks(5);
However, that results in the values for only ONE set of the stack being used for the entire Y axis. This results in an incorrect scale. The values for the range of all stacks COMBINED needs to be used to determine the range of values I believe.
Is there an easy way to do this that I'm missing? To sum the range of all the columns.
If not, how would I write a function to set the range based on the values in all 4 columns?
Here is a working JSfiddle of what I have now (which is incorrect):
https://jsfiddle.net/1jhm7ths/
If I understood correctly what you tried to achieve, you need to compute your range based on your stacked data and not the original ones. I updated your jsFiddle with the following modification on line 92:
y2.domain([0, d3.max(dataByGroup, function(d) { return d3.sum(d.values, function(v) {return v.value;}); })]); //added
What this does is taking each group, computing the sum of all values, and the taking the max of the sums.
On a side note, I would discourage learning d3 v3 and try to focus on the v4 for longer term support, latest functionalities, modulariy, and a ton of other advantages.
Say I have some data (e.g. book loans and book returns). I want to plot this in a histogram, but I also want it to be 'stacked' in that book loans should be positive (above the x axis) while book returns should be negative (below the x axis). I've not been able to find any documentation on this, and the closest example is this one, but it uses the old v3 of d3 (which has changed a LOT regarding the stack api), and it also doesn't have negative stacks. The key thing here is that originally the book return data point isn't negative (in fact, it can't be, since the histogram api generates the bins), so I can't simply force that one series to appear below the axis in that way.
edit: after struggling with histograms + bins for a while, I finally got an example going at this pen. In my case, I'd like for the 'FixedRemaining' series to appear below the axis while the 'Remaining' series stays above. The data is pretty simple, and the code for drawing the rects is taken mostly from this example. In particular, I suspect this line would have to change:
.attr("y", function(d) { return y(d[1]); })
since it currently just sets the height to be above the axis. I know the domain would also have to be modified to take into account the below-axis part.
Answered by mbostock in this block here: https://bl.ocks.org/mbostock/b5935342c6d21928111928401e2c8608
I want to be able to implement the D3 General Update Pattern III as a D3 pack layout. This means that each char would be inside a circle and that the char circles would be inside a larger enclosing circle. Everything should stay consistent with the GUPIII except that, of course, the positioning of the chars will be in a larger circle instead of on a horizontal axis.
I have tried to follow the same GUPIII structure including the important aspect of using a data join with key.
var alphaBubble = d3.layout.pack()
.size([diameter, diameter])
.padding(5);
var nodes = alphaBubble.nodes(data);
var vis = svg.selectAll("circle")
.data(nodes, function(d) { return d; });
But data join with key does not seem to work with the pack layout.
I'm having trouble getting my head around possible alternative mechanisms such as pack.value or nest. I have not been able to grasp whether they would somehow accomplish what is going on in GUPIII where incoming (enter) chars are green, existing (update) chars are black, exiting chars are removed.
Join by key is important in order to maintain the relative position of the char. I realize that this aspect of relative position is not as easy to see in the pack layout as it is in the linear axis layout, but I have a necessity for this which is not apparent in this example. I just need to successfully implement a "by key" join with pack that retains existing nodes when new data comes in.
Typically what I see with the pack layout is that either no nodes are replaced on data updates (maybe only attributes such as size are altered), or else all nodes are replaced with new incoming data, depending on the context. But I want to retain the nodes that are the same (by some key, in this case the char itself), remove the ones that are no longer present, and add the new ones "by key". Can anyone help me translate the D3 General Update Pattern II to a pack layout? I would include my full code attempt, but I really think just looking at the GUPIII is more clear if you are familiar with how the pack layout can facilitate join by key on update.
Thank you.
Ok, through much trial and error and study of existing examples I have been able to solve my question as to how to implement the D3 General Update Pattern III as a pack layout. I was not able to find any example that used data join by key, which is necessary for object constancy on update, so I'm hoping my example may provide some guidance for others who need the same pattern of behavior.
My solution is here. And my Javascript code is here.
I will point out a few important highlights. First, unlike in the linear non-pack version, the data join-by-key value must be accessed as an object property (i.e. d.id) instead of directly (i.e. d).
// Data join by key to <g> nodes
var node = svg.selectAll(".node")
.data(nodes, function(d) {
return d.id;
});
// Data join by key to circles
var circles = svg.selectAll("circle")
.data(nodes, function(d) {
return d.id;
});
Next, since the alphabet character is represented in a circle, both the circle and the char must be contained in a <g> element, i.e. node. And here was the trickiest part: the data needs to be joined by key separately to both the node and the circle elements. This is because the pack layout has performed the necessary calculation of scale (i.e. circle radii) and coordinate positions. So the circles need the data's calculated radii and the <g> node elements need the calculated coordinate positions. So a separate data join was required for each.
Once I figured this out, all of the necessary operations and references fell into place and the pack version of the General Update Pattern started working properly. Be sure to compare the linear and pack versions to each other.
The alphabetization sort is, I guess, unnecessary since the pack layout makes no accommodation for ordering. But I retained the sorting for consistency and because I wanted to observe its effect.