【问题标题】:R: purrr: using pmap for row-wise operations, but this time involving LOTS of columnsR: purrr: 使用 pmap 进行逐行操作,但这次涉及很多列
【发布时间】:2019-12-13 06:02:37
【问题描述】:

这不是问题的重复,例如Row-wise iteration like apply with purrr

我了解如何使用 pmap() 对数据框进行逐行操作:

library(tidyverse)

df1 = tribble(~col_1, ~col_2, ~col_3,
               1,      5,      12,
               9,      3,      3,
               6,     10,     7)

foo = function(col_1, col_2, col_3) {
  mean(c(col_1, col_2, col_3))
}

df1 %>% pmap_dbl(foo)

这将函数foo 应用于每一行:

[1] 6.000000 5.000000 7.666667

但是当我有多个列时,这会变得非常笨拙,因为我必须明确地将它们全部传递进去。如果我说,我的数据框 df2 中有 8 列,我想应用一个函数 bar,它可能涉及这些列中的每一列?

set.seed(12345)
df2 = rnorm(n=24) %>% matrix(nrow=3) %>% as_tibble() %>%
  setNames(c("col_1", "col_2", "col_3", "col_4", "col_5", "col_6", "col_7", "col_8"))

bar = function(col_1, col_2, col_3, col_4, col_5, col_6, col_7, col_8) {
  # imagine we do some complicated row-wise operation here
  mean(c(col_1, col_2, col_3, col_4, col_5, col_6, col_7, col_8))
}

df2 %>% pmap_dbl(bar)

给予:

[1]  0.45085420  0.02639697 -0.28121651

这显然是不够的——我必须为每一列添加一个新参数到bar。输入很多,它使代码更不可读且更脆弱。似乎应该有一种方法让它接受一个参数x,然后通过x$col_1 等访问我想要的变量。或者无论如何比上面更优雅的东西。有没有办法使用 purrr 清理这段代码?

【问题讨论】:

  • 你能添加你期望的输出吗?
  • 我发现其中一些取决于列的顺序是否与参数进入函数的顺序一致,或者您是使用所有列还是仅使用其中一些列。对于您使用所有列的简单示例,您可以执行类似bar2 = function(...) mean(c(...)) 的操作
  • @aosmith 这只是我选择的示例的人工制品——通常我需要能够按名称引用列并以不同的方式使用它们,而不是使用 ... 作为包罗万象。

标签: r parameter-passing rows purrr pmap


【解决方案1】:

... 和 en-list 在您的函数中后,您可以使用它们。

dot_tester <- function(...) {
  dots <- list(...)
  dots$Sepal.Length + dots$Petal.Width
}

purrr::pmap(head(iris), dot_tester)
[[1]]
[1] 5.3

[[2]]
[1] 5.1

[[3]]
[1] 4.9

[[4]]
[1] 4.8

[[5]]
[1] 5.2

[[6]]
[1] 5.8

但是,这不会改变您的代码“脆弱”,因为您仍然需要明确且准确地将列名匹配为函数中的名称。好处是不必在&lt;- function() 电话中列出它们。

【讨论】:

    【解决方案2】:

    我能想到的最简单(可能不是最安全)的方法是利用 ... 参数来获取任意数量的列

    library(tidyverse)
    
    set.seed(12345)
    df2  <-  rnorm(n=24) %>% matrix(nrow=3) %>% as_tibble() %>%
      setNames(c("col_1", "col_2", "col_3", "col_4", "col_5", "col_6", "col_7", "col_8"))
    #> Warning: `as_tibble.matrix()` requires a matrix with column names or a `.name_repair` argument. Using compatibility `.name_repair`.
    #> This warning is displayed once per session.
    
    bar <- function(...){
      mean(c(...))
    }
    df2 %>% pmap_dbl(bar)
    #> [1]  0.45085420  0.02639697 -0.28121651
    

    reprex package (v0.3.0) 于 2019 年 8 月 5 日创建

    【讨论】:

    • 我需要能够按名称引用列。我的实际用例并不像调用mean() 那样简单,这正是我选择保持示例简单的原因。
    • 我明白了。所以不允许更改 bar 的主体?
    • 可以修改栏的主体...例如对col_i 的引用可能会变为x[col_i] 或其他内容。
    【解决方案3】:

    @Brian 的回答有效,但我还发现了另一种使用 purrr::transpose 的方法,它允许我使用单个命名变量 x 而不是 ...,并且可以按名称访问任何列:

    foo = function(x) {
      (x$col_1 + x$col_2 + x$col_3)/3
    }
    
    df1 %>% transpose() %>% map_dbl(foo)
    

    这给出了正确答案:

    [1] 6.000000 5.000000 7.666667
    

    至于其他数据框:

    set.seed(12345)
    df2 = rnorm(n=24) %>% matrix(nrow=3) %>% as_tibble() %>%
      setNames(c("col_1", "col_2", "col_3", "col_4", "col_5", "col_6", "col_7", "col_8"))
    
    bar = function(x) {
      mean(as.double(x))
    }
    
    df2 %>% transpose() %>% map_dbl(bar)
    

    给予:

    [1]  0.45085420  0.02639697 -0.28121651
    

    但我也可以通过引用各个列来做到这一点:

    bar_2 = function(x) {
      x$col_2 + x$col_5 / x$col_3
    }
    
    df2 %>% transpose() %>% map_dbl(bar_2)
    
    [1]  0.1347090 -1.2776983  0.8232767
    

    我意识到这些特定示例可以使用 mutate 轻松完成,但有时需要真正的逐行迭代,我认为这已经足够了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-15
      • 1970-01-01
      • 1970-01-01
      • 2012-08-06
      相关资源
      最近更新 更多