Learn R Programming

vctrs (version 0.3.6)

howto-faq-fix-scalar-type-error: FAQ - Why isn't my class treated as a vector?

Description

The tidyverse is a bit stricter than base R regarding what kind of objects are considered as vectors (see the user FAQ about this topic). Sometimes vctrs won<U+2019>t treat your class as a vector when it should.

Why isn<U+2019>t my list class considered a vector?

By default, S3 lists are not considered to be vectors by vctrs:

my_list <- structure(list(), class = "my_class")

vctrs::vec_is(my_list) #> [1] FALSE

To be treated as a vector, the class must either inherit from "list" explicitly:

my_explicit_list <- structure(list(), class = c("my_class", "list"))
vctrs::vec_is(my_explicit_list)
#> [1] TRUE

Or it should implement a vec_proxy() method that returns its input if explicit inheritance is not possible or troublesome:

#' @export
vec_proxy.my_class <- function(x, ...) x

vctrs::vec_is(my_list) #> [1] FALSE

Note that explicit inheritance is the preferred way because this makes it possible for your class to dispatch on list methods of S3 generics:

my_generic <- function(x) UseMethod("my_generic")
my_generic.list <- function(x) "dispatched!"

my_generic(my_list) #> Error in UseMethod("my_generic"): no applicable method for 'my_generic' applied to an object of class "my_class"

my_generic(my_explicit_list) #> [1] "dispatched!"

Why isn<U+2019>t my data frame class considered a vector?

The most likely explanation is that the data frame has not been properly constructed.

However, if you get an <U+201C>Input must be a vector<U+201D> error with a data frame subclass, it probably means that the data frame has not been properly constructed. The main cause of these errors are data frames whose base class is not "data.frame":

my_df <- data.frame(x = 1)
class(my_df) <- c("data.frame", "my_class")

vctrs::vec_assert(my_df) #> Error: `my_df` must be a vector, not a `data.frame/my_class` object.

This is problematic as many tidyverse functions won<U+2019>t work properly:

dplyr::slice(my_df, 1)
#> Error: Input must be a vector, not a `data.frame/my_class` object.

It is generally not appropriate to declare your class to be a superclass of another class. We generally consider this undefined behaviour (UB). To fix these errors, you can simply change the construction of your data frame class so that "data.frame" is a base class, i.e.<U+00A0>it should come last in the class vector:

class(my_df) <- c("my_class", "data.frame")

vctrs::vec_assert(my_df)

dplyr::slice(my_df, 1) #> x #> 1 1

Arguments