R - Spatstat - Поиск ближайшего соседа по идентификатору с использованием таблицы данных - PullRequest
3 голосов
/ 04 марта 2020

У меня есть два больших кадра данных с именами intersections (представляющими пересечения уличной системы) и users (представляющими пользователей сети) следующим образом:

intersections имеет три столбца: x , y и label_street. Они соответственно представляют положение пересечения в прямоугольном окне наблюдения (скажем, [0,5] x [0,5]) и улицу, на которой оно находится. Вот пример:


intersections <- data.frame(x=c(0.147674, 0.235356, 0.095337, 0.147674), y=c(0.132956, 0.150813, 0.087345, 0.132956), label_street = c(5,6,5,6))

head(intersections)

            x        y label_street
1    0.147674 0.132956            5
2    0.235356 0.150813            6
3    0.095337 0.087345            5
4    0.147674 0.132956            6

Пересечение, расположенное на пересечении нескольких улиц, каждая комбинация (x,y) в таблице intersections появляется как минимум дважды, но с разными label_street (например, строки 1 и 4 в предыдущем примере). label_street может не быть номером строки (поэтому в моем примере он начинается с 5).

users имеет 4 столбца: x, y, label_street, ID. Они соответственно представляют позицию пользователя, улицу, на которой он расположен, и уникальную ID на пользователя. В этом кадре данных нет дубликатов, поскольку пользователь находится на уникальной улице и имеет уникальный ID. Вот пример (ID и label_street могут не быть номером строки)


users <- data.frame(x = c(0.20428152, 0.17840619, 0.12964668, 0.20423856, 0.19349761, 0.10861251), y = c(0.14448448, 0.13921481, 0.11724543, 0.14447573, 0.14228827, 0.09891443), label_street = c(6,6,5,6,6,5), ID = c(2703, 3460, 4325, 12506, 19753, 21282))


head(users)
              x          y label_street      ID
1    0.20428152 0.14448448            6    2703
2    0.17840619 0.13921481            6    3460
3    0.12964668 0.11724543            5    4325
4    0.20423856 0.14447573            6   12506
5    0.19349761 0.14228827            6   19753
6    0.10861251 0.09891443            5   21282

Я хочу сделать следующее: для каждой точки (x,y) из intersections, получить ID и расстояние до ближайшего соседа , разделяющих то же самое street_label в users

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

Мое рабочее решение выглядит следующим образом:

1) Написать пользовательскую функцию, которая получает идентификатор и расстояние до ближайшего соседа строки в таблице запросов

 NN <- function(row,query){
 df <- row
 window <- c(0,5,0,5)   #Need this to convert to ppp objects and compute NN distance using nncross
 NN <- nncross(as.ppp(row[,1:2],window),as.ppp(query[,1:2],window))
 df$NN.ID <- query$ID[NN$which]
 df$dist <- NN$dist
 return(df)
}

2) Примените эту определяемую пользователем функцию по строкам к моим «пересечениям» фрейма данных с запросом, представляющим собой подмножество пользователей, использующих ту же самую уличную метку, что и строка:

result <- adply(intersections, 1, function(row) NN(row, users[users$label_street == row$label_street, ])

Результат как Вот пример:

head(result)
           x           y    label_street     NN.ID         NN.dist
1   0.147674    0.132956               5      4325      0.02391247
2   0.235356    0.150813               6      2703      0.03171236
3   0.095337    0.087345               5     21282      0.01760940
4   0.147674    0.132956               6      3460      0.03136304


Поскольку мои реальные кадры данных будут огромными, я думаю, что вычисление матрицы расстояний для просмотра ближайшего соседа будет неэффективным, а adply будет медленным. У кого-нибудь есть идея data.table подобного решения? Я только сейчас об основах data.table и всегда находил это очень эффективным по сравнению с plyr.

1 Ответ

2 голосов
/ 04 марта 2020

Это решение использует пакет RANN для поиска ближайших соседей. Хитрость заключается в том, чтобы сначала убедиться, что элементы с различными label_street имеют большее расстояние между ними, чем элементы в одном и том же label_street. Мы делаем это путем добавления дополнительного столбца цифр c с очень большим значением, которое является постоянным в пределах одного и того же label_street, но различается между различными значениями label_street. Всего вы получите:

intersections <- data.frame(x=c(0.147674, 0.235356, 0.095337, 0.147674), y=c(0.132956, 0.150813, 0.087345, 0.132956), label_street = c(5,6,5,6))
users <- data.frame(x = c(0.20428152, 0.17840619, 0.12964668, 0.20423856, 0.19349761, 0.10861251), y = c(0.14448448, 0.13921481, 0.11724543, 0.14447573, 0.14228827, 0.09891443), label_street = c(6,6,5,6,6,5), number = c(2703, 3460, 4325, 12506, 19753, 21282))

# add a numeric column that is constant within each category and has a very large value
intersections$label_street_large <- intersections$label_street * 1e6
users$label_street_large <- users$label_street * 1e6

# call the nearest neighbour function (k = 1 neighbour)
nearest_neighbours <- RANN::nn2(
  intersections[, c("x", "y", "label_street_large")],
  users[, c("x", "y", "label_street_large")],
  k = 1
)

# get original IDs and distances
IDs <- users$number[c(nearest_neighbours$nn.idx)]
distances <- c(nearest_neighbours$nn.dists)

IDs
# [1]  3460 12506  2703  3460  3460  4325
distances
# [1] 0.03171236 0.03136304 0.02391247 0.03175620 0.04271763 0.01760940

Надеюсь, это вам поможет. Он должен быть очень быстрым, потому что он вызывает только nn2 один раз, что выполняется за O (N * log (N)).

...