r语言 - 实现一个并行属性向量类



我试图使用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)

由reprex包(v0.3.0)在2021-01-18创建

显然,我在这里做了一些错误的事情,但是我还没有成功地调试这个问题。我也试过为vexpr实现vec_restore()方法,但这让我更加困惑。你在什么地方见过并行属性向量的实现吗?你知道我可能做错了什么吗?

这里的相关问题:如何使用R vctrs包构建可以与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)

由reprex包(v0.3.0)在2021-01-21创建

相关内容