Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applying purrr::walk2() to a data.frame of data.frames at the end of a pipe

Tags:

r

purrr

I have an R data frame, with one column of data frames, each of which I want to print to a file:

df0 <- tibble(x = 1:3, y = rnorm(3))
df1 <- tibble(x = 1:3, y = rnorm(3))
df2 <- tibble(x = 1:3, y = rnorm(3))

animalFrames <- tibble(animals = c('sheep', 'cow', 'horse'),
                       frames = list(df0, df1, df2))

I could do this with a for loop:

for (i in 1:dim(animalFrames)[1]){
    write.csv(animalFrames[i,2][[1]], file = paste0('test_', animalFrames[i,1], '.csv'))
}

Or with purrr's walk2 function:

walk2(animalFrames$animals, animalFrames$frames,  ~write.csv(.y, file
= paste0('test_', .x, '.csv')))

Is there some way I can put this walk function at the end of a magrittr pipe?

I was thinking something like:

animalFrames %>% do({walk2(.$animals, .$frames, ~write.csv(.y, file = paste0('test_', .x, '.csv')))})

But this gives me an error:

Error: Result must be a data frame, not character
Traceback:

1. animalFrames %>% do({
 .     walk2(.$animals, .$frames, ~write.csv(.y, file = paste0("test_", 
 .         .x, ".csv")))
 . })
2. withVisible(eval(quote(`_fseq`(`_lhs`)), env, env))
3. eval(quote(`_fseq`(`_lhs`)), env, env)
4. eval(quote(`_fseq`(`_lhs`)), env, env)
5. `_fseq`(`_lhs`)
6. freduce(value, `_function_list`)
7. withVisible(function_list[[k]](value))
8. function_list[[k]](value)
9. do(., {
 .     walk2(.$animals, .$frames, ~write.csv(.y, file = paste0("test_", 
 .         .x, ".csv")))
 . })
10. do.data.frame(., {
  .     walk2(.$animals, .$frames, ~write.csv(.y, file = paste0("test_", 
  .         .x, ".csv")))
  . })
11. bad("Result must be a data frame, not {fmt_classes(out)}")
12. glubort(NULL, ..., .envir = parent.frame())
13. .abort(text)

Presumably because write.csv() is returning data frames and do() doesn't handle those or something.

I don't really have a coding requirement that I have to put walk at the end of a pipe (Indeed, I can always work around pipes), but it seems like I am missing something basic and this is bugging me. Any suggestions?

like image 520
ohnoplus Avatar asked Sep 13 '25 01:09

ohnoplus


1 Answers

I don't think you need do at all. Both of the following work for me. The first is simply the same as yours minus do I think, the second makes use of magrittr's convenient %$% operator to expose the column names to walk2 and avoid the .$. Note that if this is at the end of a pipe it doesn't matter much whether you use walk2 or map2 since you don't care what's returned after this step.

NB I also swapped out paste0 and write.csv for tidyverse equivalents out of habit but they're easily put back in.

library(tidyverse)
df0 <- tibble(x = 1:3, y = rnorm(3))
df1 <- tibble(x = 1:3, y = rnorm(3))
df2 <- tibble(x = 1:3, y = rnorm(3))

animalFrames <- tibble(animals = c('sheep', 'cow', 'horse'),
                       frames = list(df0, df1, df2))

animalFrames %>%
  walk2(
    .x = .$animals,
    .y = .$frames,
    .f = ~ write_csv(.y, str_c("test_", .x, ".csv"))
  )

library(magrittr)
#> 
#> Attaching package: 'magrittr'
#> The following object is masked from 'package:purrr':
#> 
#>     set_names
#> The following object is masked from 'package:tidyr':
#> 
#>     extract
animalFrames %$%
  walk2(
    .x = animals,
    .y = frames,
    .f = ~ write_csv(.y, str_c("test_", .x, ".csv"))
  )

Created on 2018-03-13 by the reprex package (v0.2.0).

like image 147
Calum You Avatar answered Sep 14 '25 15:09

Calum You