Ordering chords based on some property of the source data - d3.js

I have a chord diagram in d3 that looks something like this:
The color of each group is calculated base on a property of the source data (before being transformed to a matrix) and is looked up from the index of each group with something like this:
var groupPath = group.append("path")
.attr("id", function(d, i) { return "group" + i; })
.attr("d", arc)
.style("fill", function(d, i) { return MySourceData[i].inArea ? "blue" : "red"; });
Now what I'd like to do is sort the chords so that all the blues are together and all the reds are together, but there doesn't seem to be a simple way to do that. I know you provide a function to sortGroups (and sortSubGroups for that matter), but it seems the arguments passed to the function are only the values of the chords and I would need to have the indexes to be able to figure out if they should be grouped together or not.
Is there any simple way to sort the groups based on some criteria that is ultimately derived from the index?

It seems the best way to achieve this is to simply order my source data first before I transform it into a matrix. This works fine if the data isn't going to change (which, in my case it wasn't), but if you had data that might change and in changing might end up needing to be resorted, this would be a real pain since you'd need to reorder the matrix.

Related

D3.js: How to get the selection index, instead of the data index?

There are a lot of instances where i need to act upon the index of the element inside it's selection group (update, enter, exit), instead of it's index within the data. How can i get this index?
For example, if i am exiting 4 elements.
legends.exit()
.transition()
.delay(d=>i*25)
.style("opacity",0)
i in this case are [8,12,15,20]. How do i set the index to be [0,1,2,3];
My quick and dirty solution is this. Can i do this within the context of d3?
var i = 0;
legends.exit()
.transition()
.delay(d=>i++*25)
.style("opacity",0)
You should be able to adapt the following code to your solution:
.delay(function(d,i){ return (i-legends.size()); })
Alternatively, the following worked in my small test example but I'm not sure why exactly. The filter is necessary.
legends.exit().filter(function(d){return true;})
.transition()
.delay((d,i)=>i*25)
.style("opacity",0)
My best guess would be that applying the filter 'resets' the indexing of the selection.

Control what elements get shown in dc.js datatable

I'm stuck on a seamingly easy problem with dc.js and crossfilter.
I have a datatable on a dimension and it shows my data correctly. The problem is that I want to show the 'worst' data items but the datatable picks the 'best' items in the filters.
The following is a scetch of the current table code.
var alertnessDim = ndx.dimension(function(d) {return d.alertness1;});
dc.dataTable(".dc-data-table")
.dimension(alertness1)
.group(function (d) {
return d.DATE.getYear();
})
.columns([
function (d) {
return d.DATE;
},
function (d) {
return d['FLEET'];
},
function (d) {
return d.alertness1;
}
])
.sortBy(function (d) {
return d.alertness1;
})
.order(d3.ascending);
This connects to the crossfilter properly and it sorts the items in the correct order, but the 25 items it is showing are the ones with the highest alertness values, not the lowest.
Anyone have any ideas on how to solve this, preferbly without creating another dimension?
You are right to be confused here. You would think this would be a supported use case but it is not, as far as I can tell. The data table uses dimension.top so it is always going to take the highest values.
So I don't think there is a way around using a special dimension with opposite ordering/keys. For the other charts you could use group.order in order (heh) to get it to return the lowest values. But the data table doesn't use a group because it's not reducing its values.
It's confusing that the data table also has an order parameter which doesn't help here.
Hope that is acceptable. Otherwise I think you'd have to poke around in the code. Pull Requests always welcome! (preferably with tests)
One quick way to achieve descending sort on a specific column, is to sort by its negative value:
.sortBy(function(d){ return -d.ALARM_OCCURRENCE; }); // show highest alarm count first

Using fields in inherited bound data

