4
votes

(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

2
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. Here is an example 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

2 Answers

4
votes

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)

3
votes

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