Jakub Małecki
Jakub Małecki

Reputation: 634

How to implement dplyr filter on custom class tibbles

I'd like to build a structure which essentially is a data.frame/tibble but it has an extra custom class. I'd use this class for quick checks in other part of code to ensure that I can perform certain operations on the data. But dplyr throws an error when I try to filter the data. Please, look at the code below:

my_data_frame <- function(x) {
  stopifnot(is.data.frame(x))
  
  class(x) <- append(class(x), "my_data_frame")
  
  return(x)
}

# create an instance of the class
my_iris <- my_data_frame(iris)
class(my_iris)  # ---> "data.frame" "my_data_frame"

# try filtering
dplyr::filter(my_iris, Sepal.Length > 0.56)

The last command gives the following error

Error in `stop_vctrs()`:
! Input must be a vector, not a <data.frame/my_data_frame> object.

I found dplyr_row_slice function and read the help here https://dplyr.tidyverse.org/reference/dplyr_extending.html. I tried to extend the function like this:

dplyr_row_slice.my_data_frame <- function(data, i, ...) {
  message("custom handler invoked")
  # filtering code
  # ???
}

but it doesn't work. Maybe I miss something? How should I properly implement filtering so it works like in dplyr package?

Upvotes: 0

Views: 140

Answers (1)

Jakub Małecki
Jakub Małecki

Reputation: 634

I found a solution. Inspired by @MrFlick's comment I rearranged the class assignment, i.e. my custom class is assigned as the first one in the list of classes:

class(x) <- append("my_data_frame", class(x))

Next, I implemented filtering the following way:

dplyr_row_slice.my_data_frame <- function(data, i, ...) {
  data_tbl <- as.data.frame(data)
  dplyr::dplyr_row_slice(data, i, ...)
}

i.e. first coercing the data to pure data.frame class and then using native dplyr's implementation of dplyr_row_slice method. The latter uses dplyr_reconstruct under the hood, as you can see here https://github.com/tidyverse/dplyr/blob/main/R/generics.R. This brings my custom class back after the filtering is done.

Upvotes: 1

Related Questions