[英]Iteration in R using tidyverse
我试图避免使用 for 循环,而是使用 tidyverse 进行迭代。 具体来说,我有一个值向量,我想循环遍历数据框中的单个变量,以创建带有前缀的新变量。 我试过使用 dplyr::across 但当向量长度 >1 时我不成功
示例代码:
library(tidyverse)
library(glue)
data <- data.frame(id = 1:10,
y = letters[1:10],
z = LETTERS[1:10])
letter_list <- letters[1:10]
var_naming <- function(dat, list){
dat %>%
mutate(!!glue("hx_{list}") := ifelse(y == {list}, 1, 0))
}
我试过的代码:
**the correct dimensions of the data frame should be 13 variables and 10 observations**
# data_b outputs the correct number of observations but has 40 variables
data_b <- map(letter_list,
~var_naming(data, .x)) %>%
as.data.frame()
# data_c gives me the correct number of variables but has 100 observations
data_c <- map_df(letter_list,
~var_naming(data, .x))
# error message from data_d when using dplyr::across:
>> Error in `mutate()`:
>> ! Problem while computing `..1 =
>> across(...)`.
>> Caused by error in `across()`:
>> ! All unnamed arguments must be length 1
>> Run `rlang::last_error()` to see where the error occurred.
data_d <- data %>%
mutate(
across(
.cols = y,
.fns = ~ifelse(y == {letter_list}, 1, 0),
.names = glue("hx_{letter_list}")
))
Desired output:
id y z hx_a hx_b hx_c hx_d hx_e hx_f hx_g hx_h hx_i hx_j
1 a A 1 0 0 0 0 0 0 0 0 0
2 b B 0 1 0 0 0 0 0 0 0 0
3 c C 0 0 1 0 0 0 0 0 0 0
4 d D 0 0 0 1 0 0 0 0 0 0
5 e E 0 0 0 0 1 0 0 0 0 0
6 f F 0 0 0 0 0 1 0 0 0 0
7 g G 0 0 0 0 0 0 1 0 0 0
8 h H 0 0 0 0 0 0 0 1 0 0
9 i I 0 0 0 0 0 0 0 0 1 0
10 j J 0 0 0 0 0 0 0 0 0 1
代码可以修改
:=
右侧list
周围的{}
transmute
而不是mutate
可能更好,因为mutate
默认返回整个数据。bind_cols
获得列绑定 ( _dfc
) 数据,使用map
绑定原始数据library(dplyr)
library(purrr)
var_naming <- function(dat, list){
dat %>%
transmute(!!glue::glue('hx_{list}') := ifelse(y == list, 1, 0))
}
注意: list
是以base R
构造list
数据结构。 最好创建参数名称不同于保留字或 function 名称的函数。 -测试
map_dfc(letter_list, var_naming, dat = data) %>%
bind_cols(data, .)
-输出
id y z hx_a hx_b hx_c hx_d hx_e hx_f hx_g hx_h hx_i hx_j
1 1 a A 1 0 0 0 0 0 0 0 0 0
2 2 b B 0 1 0 0 0 0 0 0 0 0
3 3 c C 0 0 1 0 0 0 0 0 0 0
4 4 d D 0 0 0 1 0 0 0 0 0 0
5 5 e E 0 0 0 0 1 0 0 0 0 0
6 6 f F 0 0 0 0 0 1 0 0 0 0
7 7 g G 0 0 0 0 0 0 1 0 0 0
8 8 h H 0 0 0 0 0 0 0 1 0 0
9 9 i I 0 0 0 0 0 0 0 0 1 0
10 10 j J 0 0 0 0 0 0 0 0 0 1
获得相同结果的另一种方法:
data %>%
cbind(model.matrix(~y + 0, .)) %>%
rename_with(~str_replace(., 'y\\B', 'hx_'))
id y z hx_a hx_b hx_c hx_d hx_e hx_f hx_g hx_h hx_i hx_j
1 1 a A 1 0 0 0 0 0 0 0 0 0
2 2 b B 0 1 0 0 0 0 0 0 0 0
3 3 c C 0 0 1 0 0 0 0 0 0 0
4 4 d D 0 0 0 1 0 0 0 0 0 0
5 5 e E 0 0 0 0 1 0 0 0 0 0
6 6 f F 0 0 0 0 0 1 0 0 0 0
7 7 g G 0 0 0 0 0 0 1 0 0 0
8 8 h H 0 0 0 0 0 0 0 1 0 0
9 9 i I 0 0 0 0 0 0 0 0 1 0
10 10 j J 0 0 0 0 0 0 0 0 0 1
如果您只考虑letters_list
中的那些:
data %>%
mutate( y =factor(y, letter_list)) %>%
cbind(model.matrix(~y + 0, .) %>%
as_tibble() %>%
select(paste0('y', letter_list)) %>%
rename_with(~str_replace(., 'y', 'hx_')))
您已经接近 mutate 调用,但您最终想要的是要传递给.fns
的函数列表(一个函数对应letter_list
中的每个字母)。 由于它们是匿名函数,因此将它们命名为与letter_list
相同以帮助across
命名列
myFxs <- map(letter_list, ~function(y) ifelse(y == .x, 1, 0)) %>%
setNames(letter_list)
无论出于何种原因, .names
似乎在胶水字符向量方面存在问题(无论如何对我而言)。 由于函数是根据它们匹配的字母命名across
,因此您可以使用.fn
代词来代替将模板传递给
data %>%
mutate(
across(
.cols = y,
.fns = myFxs,
.names = "hx_{.fn}"
)
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.