I am attempting to get my head around using bound data with d3.js. I'm following the documentation and am now a little confused.
I want to produce donut charts with radii that vary depending on the data. I am comfortable producing the arcs to make up a donut using an array, but am having a hard time working out how to pass along a size parameter with the data binding of the arc. For example, if the data bound to the parent of the arc is something like {size: 20, cont: [1, 7]}, how can I bind the first element of the array as well as the size element? I have a fiddle attempting to show what I am talking about. In that example, the two donuts should be different sizes. I have commented out the kind of thing I suspect should be going on on line 14.
I have tried variations on:
var arcs = donuts.selectAll(".arc")
.data(function(d) { var temp = [];
temp.push(d.cont);
temp.push(d.size);
return temp; })
.enter()
.append("g")
.attr("class", "arc");
But it is clearly not producing what I expect.
The problem here isn't really the data inheritance, but the fact that you're passing the original data to a layout and then only the result of that to your drawing functions. The pie layout does store the original datum in the .data member of the result, but you're only passing it part of the original data.
The "proper" thing to do would be to refactor your data structure such that you can pass it in as-is and use the pie layout's .value() function to tell it how to access the data. Then you can directly access the original data.
There's however a quicker solution -- you can simply use the indices that are passed to your function to index into the original array. The code for this would look like this.
.attr("d", function(d, i, j) { return arc.outerRadius(dataset[j].size)(d); })
Note that you need two indices here because you have nested data -- i would be the index within your array of values for a single pie chart, whereas j denotes the index of the element at the level above that. Updated jsfiddle here.

D3 multi line chart - strange animation

I have created a multi line chart with a simple animation. In the beginning there are no data and after clicking a button new values are emulated and the line "move" left. The move is animated using a "shift".
The problem occurs when the lines "fill" the whole graph area (that means there are y values for all x values) and then the lines are animated in a different way. It looks like the y values are animated on a curve, not slided to the left.
The animation works good for both axes:
svg.selectAll("g .x.axis")
.transition()
.duration(500)
.ease("linear")
.call(xAxis);
svg.selectAll("g .y.axis")
.transition()
.duration(500)
.ease("linear")
.call(yAxis);
And not for lines (this code helped me a lot)
svg.selectAll("g .city path")
.data(processedData).transition().duration(500)
.ease("linear")
.attr("d", function(d, i) { return line(d.values); })
.attr("transform", null);
The Fiddle is accessible here.
Thanks for help.
The problem is that you're deleting data when there is too much. The way d3 matches data to existing data (when you call the .data() function) is by index. That is, the first element in the array that you pass to .data() matches the first bound data element, regardless of what the data actually looks like.
What happens in your case is that as soon as you start deleting data, the individual data points are updated instead of shifted. That's why you're seeing the "squiggle" -- it's updating each data point to its new value, which is the value the data point to the right had before.
With the code you currently have, this is hard to fix because you are not matching the data for individual lines explicitly. I would recommend that you have a look at nested selections which allow you to draw multiple lines and still explicitly match the data for individual ones. The key is to use the optional second argument to .data() to supply a function that tells it how to match the data (see the documentation). This way you can tell it that some data points disappeared and the other ones should be shifted.
you can get around this problem in 2 step.
in function update() : redraw you .data() with the new point at the end but without remove the first old point (with animation), like that each key is the same before and after transition.
at the end of function update() : you can remove the old value and redraw .data() without animation.

Update multi-line graph D3.js

I'm attempting to update a d3 multi-line graph by pulling data at 5 second intervals from a mySQL database using PHP. At the moment the graph is displaying but not updating - I know that there is something not right with my updateData function but have tried everything can think of and nothing seems to work. Can anyone please help?
https://gist.github.com/Majella/ab32fe0151fd487da3f6
UPDATE:
As you can see the x-axis line is only showing sporadically and some of the lines aren't lined up with the y-axis.
Updated gist:
https://gist.github.com/Majella/ab32fe0151fd487da3f6
UPDATE 2: For some bizarre reason the lines are changing colour - or moving completely not exactly sure. So while on graph above the lines are from top - blue, orange then white - when graph updating the blue might move to bottom with orange on top and white in middle etc - but happening randomly?
In your original drawing of the graph, you correctly use:
var parameter = svg.selectAll(".parameter")
.data(data, function(d) { return d.key; })
.enter().append("g")
.attr("class", "parameter");
which joins the data (data) to the elements (g.parameter)
During your update function, you will need to join the data again in order to perform updates, deletes, and adds of elements. The 3 little circles tutorial is an excellent place to learn more about this.
Anyway, in your update function, you may want something like this (untested):
// re-acquire joined data
var containers = svg.selectAll("g.parameter")
.data( data );
// update existing elements for this data
containers
.select( "path.line" )
.attr( "d", function(d) { return line(d.values); })

Resources