Column binding vectors of unequal length in tidyverse

78 Views Asked by At

Below, I'm trying to col-bind the input to create a data.frame using the map_dfc() to acheive my Desired_output.

But it looks like map_dfc() can only col-bind equally-lengthed vectors.

So, I wonder what can I do to obtain my Desired_output using map_dfc() or another function?

library(tidyverse)

set.seed(0)
input <- map(1:2, \(i) round(rbeta(4 - if(i==1) 0 else i, i+.1, i+12, i*.2),2))

Desired_output = 
"
   ..1   ..2
1  0.01  0.24
2  0.08  0.1 
3  0.01  NA
4  0.11  NA
"

# Trying to col-bind `input` using `map_dfc()` to achieve desired output:

set.seed(0)
map_dfc(1:2, \(i) round(rbeta(4 - if(i==1) 0 else i, i+.1, i+12, i*.2),2)) # How to run this?

#-> Error in `dplyr::bind_cols()`:
#-> ! Can't recycle `..1` (size 4) to match `..2` (size 2).
2

There are 2 best solutions below

5
Umar On BEST ANSWER
library(tidyverse)

set.seed(0)
input <- map(1:2, \(i) round(rbeta(4 - if(i==1) 0 else i, i+.1, i+12, i*.2),2))

df <- bind_cols(map(input, ~ c(.x, rep(NA, max(lengths(input)) - length(.x)))), .name_repair = "unique")

print(df)
# A tibble: 4 × 2
   ...1  ...2
  <dbl> <dbl>
1  0.01  0.24
2  0.08  0.1 
3  0.01 NA   
4  0.11 NA  

In above code map(input, ~ c(.x, rep(NA, max(lengths(input)) - length(.x)))) creates a list of vectors with the maximum length, filling missing values with NA. bind_cols combines these vectors into a data frame. The .name_repair = "unique" argument ensures unique column names.

result <- map_dfc(input, ~ c(.x, rep(NA, max(lengths(input)) - length(.x)))) %>%
  set_names(paste0("..", seq_along(.)))

print(result)
# A tibble: 4 × 2
    ..1   ..2
  <dbl> <dbl>
1  0.01  0.24
2  0.08  0.1 
3  0.01 NA   
4  0.11 NA   

This code uses map_dfc with a lambda function to create a data frame, similar to your original attempt. The key is to use set_names to set unique column names with the .. prefix. This should give you the desired output.

result <- input %>%
  imap(~ c(.x, rep(NA, max(lengths(input)) - length(.x)))) %>%
  as.data.frame() %>%
  set_names(paste0("..", seq_along(.)))
print(result)
   ..1  ..2
1 0.01 0.24
2 0.08 0.10
3 0.01   NA
4 0.11   NA
0
s_baldur On
input |>
  map(\(x) x[1:max(lengths(input))]) |> 
  bind_cols()

#    ...1  ...2
#   <dbl> <dbl>
# 1  0.01  0.24
# 2  0.08  0.1 
# 3  0.01 NA   
# 4  0.11 NA