Копирование значений из одного подмножества во все остальные для выбранных столбцов с помощью dplyr

#r #dplyr #copy #subset

#r #dplyr #Копировать #подмножество

Вопрос:

У меня есть фрейм данных (простой пример ниже), в котором каждый пользователь заполняет анкету разное количество раз, и каждое заполнение анкеты приводит к появлению строки в фрейме данных. В моем простом примере пользователи A, C и D имеют записи за 5 дней, но пользователь B имеет записи только за 4:

  > df
   UserId Days_From_First_Use Q1 Q2 Q3
1          A                   0  3  2  1
2          A                   1  1  0  0
3          A                   2  1  1  0
4          A                   3  0  2  0
5          A                   4  1  1  1
6          B                   0  4  8  2
7          B                   2  2  2  1
8          B                   4  5  6  5
9          B                   5  4  5  5
10         C                   0  5  7  2
11         C                   1  2  2  2
12         C                   2  5  5  4
13         C                   3  6  5  3
14         C                   4  6  6  4
15         D                   0  5  3  5
16         D                   1  5  3  4
17         D                   2  4  2  6
18         D                   3  0  0  1
19         D                   4  1  1  1
 

Теперь я вычисляю изменчивость временных рядов для каждого пользователя следующим образом:

 > df <- df %>% 
      group_by(UserId) %>%
      mutate(across(all_of(c("Q1", "Q2", "Q3")), sd,.names = paste0("Sigma_", "{.col}"))) %>%
      ungroup()
> df
# A tibble: 19 x 8
   UserId Days_From_First_Use    Q1    Q2    Q3 Sigma_Q1 Sigma_Q2 Sigma_Q3
   <fct>                <int> <int> <int> <int>    <dbl>    <dbl>    <dbl>
 1 A                        0     3     2     1     1.10    0.837    0.548
 2 A                        1     1     0     0     1.10    0.837    0.548
 3 A                        2     1     1     0     1.10    0.837    0.548
 4 A                        3     0     2     0     1.10    0.837    0.548
 5 A                        4     1     1     1     1.10    0.837    0.548
 6 B                        0     4     8     2     1.26    2.5      2.06 
 7 B                        2     2     2     1     1.26    2.5      2.06 
 8 B                        4     5     6     5     1.26    2.5      2.06 
 9 B                        5     4     5     5     1.26    2.5      2.06 
10 C                        0     5     7     2     1.64    1.87     1    
11 C                        1     2     2     2     1.64    1.87     1    
12 C                        2     5     5     4     1.64    1.87     1    
13 C                        3     6     5     3     1.64    1.87     1    
14 C                        4     6     6     4     1.64    1.87     1    
15 D                        0     5     3     5     2.35    1.30     2.30 
16 D                        1     5     3     4     2.35    1.30     2.30 
17 D                        2     4     2     6     2.35    1.30     2.30 
18 D                        3     0     0     1     2.35    1.30     2.30 
19 D                        4     1     1     1     2.35    1.30     2.30 
 

Теперь начинается та часть, которая вызывает у меня проблемы: Я хотел бы вычислить медианный sd для всех пользователей, чтобы я мог идентифицировать два подмножества пользователей с sd выше медианы и ниже медианы. Я не могу просто вычислить медианный sd по всем строкам, потому что количество наблюдений для каждого пользователя разное. Однако я могу сгруппировать по Days_From_First_Use
, а затем вычислить медианный sd для каждого дня. Поскольку у всех пользователей есть день 0 (их самый первый день), медианный sd в этот день — это значение, которое я хочу. Итак, я набираю:

 > df <- df %>% 
      group_by(UserId) %>%
      mutate(across(all_of(c("Q1", "Q2", "Q3")), sd,.names = paste0("Sigma_", "{.col}"))) %>%
      ungroup() %>%
      group_by(Days_From_First_Use) %>%
      mutate(across(all_of(paste0("Sigma_", c("Q1", "Q2", "Q3"))), median ,.names = paste0("Median_", "{.col}"))) %>%
      ungroup()
