Emman
Emman

Reputation: 4201

How to modify a dataframe nested inside a list without re-assignment

I have a list object that contains several elements. One element is a data frame that I wish to modify: I want to perform some operations such as column renaming and mutating new columns.

Although one simple way is to extract the nested data frame, modify it, and finally re-assign the output to the original parent list, I'd like to avoid such solution because it requires intermediate assignment.

Example

Data. let's build a list of several data objects

library(tibble)

my_list <- lst(letters, mtcars, co2, uspop, iris)

Task.

  1. I want to modify my_list$mtcars to:

    • rename the cyl column
    • compute a new column that takes a square root of values in mpg column
  2. I want to modify my_list$iris to:

    • select columns that start with sepal
    • rename them to lowercase

    and ultimately I expect to get back a list object that is identical to the original my_list, except for the changes I made for mtcars and iris.

My attempt. Right now, the only way I know to achieve this involves re-assignment:

library(dplyr)

my_list$mtcars <-
  my_list$mtcars %>%
  rename("Number of cylinders" = cyl) %>%
  mutate(sqrt_of_mpg = sqrt(mpg))

my_list$iris <- 
  my_list$iris %>%
  select(starts_with("Sepal")) %>%
  rename_with(tolower)

My question: Given my_list, how could I point to a nested element by its name, specify which actions should happen to modify it, and get back the parent my_list with just those modifications?

I imagine some sort of a pipe that looks like this (just to get my general idea)

## DEMO ##
my_list %>%
  update_element(which = "mtcars", what = rename, mutate) %>%
  update_element(which = "iris", what = select, rename)

Thanks!

Upvotes: 2

Views: 697

Answers (2)

Ronak Shah
Ronak Shah

Reputation: 389235

You can use imap which passes name along with data for each iteration but this is not closer to your general idea.

library(dplyr)

my_list <- purrr::imap(my_list, ~{
  if(.y == 'mtcars') 
    .x %>% rename("Number of cylinders" = cyl) %>%mutate(sqrt_of_mpg = sqrt(mpg))
  else if(.y == 'iris') 
    .x %>% select(starts_with("Sepal")) %>% rename_with(tolower)
  else .x
})

Upvotes: 1

Roman
Roman

Reputation: 17668

You can try purrr's modify_at function

library(tidyverse)
my_list %>% 
  modify_at("mtcars", ~rename(.,"Number of cylinders" = cyl) %>% 
              mutate(sqrt_of_mpg = sqrt(mpg))) %>% 
  modify_at("iris", ~select(., starts_with("Sepal")) %>%
              rename_with(tolower))

Upvotes: 2

Related Questions