我试图了解
dplyr::rename
函数如何在 R 中的 for 循环中工作。原则上,我想重命名来自另一个列表的多个数据帧的列名称。
这是一个失败的示例,我想使用来自
new.names
数据集的名称重命名每个数据集的第一列。
library(tidyverse)
iris1 <- iris
iris2 <- iris
iris3 <- iris
files <- list(iris1,iris2,iris3)
new.names <- c("change1","change2","change")
如果我尝试这个 for 循环,它会失败:
test <- list()
for(i in 1:length(files)){
test[[i]] <- files[[i]] |>
dplyr::rename(new.names[i]=1)
}
test
为什么会这样?
如果你想使用循环,你需要在左侧的名称上使用
!! sym()
以及海象运算符:=
:
library(tidyverse)
iris1 <- iris
iris2 <- iris
iris3 <- iris
files <- list(iris1,iris2,iris3)
new.names <- c("change1","change2","change")
test <- list()
test <- list()
for(i in 1:length(files)){
test[[i]] <- files[[i]] |>
dplyr::rename(!! sym(new.names[i]) := 1)
}
test |>
map(head) # for printing
#> [[1]]
#> change1 Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
#>
#> [[2]]
#> change2 Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
#>
#> [[3]]
#> change Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
我们也可以在没有
for
循环的情况下做到这一点,然后我们需要 new.names
列表作为列表的列表,然后我们可以使用 purrr::map2()
:
library(tidyverse)
iris1 <- iris
iris2 <- iris
iris3 <- iris
files <- list(iris1,iris2,iris3)
new.names <- list(
list(change1 = 1),
list(change2 = 1),
list(change = 1))
map2(files,
new.names,
~ dplyr::rename(.x, !!! .y) %>%
head() # for printing
)
#> [[1]]
#> change1 Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
#>
#> [[2]]
#> change2 Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
#>
#> [[3]]
#> change Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
创建于 2022 年 10 月 22 日,使用 reprex v2.0.2
为什么这有效?
在 dplyr 中,我们可以将调用构造为列表,然后使用
!!!
评估(或拼接)它们。这就是我们在这里所做的。
new.names
包含列表的列表。每个子列表都以新列名称作为名称,以旧列的位置作为值。
因此对于一个文件我们可以这样做:
dplyr::rename(files[[1]], !!! new.names[[1]]) |> head()
如果
new.names
只是一个简单的列表(没有子列表),那么这些值将没有名称,我们将无法构造调用。
最后,我们还可以使用
map2()
而不用列表列表构造调用,而是使用字符串:
new.names <- c("change1", "change2", "change")
map2(files,
new.names,
~ dplyr::rename(.x, !! sym(.y) := 1) %>%
head() # for printing
)
其逻辑与
for
循环中的逻辑相同:我们使用 sym()
将字符串转换为名称,使用 !!
对其求值,并使用海象运算符 :=
代替 =
。
这是使用函数的不同方法
setNames()
:
test <- list()
for(i in 1:length(files)){
test[[i]] <- files[[i]] %>%
setNames(c(new.names[i], names(files[[i]])[-1]))
}
> test %>% lapply(head)
#[[1]]
# change1 Sepal.Width Petal.Length Petal.Width Species
#1 5.1 3.5 1.4 0.2 setosa
#2 4.9 3.0 1.4 0.2 setosa
#3 4.7 3.2 1.3 0.2 setosa
#4 4.6 3.1 1.5 0.2 setosa
#5 5.0 3.6 1.4 0.2 setosa
#6 5.4 3.9 1.7 0.4 setosa
#
#[[2]]
# change2 Sepal.Width Petal.Length Petal.Width Species
#1 5.1 3.5 1.4 0.2 setosa
#2 4.9 3.0 1.4 0.2 setosa
#3 4.7 3.2 1.3 0.2 setosa
#4 4.6 3.1 1.5 0.2 setosa
#5 5.0 3.6 1.4 0.2 setosa
#6 5.4 3.9 1.7 0.4 setosa
#
#[[3]]
# change Sepal.Width Petal.Length Petal.Width Species
#1 5.1 3.5 1.4 0.2 setosa
#2 4.9 3.0 1.4 0.2 setosa
#3 4.7 3.2 1.3 0.2 setosa
#4 4.6 3.1 1.5 0.2 setosa
#5 5.0 3.6 1.4 0.2 setosa
#6 5.4 3.9 1.7 0.4 setosa
您还可以使用
dplyr::rename_with
以便将重命名部分作为函数动态传递,如下所示:
test <- list()
for(i in 1:length(files)){
test[[i]] <- files[[i]] %>%
dplyr::rename_with(function(x) { x <- new.names[i]; return(x) }, c(1) )
}
test
请阅读
renames_with
的文档,了解指定函数和整洁选择的参数。
如果你不关心
dplyr::rename
,你能做的最简单的事情就是:
test <- list()
for(i in 1:length(files)){
test[i] <- files[i]
names(test[[i]])[1] <- new.names[i]
}
test