Home > Back-end >  Rename list columns based on list names with purrr
Rename list columns based on list names with purrr

Time:08-09

I have a named list and within that list I want to rename their columns based on the names the list comes from.

The probelm with my approach seems to be the .x placeholder, which I thought would be my unique list names. But within the rename_with function, it seems the .x is evaluated within each list data frame and not from the original .x. Is there any way I can carry forward the original .x (or list names) for renaming?

my_list <- list(L1 = data.frame(x=1:3),
                L1 = data.frame(x=1:3),
                L2 = data.frame(x=1:3),
                L2 = data.frame(x=1:3))

my_list |> 
  purrr::map(.x = unique(names(my_list)),
             .f = ~my_list[names(my_list) == .x] |> 
               dplyr::bind_rows() |> 
               dplyr::rename_with(.cols = "x",
                                  .fn   = ~paste0("new_", .x)))

expected output:

[[1]]
  new_L1
1     1
2     2
3     3
4     1
5     2
6     3

[[2]]
  new_L2
1     1
2     2
3     3
4     1
5     2
6     3

CodePudding user response:

Just revise 2 places:

  • You have set .x and .f for map(), so the first line (my_list |>) is redundant. It makes my_list thrown into the ... part of map(), which works on nothing.

  • In the .fn part of rename_with(), if you use a formula-style function, the placeholder .x will conflict. So you should use a basic function and set a different argument name.

purrr::map(.x = unique(names(my_list)),
           .f = ~ my_list[names(my_list) == .x] |> 
                  dplyr::bind_rows() |> 
                  dplyr::rename_with(.cols = "x",
                                     .fn   = \(col) paste0("new_", .x)))

[[1]]
  new_L1
1      1
2      2
3      3
4      1
5      2
6      3

[[2]]
  new_L2
1      1
2      2
3      3
4      1
5      2
6      3

You could also use rename so that you don't worry about the conflict.

|> dplyr::rename(!!paste0("new_", .x) := x)

CodePudding user response:

Another approach I like to use to avoid .x conflicts is to use an anonymous function for map:

unique(names(my_list)) |> 
  # if you want a named list, use set_names
  # purrr::set_names() |>
  purrr::map(\(list_name) my_list[names(my_list) == list_name] |> 
               dplyr::bind_rows() |> 
               dplyr::rename_with(.cols = "x",
                                  .fn   = ~paste0("new_", list_name))
  )

CodePudding user response:

Another option could be:

imap(my_list,
     ~ .x %>%
     rename_with(function(listnames) paste0("new_", .y), everything())) 

$L1
  new_L1
1      1
2      2
3      3

$L1
  new_L1
1      1
2      2
3      3

$L2
  new_L2
1      1
2      2
3      3

$L2
  new_L2
1      1
2      2
3      3

If you need an unnamed list:

imap(my_list,
     ~ .x %>%
     rename_with(function(listnames) paste0("new_", .y), everything())) %>%
 unname()
  • Related