Home > database >  Use dplyr::rename function in a for loop in R
Use dplyr::rename function in a for loop in R

Time:10-23

I am trying to understand how the dplyr::rename function works in a for loop work in R. In principle, I want to rename the column names of multiple data frames coming from another list.

Here is an example that fails, where I want to rename the first column of each dataset with a name coming from the new.names data set.

library(tidyverse)

iris1 <- iris
iris2 <- iris
iris3 <- iris

files <- list(iris1,iris2,iris3)

new.names <- c("change1","change2","change")

If I try this for loop it fails, any idea why

test <- list()
for(i in 1:length(files)){
  test[[i]] <- files[[i]] |> 
    dplyr::rename(new.names[i]=1)
}
test

Any help or guidance is appreciated

CodePudding user response:

If you want to use a loop you need to use !! sym() on the names names of the lefthand side as well as the walrus operator :=:

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

We can also do it without a for loop then we need the new.names list to be a list of lists and we can then use 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

Created on 2022-10-22 with reprex v2.0.2

CodePudding user response:

This is a different apporach using the function 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

CodePudding user response:

You can also use dplyr::rename_with in order to pass dynamically the renaming part as a function like this:

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

Please read renames_with's documentation regarding the parameters for both the specified function and tidy select.

In case you don't care about dplyr::rename, the most simple thing that you can do is:

test <- list()
for(i in 1:length(files)){
  
  test[i] <- files[i]
  
  names(test[[i]])[1] <- new.names[i]
}
test
  • Related