循环字符向量并使用元素作为 lambda 函数中的列名
Loop over character vectors and use elements as column names within lambda function
我想用 purrr
遍历变量名称的向量,然后用 dplyr
在函数内使用变量,如下面的代码:
library(dplyr)
library(purrr)
#creating index
index<-c('Sepal.Length', 'Sepal.Width')
#mapping over index with lambda function
map(index, ~iris %>% filter (.x > mean(.x)))
我期待看到一个包含两个 data.frames 的列表,如
list(Sepal.Length = iris %>% filter (Sepal.Length > mean(Sepal.Length)),
Sepal.Width = iris %>% filter (Sepal.Width > mean(Sepal.Width)))
有没有办法在 lambda 函数的 data.frames 中使用 .x
变量作为列名?
我认为这可能与数据屏蔽和非标准评估有关,我怀疑 rlang
在这里可能会有帮助,但我不熟悉这个主题。
谢谢
那些是字符串。我们需要转换为 sym
bol 并计算 (!!
)
library(purrr)
library(dplyr)
out <- map(index, ~iris %>%
filter (!! rlang::sym(.x) > mean(!! rlang::sym(.x))))
names(out) <- index
-输出
> str(out)
List of 2
$ Sepal.Length:'data.frame': 70 obs. of 5 variables:
..$ Sepal.Length: num [1:70] 7 6.4 6.9 6.5 6.3 6.6 5.9 6 6.1 6.7 ...
..$ Sepal.Width : num [1:70] 3.2 3.2 3.1 2.8 3.3 2.9 3 2.2 2.9 3.1 ...
..$ Petal.Length: num [1:70] 4.7 4.5 4.9 4.6 4.7 4.6 4.2 4 4.7 4.4 ...
..$ Petal.Width : num [1:70] 1.4 1.5 1.5 1.5 1.6 1.3 1.5 1 1.4 1.4 ...
..$ Species : Factor w/ 3 levels "setosa","versicolor",..: 2 2 2 2 2 2 2 2 2 2 ...
$ Sepal.Width :'data.frame': 67 obs. of 5 variables:
..$ Sepal.Length: num [1:67] 5.1 4.7 4.6 5 5.4 4.6 5 4.9 5.4 4.8 ...
..$ Sepal.Width : num [1:67] 3.5 3.2 3.1 3.6 3.9 3.4 3.4 3.1 3.7 3.4 ...
..$ Petal.Length: num [1:67] 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.5 1.5 1.6 ...
..$ Petal.Width : num [1:67] 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.1 0.2 0.2 ...
..$ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
-使用 OP 的预期进行测试
> expected <- list(Sepal.Length = iris %>% filter (Sepal.Length > mean(Sepal.Length)),
+ Sepal.Width = iris %>% filter (Sepal.Width > mean(Sepal.Width)))
>
> identical(out, expected)
[1] TRUE
或 cur_data()
的子集
map(index, ~ iris %>%
filter(cur_data()[[.x]] > mean(cur_data()[[.x]])))
或使用across
或if_all
,直接取string
map(index, ~ iris %>%
filter(across(all_of(.x), ~ . > mean(.))))
类似
的解决方案
map(index, function(i, x) filter(x, x[[i]] > mean(x[[i]])), iris)
似乎平衡了 dplyr 的功能(例如,用 NA
值做一些明智的事情),没有过多的非标准评估障碍,同时也强调了一些有用的 R 习语,比如使用 [[
当非标准评估变得非常麻烦时,按名称提取可能有用的列。
就我个人而言,我会使用 lapply()
而不是 map()
,这样我就不必再学习另一个包了。如果我想要命名列表元素,我会这样做 'up front' 而不是事后添加
names(index) <- index
lapply(index, function(i, x) filter(x, x[[i]] > mean(x[[i]])), iris)
或者也许
lapply(setNames(nm=index), function(i, x) filter(x, x[[i]] > mean(x[[i]])), iris)
如果这是我代码中的常见场景(或者即使这是一次性的),我可能会编写一个简短的辅助函数
filter_greater_than_column_mean <- function(i, x)
dplyr::filter( x, x[[i]] > mean(x[[i]]) )
lapply(index, filter_greater_than_column_mean, iris)
如果我以自己的方式成为业余爱好者并试图变得更笼统,我可能会变得过于复杂
filter_by_column_mean <- function(i, x, op = `>`) {
idx <- op(x[[i]], mean(x[[i]]))
dplyr::filter(x, idx)
}
lapply(index, filter_by_column_mean, iris)
lapply(index, filter_by_column_mean, iris, `<=`)
甚至
filter_by_column <- function(i, x, op = `>`, aggregate = mean) {
idx <- op(x[[i]], aggregate(x[[i]]))
dplyr::filter(x, idx)
}
lapply(index, filter_by_column, iris, op = `<=`)
lapply(index, filter_by_column, iris, `<=`, median)
既然我没有使用非标准评估,我可能会瞄准基础 R 的 subset()
,它也可以用 NA
做一些明智的事情。所以
filter_by_column <- function(i, x, aggregate = mean, op = `>`) {
idx <- op(x[[i]], aggregate(x[[i]]))
subset(x, idx)
}
我知道这意味着我已经了解了很多关于 base R 的知识,也许我应该学习 !!
与 !!!
与...,但无论如何我学习了
- 像
mean
这样的函数是'first class',我可以将代表函数的符号(例如mean
)分配给一个变量(例如aggregate
)然后将变量用作函数 (aggregate(...)
).
- 像
<
这样的操作符其实就是函数,而lhs < rhs
可以写成`<`(lhs, rhs)
(而且写到我还得学markdown写反引号!)
更平淡
-
lapply()
的 FUN
参数除了被迭代的参数之外还接受参数。这些可以命名或未命名,应用参数匹配的通常规则(首先按名称匹配,然后按位置匹配)。
[[
可用于按名称进行子集化,避免需要 seq_along()
或其他依赖数字索引的不太健壮的操作。
基数 R:
index<-c('Sepal.Length', 'Sepal.Width')
df <- iris
setNames(
lapply(
seq_along(index),
function(i){
mu <- mean(df[,index[i]], na.rm = TRUE)
df[df[,index[i],drop = TRUE] > mu, ]
}
),
index
)
你可以使用.data
-
library(dplyr)
library(purrr)
index<-c('Sepal.Length', 'Sepal.Width')
map(index, ~iris %>% filter (.data[[.x]] > mean(.data[[.x]])))
我想用 purrr
遍历变量名称的向量,然后用 dplyr
在函数内使用变量,如下面的代码:
library(dplyr)
library(purrr)
#creating index
index<-c('Sepal.Length', 'Sepal.Width')
#mapping over index with lambda function
map(index, ~iris %>% filter (.x > mean(.x)))
我期待看到一个包含两个 data.frames 的列表,如
list(Sepal.Length = iris %>% filter (Sepal.Length > mean(Sepal.Length)),
Sepal.Width = iris %>% filter (Sepal.Width > mean(Sepal.Width)))
有没有办法在 lambda 函数的 data.frames 中使用 .x
变量作为列名?
我认为这可能与数据屏蔽和非标准评估有关,我怀疑 rlang
在这里可能会有帮助,但我不熟悉这个主题。
谢谢
那些是字符串。我们需要转换为 sym
bol 并计算 (!!
)
library(purrr)
library(dplyr)
out <- map(index, ~iris %>%
filter (!! rlang::sym(.x) > mean(!! rlang::sym(.x))))
names(out) <- index
-输出
> str(out)
List of 2
$ Sepal.Length:'data.frame': 70 obs. of 5 variables:
..$ Sepal.Length: num [1:70] 7 6.4 6.9 6.5 6.3 6.6 5.9 6 6.1 6.7 ...
..$ Sepal.Width : num [1:70] 3.2 3.2 3.1 2.8 3.3 2.9 3 2.2 2.9 3.1 ...
..$ Petal.Length: num [1:70] 4.7 4.5 4.9 4.6 4.7 4.6 4.2 4 4.7 4.4 ...
..$ Petal.Width : num [1:70] 1.4 1.5 1.5 1.5 1.6 1.3 1.5 1 1.4 1.4 ...
..$ Species : Factor w/ 3 levels "setosa","versicolor",..: 2 2 2 2 2 2 2 2 2 2 ...
$ Sepal.Width :'data.frame': 67 obs. of 5 variables:
..$ Sepal.Length: num [1:67] 5.1 4.7 4.6 5 5.4 4.6 5 4.9 5.4 4.8 ...
..$ Sepal.Width : num [1:67] 3.5 3.2 3.1 3.6 3.9 3.4 3.4 3.1 3.7 3.4 ...
..$ Petal.Length: num [1:67] 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.5 1.5 1.6 ...
..$ Petal.Width : num [1:67] 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.1 0.2 0.2 ...
..$ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
-使用 OP 的预期进行测试
> expected <- list(Sepal.Length = iris %>% filter (Sepal.Length > mean(Sepal.Length)),
+ Sepal.Width = iris %>% filter (Sepal.Width > mean(Sepal.Width)))
>
> identical(out, expected)
[1] TRUE
或 cur_data()
map(index, ~ iris %>%
filter(cur_data()[[.x]] > mean(cur_data()[[.x]])))
或使用across
或if_all
,直接取string
map(index, ~ iris %>%
filter(across(all_of(.x), ~ . > mean(.))))
类似
的解决方案map(index, function(i, x) filter(x, x[[i]] > mean(x[[i]])), iris)
似乎平衡了 dplyr 的功能(例如,用 NA
值做一些明智的事情),没有过多的非标准评估障碍,同时也强调了一些有用的 R 习语,比如使用 [[
当非标准评估变得非常麻烦时,按名称提取可能有用的列。
就我个人而言,我会使用 lapply()
而不是 map()
,这样我就不必再学习另一个包了。如果我想要命名列表元素,我会这样做 'up front' 而不是事后添加
names(index) <- index
lapply(index, function(i, x) filter(x, x[[i]] > mean(x[[i]])), iris)
或者也许
lapply(setNames(nm=index), function(i, x) filter(x, x[[i]] > mean(x[[i]])), iris)
如果这是我代码中的常见场景(或者即使这是一次性的),我可能会编写一个简短的辅助函数
filter_greater_than_column_mean <- function(i, x)
dplyr::filter( x, x[[i]] > mean(x[[i]]) )
lapply(index, filter_greater_than_column_mean, iris)
如果我以自己的方式成为业余爱好者并试图变得更笼统,我可能会变得过于复杂
filter_by_column_mean <- function(i, x, op = `>`) {
idx <- op(x[[i]], mean(x[[i]]))
dplyr::filter(x, idx)
}
lapply(index, filter_by_column_mean, iris)
lapply(index, filter_by_column_mean, iris, `<=`)
甚至
filter_by_column <- function(i, x, op = `>`, aggregate = mean) {
idx <- op(x[[i]], aggregate(x[[i]]))
dplyr::filter(x, idx)
}
lapply(index, filter_by_column, iris, op = `<=`)
lapply(index, filter_by_column, iris, `<=`, median)
既然我没有使用非标准评估,我可能会瞄准基础 R 的 subset()
,它也可以用 NA
做一些明智的事情。所以
filter_by_column <- function(i, x, aggregate = mean, op = `>`) {
idx <- op(x[[i]], aggregate(x[[i]]))
subset(x, idx)
}
我知道这意味着我已经了解了很多关于 base R 的知识,也许我应该学习 !!
与 !!!
与...,但无论如何我学习了
- 像
mean
这样的函数是'first class',我可以将代表函数的符号(例如mean
)分配给一个变量(例如aggregate
)然后将变量用作函数 (aggregate(...)
). - 像
<
这样的操作符其实就是函数,而lhs < rhs
可以写成`<`(lhs, rhs)
(而且写到我还得学markdown写反引号!)
更平淡
-
lapply()
的FUN
参数除了被迭代的参数之外还接受参数。这些可以命名或未命名,应用参数匹配的通常规则(首先按名称匹配,然后按位置匹配)。 [[
可用于按名称进行子集化,避免需要seq_along()
或其他依赖数字索引的不太健壮的操作。
基数 R:
index<-c('Sepal.Length', 'Sepal.Width')
df <- iris
setNames(
lapply(
seq_along(index),
function(i){
mu <- mean(df[,index[i]], na.rm = TRUE)
df[df[,index[i],drop = TRUE] > mu, ]
}
),
index
)
你可以使用.data
-
library(dplyr)
library(purrr)
index<-c('Sepal.Length', 'Sepal.Width')
map(index, ~iris %>% filter (.data[[.x]] > mean(.data[[.x]])))