5

(note - this is the same piece of work as using multiple size scales in a ggplot, but I'm asking a different question)

I'm trying to construct a plot which shows transitions from one class to another. I want to have circles representing each class, and arrows from one class to another representing transitions.

I'm using geom_segment with arrow() to draw the arrows. Is there any way to:

  • make the arrows stop before they reach the circles
  • adjust the position so that if there is an arrow in both directions, they are "dodged" rather than overlapping.

I couldn't get position="dodge" to do anything useful here.

As an example:

library(ggplot2)
points <- data.frame( x=runif(10), y=runif(10),class=1:10, size=runif(10,min=1000,max=100000) )
trans <- data.frame( from=rep(1:10,times=10), to=rep(1:10,each=10), amount=runif(100)^3 )
trans <- merge( trans, points, by.x="from", by.y="class" )
trans <- merge( trans, points, by.x="to", by.y="class", suffixes=c(".to",".from") )
ggplot( points, aes( x=x, y=y ) ) + geom_point(aes(size=size),color="red",shape=1) + 
    scale_size_continuous(range=c(4,20)) +
    geom_segment( data=trans[trans$amount>0.6,], aes( x=x.from, y=y.from, xend=x.to, yend=y.to ),lineend="round",arrow=arrow(),alpha=0.5, size=0.3)

Example graph

Community
  • 1
  • 1
mo-seph
  • 6,073
  • 9
  • 34
  • 35
  • Interesting problem. This isn't directly possible in ggplot without first transforming the underlying data, but I've suggested in other places to dodge reciprocal arrows using [bending in the lines](https://andrewpwheeler.wordpress.com/2012/07/12/why-great-circle-lines-look-nicer-in-flow-maps/). [Here is an example](http://kbroman.wordpress.com/2012/10/11/curved-arrows-in-r/) of making such curved arrows in R. It will still be a complicated plot though, I might suggest small multiples where you facet by initial stage, so each facet only shows the out-flows. – Andy W Feb 01 '13 at 16:24

2 Answers2

6

I thought since nobody has given a solution i would provide an example of package more aimed a this sort of problem:

vecs  <- data.frame(vecs =1:6,size=sample(1:100,6))
edges <- data.frame(from=sample(1:6,9,replace=TRUE), to=sample(1:6,9,replace=TRUE))

library(igraph)

g      <- graph.data.frame(edges, vertices = vecs, directed = TRUE)
coords <- cbind(sample(1:20,6), sample(1:20,6))


plot(g, vertex.size=V(g)$size,vertex.color="white",layout=coords,axes=TRUE)

This will at least solve your arrows before the circle issue and also when there are reciprocal arrows it will adjusts them with the curved lines as in 2<->5:

enter image description here

(arrrow sizes, line widths, colours etc can of course be modified)

user1317221_G
  • 15,087
  • 3
  • 52
  • 78
  • 1
    It appears that the curve between 2 and 5 is due to multiple edges pointing in the same direction between two points. For some reason, autocurve.edges() does not seem to add curvature if the two edges are in opposite directions (you can even see this here with 2->5 being straight). Any suggestions to force curvature with overlapping edges with same directions? – Etienne Low-Décarie Jun 01 '13 at 17:40
3

I've put together a simple extension of geom_segment, which allows specification of

  • shortening at the start and end of the lines
  • an amount to offset lines which share a reversed source and destination

It's up on pastebin here: geom_segment_plus.

I used code along the lines of this:

ggplot( points, aes( x=x, y=y ) ) + geom_point(aes(size=size),color="red",shape=1) +
    scale_size_continuous(range=c(4,20)) + 
    geom_segment_plus( data=trans[trans$amount>0.3,], 
        aes( x=x.from, y=y.from, xend=x.to, yend=y.to ),
        lineend="round",arrow=arrow(length=unit(0.15, "inches")),
        alpha=0.5, size=1.3, 
        offset=0.01, shorten.start=0.03, shorten.end=0.03)

It's definitely not perfect, but it works - you can see a double arrow going to the bottom left point here.

offset, shorten.start and shorten.end are the aes elements added. They can be set to data points, but I haven't figured out how to scale them properly.

enter image description here

mo-seph
  • 6,073
  • 9
  • 34
  • 35
  • 1
    Adding a comment for the original poster of 'geom_segment_plus'. This perfectly solves a problem I'm having but it doesn't seem this code works with the changes to ggplot 2.0. Any chance of a a fix? My [SO post is here:](http://stackoverflow.com/questions/35904363/offset-geom-segment-in-ggplot) – noLongerRandom Mar 10 '16 at 16:18
  • I try to use your code, and get: `Error in eval(quote({ : could not find function "eval"` I also found other version from https://stackoverflow.com/questions/52568493/how-to-offset-arrow-lengths-in-ggplot, but then i get: `Error in geom_segment_plus(arrow = arrow(length = unit(0.2, "cm")), lineend = "round", : attempt to apply non-function` – AAAA Mar 09 '21 at 17:13
  • I updated mo-seph's code so it now works with ggplot2 v3.3: https://gist.github.com/burchill/9d07c306c90f10a50efae6f2c14e288f – Zeke Nov 30 '21 at 18:18