r - 实现并行属性 vctrs 类

标签 r vctrs

我正在尝试使用 vctrs 创建一个向量类包,存储表达式。主要是因为我想在不同的 vctrs 向量中使用它。表达式不是向量类型,因此向量表达式(此处名为 vexpr)的简单实现会失败。

library(vctrs)

expr <- scales::math_format()(1:10)

new_vexpr <- function(x) {
  new_vctr(x, class = 'vexpr')
}

new_vexpr(expr)
#> Error: `.data` must be a vector type.
所以,我想,也许我可以将表达式本身实现为与向量并行的属性。
new_vexpr <- function(x) {
  if (!is.expression(x)) {
    stop()
  }
  new_vctr(seq_along(x),
           expr = x,
           class = "vexpr")
}

format.vexpr <- function(x, ...) {
  ifelse(is.na(vec_data(x)), NA, format(as.list(attr(x, "expr"))))
}

# Works!
x <- new_vexpr(expr)
我很快就遇到了麻烦,因为我还没有实现样板文件 vec_ptype2()vec_cast()方法呢。
# Looks like it might work
c(x, x)
#> <vexpr[20]>
#>  [1] 10^1L  10^2L  10^3L  10^4L  10^5L  10^6L  10^7L  10^8L  10^9L  10^10L
#> [11] 10^1L  10^2L  10^3L  10^4L  10^5L  10^6L  10^7L  10^8L  10^9L  10^10L

# Expression not concatenated (as might be expected)
attr(c(x, x), "expr")
#> expression(10^1L, 10^2L, 10^3L, 10^4L, 10^5L, 10^6L, 10^7L, 10^8L, 
#>     10^9L, 10^10L)
所以我尝试实现样板方法。
vec_ptype2.vexpr.vexpr <- function(x, y, ...) {
  new <- c(attr(x, "expr"), attr(y, "expr"))
  new_vctr(integer(0), expr = new, class = "vexpr")
}

vec_cast.vexpr.vexpr <- function(x, to, ...) {
  new_vctr(vec_data(x), expr = attr(to, "expr"),
           class = "vexpr")
}
这有助于连接向量,但返回错误的子集结果。
# Expression is concatenated!
attr(c(x, x), "expr")
#> expression(10^1L, 10^2L, 10^3L, 10^4L, 10^5L, 10^6L, 10^7L, 10^8L, 
#>     10^9L, 10^10L, 10^1L, 10^2L, 10^3L, 10^4L, 10^5L, 10^6L, 
#>     10^7L, 10^8L, 10^9L, 10^10L)

# Subsetting doesn't make sense, should be 10^2L
x[2]
#> <vexpr[1]>
#> [1] 10^1L

# Turns out, full expression still there
attr(x[2], "expr")
#> expression(10^1L, 10^2L, 10^3L, 10^4L, 10^5L, 10^6L, 10^7L, 10^8L, 
#>     10^9L, 10^10L)
很好,所以我在 vctrs 系统之外定义了我自己的子集方法,这最初似乎有效。
# Define S3 subsetting method
`[.vexpr` <- function(x, i, ...) {
  expr <- attr(x, "expr")
  ii <- vec_as_location(i, length(expr), names = names(x),
                        missing = "propagate")
  
  new_vctr(vec_data(x)[ii],
           expr = expr[ii],
           class = "vexpr")
}

# Subsetting works!
x[2]
#> <vexpr[1]>
#> [1] 10^2L

# Seemingly sensible concatenation
c(x[2], NA)
#> <vexpr[2]>
#> [1] 10^2L <NA>
但也开始产生荒谬的结果。
# expr is duplicated? would have liked the 2nd expression to be `expression(NA)`
attr(c(x[2], NA), "expr")
#> expression(10^2L, 10^2L)
创建于 2021-01-18 由 reprex package (v0.3.0)
显然,我在这里做错了,但我没有成功调试这个问题。我也尝试过实现 vec_restore() vexpr 的方法,但这让我更加困惑。你在某处看到过并行属性 vctrs 的很好的实现吗?你知道我可能做错了什么吗?
相关问题在这里:How do I build an object with the R vctrs package that can combine with c() (将 vctrs 与属性连接起来)
相关讨论在这里:https://github.com/r-lib/vctrs/issues/559
编辑:我不喜欢平行属性的想法。如 vec_data(x)将是 attr(x, "expr") 的索引这也可以,但我也没有做到这一点。
EDIT2:将表达式包装在调用列表中似乎很适合一切。但是,我仍然对并行属性/索引属性的稳定性感兴趣。列表包装示例(似乎所有方法都可以正常工作!):
new_vexpr <- function(x) {
  if (!is.expression(x)) {
    x <- as.expression(x)
    if (!is.expression(x)) {
      stop()
    }
  }
  x <- as.list(x)
  new_vctr(x,
           class = "vexpr")
}

as.expression.vexpr <- function(x) {
  do.call(expression, vec_data(x))
}

最佳答案

您可以将表达式包装在一个列表中:

library(vctrs)

expr <- scales::math_format()(1:10)

new_vexpr <- function(x) {
  new_vctr(list(x), class = 'vexpr')
}

res <- c(new_vexpr(expr), new_vexpr(expr))
res
#> <vexpr[2]>
#> [1] expression(10^1L, 10^2L, 10^3L, 10^4L, 10^5L, 10^6L, 10^7L, 10^8L, ,     10^9L, 10^10L)
#> [2] expression(10^1L, 10^2L, 10^3L, 10^4L, 10^5L, 10^6L, 10^7L, 10^8L, ,     10^9L, 10^10L)

res[2]
#> <vexpr[1]>
#> [1] expression(10^1L, 10^2L, 10^3L, 10^4L, 10^5L, 10^6L, 10^7L, 10^8L, ,     10^9L, 10^10L)
创建于 2021-01-21 由 reprex package (v0.3.0)

关于r - 实现并行属性 vctrs 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65777709/

相关文章:

R:如何高效地判断data.frame A是否包含在data.frame B中?

r - 从旧的数据帧中产生一个新的数据帧?

r - tidyverse 未加载,显示 "namespace ‘vctrs’ 0.2.0 已加载,但需要 >= 0.2.1”

具有唯一性约束的 R vctr 子类化

r - 在 R 中的 "%H%M"箱中聚合平均值 "week"

r - slidify 布局 : title-only, 标题和正文、标题和两列、标题标题和空白

r - R:如何将两个箱形图彼此相邻放置,并保持两个相同的y范围?