Используйте `dplyr`, чтобы избежать цикла` for`: вычислите расстояние до наблюдений - PullRequest
0 голосов
/ 11 декабря 2018

У меня есть два набора данных A и B, и для каждого наблюдения в A я хочу вычислить расстояние distance (например, евклидово расстояние, расстояние L1 или что-то еще) для каждого наблюдения в B (расчет расстояния основан на переменных в наборах данных).Наблюдение из A следует затем связать с наблюдением в B, для которого это расстояние минимально.

Например, если A имеет 5000 наблюдений, а B имеет 10000 наблюдений, то

for(i in 1:5000)
{
     x = data.frame(x = numeric(), y = numeric())

     for(j in 1:10000)
     {
         x[j,] = distance(A[i,], B[j,])
     }

     A[i,]$associated_row_B = x[which.min(x[1,]),1]
}

делает в основном то, что я хочу (мне все еще нужно решить, если наблюдения имеют одинаковое расстояние).Но так как я использую dplyr, мне почти никогда не приходилось использовать цикл for.Моему решению нужны даже два цикла, поэтому мне интересно, есть ли возможность избежать цикла for, используя решение из dplyr / tidyverse.

Очень простой пример:

A:

i           a b
1 -0.5920377 a
2  0.4263199 b
3  0.6737029 a
4  1.3063658 c
5  0.1314103 d

B:

i           a b
1 -0.30201541 a
2 -0.07093386 b
3  0.96317764 c
4 -0.33303061 d
5 -1.00834895 d

и функция расстояния:

distance = function(x,y) return(c((x[2] - y[2])^2 + abs(x[3] - y[3]), y[1])

Первый элемент возвращаемого значения - это фактическое расстояние, второе значение -идентификатор из B.

1 Ответ

0 голосов
/ 11 декабря 2018

Справедливое предупреждение: это будет довольно неэффективно для больших наборов данных!

Вы можете сделать это, используя crossing из tidyr и slice из dplyr.

Во-первых, давайте создадим два фиктивных фрейма данных, A_df и B_df

A_df <- data.frame(
  observation_A = runif(100),
  id_A = 1:100
)

B_df <- data.frame(
  observation_B = runif(50),
  id_B = 1:50
)

Для ясности, я сохранил уникальные имена столбцов между A_df и B_df.Далее мы будем использовать tidyr::crossing, чтобы найти каждую комбинацию строк между двумя кадрами данных.Далее мы используем mutate для вычисления расстояния (здесь я произвольно взял абсолютное значение их разности, но здесь вы можете применить свою собственную функцию расстояния).Наконец, мы группируем по id_A и сохраняем только минимум, используя slice (и базу R which.max).

library(tidyverse)


full_df <- A_df %>% 
  crossing(B_df) %>% 
  mutate(distance = abs(observation_A-observation_B)) %>% 
  group_by(id_A) %>% 
  slice(which.min(distance))

Глядя на full_df, мы получаем то, на что надеялись:

> full_df
# A tibble: 100 x 5
# Groups:   id_A [100]
   observation_A  id_A observation_B  id_B distance
           <dbl> <int>         <dbl> <int>    <dbl>
 1         0.826     1         0.851    44  0.0251 
 2         0.903     2         0.905     3  0.00176
 3         0.371     3         0.368    18  0.00305
 4         0.554     4         0.577    34  0.0232 
 5         0.656     5         0.654    10  0.00268
 6         0.120     6         0.110    37  0.0101 
 7         0.991     7         0.988     6  0.00244
 8         0.983     8         0.988     6  0.00483
 9         0.325     9         0.318    45  0.00649
10         0.860    10         0.864    40  0.00407
# ... with 90 more rows
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...