R:purrr:使用 pmap 进行逐行操作,但这次涉及很多列
R: purrr: using pmap for row-wise operations, but this time involving LOTS of columns
这不是重复的问题,例如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
但是当我有多个列时,这会变得非常笨拙,因为我必须显式地传递它们。如果我说,我的数据框中有 8 列 df2
并且我想应用一个函数 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 清理这段代码吗?
我能想到的最简单(可能不是最安全)的方法是利用 ...
参数,获取任意数量的列
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-08-05 创建
您可以在函数中使用 ...
和 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
但是,这不会改变您的代码 "fragile",因为您仍然明确且准确地需要将您的列名称与函数中的名称相匹配。好处是不必在 <- function()
电话中列出它们。
@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
完成,但对于需要真正的逐行迭代的时候,我认为这很好用。
这不是重复的问题,例如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
但是当我有多个列时,这会变得非常笨拙,因为我必须显式地传递它们。如果我说,我的数据框中有 8 列 df2
并且我想应用一个函数 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 清理这段代码吗?
我能想到的最简单(可能不是最安全)的方法是利用 ...
参数,获取任意数量的列
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-08-05 创建您可以在函数中使用 ...
和 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
但是,这不会改变您的代码 "fragile",因为您仍然明确且准确地需要将您的列名称与函数中的名称相匹配。好处是不必在 <- function()
电话中列出它们。
@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
完成,但对于需要真正的逐行迭代的时候,我认为这很好用。