Как присвоить наблюдениям широты и долготы несколько имен

У меня есть два фрейма данных: df1 содержит наблюдения с координатами широты и долготы; df2 имеет имена с координатами широты и долготы. Я хочу создать новую переменную df1$names, которая будет иметь для каждого наблюдения имена df2, которые находятся на определенном расстоянии от этого наблюдения.

Некоторые примеры данных для df1:

df1 <- structure(list(lat = c(52.768, 53.155, 53.238, 53.253, 53.312, 53.21, 53.21, 53.109, 53.376, 53.317, 52.972, 53.337, 53.208, 53.278, 53.316, 53.288, 53.341, 52.945, 53.317, 53.249), lon = c(6.873, 6.82, 6.81, 6.82, 6.84, 6.748, 6.743, 6.855, 6.742, 6.808, 6.588, 6.743, 6.752, 6.845, 6.638, 6.872, 6.713, 6.57, 6.735, 6.917), cat = c(2L, 1L, 2L, 2L, 2L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 3L, 2L, 2L, 2L, 2L, 2L), diff = c(6.97305555555555, 3.39815972222222, 14.2874305555556, -0.759791666666667, 34.448275462963, 4.38783564814815, 0.142430555555556, 0.698599537037037, 1.22914351851852, 7.0008912037037, 1.3349537037037, 8.67978009259259, 1.6090162037037,    25.9466782407407, 9.45068287037037, 4.76284722222222, 1.79163194444444, 16.8280787037037, 1.01336805555556, 3.51240740740741)), .Names = c("lat", "lon", "cat", "diff"), row.names = c(125L, 705L, 435L, 682L, 186L, 783L, 250L, 517L, 547L, 369L, 618L, 280L, 839L, 614L, 371L, 786L, 542L, 100L, 667L, 785L), class = "data.frame")

Некоторые примеры данных для df2:

df2 <- structure(list(latlonloc = structure(c(6L, 3L, 4L, 2L, 5L, 1L), .Label = c("Boelenslaan", "Borgercompagnie", "Froombosch", "Garrelsweer", "Stitswerd", "Tinallinge"), class = "factor"), lat = c(53.356789, 53.193886, 53.311237, 53.111339, 53.360848, 53.162031), lon = c(6.53493, 6.780792, 6.768608, 6.82354, 6.599604, 6.143804)), .Names = c("latlonloc", "lat", "lon"), class = "data.frame", row.names = c(NA, -6L))

Создание матрицы расстояний с помощью пакета geosphere:

library(geosphere)
mat <- distm(df1[,c('lon','lat')], df2[,c('lon','lat')], fun=distHaversine)

Полученные расстояния указаны в метрах (по крайней мере, я так думаю, иначе что-то не так с матрицей расстояний).

Указанное расстояние рассчитывается с помощью (df1$cat)^2)*1000. Я попробовал df1$names <- df2$latlonloc[apply(distmat, 1, which(distmat < ((df1$cat)^2)*1000 ))], но получил сообщение об ошибке:

Error in match.fun(FUN) : 
  'which(distmat < ((df1$cat)^2) * 1000)' is not a function, character or symbol

Вероятно, это неправильная оценка, но мне нужно следующее:

df1$names <- #code or function which gives me a string of names which are within a specified distance of the observation

Как я могу создать строку с именами, находящимися на заданном расстоянии от наблюдений?


person Jaap    schedule 23.02.2014    source источник
comment
Когда вы вызываете apply, третий аргумент должен быть функцией, либо просто именем функции, либо полной функцией с аргументом, который будет соответствовать каждому элементу первого аргумента apply. Вам нужно что-то вроде apply(distmat, 1, function(x) ...), где ... - это тело функции, использующее аргумент x. Однако я не могу сказать из вашего кода, что вы хотите / нуждаетесь в этом.   -  person Thomas    schedule 23.02.2014
comment
@ Томас Я редактировал вопрос. Это теперь ясно?   -  person Jaap    schedule 23.02.2014
comment
Что вы ожидаете от which(distmat < ((df1$cat)^2) * 1000)?   -  person Thomas    schedule 23.02.2014
comment
@Thomas Я ожидаю, что он даст мне набор имен, находящихся на определенном расстоянии ((df1$cat)^2)*1000) от места наблюдения.   -  person Jaap    schedule 24.02.2014
comment
which задокументирован как целые числа. Как вы думаете, почему он дает имена?   -  person IRTFM    schedule 24.02.2014
comment
@IShouldBuyABoat на основе этого ответа Я догадался, что желаемого результата можно добиться и с which.   -  person Jaap    schedule 24.02.2014
comment
which.min использовался для выбора элемента из вектора символов с помощью числовой индексации.   -  person IRTFM    schedule 25.02.2014


Ответы (1)


Вам нужно работать с каждой строкой df1 (или mat), чтобы выяснить для каждой строки, как далеко находится каждый объект в df2. Исходя из этого, вы можете выбрать те, которые соответствуют вашему критерию расстояния.

Я думаю, вы немного запутались в использовании apply и which. Чтобы which действительно работал на вас, вам нужно применить его к каждой строке mat, тогда как ваш текущий код применяет его ко всей mat матрице. Также обратите внимание, что здесь сложно использовать apply, потому что вы сравниваете каждую строку mat с соответствующим элементом вектора, определенного ((df1$cat)^2)*1000). Вместо этого я покажу вам примеры с использованием sapply и lapply. Вы также можете использовать mapply здесь, но я думаю, что синтаксис _15 _ / _ 16_ более понятен.

Для достижения желаемого результата я покажу два примера. Один возвращает список, содержащий для каждой строки в df1 имена элементов в df2, которые находятся в пределах порогового расстояния. Это будет нелегко вернуться в исходный df1 как переменную, потому что каждый элемент в списке может содержать несколько имен. Во втором примере эти имена вставляются в одну строку символов, разделенных запятыми, чтобы создать новую переменную, которую вы ищете.

Пример 1:

out1 <- lapply(1:nrow(df1), function(x) {
    df2[which(mat[x,] < (((df1$cat)^2)*1000)[x]),'latlonloc']
})

Результат:

> str(out1)
List of 20
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 2
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 4
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 6 4 5
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 
 $ : Factor w/ 6 levels "Boelenslaan",..: 4
 $ : Factor w/ 6 levels "Boelenslaan",..: 

Пример 2:

out2 <- sapply(1:nrow(df1), function(x) {
    paste(df2[which(mat[x,] < (((df1$cat)^2)*1000)[x]),'latlonloc'], collapse=',')
})

Результат:

> out2
 [1] ""                                 ""                                
 [3] ""                                 ""                                
 [5] ""                                 ""                                
 [7] ""                                 "Borgercompagnie"                 
 [9] ""                                 "Garrelsweer"                     
[11] ""                                 ""                                
[13] ""                                 ""                                
[15] "Tinallinge,Garrelsweer,Stitswerd" ""                                
[17] ""                                 ""                                
[19] "Garrelsweer"                      ""

Я думаю, что второй из них, вероятно, наиболее близок к тому, что вы собираетесь делать.

person Thomas    schedule 23.02.2014
comment
Пример 2 дает мне желаемый ответ. Спасибо! - person Jaap; 25.02.2014