>     
> df
# A tibble: 19 x 11
   UserId Days_From_First_Use    Q1    Q2    Q3 Sigma_Q1 Sigma_Q2 Sigma_Q3 Median_Sigma_Q1 Median_Sigma_Q2 Median_Sigma_Q3
   <fct>                <int> <int> <int> <int>    <dbl>    <dbl>    <dbl>           <dbl>           <dbl>           <dbl>
 1 A                        0     3     2     1     1.10    0.837    0.548            1.45            1.59            1.53
 2 A                        1     1     0     0     1.10    0.837    0.548            1.64            1.30            1   
 3 A                        2     1     1     0     1.10    0.837    0.548            1.45            1.59            1.53
 4 A                        3     0     2     0     1.10    0.837    0.548            1.64            1.30            1   
 5 A                        4     1     1     1     1.10    0.837    0.548            1.45            1.59            1.53
 6 B                        0     4     8     2     1.26    2.5      2.06             1.45            1.59            1.53
 7 B                        2     2     2     1     1.26    2.5      2.06             1.45            1.59            1.53
 8 B                        4     5     6     5     1.26    2.5      2.06             1.45            1.59            1.53
 9 B                        5     4     5     5     1.26    2.5      2.06             1.26            2.5             2.06
10 C                        0     5     7     2     1.64    1.87     1                1.45            1.59            1.53
11 C                        1     2     2     2     1.64    1.87     1                1.64            1.30            1   
12 C                        2     5     5     4     1.64    1.87     1                1.45            1.59            1.53
13 C                        3     6     5     3     1.64    1.87     1                1.64            1.30            1   
14 C                        4     6     6     4     1.64    1.87     1                1.45            1.59            1.53
15 D                        0     5     3     5     2.35    1.30     2.30             1.45            1.59            1.53
16 D                        1     5     3     4     2.35    1.30     2.30             1.64            1.30            1   
17 D                        2     4     2     6     2.35    1.30     2.30             1.45            1.59            1.53
18 D                        3     0     0     1     2.35    1.30     2.30             1.64            1.30            1   
19 D                        4     1     1     1     2.35    1.30     2.30             1.45            1.59            1.53
 

Для справки, (неправильные) медианы по всему фрейму данных равны 1,64, 1,30 и 1 соответственно, в то время как правильные медианы равны 1,45, 1,59 и 1,53.

Теперь я хочу заменить все медианные sds на sd в день 0. Как только я это сделаю, я смогу правильно разделить фрейм данных на подмножества с высоким sd и низким sd.

Вопрос: Как я могу скопировать правильные медианы дня 0 по этим трем столбцам, а затем создать новые столбцы с подмножествами с низким sd и высоким sd, определенными sd пользователя относительно медианной волатильности для каждого вопроса?

С уважением и заранее большое спасибо

Томас Филипс

Ответ №1:

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

 df = 
  tibble(
    UserId = c("A", "A", "A", "A", "A", "B", "B", "B", "B", "C", "C", "C", "C", "C", "D", "D", "D", "D", "D"),
    DFFU = c(0, 1, 2, 3, 4, 0, 2, 4, 5, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4),
    Q1 = c(3, 1, 1, 0, 1, 4, 2, 5, 4, 5, 2, 5, 6, 6, 5, 5, 4, 0, 1),
    Q2 = c(2,0,1,2,1,8,2,6,5,7,2,5,5,6,3,3,2,0,1),
    Q3 = c(1,0,0,0,1,2,1,5,5,2,2,4,3,4,5,4,6,1,1)
  )

df <- df %>% 
  group_by(UserId) %>%
  mutate(across(all_of(c("Q1", "Q2", "Q3")), sd,.names = paste0("Sigma_", "{.col}"))) %>%
  ungroup()

df %>%
  filter(DFFU == 0) %>%
  transmute(UserId = UserId, across(all_of(paste0("Sigma_", c("Q1", "Q2", "Q3"))), median ,.names = paste0("Median_", "{.col}"))) %>%
  {left_join(df, .)}
 

