Chris Umphlett
Chris Umphlett

Reputation: 430

How to use r purrr:map to create multiple of the same object (such as data frame)

I want to know to what extent it is possible to use purrr's mapping functions to create objects in general, though at the moment and with the example below I'm looking at data frames.

A<-seq(1:5)
B<-seq(6:10)
C<-c("x","y","x","y","x")
dat<data.frame(A,B,C)
cols<-names(dat)

create_df<-function(x) {
    x<- dat[x]
    return(x)
}
A<-create_df("A")

This will create a data frame called A with column A from dat. I want to create data frames A/B/C, each with one column. I have tried different ways of specifying the .f argument as well as different map functions (map, map2, map_dfc, etc.). My original best guess:

map(.x=cols,~create_df(.x))    

Clarification: I am asking for help because all of the specifications of map that I have tried have given an error.

Code that worked:

map(names(dat), ~assign(.x, dat[.x], envir = .GlobalEnv))

This creates A/B/C as data frames and prints to the console (which I don't need but does not bother me for now).

Upvotes: 1

Views: 1788

Answers (2)

Matt L.
Matt L.

Reputation: 2954

Using the purrr package, I think your custom function is not necessary. The function includes a reference to the data, which is not optimal (especially if it doesn't exist in the environment).

to return as a list of single column dataframes:

cols<-names(dat)
map(cols, ~dat[.x])

or alternatively: map(names(dat), ~dat[.x])

returns:

[[1]]
# A tibble: 5 x 1
      A
  <int>
1     1
2     2
3     3
4     4
5     5

[[2]]
# A tibble: 5 x 1
      B
  <int>
1     1
2     2
3     3
4     4
5     5

[[3]]
# A tibble: 5 x 1
  C    
  <chr>
1 x    
2 y    
3 x    
4 y    
5 x    

If you want to stick with tidyverse principles, you can store them within a dataframe as a list-column.

dfs <-
  data_frame(column = cols) %>% 
    mutate(data = map(cols, ~dat[.x]))

# A tibble: 3 x 2
  column data            
  <chr>  <list>          
1 A      <tibble [5 x 1]>
2 B      <tibble [5 x 1]>
3 C      <tibble [5 x 1]>

You can pull out individual data as needed:

B <- dfs$data[[2]]  

# A tibble: 5 x 1
      B
  <int>
1     1
2     2
3     3
4     4
5     5

Along the lines of your original suggestion, here's an alternative function that uses purrr:map within it. I'm not sure how good of an idea this is, but maybe it has a use:

create_objects_from_df <- function(dat) {
  map(names(dat), ~assign(.x, dat[.x], envir = .GlobalEnv))
  }
create_objects_from_df(dat)

This creates the objects in your global environment, as individual objects with the column names.

Upvotes: 3

akrun
akrun

Reputation: 886948

We can use split from base R to get a list of one column data.frames

lst <- split.default(dat, names(dat))

It is better to keep it in a list, but if the intention is to have multiple objects in the global environment

list2env(lst, envir = .GlobalEnv)

Upvotes: 2

Related Questions