【问题标题】:How do I write a R function that let me manipulate multiple R variables using dplyr's %>% pipes?如何编写一个 R 函数,让我使用 dplyr 的 %>% 管道操作多个 R 变量?
【发布时间】:2020-12-13 11:50:24
【问题描述】:

我正在尝试创建一个函数来操作不同的数据集,但我面临着这个任务的几个问题。我在下面的 dput() 输出中提供了我试图操作的数据的简化版本:

structure(list(id = structure(c(2, 4, 6, 8, 10), label = "iid", format.spss = "F4.0", display_width = 0L), A = c(13, 9, 14, 14, 13), B = c(12, 0, 9, 3, 10), C = c(13, 8, 14, 13, 11)), row.names = c(NA, -5L), class = c("tbl_df", "tbl", "data.frame"))

我正在尝试做几件事,但由于数据的格式化方式,我在不同的时刻卡住了。首先,我需要将每一行的 A:D 列的值汇总到一个名为 total 的变量中。接下来,我需要通过将每一列 A:D 除以 total 来计算概率。

这是我面临一些问题的地方。我写了一个函数来执行上述操作:

functa <- function(x, id, vars) {
  
  x %>%
    mutate(total = rowSums(.[vars])) %>%
    mutate(prob = .[vars]/total)

}

当我使用以下行调用函数时:

test <- functa(df_ED, "pid", c("A", "B", "C", "D"))

我得到一个有 5 个观察值的对象,但只有 7 个变量(而不是 10 个)。当我检查对象时,我看到了 4 个新变量(即 prob.A、prob.B、prob.C、prob.D),但它们被作为单个变量读入。

因此,我想对此数据集执行的任何后续操作都无法按预期进行。在过去的两天里,我一直在研究这个问题,但找不到任何关于这种现象的信息,我猜我想不通。

我使用此功能的最终目标是:

  1. 计算一个total 变量(A:D 的总和)
  2. 计算应输出 4 个变量(即 A/total、B/total 等)的 prob 变量
  3. 重新编码prob 变量,以便将所有无穷大值(即“Inf”)重新编码为 0
  4. 将所有 4 个 prob 变量加到一个 totalprob 变量中

希望对此有任何见解!

【问题讨论】:

    标签: r function dplyr


    【解决方案1】:

    如果您想将一个函数应用于多个列,请使用 across

    library(dplyr)
    
    functa <- function(x, id, vars) {
      
      x %>%
               #sum all vars column
        mutate(total = rowSums(.[vars]),
               #Divide vars column with total and create new columns with prob
               across(all_of(vars), ~./total, .names = '{col}_prob'), 
               #Replace infinite value in prob column with 0
               across(ends_with('_prob'), ~replace(., is.infinite(.), 0))) %>%
               #Sum all prob columns. 
        mutate(totalprob = rowSums(select(., ends_with('prob'))))      
      
    }
    
    functa(df_ED, "pid", c("A", "B", "C"))
    
    #     id     A     B     C total A_prob B_prob C_prob totalprob
    #  <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>  <dbl>  <dbl>     <dbl>
    #1     2    13    12    13    38  0.342  0.316  0.342         1
    #2     4     9     0     8    17  0.529  0      0.471         1
    #3     6    14     9    14    37  0.378  0.243  0.378         1
    #4     8    14     3    13    30  0.467  0.1    0.433         1
    #5    10    13    10    11    34  0.382  0.294  0.324         1
    

    【讨论】:

    • 谢谢罗纳克。关于across 中的.names 参数的另一个问题——如果我在后面的行中使用across 运行其他计算,有没有办法可以用类似{col}_abc 的东西替换以前命名的{col}_prob?如果我跟进 across 语句,这次选择先前计算的变量(例如,A_prob,我会得到像 A_prob_abc 这样的变量名称,而不是预期的 A_abc
    • @Bear25 如果您将.names 更改为.names = '{col}_abc',它应该为您提供列名A_abc
    • 实际上我认为我不清楚我的后续问题。因为在下一行,我选择了以_prob结尾的变量做进一步的计算,然后用.names = '{col}_abc命名,但是变量名变成了A_prob_abc。因此,我想知道是否有办法在 .names 参数中进行替换,以便在命名新变量时可以将 _prob 替换为 _abc
    • 不,您将无法以这种方式获得所需的名称。你有两个选择。 1) 不要在第一步中使用.names = '{col}_prob 更改名称,因此您会覆盖原始列名,并且您的列名仍称为“A”、“B”等或 2) 使用 rename_with 通过删除重命名列prob 在第二个 across 之后来自它。
    • 感谢您的澄清!我使用setNames() 让代码工作。我试过但无法让rename_with 正常工作。可能需要多读一点。
    【解决方案2】:

    另一种解决方案是更改表格的布局,在第一步通过pivot_longer 计算概率,在下一步通过pivot_wider 获得所需的最终布局。

    > df %>% 
    +   pivot_longer(-id, names_to = "key", values_to = "value") %>%
    +   group_by(id) %>%
    +   mutate(prob = value / sum(value)) %>%
    +   pivot_wider(names_from = key, values_from = c(value, prob))
    # A tibble: 5 x 7
    # Groups:   id [5]
         id value_A value_B value_C prob_A prob_B prob_C
      <dbl>   <dbl>   <dbl>   <dbl>  <dbl>  <dbl>  <dbl>
    1     2      13      12      13  0.342  0.316  0.342
    2     4       9       0       8  0.529  0      0.471
    3     6      14       9      14  0.378  0.243  0.378
    4     8      14       3      13  0.467  0.1    0.433
    5    10      13      10      11  0.382  0.294  0.324
    

    【讨论】:

      猜你喜欢
      • 2022-06-18
      • 1970-01-01
      • 1970-01-01
      • 2019-04-26
      • 1970-01-01
      • 1970-01-01
      • 2017-04-27
      • 2022-01-20
      • 2019-04-13
      相关资源
      最近更新 更多