Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proportionally sized arrows in ggplot

Tags:

r

ggplot2

Building on ggplot2's seals example, I'm trying to change the thickness of arrows so their overall size better reflects the data variable. I can specify length and thickness, but don't know how to change the size of the arrow-head. Very grateful for any suggestions.

require(ggplot2)
require(grid)

d = seals[sample(1:nrow(seals), 100),]
d$size = sqrt(sqrt(d$delta_long^2 + d$delta_lat^2))

ggplot(d, aes(x = long, y = lat, size = size)) +
  geom_segment(aes(xend = long + delta_long, yend = lat + delta_lat), arrow = arrow(length = unit(0.1,"cm")))

enter image description here


Edit

Solution code:

ggplot(d, aes(x = long, y = lat, size = size)) +
  geom_segment(aes(xend = long + delta_long, yend = lat + delta_lat), 
               arrow = arrow(length = unit(d$size/3, "cm"), type='closed')) +
  scale_size(range = c(0, 2))
like image 209
geotheory Avatar asked Dec 18 '13 10:12

geotheory


1 Answers

I can't say this is a complete solution to your problem, but at least it can be a start.

ggplot(d, aes(x = long, y = lat, size = size)) +
  geom_segment(aes(xend = long + delta_long, yend = lat + delta_lat), 
              arrow = arrow(length = unit(0.7, "cm"))) + 
  scale_size(range = c(1, 2))

My changes are minimal: bigger arrow heads and size scale. The upper limit on size scale is the most important if you aim to avoid overplotting.

enter image description here

From now on, it's probably a good idea to leave only the arrow heads, since the lines are not visible when the size is small. Here's a dirty hack for that:

 ggplot(d, aes(x = long, y = lat, size = size)) +
   geom_segment(aes(xend = long + delta_long/100, yend = lat + delta_lat/100), 
               arrow = arrow(length = unit(0.7,"cm"))) + 
   scale_size(range = c(1, 2))

enter image description here

Of course, it is crucial to keep an appropriate visual relation between big and small values! Otherwise, your plot may become misleading. But that depends on the data, so I cannot give you further advice. Sorry if that's an obvious point.

UPD: turns out, unit() function is vectorized, so it comes to the rescue!

ggplot(d, aes(x = long, y = lat, size = size)) +
  geom_segment(aes(xend = long + delta_long/100, yend = lat + delta_lat/100), 
              arrow = arrow(length = unit(d$size * 5,"cm"))) + 
  scale_size(range = c(1, 2))

enter image description here

like image 97
tonytonov Avatar answered Oct 22 '22 19:10

tonytonov