Найдите ближайшего соседа точек с одинаковым значением при сравнении двух разных наборов данных в R

У меня есть 2 фрейма данных (df1 и df2), которые состоят из трех столбцов; Координата x, координата y, категория (с 5 уровнями A-E). Таким образом, у меня есть 2 набора данных точек, каждая из которых относится к категории.

e.g.

X    Y    Cat
1    1.5  A
2    1.5  B
3.3  1.9  C

и т. д. (хотя оба моих фрейма данных содержат по 100 точек)

Я хотел бы найти ближайшего соседа той же категории для каждой точки в моем первом кадре данных (df1) из второго кадра данных (df2).

Я использовал nncross в пакете spatstat, чтобы найти ближайшего соседа для каждой точки в df1 с помощью df2, а затем перечислить каждое из этих расстояний следующим образом;

# Convert the dataframes to ppp objects

df1.ppp <- ppp(df1$X,df1$Y,c(0,10),c(0,10),marks=df1$Cat)
df2.ppp <- ppp(df2$X,df2$Y,c(0,10),c(0,10),marks=df2$Cat)

# Produce anfrom output that lists the distance from each point in df1 to its nearest neighbour in df2

out<-nncross(X=df1.ppp,Y=df2.ppp,what=c("dist","which"))

Но я изо всех сил пытаюсь понять, как использовать метки категорий, хранящиеся в объектах ppp (как определено метками), чтобы найти ближайшего соседа из той же категории. Я уверен, что это должно быть довольно просто, но если у кого-то есть какие-либо предложения или альтернативные методы для достижения того же результата, я был бы очень благодарен.


person J. Cee    schedule 04.02.2016    source источник


Ответы (2)


Сначала немного искусственных данных для работы:

library(spatstat)

# Artificial data similar to the question
set.seed(42)
X1 <- rmpoint(100, win = square(10), types = factor(LETTERS[1:5]))
X2 <- rmpoint(100, win = square(10), types = factor(LETTERS[1:5]))

Затем простое решение (но оно теряет информацию об идентификаторе):

# Separate patterns for each type:
X1list <- split(X1)
X2list <- split(X2)

# For each point in X1 find nearest neighbour of same type in X2:
out <- list()
for(i in 1:5){
  out[[i]] <- nncross(X1list[[i]], X2list[[i]], what=c("dist","which"))
}

Наконец, уродливое решение, которое восстанавливает идентификатор соседа:

# Make separate marks for pattern 1 and 2 and collect into one pattern
marks(X1) <- factor(paste0(marks(X1), "1"))
marks(X2) <- factor(paste0(marks(X2), "2"))
X <- superimpose(X1, X2)

# For each point get the nearest neighbour of each type from both X1 and X2
# (both dist and index)
nnd <- nndist(X, by = marks(X))
nnw <- nnwhich(X, by = marks(X))

# Type to look for. I.e. the mark with 1 and 2 swapped
# (with 0 as intermediate step)
type <- marks(X)
type <- gsub("1", "0", type)
type <- gsub("2", "1", type)
type <- gsub("0", "2", type)

# Result
rslt <- cbind(as.data.frame(X), dist = 0, which = 0)
for(i in 1:nrow(rslt)){
  rslt$dist[i] <- nnd[i, type[i]]
  rslt$which[i] <- nnw[i, type[i]]
}

# Separate results
rslt1 <- rslt[1:npoints(X1),]
rslt2 <- rslt[npoints(X1) + 1:npoints(X2),]
rslt1$which <- rslt1$which - npoints(X1)
person Ege Rubak    schedule 04.02.2016
comment
Спасибо за это, это отлично работает с файлами точек, хотя мне также удалось решить эту проблему, создав матрицу расстояний из исходных фреймов данных! - person J. Cee; 05.02.2016

У меня также была еще одна попытка решить эту проблему, но, используя пакетную геосферу, чтобы создать матрицу расстояний из моих исходных фреймов данных, я нашел довольно простой способ решить эту проблему.

# load geosphere library 
library("geosphere")

#create a distance matrix between all points in the 2 dataframes
dist<-distm(df1[,c('X','Y')],df2[,c('X','Y')])

# find the nearest neighbour to each point
df1$nearestneighbor <- apply(dist,1,min)

# create a distance matrix where only the distances between points of the same category are recorded
sameCat <- outer(df1$Cat, df2$Cat, "!=")
dist2 <- dist + ifelse(sameCat, Inf, 0)

# find the nearest neighbour of the same category
df1$closestmatch <- apply(dist2,1,min)
person J. Cee    schedule 05.02.2016
comment
Очень красивое решение. Однако я думаю, вы теряете информацию о том, какая точка в df2 является ближайшим соседом к точке в df1. Вам это не нужно? Чтобы сделать это с помощью spatstat, вам просто нужно заменить distm на crossdist. - person Ege Rubak; 05.02.2016
comment
Да (хотя это и не обязательно), конечно, очень полезно иметь информацию о том, какие точки соседи и какие и поэтому ваше решение очень удобно для этого. - person J. Cee; 08.02.2016