Редактировать: добавлена альтернативная формулировка с использованием объединения. Я ожидаю, что этот подход будет намного быстрее для очень широкого фрейма данных со множеством столбцов для сравнения.
Подход 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