【问题标题】:Mutate new column based on different datasets根据不同的数据集改变新列
【发布时间】:2021-01-21 11:14:37
【问题描述】:

示例我有三个数据集:df1_mean(基于 df1 的每个变量的平均值)、df1_sd(基于 df1 的每个变量的 sd)和 df2(df2 的值)。

df1_mean:

  A_mean B_mean C_mean D_mean E_mean
1     10     15     12     25     29

df1_sd:

  A_sd B_sd C_sd D_sd E_sd
1    3    2    5    4    2

df2:

  A  B  C  D  E
1 20 32 12 14 22
2 21 35 14 52 13
3 25 23 21 32 35
4 23 12 11 52 21
5 20 53 43 12 64
6 30 12 23 53 31

理想情况下,我想为 df2 中的每个变量(即分别为 A、B、C、D、E)匹配来自 df1*_mean*_sd strong>,然后mutate()一个基于公式的新列,并为每个输出新列。

对于每个变量,最终结果应如下所示:

df2$A_output = (df2$A - df1$A_mean) / df1$A_sd

有人知道是否有一种方法可以将来自不同数据集的数据用于mutate() 新列?或者什么是自动化而不是手动使用A_output = (A-10)/3, B_output = (B-15)/2, ... 的最简单方法?谢谢!

