Как применить несколько функций на всевозможных сочетаниях строк в кадре данных в R? - PullRequest
4 голосов
/ 14 февраля 2020

У меня есть фрейм данных с координатами (lon, lat)

    lon <- list(505997.627175236, 505997.627175236, 505997.627175236, 505997.627175236)   
    lon <- do.call(rbind.data.frame, lon)

    lat <- list(7941821.025438220, 7941821.025438220, 7941821.025438220, 7941821.025438220)
    lat <- do.call(rbind.data.frame, lat)

    coord <- cbind(lon, lat)
    colnames(coord) <- c("lon", "lat")

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

     lon   lat       apply function on every possible combinations such as v1-v2, v1-v3, v1-v4,
v1   x1    y1        v2-v3 and so on...
v2   x2    y2         
v3   x3    y3        here are the two functions applied beetween v1 and v2 :
v4   x4    y4        **euclidian distance**    sqrt((x1-x2)^2 + (y1-y2)^2)
                     **angle**                 atan2((y1-y2),(x1-x2))*(180/pi)

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

Заранее спасибо за ваши ответы и извините, если вопрос кажется глупым. Я просмотрел очень много постов, но не смог найти решение, которое смог бы понять и воспроизвести.

Ответы [ 4 ]

3 голосов
/ 14 февраля 2020

Функция Base R combn генерирует комбинации элементов вектора, взятых за раз m, и может, необязательно, применять функцию FUN к этим комбинациям. Поскольку входные данные "data.frame", я объединю rownames 2 на 2.

euclidean <- function(k){
  f <- function(x, y) sqrt((x[1] - y[1])^2 + (x[2] - y[2])^2)
  x <- unlist(coord[k[1], 1:2])
  y <- unlist(coord[k[2], 1:2])
  f(x, y)
}

angle <- function(k){ 
  f <- function(x, y) atan2(x[2] - y[2], x[1] - y[1])*(180/pi)
  x <- unlist(coord[k[1], 1:2])
  y <- unlist(coord[k[2], 1:2])
  f(x, y)
}

combn(rownames(coord), 2, euclidean)
#[1]   4019.95 800062.50  20012.25 804067.26  24001.87 780073.39

combn(rownames(coord), 2, angle)
#[1] -84.28941  90.71616  87.99547  90.74110  89.28384 -89.21407

Данные.

Это данные в Ответ ОП, но без колонки id.

lon <- c(505997.627175236, 505597.627175236,
         515997.627175236, 505297.627175236)   
lat <- c(7941821.025438220, 7945821.025438220,
         7141821.025438220, 7921821.025438220)
coord <- data.frame(lon, lat)
2 голосов
/ 14 февраля 2020
# two vectors (I changed them a little bit)
lon <- c(505997.627175236, 505597.627175236, 515997.627175236, 505297.627175236)   
lat <- c(7941821.025438220, 7945821.025438220, 7141821.025438220, 7921821.025438220)

# a function for the euclidean distance
eDistance <- function(x1, x2, y1, y2) sqrt((x1-x2)^2 + (y1-y2)^2)

# now we create a dataframe...
df <- data.frame(lon, lat) %>%
    mutate(joinIndex = 1:nrow(.)) # and we add an index column

# ...that looks like this
#        lon     lat joinIndex
# 1 505997.6 7941821         1
# 2 505597.6 7945821         2
# 3 515997.6 7141821         3
# 4 505297.6 7921821         4

# create all combinations of the join indeces
df_combinations <- expand.grid(1:nrow(df), 1:nrow(df))

#    Var1 Var2
# 1     1    1
# 2     2    1
# 3     3    1
# 4     4    1
# 5     1    2
# 6     2    2
# 7     3    2
# 8     4    2
# 9     1    3
# 10    2    3
# 11    3    3
# 12    4    3
# 13    1    4
# 14    2    4
# 15    3    4
# 16    4    4

# and join our dataframe first on one index then on the other
df_final <- df_combinations %>%
    left_join(df, by = c("Var1" = "joinIndex")) %>%
    left_join(df, by = c("Var2" = "joinIndex"))

# and then finally calculate the euclidean distance
df_final %>%
    mutate(distance = eDistance(lon.x, lon.y, lat.x, lat.y))

   Var1 Var2    lon.x   lat.x    lon.y   lat.y  distance
1     1    1 505997.6 7941821 505997.6 7941821      0.00
2     2    1 505597.6 7945821 505997.6 7941821   4019.95
3     3    1 515997.6 7141821 505997.6 7941821 800062.50
4     4    1 505297.6 7921821 505997.6 7941821  20012.25
5     1    2 505997.6 7941821 505597.6 7945821   4019.95
6     2    2 505597.6 7945821 505597.6 7945821      0.00
7     3    2 515997.6 7141821 505597.6 7945821 804067.26
8     4    2 505297.6 7921821 505597.6 7945821  24001.87
9     1    3 505997.6 7941821 515997.6 7141821 800062.50
10    2    3 505597.6 7945821 515997.6 7141821 804067.26
11    3    3 515997.6 7141821 515997.6 7141821      0.00
12    4    3 505297.6 7921821 515997.6 7141821 780073.39
13    1    4 505997.6 7941821 505297.6 7921821  20012.25
14    2    4 505597.6 7945821 505297.6 7921821  24001.87
15    3    4 515997.6 7141821 505297.6 7921821 780073.39
16    4    4 505297.6 7921821 505297.6 7921821      0.00
0 голосов
/ 18 февраля 2020

В итоге я адаптировал код, предоставленный Georgery, но я использовал «combn» вместо «expand.grid», чтобы избежать повторения сочетаний строк при применении функций к окончательному кадру данных. Мне также пришлось использовать функцию «конвертировать» из пакета «хаблар», чтобы правильно преобразовать коэффициенты моего информационного кадра "ordin_combn "в числовые значения c.

Вот код:

lon <- c(505997.627175236, 505597.627175236, 515997.627175236, 505297.627175236)   
lat <- c(7941821.025438220, 7945821.025438220, 7141821.025438220, 7921821.025438220)

# dataframe creation + adding of an id column
coord <- data.frame(lon, lat) %>% 
                 mutate(id = 1:nrow(.))

coord_combn <- combn(rownames(coord), 2) # all the possible row combinations
coord_combn <- as.data.frame(t(coord_combn)) # transpose columns into rows
coord_combn <- coord_combn %>% 
                 convert(num(V1, V2)) # factor to numeric

#join our dataframe first on one index then on the other
coord_final <- coord_combn %>%
  left_join(coord, by = c("V1" = "id")) %>%
  left_join(coord, by = c("V2" = "id"))

eDistance <- function(x1, x2, y1, y2) sqrt((x1-x2)^2 + (y1-y2)^2)
eAngle <- function(x1, x2, y1, y2) atan2((y1-y2),(x1-x2))*(180/3.14159265359)

# euclidean distance calculation
coord_final <- coord_final %>% 
                 mutate(distance = eDistance(lon.x, lon.y, lat.x, lat.y)) 
# angle calculation
coord_final <- coord_final %>% 
                 mutate(angle = eAngle(lon.x, lon.y, lat.x, lat.y)) 

Спасибо всем, вы мне очень помогли.

0 голосов
/ 15 февраля 2020

Для быстрых евклидовых вычислений вы можете посмотреть на this

Для другой функции вы можете сделать что-то вроде

atan2(outer(coord$lat, coord$lat, `-`), outer(coord$lon, coord$lon, `-`))*180/pi
...