8

I have a question about how best to visualise the predicted direction of migration given a set of points with estimated ages.

Imagine I have a set of geo coordinates, representing communities. For each point, I have an estimate of the age of that community. I want to draw an arrow on the map that indicates the most likely direction of general migration over time. I also want the magnitude of the arrow to represent how consistent the gradient is / how confident we are that there's a meaningful alignment between time and space. I'm not asking about the theory of the process of migration, just about how you would decide on the angle, position and magnitude of the arrow.

For example, below red points are older than green points. In the first box, it looks like a sensible conclusion that migration was South-East. In the second box, there's no clear pattern, so the arrow is smaller.

Example

Is there a standard, principled way of doing this? One way I thought of was to draw a vector between the oldest point and the 2nd oldest, then between the 2nd oldest and 3rd oldest, and so on. Then sum the vectors (but how to decide the start location?). Or maybe you need to take the vectors between all pairs of points and weight them by the relative ages? Or maybe I need to work out the contours, then draw a path that starts at the highest point and heads downhill?

An R-based solution would be ideal.

  • it reminds me to regression models or to surfaces, calculating the flow direction. – Andreas Müller Feb 16 '18 at 9:33
  • 1
    Yes, fitting z~x+y to the data points gives the equation of a plane that fits the points, then draw an arrow with direction defined by the coefficients and size based on significance. Centre the arrow at the centroid of the points. – Spacedman Feb 16 '18 at 14:36
  • This is an excellent question (+1). I would recommend adding your edit as an answer. Tacking the edit on to the question leaves your question a bit unfocused. It is best to open a new question asking how to improve features of your code. – Aaron Feb 22 '18 at 2:16
2

Here's my current solution, based on the suggestion above to fit a surface. This solution does not address scaling properly, so the coordinates and z values must be small (e.g. between -1 and 1).

drawDirection = function(x,y,z){
  # use linear model to fit surface
  m = lm(z ~ x+y)

  # Treat coefficeints like vectors and 
  # define arrow start and end points
  arrow = c(0,0,
            m$coefficients["x"],
            m$coefficients['y'])
  # Move arrow to centroid
  adj.x = mean(x)- (m$coefficients["x"]/2)
  adj.y = mean(y)- (m$coefficients["y"]/2)
  arrow = arrow + c(adj.x,adj.y,adj.x,adj.y)

  # colours for points
  # Yellow = higher = more recent
  z.col = heat.colors(10)[as.numeric(cut(z,breaks=10))]
  # Plot points with some extra space around
  plot(x,y, col=z.col, pch=16,
       xlim=c(min(x)-sd(x)*2,max(x)+sd(x)*2),
       ylim=c(min(y)-sd(y)*2,max(y)+sd(y)*2))
  # Plot arrow
  # (scale width by 10 x the R squared)
  arrows(arrow[1],arrow[2],arrow[3],arrow[4],
         lwd = 1+(10*summary(m)$adj.r.squared))
}

par(mfrow=c(1,2))

# Simulate some data
n = 30
# Correlated North-East
x = runif(n,0,1)
y = jitter(x,amount=1)
z = jitter(x+y,amount=1)
x = x
y = y
drawDirection(x,y,z)

# Uncorrelated
x2 = runif(n,0,1)
y2 = runif(n,0,1)
z2 = runif(n,0,1)

drawDirection(x2,y2,z2)

Output

However, I think I'm doing the scaling wrong - if I increase the range of x and y, then the arrow doesn't scale properly:

# Correlated North-East, 
#  with x and y variables between 0 and 360
x = runif(n,0,1)
y = jitter(x,amount=1)
z = jitter(x+y,amount=1)
x = x*360
y = y*360
drawDirection(x,y,z)

Arrow length is not scaling properly

So for now you could scale the geo coordinates and z value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.