4

In this example, I have two lists

tiers <- list("tier 1", "tier 2", "tier 3")

main <- list(data.frame(a = c("this", "that")), 
             data.frame(a = c("the other", "that too")), 
             data.frame(a = c("once more", "kilgore trout")))

and I'd like to mutate() each list element (i.e., data.frame()) in main by adding the value in tiers from the corresponding element. I thought mapply() would do it:

library(dplyr)

mapply(function(x, y) y %>% mutate(tier = x), tiers, main)

but I get an unexpected result

> mapply(function(x, y) y %>% mutate(tier = x), tiers, main)
     [,1]        [,2]        [,3]       
a    factor,2    factor,2    factor,2   
tier Character,2 Character,2 Character

while what I expected was

[[1]]
     a   tier
1 this tier 1
2 that tier 1

[[2]]
          a   tier
1 the other tier 2
2  that too tier 2

[[3]]
              a   tier
1     once more tier 3
2 kilgore trout tier 3

Am I using mapply() correctly? If not, is there something I should be using to get the result I'm expecting? I should note that actual data may have up to n list elements; I can't hard-code any values in terms of 1:n.

Steven
  • 3,238
  • 21
  • 50

4 Answers4

2

What you needed was to add SIMPLIFY = FALSE in your mapply call

library(dplyr)
mapply(function(x, y) y %>% mutate(tier = x), tiers, main, SIMPLIFY = FALSE)


#     a   tier
#1 this tier 1
#2 that tier 1

#[[2]]
#          a   tier
#1 the other tier 2
#2  that too tier 2

#[[3]]
#              a   tier
#1     once more tier 3
#2 kilgore trout tier 3

?mapply says

SIMPLIFY - attempt to reduce the result to a vector, matrix or higher dimensional array;

SIMPLIFY argument is by default TRUE in mapply and FALSE in Map

Map(function(x, y) y %>% mutate(tier = x), tiers, main)

If you want to keep everything in base R, you could use cbind

Map(cbind, main, tiers)
Ronak Shah
  • 377,200
  • 20
  • 156
  • 213
2

Also, consider within or transform with Map (list version wrapper to mapply) and avoid loading a package for one function, mutate:

Map(function(x, y) within(y, tier <- x), tiers, main)

Map(function(x, y) transform(y, tier = x), tiers, main)
Parfait
  • 104,375
  • 17
  • 94
  • 125
2

In base R you could use Map with the function data.frame if you name the tier argument :

Map(data.frame, main, tier=tiers)
# [[1]]
#      a   tier
# 1 this tier 1
# 2 that tier 1
# 
# [[2]]
#           a   tier
# 1 the other tier 2
# 2  that too tier 2
# 
# [[3]]
#               a   tier
# 1     once more tier 3
# 2 kilgore trout tier 3
moodymudskipper
  • 46,417
  • 11
  • 121
  • 167
1

We can do this in tidyverse with map2

library(tidyverse)
map2(main, tiers, ~ .x %>%
           mutate(tiers = .y))
#[[1]]
#     a  tiers
#1 this tier 1
#2 that tier 1

#[[2]]
#          a  tiers
#1 the other tier 2
#2  that too tier 2

#[[3]]
#              a  tiers
#1     once more tier 3
#2 kilgore trout tier 3
akrun
  • 874,273
  • 37
  • 540
  • 662
  • 1
    Thanks. I need to explore more of the `tidyverse`. I get stuck on the functions that I know and love and I know there's a lot more "new" stuff that's out there. – Steven Jan 31 '19 at 13:52