如何在 R6 中正确编写 class 方法并将它们链接起来

How to correctly write class methods in R6 and chain them

我需要创建自己的 class 对象,该对象采用数据框并具有选择数据框的方法 'get_data', 'select' 到 select 列按名称和 'filter' 过滤具有特定值的行。 Select和filter是一种类似于dplyr的方法,但是没有使用dplyr。

我希望它们可以这样链接:

result <- df_object$get_data(df)$select(col1, col2, period)$filter(period)

我该怎么做才能让 'filter' 方法过滤已经 select 的值?现在它过滤初始数据集。另外如何更改方法以使 select 和过滤器不需要数据参数?请给我一些提示,我觉得我做错了。我需要向 class 添加一些字段吗?

dataFrame <- R6Class("dataFrame", 
                          list(data = "data.frame"),
                      public = list(
  get_data = function(data) {data},                      
  select_func = function(data, columns) {data[columns]},
  filter_func = function(data, var) {data[var, ]}
  ))
# Create new object  
df_object <- dataFrame$new()
# Call methods
df_object$get_data(df)
df_object$select_func(df, c("month", "forecast"))
df_object$filter_func(df[df$month %in% c(1, 2), ])

如果要链接成员函数,则需要将这些成员函数链接到 return self。这意味着 R6 对象必须修改它包含的数据。由于 R6 的好处是减少副本,我可能会保留数据的完整副本,并让 select_funcfilter_func 更新一些行和列索引:

library(R6)

dataFrame <- R6Class("dataFrame", 
                      public = list(
  data = data.frame(),
  rows = 0,
  columns = 0,
  initialize = function(data) { 
    self$data <- data
    self$rows <- seq(nrow(data))
    self$columns <- seq_along(data)
  },
  get_data = function() {self$data[self$columns][self$rows,]},
  select_func = function(cols) {
    if(is.character(cols))  cols <- match(cols, names(self$data))
    self$columns <- cols
    self
  },
  filter_func = function(r) {
    if(is.logical(r)) r <- which(r)
    self$rows <- r
    self
  })
)

这允许我们链接过滤器和 select 方法:

dataFrame$new(iris)$filter_func(1:5)$select_func(1:2)$get_data()
#>   Sepal.Length Sepal.Width
#> 1          5.1         3.5
#> 2          4.9         3.0
#> 3          4.7         3.2
#> 4          4.6         3.1
#> 5          5.0         3.6

我们的 select 方法也可以取名字:

dataFrame$new(mtcars)$select_func(c("mpg", "wt"))$get_data()
#>                      mpg    wt
#> Mazda RX4           21.0 2.620
#> Mazda RX4 Wag       21.0 2.875
#> Datsun 710          22.8 2.320
#> Hornet 4 Drive      21.4 3.215
#> Hornet Sportabout   18.7 3.440
#> Valiant             18.1 3.460
#> Duster 360          14.3 3.570
#> Merc 240D           24.4 3.190
#> Merc 230            22.8 3.150
#> Merc 280            19.2 3.440
#> Merc 280C           17.8 3.440
#> Merc 450SE          16.4 4.070
#> Merc 450SL          17.3 3.730
#> Merc 450SLC         15.2 3.780
#> Cadillac Fleetwood  10.4 5.250
#> Lincoln Continental 10.4 5.424
#> Chrysler Imperial   14.7 5.345
#> Fiat 128            32.4 2.200
#> Honda Civic         30.4 1.615
#> Toyota Corolla      33.9 1.835
#> Toyota Corona       21.5 2.465
#> Dodge Challenger    15.5 3.520
#> AMC Javelin         15.2 3.435
#> Camaro Z28          13.3 3.840
#> Pontiac Firebird    19.2 3.845
#> Fiat X1-9           27.3 1.935
#> Porsche 914-2       26.0 2.140
#> Lotus Europa        30.4 1.513
#> Ford Pantera L      15.8 3.170
#> Ferrari Dino        19.7 2.770
#> Maserati Bora       15.0 3.570
#> Volvo 142E          21.4 2.780

为了完整起见,您需要一些类型安全,我还会添加一个重置方法来删除所有过滤。这有效地为您提供了一个数据框,其中过滤和 selecting 是 non-destructive,这实际上可能非常有用。

reprex package (v2.0.1)

创建于 2022-05-01