Получение:

 > df
# A tibble: 19 x 11
   UserId  DFFU    Q1    Q2    Q3 Sigma_Q1 Sigma_Q2 Sigma_Q3 Median_Sigma_Q1 Median_Sigma_Q2 Median_Sigma_Q3
   <chr>  <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>    <dbl>           <dbl>           <dbl>           <dbl>
 1 A          0     3     2     1     1.10    0.837    0.548            1.45            1.59            1.53
 2 A          1     1     0     0     1.10    0.837    0.548            1.45            1.59            1.53
 3 A          2     1     1     0     1.10    0.837    0.548            1.45            1.59            1.53
 4 A          3     0     2     0     1.10    0.837    0.548            1.45            1.59            1.53
 5 A          4     1     1     1     1.10    0.837    0.548            1.45            1.59            1.53
 6 B          0     4     8     2     1.26    2.5      2.06             1.45            1.59            1.53
 7 B          2     2     2     1     1.26    2.5      2.06             1.45            1.59            1.53
 8 B          4     5     6     5     1.26    2.5      2.06             1.45            1.59            1.53
 9 B          5     4     5     5     1.26    2.5      2.06             1.45            1.59            1.53
10 C          0     5     7     2     1.64    1.87     1                1.45            1.59            1.53
11 C          1     2     2     2     1.64    1.87     1                1.45            1.59            1.53
12 C          2     5     5     4     1.64    1.87     1                1.45            1.59            1.53
13 C          3     6     5     3     1.64    1.87     1                1.45            1.59            1.53
14 C          4     6     6     4     1.64    1.87     1                1.45            1.59            1.53
15 D          0     5     3     5     2.35    1.30     2.30             1.45            1.59            1.53
16 D          1     5     3     4     2.35    1.30     2.30             1.45            1.59            1.53
17 D          2     4     2     6     2.35    1.30     2.30             1.45            1.59            1.53
18 D          3     0     0     1     2.35    1.30     2.30             1.45            1.59            1.53
19 D          4     1     1     1     2.35    1.30     2.30             1.45            1.59            1.53
 

Одна из причин, по которой ваш анализ становится таким странным, заключается в том, что вы нарушаете принципы аккуратных данных. В вашем исходном наборе данных каждая строка представляет один опрос, но стандартное отклонение применяется к каждому учащемуся, а не к каждому опросу. Таким образом, значения стандартного отклонения должны отображаться в таблице с 5 строками, по одной строке для каждого учащегося. Затем медиана представляет совокупность учащихся. Существует только одна совокупность, поэтому должна быть только одна строка. Поэтому я бы рекомендовал:

 sd_df <- 
  df %>%
  group_by(UserId) %>%
  summarize(
    across(
      all_of(c("Q1", "Q2", "Q3")), 
      .fns = sd, 
      .names = paste0("Sigma_", "{.col}")
    )
  )

median_sd_df <-
  sd_df %>%
  summarize(
    across(
      all_of(paste0("Sigma_", c("Q1", "Q2", "Q3"))), 
      .fns = median, 
      .names = paste0("Sigma_", "{.col}")
    ),
    n = n()
  )
 

что дает вам:

 > sd_df
# A tibble: 4 x 5
  UserId Sigma_Q1 Sigma_Q2 Sigma_Q3     n
  <chr>     <dbl>    <dbl>    <dbl> <int>
1 A          1.10    0.837    0.548     5
2 B          1.26    2.5      2.06      4
3 C          1.64    1.87     1         5
4 D          2.35    1.30     2.30      5

>  median_sd_df
# A tibble: 1 x 3
  Median_Sigma_Q1 Median_Sigma_Q2 Median_Sigma_Q3
            <dbl>           <dbl>           <dbl>
1            1.45            1.59            1.53
 

Комментарии:

