3

In R, I need to calculate the distance between a coordinate and all the other coordinates.
I have a dataset similar to this:

ID Morph  Sex    E   N  
a    o     m    34  34    
b    w     m    56  34    
c    y     f    44  44   

In which each "ID" represents a different animal, and E/N points represent the coordinates for the center of their home range.

I'd need to calculate the distance between each center and the centers of all the other animals. Distance with himself should not appear.

Ideally, I need to obtain two different data frames: an exhaustive one with repeated distances (a to b & b to a):

ID.1 Morph.1  Sex.1  ID.2  Sex.2  Morph.2  Dist
a       o        m    b     m      w       12     
a       o        m    c     m      y       22   
b       w        m    a     m      o       12
b       w        m    c     f      y       13     
c       y        f    a     m      o       22   
c       y        f    b     m      w       13

And another data frame with just the non repeated distances:

ID.1 Morph.1  Sex.1  ID.2  Sex.2  Morph.2  Dist
a       o        m    b     m      w       12     
a       o        m    c     m      y       22   
b       w        m    c     f      y       13     

It is very important to me to keep the information of Sex and Morph in the new data frames! I am interested in comparing the distances among morphs (o-o, o-w, o-y, etc).

I've been doing it in Excel using Pithagoras but I am sure there must be a relatively simple code which could help me to ignite the process in R.

3

Sample data:

> d = data.frame(ID=c("a","b","c"),Morph=c("o","w","y"),Sex=c("m","m","f"),E=c(34,56,44),N=c(34,34,44),stringsAsFactors=FALSE)

Use the dplyr package (install this if you don't have it):

> library(dplyr)

Need one dummy variable that is 1 for each row so we can join the data frame to itself. There may be a better way to do this:

> d$ZZZ=1

Then this:

> d %>% full_join(d,c("ZZZ"="ZZZ")) %>% 
        filter(ID.x != ID.y) %>% 
        mutate(dist=sqrt((E.x-E.y)^2 + (N.x-N.y)^2))

The first line creates a data frame of all combinations of rows from d. The second line filters out the rows with the same ID. The last line computes the distance. The result is:

  ID.x Morph.x Sex.x E.x N.x ZZZ ID.y Morph.y Sex.y E.y N.y     dist
1    a       o     m  34  34   1    b       w     m  56  34 22.00000
2    a       o     m  34  34   1    c       y     f  44  44 14.14214
3    b       w     m  56  34   1    a       o     m  34  34 22.00000
4    b       w     m  56  34   1    c       y     f  44  44 15.62050
5    c       y     f  44  44   1    a       o     m  34  34 14.14214
6    c       y     f  44  44   1    b       w     m  56  34 15.62050
> 

and you can save this in something and do d$ZZZ=NULL to get rid of that dummy variable, even add an additional ZZZ=NULL term in the mutate clause at the end.

As for the non-repeating distances thing, that needs a bit more thought because you will be comparing floating-point numbers for equality, and that's not a good thing to do. But if your coordinates really are all integers then you might be able to not do the square root and get an integer squared distance which you can compare. Also, that's a separate question itself...

4

Here is a quite fast solution that uses data.table and distGeo{geosphere} to calculate distances on an ellipsoid.

library(geosphere)
library(data.table)

setDT(dt)[ , dist_km := distGeo(matrix(c(long, lat), ncol = 2), 
                                  matrix(c(long_dest, lat_dest), ncol = 2))/1000]

Data for reproducible example:

library(maps)
library(reshape)  

# Load world cities data and keep only 300 cities, which will give us 90,000 pairs
data(world.cities)
world.cities <- world.cities[1:300,] 

# create all possible pairs of origin-destination in a long format
dt <- expand.grid.df(world.cities,world.cities)
names(dt)[10:11] <- c("lat_dest","long_dest")

# calculate distances in meters:
setDT(dt)[ , dist_km := distGeo(matrix(c(long, lat), ncol = 2), 
                                  matrix(c(long_dest, lat_dest), ncol = 2))/1000]

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.