R: Рассчитать расстояние между первой и текущей строкой сгруппированного кадра данных. - PullRequest
1 голос
/ 28 апреля 2019

Мне нужно вычислить евклидово расстояние между первой и текущей строками в кадре данных. Каждая строка имеет ключ (группа, месяц) и имеет список значений. В приведенном ниже примере с игрушкой ключ - c (месяц, студент), а значения - в c (A, B). Я хочу создать столбец расстояния C, который равен sqrt ((A_i-A_1) ^ 2 + (B_i - B_1) ^ 2).

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

df <- data.frame(month=rep(1:3,2),
                 student=rep(c("Amy", "Bob"), each=3),
                 A=c(9, 6, 6, 8, 6, 9),
                 B=c(6, 2, 8, 5, 6, 7))

# Pull in each column's first values for each group
df %>% 
  group_by(student) %>% 
  mutate_all(list(first = first)) %>% 
# TODO: Calculate the distance, i.e. SQRT(sum_i[(x_i - x_1)^2]).

#Output:
  month student     A     B month_first A_first B_first
1     1 Amy         9     6           1       9       6
2     2 Amy         6     2           1       9       6
...

Желаемый вывод:

#Output:
  month student     A     B month_first A_first B_first dist_from_first
1     1 Amy         9     6           1       9       6    0
2     2 Amy         6     2           1       9       6    5
...

Ответы [ 3 ]

2 голосов
/ 28 апреля 2019

Вот еще один способ использования компактного dplyr кода.Это может быть использовано для любого количества столбцов

df %>% 
  select(-month) %>%
  group_by(student) %>% 
  mutate_each(function(x) (first(x) - x)^2) %>%
  ungroup() %>%
  mutate(euc.dist = sqrt(rowSums(select(., -1))))

# A tibble: 6 x 4
  student     A     B euc.dist
  <chr>   <dbl> <dbl>    <dbl>
1 Amy         0     0     0   
2 Amy         9    16     5   
3 Amy         9     4     3.61
4 Bob         0     0     0   
5 Bob         4     1     2.24
6 Bob         1     4     2.24
1 голос
/ 28 апреля 2019

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

Подход 1: Чтобы получить евклидово расстояние для большого числа столбцов, один из способов - это переставить данные таким образом, чтобы в каждой строке отображался один месяц, один студент и один исходный столбец (например, A или B в OP), но затем два столбца, представляющих значение текущего месяца и первое значение. Затем мы можем возвести разницу в квадрат и сгруппировать по всем столбцам, чтобы получить евклидово расстояние, то есть среднеквадратичное / среднеквадратичное значение для каждого студенческого месяца.

  library(tidyverse)
  df %>% 
    group_by(student) %>% 
    mutate_all(list(first = first)) %>%
    ungroup() %>%
  # gather into long form; make col show variant, col2 show orig column
  gather(col, val, -c(student, month, month_first)) %>%
  mutate(col2 = col %>% str_remove("_first")) %>% 
  mutate(col = if_else(col %>% str_ends("_first"),
                        "first",
                        "comparison")) %>% 
  spread(col, val) %>% 
  mutate(square_dif = (comparison - first)^2) %>%
  group_by(student, month) %>%
  summarize(RMS = sqrt(sum(square_dif)))

# A tibble: 6 x 3
# Groups:   student [2]
  student month   RMS
  <fct>   <int> <dbl>
1 Amy         1  0   
2 Amy         2  5   
3 Amy         3  3.61
4 Bob         1  0   
5 Bob         2  2.24
6 Bob         3  2.24

Подход 2. Здесь длинная версия данных объединяется с версией, которая является самым ранним месяцем для каждого студента.

library(tidyverse)
df_long <- gather(df, col, val, -c(month, student))
df_long %>% left_join(df_long %>% 
              group_by(student) %>%
              top_n(-1, wt = month) %>%
              rename(first_val = val) %>% 
              select(-month),
            by = c("student", "col")) %>%
  mutate(square_dif = (val - first_val)^2) %>%
  group_by( student, month) %>%
  summarize(RMS = sqrt(sum(square_dif)))

# A tibble: 6 x 3
# Groups:   student [2]
  student month   RMS
  <fct>   <int> <dbl>
1 Amy         1  0   
2 Amy         2  5   
3 Amy         3  3.61
4 Bob         1  0   
5 Bob         2  2.24
6 Bob         3  2.24
0 голосов
/ 28 апреля 2019

Вместо вызова mutate_all было бы проще напрямую вычислить dist_from_first. Единственное, что мне неясно, это то, должен ли месяц быть включен в оператор group_by().

library(tidyverse)

df <- tibble(month=rep(1:3,2),
                 student=rep(c("Amy", "Bob"), each=3),
                 A=c(9, 6, 6, 8, 6, 9),
                 B=c(6, 2, 8, 5, 6, 7))

df%>%
  group_by(student)%>%
  mutate(dist_from_first = sqrt((A - first(A))^2 + (B - first(B))^2))%>%
  ungroup()

# A tibble: 6 x 5
#  month student     A     B dist_from_first
#  <int> <chr>   <dbl> <dbl>           <dbl>
#1     1 Amy         9     6            0   
#2     2 Amy         6     2            5   
#3     3 Amy         6     8            3.61
#4     1 Bob         8     5            0   
#5     2 Bob         6     6            2.24
#6     3 Bob         9     7            2.24
...