Вычисление минимального расстояния между наблюдениями внутри групп - PullRequest
0 голосов
/ 10 июля 2020

В приведенном ниже наборе данных, как я могу создать новый столбец min.diff , в котором для данного наблюдения x указывается минимальное расстояние между x и любым другим наблюдением y в его группе (обозначенной group столбец)? Я хотел бы измерить расстояние между x и y с помощью abs(x-y).

    set.seed(1)

df <- data.frame(
  group = c('A', 'A', 'A', 'B', 'B', 'C', 'C', 'C'),
  value = sample(1:10, 8, replace = T)
)

Ожидаемый результат:

  group value min.diff
1     A     9   2
2     A     4   3
3     A     7   2
4     B     1   1
5     B     2   1
6     C     7   4
7     C     2   1
8     C     3   1

Я предпочитаю решение с использованием dplyr. Единственный способ, который я имею в виду, - это расширить фрейм данных, добавив больше строк, чтобы получить каждую возможную пару в группах, вычислить расстояния и затем отфильтровать наименьшее значение в каждой группе. Есть способ более компактный?

Ответы [ 3 ]

1 голос
/ 10 июля 2020

Мы можем использовать combn для попарной разницы между 'value', получить min из abs olute значений

library(dplyr)
df1 <- df %>% 
          mutate(new = min(abs(combn(value, 2, FUN = function(x) x[1] - x[2]))))

Если мы хотим получить min imum между заданным элементом, т.е. first от остальных

 df1 <- df %>%
            mutate(new = min(abs(value[-1] - first(value))))
0 голосов
/ 10 июля 2020

Если порядок не имеет значения ...

library(dplyr)

df %>% 
  arrange(group, value) %>% #Order ascending by value, within each group
  group_by(group) %>% 
  mutate(min.diff = case_when(lag(group) == group & lead(group) == group ~ min(c(abs(value - lag(value)), abs(value - lead(value))), na.rm = T), #If the "group" for the previous and next entry are the same as the current group, take the smallest of the two differences
                              lag(group) == group ~ abs(value - lag(value)), #Otherwise, if only the previous entry's group is the same as the current one, take the difference from the previous
                              lead(group) == group ~ abs(value - lead(value)) #Otherwise, if only the next entry's group is the same as the current one, take the difference from the next
                              )
         ) %>%
  ungroup()

  #    group value min.diff
  #    <chr> <int>    <int>
  #  1 A         4        3
  #  2 A         7        2
  #  3 A         9        2
  #  4 B         1        1
  #  5 B         2        1
  #  6 C         2        1
  #  7 C         3        1
  #  8 C         7        4

Если порядок важен, вы можете добавить индекс и переставить его после, например:

library(dplyr)

df %>% 
  group_by(group) %>%
  mutate(index = row_number()) %>% #create the index
  arrange(group, value) %>%
  mutate(min.diff = case_when(lag(group) == group & lead(group) == group ~ min(c(abs(value - lag(value)), abs(value - lead(value))), na.rm = T),
                              lag(group) == group ~ abs(value - lag(value)),
                              lead(group) == group ~ abs(value - lead(value))
                              )
         ) %>%
  ungroup() %>%
  arrange(group, index) %>% #rearrange by the index
  select(-index) #remove the index


#   group value min.diff
#   <chr> <int>    <int>
# 1 A         9        2
# 2 A         4        3
# 3 A         7        2
# 4 B         1        1
# 5 B         2        1
# 6 C         7        4
# 7 C         2        1
# 8 C         3        1
0 голосов
/ 10 июля 2020

Мы можем использовать map_dbl, чтобы вычесть текущее значение из всех остальных значений и выбрать минимум для каждого group.

library(dplyr)
library(purrr)

df %>%
  group_by(group) %>%
  mutate(min.diff = map_dbl(row_number(), ~min(abs(value[-.x] - value[.x]))))
       

#  group value min.diff
#  <chr> <int>    <dbl>
#1 A         9        2
#2 A         4        3
#3 A         7        2
#4 B         1        1
#5 B         2        1
#6 C         7        4
#7 C         2        1
#8 C         3        1
...