4

I've been testing out the vctrs package recently, especially what they call "record-style" objects recently, and I'm wondering if there's any way to get them to play nice with dplyr::mutate. Currently, when dplyr::mutate gives me an error about the length of the objects whenever I try to use them.

I don't know of a built in type that's appropriate, so as a reprex I'm going to use the rational class described in this vignette.

library("vctrs")
library("dplyr")
new_rational <- function(n = integer(), d = integer()) {
  vec_assert(n, ptype = integer())
  vec_assert(d, ptype = integer())

  new_rcrd(list(n = n, d = d), class = "vctrs_rational")
}

format.vctrs_rational <- function(x, ...) {
  n <- field(x, "n")
  d <- field(x, "d")

  out <- paste0(n, "/", d)
  out[is.na(n) | is.na(d)] <- NA

  out
}

So far so good, but when I try to create a column of rationals using dplyr::mutate I get an error

df <- data.frame(n = c(1L, 2L, 3L), d = 2L)
df %>% dplyr::mutate(frac = new_rational(n, d))
#> Error: Column `frac` must be length 3 (the number of rows) or one, not 2

But creating the column in base R works just fine:

df$rational <- new_rational(df$n, df$d)
df
#>   n d rational
#> 1 1 2      1/2
#> 2 2 2      2/2
#> 3 3 2      3/2

Is there some trick to get this to work using dplyr::mutate, or is this just not possible?

Paul PR
  • 168
  • 10

2 Answers2

2

new_rational returns the output in a list format as you can see below

> typeof(new_rational(n=1L, d=2L))
[1] "list"

so, we can get the output as a list using map or as.list "@Ronak's suggestion" then use unnest.

df %>% dplyr::mutate(frac = purrr::map2(n,d, ~new_rational(.x, .y))) %>% 
       tidyr::unnest(cols=c(frac))
# A tibble: 3 x 3
      n     d       frac
  <int> <int> <vctrs_rt>
1     1     2        1/2
2     2     2        2/2
3     3     2        3/2
A. Suliman
  • 12,923
  • 5
  • 24
  • 37
  • `new_rational` **is** vectorized. `new_rational(df$n, df$d)` works. – Ronak Shah Jan 14 '20 at 05:34
  • @RonakShah sorry, I think I misunderstand the concept. now I see the difference between using `as.list` and `list` combined with `rowwise` inside `mutate` – A. Suliman Jan 14 '20 at 05:40
0

As of vctrs 0.3.6 / R 4.0.3, your reprex works as expected:

library("vctrs")
library("dplyr")
new_rational <- function(n = integer(), d = integer()) {
  vec_assert(n, ptype = integer())
  vec_assert(d, ptype = integer())
  
  new_rcrd(list(n = n, d = d), class = "vctrs_rational")
}

format.vctrs_rational <- function(x, ...) {
  n <- field(x, "n")
  d <- field(x, "d")
  
  out <- paste0(n, "/", d)
  out[is.na(n) | is.na(d)] <- NA
  
  out
}

df <- data.frame(n = c(1L, 2L, 3L), d = 2L)
df %>% dplyr::mutate(frac = new_rational(n, d))
#>   n d frac
#> 1 1 2  1/2
#> 2 2 2  2/2
#> 3 3 2  3/2

Created on 2021-02-03 by the reprex package (v0.3.0)

Joe Roe
  • 626
  • 3
  • 12