Nick
Nick

Reputation: 437

unnest dataframe within dataframe R

I would like to unnest this dataframe.

df <- tibble(
  bears = c(1,2,3),
  eagles = tibble(
    talons = c(2,3,4),
    beaks = c("x","y","z")
  )
)

So that it looks like

tibble(
  bears = c(1,2,3),
  talons = c(2,3,4),
   beaks = c("x","y","z")
)

I have tried using unnest and unnest_wider, flatten and unlist but to no avail. If I run, for example,

test <- df %>%
  unnest_wider(eagles, names_sep = "_")

The error is

Error: Assigned data `map(data[[col]], vec_to_wide, col = col, names_sep = names_sep)` must be compatible with existing data.
x Existing data has 3 rows.
x Assigned data has 2 rows.
ℹ Only vectors of size 1 are recycled.

I'm not sure how to resolve this error. Thanks!

Upvotes: 9

Views: 3202

Answers (3)

MR_MPI-BGC
MR_MPI-BGC

Reputation: 285

Using unpack from tidyr does the job I think:

df <- tibble(
         bears = c(1,2,3),
         eagles = tibble(
             talons = c(2,3,4),
             beaks = c("x","y","z")
         )
    )
tidyr::unpack(df, cols=c(eagles))

Output:

# A tibble: 3 x 3
  bears talons beaks
  <dbl>  <dbl> <chr>
1     1      2 x    
2     2      3 y    
3     3      4 z  

If you want to keep "eagles", do

 unpack(df, cols=c(eagles), names_sep = "_")

Upvotes: 5

Lukas Wallrich
Lukas Wallrich

Reputation: 440

An alternative solution that might help people who want to figure out why unnesting does not work: unnest() works on list-columns of data frames. So if the original df is changed to contain a list column, everything works as intended by the OP.

df <- tibble(
  bears = c(1,2,3),
  eagles = list(tibble(
    talons = c(2,3,4),
    beaks = c("x","y","z")
  ))
)

unnest(df, eagles)

Upvotes: 3

akrun
akrun

Reputation: 887501

We can use reduce with tibble

library(purrr)
df1 <- bind_cols(df[1], reduce(df[-1], tibble))
str(df1)
#tibble [3 × 3] (S3: tbl_df/tbl/data.frame)
# $ bears : num [1:3] 1 2 3
# $ talons: num [1:3] 2 3 4
# $ beaks : chr [1:3] "x" "y" "z"

Or if we need to rename

library(dplyr)
library(stringr)
df %>%
    select(where(is.tibble)) %>%
    imap_dfc(~  set_names(.x, str_c(.y, '_', names(.x)))) %>%
    bind_cols(df %>%
          select(where(negate(is.tibble))), .)
# A tibble: 3 x 3
#  bears eagles_talons eagles_beaks
#  <dbl>         <dbl> <chr>       
#1     1             2 x           
#2     2             3 y           
#3     3             4 z           

Or using

df %>%
  reduce(data.frame) 

Or an easier option is do.call in base R

df1 <- do.call(data.frame, df)
str(df1)
#'data.frame':  3 obs. of  3 variables:
# $ bears        : num  1 2 3
# $ eagles.talons: num  2 3 4
# $ eagles.beaks : chr  "x" "y" "z"

Upvotes: 8

Related Questions