【问题讨论】:

    标签: r match dplyr


    【解决方案1】:

    以下是一些基本的 R 选项:

    • 使用rep
    dfout <- (df2 - df1_mean[rep(1,nrow(df2)),])/df1_sd[rep(1,nrow(df2)),]
    
    • 使用sweep
    dfout <- sweep(sweep(df2,2,unlist(df1_mean)),2,unlist(df1_sd),FUN = `/`)
    

    两者都会给予

    > dfout
             A    B    C     D    E
    1 3.333333  8.5  0.0 -2.75 -3.5
    2 3.666667 10.0  0.4  6.75 -8.0
    3 5.000000  4.0  1.8  1.75  3.0
    4 4.333333 -1.5 -0.2  6.75 -4.0
    5 3.333333 19.0  6.2 -3.25 17.5
    6 6.666667 -1.5  2.2  7.00  1.0
    

    数据

    > dput(df1_mean)
    structure(list(A_mean = 10L, B_mean = 15L, C_mean = 12L, D_mean = 25L,
        E_mean = 29L), class = "data.frame", row.names = "1")
    
    > dput(df1_sd)
    structure(list(A_sd = 3L, B_sd = 2L, C_sd = 5L, D_sd = 4L, E_sd = 2L), class = "data.frame", row.names = "1")
    
    > dput(df2)
    structure(list(A = c(20L, 21L, 25L, 23L, 20L, 30L), B = c(32L,
    35L, 23L, 12L, 53L, 12L), C = c(12L, 14L, 21L, 11L, 43L, 23L),
        D = c(14L, 52L, 32L, 52L, 12L, 53L), E = c(22L, 13L, 35L, 
        21L, 64L, 31L)), class = "data.frame", row.names = c("1",
    "2", "3", "4", "5", "6"))
    

    【讨论】:

      【解决方案2】:

      试试这个

      as.data.frame(Map(function(x, mu, sig) (x - mu) / sig, df2, df1_mean, df1_sd))
      

      输出

               A    B    C     D    E
      1 3.333333  8.5  0.0 -2.75 -3.5
      2 3.666667 10.0  0.4  6.75 -8.0
      3 5.000000  4.0  1.8  1.75  3.0
      4 4.333333 -1.5 -0.2  6.75 -4.0
      5 3.333333 19.0  6.2 -3.25 17.5
      6 6.666667 -1.5  2.2  7.00  1.0
      

      【讨论】:

      • 作为一个代码非常倾向于 tidyverse 的人,我真的很高兴看到一行基本代码比我所做的所有管道都更加高效和直接
      • @camille 将问题移入和移出 tidyverse 长数据范式需要工作。如果df2 已经是具有列 var 和 value 的长格式,而 df1 是具有 3 列(var、mean、sd)的单一数据框,并且所需的结果也很整洁,这将是一个简单的 2 dplyr 的行(或 2 行 base,只有一个 merge 和一个 transform)。同样,df1 是一个矩阵,df2_meandf2_sd 是向量,而不是数据帧,我的base 答案会更简单。这个答案很好地解决了它所在的问题,但问题出在一个不幸的起点,恕我直言。
      • (并不是真的@你,因为我很欣赏你的评论,它激发了我的一些想法......我已经在这里看到了足够多的你的贡献,知道你没有懈怠:)
      【解决方案3】:

      试试这个tidyverse 方法:

      library(tidyverse)
      #Code
      Output <- df2 %>% mutate(id=1:n()) %>% pivot_longer(-id) %>%
        left_join(df1_mean %>% pivot_longer(everything()) %>%
                    separate(name,c('name','Var'),sep='_') %>%
                    rename(Mean=value) %>% select(-Var)
        ) %>%
        left_join(
          df1_sd %>% pivot_longer(everything()) %>%
            separate(name,c('name','Var'),sep='_') %>%
            rename(SD=value) %>% select(-Var)
        ) %>% mutate(Val=(value-Mean)/SD) %>% select(-c(value,Mean,SD)) %>%
        pivot_wider(names_from = name,values_from=Val) %>% select(-id)
      

      输出:

      # A tibble: 6 x 5
            A     B     C     D     E
        <dbl> <dbl> <dbl> <dbl> <dbl>
      1  3.33   8.5   0   -2.75  -3.5
      2  3.67  10     0.4  6.75  -8  
      3  5      4     1.8  1.75   3  
      4  4.33  -1.5  -0.2  6.75  -4  
      5  3.33  19     6.2 -3.25  17.5
      6  6.67  -1.5   2.2  7      1  
      

      使用的一些数据:

      #Data 1
      df1_mean <- structure(list(A_mean = 10L, B_mean = 15L, C_mean = 12L, D_mean = 25L,E_mean = 29L), class = "data.frame", row.names = "1")
      
      #Data 2
      df1_sd <-structure(list(A_sd = 3L, B_sd = 2L, C_sd = 5L, D_sd = 4L, E_sd = 2L), class = "data.frame", row.names = "1")
      
      #Data 3
      df2 <- structure(list(A = c(20L, 21L, 25L, 23L, 20L, 30L), B = c(32L, 
      35L, 23L, 12L, 53L, 12L), C = c(12L, 14L, 21L, 11L, 43L, 23L), 
          D = c(14L, 52L, 32L, 52L, 12L, 53L), E = c(22L, 13L, 35L, 
          21L, 64L, 31L)), class = "data.frame", row.names = c("1", 
      "2", "3", "4", "5", "6"))
      

      【讨论】:

      • 我尝试了类似的方法,但是您有一些地方可以通过使用 pivot 函数中的参数来删除其他函数。如果您设置了names_pattern 参数并使用".value" 快捷方式,则无需调用separaterename
      • @Duck 谢谢!我为测试数据尝试了这个,它运行成功。我在尝试使用我的数据集时遇到了一个错误,Expected 2 pieces. Additional pieces discarded in 1 rows [1].Joining, by = "name",现在正在尝试修复它。
      • @smicaela 你好,micaela。也许在您的数据中,其他数据框中的后缀不同。您可以检查names(),看看是否有其他分隔符不同于_
      【解决方案4】:

      这是一种使用矢量化数学和一些转置来进行回收工作的方法:

      t( (t(df2) - unlist(df1_mean)) / unlist(df1_sd) )
      #          A    B    C     D    E
      # 1 3.333333  8.5  0.0 -2.75 -3.5
      # 2 3.666667 10.0  0.4  6.75 -8.0
      # 3 5.000000  4.0  1.8  1.75  3.0
      # 4 4.333333 -1.5 -0.2  6.75 -4.0
      # 5 3.333333 19.0  6.2 -3.25 17.5
      # 6 6.666667 -1.5  2.2  7.00  1.0
      

      它依赖于三个数据框的列按相应的顺序排列。只要这是真的,它应该是非常有效的。

      【讨论】:

      • 也许使用unlist(..., FALSE, FALSE) 来提高效率?
      • 怀疑保存的微秒是否重要,但对于任何对超效率感兴趣的人来说,这是一个很好的评论。我会保留它,而不仅仅是为了让代码更具可读性。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-08
      相关资源
      最近更新 更多