【问题标题】:Copying values from one subset to all others for selected columns using dplyr使用 dplyr 将选定列的值从一个子集复制到所有其他子集
【发布时间】:2020-12-20 19:42:13
【问题描述】:

我有一个数据框(下面的简单示例),其中每个用户填写问卷的次数不同,每次完成问卷都会在数据框中产生一行。在我的简单示例中,用户 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。

我现在想用第 0 天的 sd 替换所有中值 sd。一旦这样做,我就可以正确地将数据帧拆分为高 sd 和低 sd 子集。

问题:我如何在这三列中复制正确的第 0 天中位数,然后创建新列,其中包含由用户相对于每个问题的中位数波动率定义的低 sd 和高 sd 子集?

在此致以诚挚的谢意

托马斯·飞利浦

【问题讨论】:

    标签: r dplyr copy subset


    【解决方案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
    

    【讨论】:

    • 不幸的是,我无法重新定义问题 - 它就是这样。标准差确实适用于每个受试者,而不是每一行,并且每个受试者的行数本质上是可变的:一些受试者定期填写调查,而其他人则没有。向我建议了summarize / left join方法,但我不明白,所以我采取了我做的路线。我喜欢你的方法,因为它比我的更干净,我会采用它。感谢您的指导。
    • @ThomasPhilips,澄清一下,我希望我没有遇到批评。您当然无法控制人们填写调查的频率!当然,您的样本量是可变的这一事实具有一些统计上的影响,但数据就是这样,我们都尽力而为!为了透明起见,您可以考虑在编写结果时按个人报告您的样本量。我将修改我的答案以包含该信息。 HTH!
    • 您根本不会觉得自己很挑剔——他们的数据集就是这样,我无法改变它。其实,既然是自报健康数据,改了就错了,
    【解决方案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 有一种更优雅的方式来做到这一点,但我一直找不到。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多