首页 > 解决方案 > 使用 dplyr 和 tidyr 逐行划分行

问题描述

我正在尝试使用 dplyr 处理(相当大的)数据集,我相信我的问题源于对“summarise_if”函数的使用不当。这是一些生成看起来像我的虚拟数据的代码:

df <- data.frame(Block = c(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4),
                 Treatment = c(rep("Control", 4), rep("CF", 4), rep("LR", 4)),
                 var1 = c(9, 12, 15, 16, 11, 9, 13, 11, 5, 11, 5, 11), 
                 var2 = c(0, 4, 9, 3, 6, 0, 0, 10, 15, 25, 0, 0))

我想按块和处理对数据进行分组,然后,对于每个变量(var1、var2、...),我想将“处理”值除以每个块的“控制”值。生成的数据框看起来像这样(我还没有为我的虚拟数据完成所有数学运算,所以我只是将示例公式放在每个单元格中应该计算的内容):

    Treatment    Block              var1              var2
    CF               1        CF/control        CF/control
    CF               2        CF/control        CF/control
    CF               3        CF/control        CF/control
    CF               4        CF/control        CF/control
    LR               1        LR/control        LR/control
    LR               2        LR/control        LR/control
    LR               3        LR/control        LR/control
    LR               4        LR/control        LR/control

一些值将是 NaN 或无穷大,因为我在某些处理中除以零,但这没关系。

我可以使用以下代码一次为单个变量和处理生成我想要的内容:

df %>% 
  dplyr::group_by(Block) %>%
  dplyr::summarise(value = var1[Treatment=="CF"] / var1[Treatment=="Control"])

但这在许多变量和治疗中变得乏味。但是,当我尝试对整个数据框执行此操作时,我会遇到各种错误。我最好的猜测是这样的:

df %>% 
  dplyr::group_by(Block, Treatment) %>%
  dplyr::summarise_if(is.numeric, value = .[Treatment=="CF"] / .[Treatment=="Control"])

这给了我错误“未找到对象'治疗'”,并且仍然迫使我逐个治疗。

任何帮助表示赞赏!

标签: rdplyr

解决方案


您可以使用以下内容:

df %>% 
  dplyr::group_by(Block) %>%
  dplyr::summarise(across(where(is.numeric), 
          list(CF = ~.[Treatment=="CF"] / .[Treatment=="Control"], 
               LR = ~.[Treatment=="LR"] / .[Treatment=="Control"]))) 


#  Block var1_CF var1_LR var2_CF var2_LR
#  <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
#1     1   1.22    0.556  Inf     Inf   
#2     2   0.75    0.917    0       6.25
#3     3   0.867   0.333    0       0   
#4     4   0.688   0.688    3.33    0   

如果您希望输出遵循与预期输出中所示相同的格式,您可以使用pivot_longer.

df %>% 
  dplyr::group_by(Block) %>%
  dplyr::summarise(across(where(is.numeric), 
           list(CF = ~.[Treatment=="CF"] / .[Treatment=="Control"], 
                LR = ~.[Treatment=="LR"] / .[Treatment=="Control"]))) %>%
  tidyr::pivot_longer(cols = -Block, 
               names_to = c('.value', 'Treatment'), 
               names_sep = '_')


#  Block Treatment  var1   var2
#  <dbl> <chr>     <dbl>  <dbl>
#1     1 CF        1.22  Inf   
#2     1 LR        0.556 Inf   
#3     2 CF        0.75    0   
#4     2 LR        0.917   6.25
#5     3 CF        0.867   0   
#6     3 LR        0.333   0   
#7     4 CF        0.688   3.33
#8     4 LR        0.688   0   

推荐阅读