Return list using mutate and rowwise

1k views Asked by At

I'm trying to return a list using mutate and rowwise but get the error shown in the code. These questions Q1 Q2 helped, but I'd like to keep it simple by iterating over rows using rowwise(), and the questions are 3yr 7mth old. Thanks.

library(tidyverse)    
df <-  data.frame(Name=c("a","a","b","b","c"),X=c(1,2,3,4,5), Y=c(2,3,4,2,2))

    TestFn <- function(X,Y){
      Z <- list(X*5,Y/2,X+Y,X*2+5*Y)
      return (Z)
    }

    #this works
    SingleResult <- TestFn(5,20)

    #error - Error in mutate_impl(.data, dots) : incompatible size (4), expecting 1 (the group size) or 1
    dfResult <- df %>% 
      rowwise() %>% 
      mutate(R=TestFn(X,Y))
2

There are 2 answers

1
Psidom On BEST ANSWER

Your TestFn returns a 4 elements list per row, which can't really be fit in a row; You can wrap the returned elements in a vector first so the returned list is a single element list:

TestFn <- function(X, Y) list(c(X*5, Y/2, X+Y, X*2+5*Y))
#                             ^ 
df %>% rowwise() %>% mutate(R=TestFn(X,Y)) %>% pull(R)
#[[1]]
#[1]  5  1  3 12

#[[2]]
#[1] 10.0  1.5  5.0 19.0

#[[3]]
#[1] 15  2  7 26

#[[4]]
#[1] 20  1  6 18

#[[5]]
#[1] 25  1  7 20

rowwise is usually not as efficient, if you want to vectorize the solution, you can calculate the four expressions firstly and then transpose the result:

df$R = with(df, data.table::transpose(list(X*5, Y/2, X+Y, X*2+5*Y)))
df
#  Name X Y                    R
#1    a 1 2          5, 1, 3, 12
#2    a 2 3 10.0, 1.5, 5.0, 19.0
#3    b 3 4         15, 2, 7, 26
#4    b 4 2         20, 1, 6, 18
#5    c 5 2         25, 1, 7, 20
0
CPak On

tidyverse lets you nest complicated data structures now

tidyverse solution

df %>%
  mutate(copyX = X, copyY = Y) %>%
  nest(copyX, copyY) %>%
  mutate(data = map(data, ~TestFn(.x$copyX, .x$copyY)))

output

    Name     X     Y       data
  <fctr> <dbl> <dbl>     <list>
1      a     1     2 <list [4]>
2      a     2     3 <list [4]>
3      b     3     4 <list [4]>
4      b     4     2 <list [4]>
5      c     5     2 <list [4]>

How to convert to data frame?

Since your function returns a list, I added an additional step before unnesting

df %>%
  mutate(copyX = X, copyY = Y) %>%
  nest(copyX, copyY) %>%
  mutate(data = map(data, ~TestFn(.x$copyX, .x$copyY))) %>%
  mutate(data = map(data, ~unlist(.x))) %>%
  unnest(data)

output

     Name     X     Y  data
   <fctr> <dbl> <dbl> <dbl>
 1      a     1     2   5.0
 2      a     1     2   1.0
 3      a     1     2   3.0
 4      a     1     2  12.0
 5      a     2     3  10.0
 6      a     2     3   1.5
 # etc