【问题标题】:Normalise multiple variables to separate control values标准化多个变量以分离控制值
【发布时间】:2019-02-20 05:39:26
【问题描述】:

我有兴趣将多个变量标准化为控制子组的各自平均值。

假设我有一个数据框,我在其中测量来自 3 个不同条件(对照、药物 1、药物 2)的两个变量(分数 1 和分数 2)。

df <- data.frame(Treatment=rep(c( "Control", "Drug 1",
    "Drug 2"), each=6 ),
    Score1=c(4,5,4,5,5,6,8,9,10,8,9,9,14,15,13,15,14,15), 
    Score2=c(1,2,1,2,3,3,8,8,9,9,8,8,14,14,15,12,14,15)) 
df

   Treatment Score1 Score2
1    Control      4      1
2    Control      5      2
3    Control      4      1
4    Control      5      2
5    Control      5      3
6    Control      6      3
7     Drug 1      8      8
8     Drug 1      9      8
9     Drug 1     10      9
10    Drug 1      8      9
11    Drug 1      9      8
12    Drug 1      9      8
13    Drug 2     14     14
14    Drug 2     15     14
15    Drug 2     13     15
16    Drug 2     15     12
17    Drug 2     14     14
18    Drug 2     15     15

我想将每个分数标准化为对照组的平均值(针对该分数)。最终结果是:

df.normal <- df
x <- mean(df$Score1[df$Treatment=="Control"])
y <- mean(df$Score2[df$Treatment=="Control"])
df.normal$Score1_normalised <- df$Score1 / x
df.normal$Score2_normalised <- df$Score2 / y
df.normal

Treatment Score1 Score2 Score1_normalised Score2_normalised
1    Control      4      1         0.8275862               0.5
2    Control      5      2         1.0344828               1.0
3    Control      4      1         0.8275862               0.5
4    Control      5      2         1.0344828               1.0
5    Control      5      3         1.0344828               1.5
6    Control      6      3         1.2413793               1.5
7     Drug 1      8      8         1.6551724               4.0
8     Drug 1      9      8         1.8620690               4.0
9     Drug 1     10      9         2.0689655               4.5
10    Drug 1      8      9         1.6551724               4.5
11    Drug 1      9      8         1.8620690               4.0
12    Drug 1      9      8         1.8620690               4.0
13    Drug 2     14     14         2.8965517               7.0
14    Drug 2     15     14         3.1034483               7.0
15    Drug 2     13     15         2.6896552               7.5
16    Drug 2     15     12         3.1034483               6.0
17    Drug 2     14     14         2.8965517               7.0
18    Drug 2     15     15         3.1034483               7.5

我认为使用 dplyr 可以做到这一点,但我一直在努力开始,并且由于我有大约 20 个变量,我希望有一条捷径,而不是走很长的路。

任何帮助将不胜感激!

【问题讨论】:

  • 20 个变量是指 20 个分数吗?这能解决问题吗? mutate_at(df, vars(starts_with("Score")), funs("normalised" = . / mean(.[Treatment == "Control"])))

标签: r dplyr


【解决方案1】:

这里是aggregate()和mapply():

> Medias <- aggregate(df[c("Score1", "Score2")], list(df$Treatment), mean)
> Medias
  Group.1    Score1    Score2
1 Control  4.833333  2.000000
2  Drug 1  8.833333  8.333333
3  Drug 2 14.333333 14.000000
> 
> mapply( function(x, y) {x / y}, x = df[c("Score1", "Score2")], y = Medias[Medias$Group.1 == "Control" , c("Score1", "Score2")])
         Score1 Score2
 [1,] 0.8275862    0.5
 [2,] 1.0344828    1.0
 [3,] 0.8275862    0.5
 [4,] 1.0344828    1.0
 [5,] 1.0344828    1.5
 [6,] 1.2413793    1.5
 [7,] 1.6551724    4.0
 [8,] 1.8620690    4.0
 [9,] 2.0689655    4.5
[10,] 1.6551724    4.5
[11,] 1.8620690    4.0
[12,] 1.8620690    4.0
[13,] 2.8965517    7.0
[14,] 3.1034483    7.0
[15,] 2.6896552    7.5
[16,] 3.1034483    6.0
[17,] 2.8965517    7.0
[18,] 3.1034483    7.5
> 

希望对你有帮助。