1. К сожалению, я не могу переформулировать проблему — это то, что есть. Стандартное отклонение действительно применяется к каждому субъекту, а не к каждой строке, и количество строк для каждого субъекта по своей сути является переменным: некоторые субъекты заполняют опрос регулярно, а другие нет. Мне был предложен подход summary / left join, но я его не понял, и поэтому я выбрал маршрут, который я сделал. Мне нравится ваш подход, поскольку он чище моего, и я его приму. Спасибо за руководство.

2. @ThomasPhilips, просто чтобы уточнить, я надеюсь, что я не столкнулся с критикой. Вы, конечно, не можете контролировать, как часто люди заполняют опрос! Конечно, тот факт, что размер вашей выборки является переменным, имеет некоторые статистические последствия, но данные такие, какие они есть, и мы все делаем все возможное! Для прозрачности вы могли бы рассмотреть возможность предоставления информации о размере выборки по отдельности при записи результатов. Я изменю свой ответ, чтобы включить эту информацию. HTH!

3. Вы вообще не кажетесь критичным — набор данных такой, какой он есть, и я ничего не могу сделать, чтобы его изменить. На самом деле, поскольку это данные о работоспособности, о которых сообщается самостоятельно, было бы неправильно их изменять,

Ответ №2:

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

 > df_median_sigma <- df %>% 
                       arrange(Days_From_First_Use) %>%
                       select(starts_with("Median_Sigma")) %>% 
                       filter(row_number( ) == 1)
> df_median_sigma
# A tibble: 1 x 3
  Median_Sigma_Q1 Median_Sigma_Q2 Median_Sigma_Q3
            <dbl>           <dbl>           <dbl>
1            1.45            1.59            1.53
 

Наконец, перепишите все соответствующие столбцы в df с правильными медианами:

     > df[paste0("Median_Sigma_", c("Q1", "Q2", "Q3"))] <- df_median_sigma
> df
# A tibble: 19 x 11
   UserId Days_From_First_Use    Q1    Q2    Q3 Sigma_Q1 Sigma_Q2 Sigma_Q3 Median_Sigma_Q1 Median_Sigma_Q2 Median_Sigma_Q3
   <fct>                <int> <int> <int> <int>    <dbl>    <dbl>    <dbl>           <dbl>           <dbl>           <dbl>
 1 A                        0     3     2     1     1.10    0.837    0.548            1.45            1.59            1.53
 2 A                        1     1     0     0     1.10    0.837    0.548            1.45            1.59            1.53
 3 A                        2     1     1     0     1.10    0.837    0.548            1.45            1.59            1.53
 4 A                        3     0     2     0     1.10    0.837    0.548            1.45            1.59            1.53
 5 A                        4     1     1     1     1.10    0.837    0.548            1.45            1.59            1.53
 6 B                        0     4     8     2     1.26    2.5      2.06             1.45            1.59            1.53
 7 B                        2     2     2     1     1.26    2.5      2.06             1.45            1.59            1.53
 8 B                        4     5     6     5     1.26    2.5      2.06             1.45            1.59            1.53
 9 B                        5     4     5     5     1.26    2.5      2.06             1.45            1.59            1.53
10 C                        0     5     7     2     1.64    1.87     1                1.45            1.59            1.53
11 C                        1     2     2     2     1.64    1.87     1                1.45            1.59            1.53
12 C                        2     5     5     4     1.64    1.87     1                1.45            1.59            1.53
13 C                        3     6     5     3     1.64    1.87     1                1.45            1.59            1.53
14 C                        4     6     6     4     1.64    1.87     1                1.45            1.59            1.53
15 D                        0     5     3     5     2.35    1.30     2.30             1.45            1.59            1.53
16 D                        1     5     3     4     2.35    1.30     2.30             1.45            1.59            1.53
17 D                        2     4     2     6     2.35    1.30     2.30             1.45            1.59            1.53
18 D                        3     0     0     1     2.35    1.30     2.30             1.45            1.59            1.53
19 D                        4     1     1     1     2.35    1.30     2.30             1.45            1.59            1.53
 

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