使用 dplyr 或 apply 对多个变量的多个观测值应用计算

Posted

技术标签:

【中文标题】使用 dplyr 或 apply 对多个变量的多个观测值应用计算【英文标题】:Using dplyr or apply to apply calculation on multiple observations of multiple variables 【发布时间】:2017-02-11 16:39:45 【问题描述】:

我有一个包含 200 多个变量的数据框(下面是一个示例):

  | x | P      | Var1_mean | Var2_mean | Var3_mean | Var1_sd | Var2_sd | Var3_sd
------------------------------------------------------------------------------
1 | A | P1     | 100       | 50.47     | 298.2     | 2.33    | 0.04    | 8.77
2 | A | P2     | 98        | 18        | 350.33    | 2.32    | 0.04    | 10.3
3 | B | P1     | 100       | 30.93     | 152.73    | 2.33    | 0.04    | 4.49
4 | B | P2     | 100       | 25.33     | 237.67    | 2.33    | 0.04    | 6.99
5 | C | P1     | 99.9      | 25.07     | 184.93    | 2.32    | 0.04    | 5.44
6 | C | P2     | 100       | 18.33     | 132.33    | 2.32    | 0.04    | 3.89

每个变量都有参考周期 P1 和测量周期 P2 的 N 个观测值(A、B、C 等)。

我希望为每个观察结果计算每个变量的两个周期之间的差异,并将其除以参考周期的标准差。

使用上面的例子:

df <- data.frame(x=c("A","A","B","B","C","C"),
                 P=c("P1","P2","P1","P2","P1","P2"),        
             Var1_mean=c(100.0,98,100.0,100.0,99.9,100.0),
             Var2_mean = c(50.47,18,30.93,25.33,25.07,18.33),
             Var3_mean = c(298.2,350.33,152.73,237.67,184.93,132.33),
             Var1_sd = c(2.33,2.32,2.33,2.33,2.32,2.32),
             Var2_sd = c(0.04,0.04,0.04,0.04,0.04,0.04),
             Var3_sd = c(8.77,10.3,4.49,6.99,5.44,3.89))

Z.A.Var1 <- (df$Var1_mean[df$x=="A" & df$P=="P1"] - df$Var1_mean[df$x=="A" & df$P=="P2"])
            / df$Var1_sd[df$x=="A" & df$P=="P1"]

Z.A.Var2 <- (df$Var2_mean[df$x=="A" & df$P=="P1"] - df$Var2_mean[df$x=="A" & df$P=="P2"])
            / df$Var2_sd[df$x=="A" & df$P=="P1"]

等等。

我可以使用“for”循环进行计算,扫描观察结果和变量,但运行起来会很麻烦且速度很慢。

是否有人对如何以更智能的方式执行此操作提出建议,例如使用 dplyr os 类似的东西?

【问题讨论】:

【参考方案1】:

尝试使用data.table

library(data.table)
dat <- data.table(df)
dat[, .(calc_V1 = ((Var1_mean[P == "P1"] - Var1_mean[P == "P2"])/Var1_sd[P == "P1"]),
        calc_V2 = ((Var2_mean[P == "P1"] - Var2_mean[P == "P2"])/Var2_sd[P == "P1"]),
        calc_V3 = ((Var3_mean[P == "P1"] - Var3_mean[P == "P2"])/Var3_sd[P == "P1"])),
    by = .(x)]

   x     calc_V1 calc_V2    calc_V3
1: A  0.85836910  811.75  -5.944128
2: B  0.00000000  140.00 -18.917595
3: C -0.04310345  168.50   9.669118

以上假设您的 200 多个变量位于 x 列中。相反,如果您的真实数据集非常宽(200 多个变量中的每一个都有两列),您将需要一种不同的方法:

dat2 <- melt(dat, id.vars = c("x", "P"))
dat2[, c("variable_val", "variable_type") := tstrsplit(variable, "_", fixed = TRUE)]
dat2[, .(calc_val = ((value[P == "P1" & variable_type == "mean"] - 
                        value[P == "P2" & variable_type == "mean"])/
                       value[P == "P1" & variable_type == "sd"])),
         by = .(x, variable_val)]

   x variable_val     calc_val
1: A         Var1   0.85836910
2: B         Var1   0.00000000
3: C         Var1  -0.04310345
4: A         Var2 811.75000000
5: B         Var2 140.00000000
6: C         Var2 168.50000000
7: A         Var3  -5.94412771
8: B         Var3 -18.91759465
9: C         Var3   9.66911765

【讨论】:

【参考方案2】:

我总是尽量避免 for 循环。我会在两个表中发送 P1 和 P2 变量,在另一个表中发送 SD。然后使用矩阵运算。像这样的:

desired.stuff <- (P1.stuff - P2.stuff) / sd.stuff

此解决方案适用于任意数量的变量,只要 P1 和 P2 具有相同数量的相同顺序的变量,就可以了。

这是一个使用 data.table 语法的解决方案:

library(data.table)
df <- data.table(df) #make a data.table
P1.stuff <- df[P=="P1", c(1:3), with=F] #select the P1 variables
P2.stuff <- df[P=="P2", c(3:5), with=F] #select the P2 variables
sd.stuff <- df[P=="P1", c(6:8), with=F)] #select de P1 SDs
desired.stuff <- P1.stuff - P2.stuff / sd.stuff 

此外,由于您似乎在进行 Cohen 的 D 计算,您可能需要查看 effsize 包: https://cran.r-project.org/web/packages/effsize/effsize.pdf

【讨论】:

【参考方案3】:

这可以通过tidyverse 用于重塑和聚合数据的包来完成:

library(dplyr)
library(tidyr)

df.new <- gather(df, variable, value, -x, -P) %>% # convert data to 'long' format
    separate(variable, c('variable', 'measure')) %>% # separate out variable number and measurement (mean and sd) columns
    spread(measure, value) %>% # make each row have a mean and sd column
    group_by(variable, x) %>% # group by variable and observation and...
    summarize(result = diff(mean) / sd[P == 'P1']) # compute the requested summary, storing in 'result'

  variable      x        result
     <chr> <fctr>         <dbl>
1     Var1      A   -0.85836910
2     Var1      B    0.00000000
3     Var1      C    0.04310345
4     Var2      A -811.75000000
5     Var2      B -140.00000000
6     Var2      C -168.50000000
7     Var3      A    5.94412771
8     Var3      B   18.91759465
9     Var3      C   -9.66911765

【讨论】:

原题中的示例计算不包括abs(),否则得到相同的结果。总是很高兴看到做某事的替代方法。 哎呀,你说得对。编辑删除绝对值。 看来您的符号有误。第一行应该是(100-98)/2.33 = 0.858,你得到-0.858。在您的最后一行中,diff(mean) 如何确定 P1-P2 或 P2-P1 的顺序?

以上是关于使用 dplyr 或 apply 对多个变量的多个观测值应用计算的主要内容,如果未能解决你的问题,请参考以下文章

使用 group_by(多个变量)时的 dplyr 问题

R dplyr:使用字符串函数重命名变量

dplyr summarise :在循环中按多个变量分组并将结果添加到同一数据框中

如何在 R 中 dplyr::inner_join 多个 tbls 或 data.frames

使用 dplyr 嵌套或分组两个变量,然后对数据执行 Cronbach 的 alpha 函数或其他统计

在 R dplyr 中过滤具有多个条件名称匹配的数据框