corybrunson / ggalluvial

ggplot2 extension for alluvial plots
http://corybrunson.github.io/ggalluvial/
GNU General Public License v3.0
492 stars 35 forks source link

Ordering of streams within each block #3

Closed tanerumit closed 7 years ago

tanerumit commented 7 years ago

Hello,

I'm trying to replicate a plot I made with alluvial() using ggalluvial. Everything works perfect, but there is one annoying problem (at least for me). In ggalluvial, I cannot find a way to order the streams within each block.

So here is the problem I am facing:

alluvial() version: alluvial

ggalluvial version ggplot2

As you see in the first plot, the red streams are grouped within each block, so it is easier to identify. In the ggalluvial version, lightgrey and red streams are mixed (see the last two columns).

the code I am using for plotting is:

ggplot(alluv_data, aes(weight = n, axis1 = variable, axis2 = prec, axis3 = dem, axis4 = sed)) + theme_light() + geom_alluvium(aes(fill = colorkey), ribbon_bend = 1/5) + geom_stratum(axis_width = 1/12, color = "black", fill = "gray90", size = 1) + geom_label(stat = "stratum")

So what I want to do is I'd like to sort the streams in each block based on the column colorkey'.

Thanks!

tanerumit commented 7 years ago

OK, I realized that the order of the streams in alluvial() is controlled by the row order. However, I in the ggplot2 version, even if I shuffle the rows, I get the same plot. How do I change it?

corybrunson commented 7 years ago

The within-axis ordering is determined by the zigzag() function, which attempts to minimize the number of alluvium overlaps between adjacent axes. This can also be seen by comparing the plots above. Making other options available via a new parameter is on my list (see the README), but it will be a few weeks until i can experiment with this package a bit. I'd definitely welcome PRs on this front. : )

tanerumit commented 7 years ago

i actually solved the problem. A very quick & dirty solution but replacing the zigzag function with the one below results the same ordering with alluvial():

zigzag_new <- function(n, i, k=1) { 
  c(union(i,k), setdiff(1:n, union(i,k)))
}

in this case, k is the my color coding axis. Order: i, k, and then rest of the axes.

corybrunson commented 7 years ago

Aha—yes, if one of the axes determines the alluvium color scheme, then positioning that axis second (after the index axis) at each step is enough; the remaining axes could be taken in any order. If, on the other hand, the color scheme depends on a variable not used as an axis, then i see no way to impose this constraint.

My plan for a future update is to add a new character-valued parameter that picks from among several available ordering functions, including the one from alluvial that proceeds rightward after the index axis and a counterpart that proceeds leftward. I'd expected only to include the axis variables as arguments; but, now that you've raised the point, it also makes sense to incorporate other aesthetic parameters into some of the options. Thanks for bringing this up.

mbojan commented 7 years ago

@tanerumit It seems you found a solution. However, please have a look at the section "Adjusting vertical order of alluvia" of the vignette in alluvial package. It is an experimental feature but you should able to reorder the alluvia on any of the axes in any way you want.

I hope these features get integrated across the two packages at some point (zigzag() in alluvial() and alluvial reordering in ggalluvial()).

corybrunson commented 7 years ago

Today i incorporated a couple of parameters to handle some of these issues. (In the future i'll try to be more careful about leaving changes on a branch for experimenting before merging everything to master; i'm still relatively new to this.) Referring to the intersections of alluvia with strata as "lodes", the parameter lode_favor determines whether axes (the default) or aesthetics take precedence when ordering the lodes, and the parameter lode_order determines the function that sorts the axes for the process (defaults to the zigzag function but now has leftward and rightward options). Both parameters are character-valued. I included some examples in the alluvium documentation.

I still need to try out the completely user-controlled vertical adjustments in alluvial, so i'll keep this issue open in reference to that task. Thanks, @mbojan , for reminding me about it. : )

corybrunson commented 7 years ago

Try using lode_ordering in the new branch omit-aggregation. See the alluvium documentation.

The primary obstacle to something like alluvial's ordering parameter is that StatAlluvium$setup_data() aggregate()s the imputed data frame by weight, so that otherwise equivalent alluvia are combined. This is done so that no two rows in the data frame returned by setup_data() are assigned to the same group. This, in turn, is because of the central "cheat" of ggalluvial: I override GeomAlluvium$draw_group() to draw the alluvia, so that i need only invoke grid::xsplineGrob() once inside the draw_*() function. The alternative would be to override draw_panel() instead and have to draw multiple splines manually—and i don't know how to do that using grid. With a bit more time i can produce a small example and post it to Stack Overflow.

The most straightforward way this can be done, i think, is to have setup_data() override group (whether user-specified or internally generated) to be 1:nrow(data), which produces one spline per row of the original data frame, and skip the aggregation. This is computationally redundant but visually (almost) undetectable. The user can then give a list of vectors as the argument to an ordering-like parameter, which is used to order the lodes within each axis and stratum, overriding lode_aes and lode_order. The lode_ordering parameter is my attempt!

corybrunson commented 7 years ago

OK, i've merged in the new parameters, with different names: lode.guidance for the function that defines the axis preferences for lode sorting, the logical bind.by.aes to put aesthetics first (after the index axis), and lode.ordering to mimic the behavior of ordering in alluvial, with the added ability to give a matrix argument instead of a list. Thanks for the great suggestions and prompts.

corybrunson commented 7 years ago

Any objections to closing this issue? See the new vignette for an illustration.