【讨论】:

    【解决方案2】:

    这是dplyr + tidyr 工作流程。它可以很好地扩展,但不幸的是,当您需要进行一些重塑时会变得有些复杂。

    使用一些基本的dplyr 动词,您可以获得控制值并计算以"Score" 开头的任何列的平均值。由于该数据框只有一行,因此您可以轻松地将这些平均分数用于标准化 df

    library(dplyr)
    
    control_means <- df %>%
      filter(Treatment == "Control") %>%
      summarise_at(vars(starts_with("Score")), mean)
    
    df %>%
      mutate(Score1_norm = Score1 / control_means$Score1,
             Score2_norm = Score2 / control_means$Score2) %>%
      head()
    #>   Treatment Score1 Score2 Score1_norm Score2_norm
    #> 1   Control      4      1   0.8275862         0.5
    #> 2   Control      5      2   1.0344828         1.0
    #> 3   Control      4      1   0.8275862         0.5
    #> 4   Control      5      2   1.0344828         1.0
    #> 5   Control      5      3   1.0344828         1.5
    #> 6   Control      6      3   1.2413793         1.5
    

    但是,为更多分数列复制此内容会很快变旧。通常您可以改用mutate_at 来减少重复,但我认为这不太可行,因为您每次都会引入不同的control_means 列。

    相反,您可以将均值和数据重新整形为长形状,然后按分数 1、分数 2 等分组(不知道您还能如何称呼它们)。

    control_means_long <- control_means %>%
      gather(key = group, value = mean_score)
    
    control_means_long
    #>    group mean_score
    #> 1 Score1   4.833333
    #> 2 Score2   2.000000
    
    df %>%
      gather(key = group, value = score, starts_with("Score")) %>%
      left_join(control_means_long, by = "group") %>%
      mutate(score_norm = score / mean_score) %>%
      head()
    #>   Treatment  group score mean_score score_norm
    #> 1   Control Score1     4   4.833333  0.8275862
    #> 2   Control Score1     5   4.833333  1.0344828
    #> 3   Control Score1     4   4.833333  0.8275862
    #> 4   Control Score1     5   4.833333  1.0344828
    #> 5   Control Score1     5   4.833333  1.0344828
    #> 6   Control Score1     6   4.833333  1.2413793
    

    之后您可能想要删除均值列。如果可以将其保留为该格式,那么您就完成了。但是,如果您需要像开始时那样恢复宽大的形状,则必须进行几轮重塑。

    计算后,我将创建一个列score_type 以显示值是按gather 测量或规范的。然后将该文本与组粘贴在一起,形成Score1_measuredScore1_normed 等列。添加临时行号以让spread 正确匹配这些分数,并将其放回宽形。

    df %>%
      gather(key = group, value = measured, starts_with("Score")) %>%
      left_join(control_means_long, by = "group") %>%
      mutate(normed = measured / mean_score) %>%
      select(-mean_score) %>%
      gather(key = score_type, value = value, measured, normed) %>%
      unite(group_and_type, group, score_type) %>%
      group_by(group_and_type) %>%
      mutate(row = row_number()) %>%
      spread(key = group_and_type, value = value) %>%
      select(-row) %>%
      head()
    #> # A tibble: 6 x 5
    #>   Treatment Score1_measured Score1_normed Score2_measured Score2_normed
    #>   <fct>               <dbl>         <dbl>           <dbl>         <dbl>
    #> 1 Control                 4         0.828               1           0.5
    #> 2 Control                 5         1.03                2           1  
    #> 3 Control                 4         0.828               1           0.5
    #> 4 Control                 5         1.03                2           1  
    #> 5 Control                 5         1.03                3           1.5
    #> 6 Control                 6         1.24                3           1.5
    

    reprex package (v0.2.1) 于 2019 年 2 月 19 日创建

    【讨论】:

      【解决方案3】:

      非常感谢您的建议!我应该在我的问题中更清楚地说明,我在这里命名为“分数 1 和分数 2”的变量实际上在我的数据集中被命名为一堆不同的东西,例如面积、数量、长度等。

      最终为我工作的是 dplyr 和 mapply 的组合。虽然我很欣赏有用的 dplyr 提示 Camille!

      我得到了所有变量的平均值(按治疗分组),如下所示:

      Means<- df %>% group_by(Treatment) %>%          
          summarise_each(funs(mean(., na.rm = TRUE)))

      然后使用 mapply 通过其控制处理均值对每个变量进行归一化:

      normalised.df <-mapply( function(x,y) {x / y},
                                   x = df[c("area", "number", "length")],
                                   y = Means[Means$Treatment == "Control", c("area", "number", "length")])

      非常感谢!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-01
        • 2018-10-19
        • 2021-04-04
        • 2017-06-19
        • 1970-01-01
        • 2017-05-29
        相关资源
        最近更新 更多