Home > Back-end >  How to build a tibble from vectors/lists while applying `unique()` on them, without repeating callin
How to build a tibble from vectors/lists while applying `unique()` on them, without repeating callin

Time:01-23

I have several vectors and lists, for example:

colors      <- c("blue", "blue", "red", "green", "green")
shapes      <- c("square", "square", "triangle", "triangle", "circle")
numbers     <- c(22, 50, 50, 50, 13, 13)
list_of_dfs <- list(mtcars, mtcars, iris, iris, trees, trees, trees)
is_foo      <- c(TRUE, FALSE, TRUE)

It is not a coincidence that except for is_foo, all other objects are of the same length after calling unique() on them.

I want to build a tibble such that I take the unique() of some (but not all) such that:

library(tibble)

my_tib <-
  tibble(colors  = unique(colors),
         shapes  = unique(shapes),
         numbers = unique(numbers),
         dfs     = unique(list_of_dfs),
         is_foo  = is_foo)

My problem: I want to build my_tib in a more succinct way. Meaning, I don't want to call unique() on each column separately, but all at once for the columns that need to be unique'ed (in this case, all except for is_foo that goes as-is).

How can we do so if colors, shapes, numbers, list_of_dfs, and is_foo are given?

CodePudding user response:

You can build a list first and use purrr:modify_at to apply unique to only selected elements. purrr::modify_at also allows tidyselect syntax so you have better control over the elements you want to modify. However, I don't see the point here since it does not shorten your code or reduce the number of operations to perform.

library(tibble)
library(purrr)

as_tibble(modify_at(list(
  colors = colors, 
  shapes = shapes, 
  numbers = numbers, 
  dfs = list_of_dfs, 
  is_foo = is_foo
), vars(!is_foo), unique))

Output

# A tibble: 3 x 5
  colors shapes   numbers dfs            is_foo
  <chr>  <chr>      <dbl> <list>         <lgl> 
1 blue   square        22 <df [32 x 11]> TRUE  
2 red    triangle      50 <df [150 x 5]> FALSE 
3 green  circle        13 <df [31 x 3]>  TRUE

Or maybe use mget to get the variables so that you do not have to list them all.

cols <- c("colors", "shapes", "numbers", "list_of_dfs", "is_foo")
as_tibble(modify_at(mget(cols), vars(!is_foo), unique))

Output

# A tibble: 3 x 5
  colors shapes   numbers list_of_dfs    is_foo
  <chr>  <chr>      <dbl> <list>         <lgl> 
1 blue   square        22 <df [32 x 11]> TRUE  
2 red    triangle      50 <df [150 x 5]> FALSE 
3 green  circle        13 <df [31 x 3]>  TRUE 

CodePudding user response:

Making use of a helper function which makes use of tibble::lst you could do:

Note: At least for the general case multiple columns to add as is I failed to pass the columns as a simple list. Instead they have to be wrapped inside tibble::lst.

library(dplyr)

make_tibble <- function(..., add) {
  args <- lapply(lst(...), unique)
  tibble(!!!c(args, add))
}

make_tibble(colors, shapes, numbers, list_of_dfs, add = lst(is_foo, bar))
#> # A tibble: 3 × 6
#>   colors shapes   numbers list_of_dfs    is_foo   bar
#>   <chr>  <chr>      <dbl> <list>         <lgl>  <int>
#> 1 blue   square        22 <df [32 × 11]> TRUE       1
#> 2 red    triangle      50 <df [150 × 5]> FALSE      2
#> 3 green  circle        13 <df [31 × 3]>  TRUE       3
  •  Tags:  
  • Related