r - 在矩阵中使用 vctrs
问题描述
我正在试验这个vctrs
包。我的实际用例在相关方面类似于主页上rational
有用的S3 向量文章中实现的类vctrs
,因为它rcrd
用于配对数据。为了清楚起见,我将使用它作为我的代表。(编辑:但是,我对理性并不特别感兴趣。)让我先粘贴相关部分:
library(vctrs)
library(zeallot)
new_rational <- function(n = integer(), d = integer()) {
vec_assert(n, ptype = integer())
vec_assert(d, ptype = integer())
new_rcrd(list(n = n, d = d), class = "vctrs_rational")
}
rational <- function(n, d) {
c(n, d) %<-% vec_cast_common(n, d, .to = integer())
c(n, d) %<-% vec_recycle_common(n, d)
new_rational(n, d)
}
format.vctrs_rational <- function(x, ...) {
n <- field(x, "n")
d <- field(x, "d")
out <- paste0(n, "/", d)
out[is.na(n) | is.na(d)] <- NA
out
}
vec_ptype_abbr.vctrs_rational <- function(x, ...) "rtnl"
vec_ptype_full.vctrs_rational <- function(x, ...) "rational"
一个使用这个的例子:
(x <- rational(1, 1:15))
#> <rational[15]>
#> [1] 1/1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 1/9 1/10 1/11 1/12 1/13 1/14 1/15
尝试在 a 中使用这样的类时出现了我的问题matrix
:
matrix(x, ncol = 5, nrow = 3)
#> Warning in matrix(x, ncol = 5, nrow = 3): data length [2] is not a sub-multiple
#> or multiple of the number of rows [3]
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] Integer,15 Integer,15 Integer,15 Integer,15 Integer,15
#> [2,] Integer,15 Integer,15 Integer,15 Integer,15 Integer,15
#> [3,] Integer,15 Integer,15 Integer,15 Integer,15 Integer,15
由reprex 包(v0.3.0)于 2020 年 6 月 5 日创建
我希望得到一个 3×5 矩阵,其中每个单元格包含一个来自 的值,如果是“正常”向量x
就会发生这种情况。x
相反,我得到了一个 3×5 的列表矩阵,其中vctrs
尝试使交替的行分别包含n
和d
值。
因此,我的问题是,对于这种情况,是否有可能以vctrs
“预期”的方式使用矩阵,如果可以,如何?通过实验,我感觉到这可能与实现dim.rational
and有关`dim<-.rational`
,但我无法让它发挥作用。
编辑:如果所需的矩阵不清楚(如评论中所建议的那样),我想要一个类似于以下内容的矩阵对象,我已经手动编辑了它:
(m <- matrix(x, ncol = 5, nrow = 3))
#> <rational[15]>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 1/1 1/4 1/7 1/10 1/13
#> [2,] 1/2 1/5 1/8 1/11 1/14
#> [3,] 1/3 1/6 1/9 1/12 1/15
这样正常的矩阵运算将起作用m
,例如
m[1,]
#> <rational[5]>
#> 1/1 1/4 1/7 1/10 1/13
解决方案
该类的整个设计rational
似乎建立在保留其类型安全性和对用户隐藏实现的基础上,我可以看到这对于让它始终如一地工作是必要的,但这意味着你不能指望它与 R 的默认设置很好地配合S3 方法。
帮助文件vctrs
专门说
- dims()、dims<-、dimnames()、dimnames<-、levels() 和 levels<- 方法会引发错误。
这表明 的作者vctrs
并不认为它是构建矩阵方法的一个很好的基础。
在任何情况下,我都不会急于尝试将其放入矩阵中,因为一旦它存在,您将无法对其进行任何操作:您没有可用的算术方法:
x + 2
#> Error: <rational> + <double> is not permitted
#> Run `rlang::last_error()` to see where the error occurred.
x * 2
#> Error: <rational> * <double> is not permitted
#> Run `rlang::last_error()` to see where the error occurred.
x + x
#> Error: <rational> + <rational> is not permitted
#> Run `rlang::last_error()` to see where the error occurred.
所以你需要先定义算术方法。在你这样做之前,你需要$
一个用于分子和分母的访问器,一个is.rational
在尝试算术之前检查类型的函数,一个找到最大公分母的函数,以及一个基于它简化你的有理数的函数。
`$.vctrs_rational` <- function(vec, symb) unclass(vec)[[as.character(symb)]]
is.rational <- function(num) class(num)[1] == "vctrs_rational"
gcd <- function(x, y) ifelse(x %% y, gcd(y, x %% y), y)
simplify <- function(num) {
common <- gcd(num$n, num$d)
rational(num$n / common, num$d/common)
}
所以现在你可以做
x$n
#> [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
x$d
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
is.rational(x)
#> [1] TRUE
现在编写算术函数。例如,这是一个涵盖数字和有理类型的基本算术实现:
Ops.vctrs_rational <- function(vec, num)
{
if(!is.rational(vec)) {tmp <- vec; vec <- num; num <- tmp; }
if(.Generic == '*'){
if(is.rational(num)) return(simplify(rational(vec$n * num$n, vec$d * num$d)))
else return(simplify(rational(vec$n * 2, vec$d)))
}
else if (.Generic == '/'){
if(is.rational(num)) return(vec * rational(num$d, num$n))
else return(vec * rational(1, num))
}
else if (.Generic == '+'){
if(is.rational(num)){
new_n <- vec$n * (vec$d * num$d)/vec$d + num$n * (vec$d * num$d)/num$d
return(simplify(rational(new_n, vec$d * num$d)))
}
else return(simplify(rational(num * vec$d + vec$n, vec$d)))
}
else if (.Generic == '-'){
if(is.rational(num)) return(vec + rational(-num$n, num$d))
else return(vec + (-num))
}
else if (.Generic == '^'){
if(is.rational(num) | num < 0) stop("fractional and negative powers not supported")
return(simplify(rational(vec$n ^ num, vec$d ^ num)))
}
}
这现在允许您执行以下操作,例如:
x * 3
#> <rational[15]>
#> [1] 3/1 3/2 1/1 3/4 3/5 1/2 3/7 3/8 1/3 3/10 3/11 1/4 3/13 3/14 1/5
x + x
#> <rational[15]>
#> [1] 2/1 1/1 2/3 1/2 2/5 1/3 2/7 1/4 2/9 1/5 2/11 1/6 2/13 1/7 2/15
(2 + x)^2 / (3 * x + 1)
#> <rational[15]>
#> [1] 3/1 25/8 49/15 27/8 121/35 169/48 25/7 289/80
#> [9] 361/99 147/40 529/143 625/168 243/65 841/224 961/255
尝试matrix()
直接使用它自己可能是行不通的,因为matrix
通过转换为基向量然后调用 C 代码来工作。这去除了类信息。
这意味着您需要定义一个单独的rational_matrix
类,这反过来又会从支持rational_vector
类中受益。然后我们可以定义具体的格式和打印方法:
as.vector.vctrs_rational <- function(x, ...) {
n <- x$n/x$d
attr(n, "denom") <- x$d
attr(n, "numerator") <- x$n
class(n) <- "rational_attr"
n
}
rational_matrix <- function(data, nrow = 1, ncol = 1,
byrow = FALSE, dimnames = NULL){
d <- as.vector(data)
m <- .Internal(matrix(d, nrow, ncol, byrow, dimnames, missing(nrow),
missing(ncol)))
m_dim <- dim(m)
attributes(m) <- attributes(d)
dim(m) <- rev(m_dim)
class(m) <- c("rational_matrix", "matrix")
m
}
format.rational_matrix <- function(x) {
return(paste0(attr(x, "numerator"), "/", attr(x, "denom")))
}
print.rational_matrix <- function(x)
{
print(matrix(format(x), nrow = dim(x)[2]), quote = FALSE)
}
最后,您需要覆盖matrix()
以使其成为 S3 方法,确保首先将函数复制为matrix.default
matrix.default <- matrix
matrix <- function(data = NA, ...) UseMethod("matrix")
matrix.vctrs_rational <- function(data, ...) rational_matrix(data, ...)
所以现在你可以这样做:
matrix(x, nrow = 5)
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 1/1 1/4 1/7 1/10 1/13
#> [2,] 1/2 1/5 1/8 1/11 1/14
#> [3,] 1/3 1/6 1/9 1/12 1/15
rational_matrix(x + 5, nrow = 3)
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 6/1 21/4 36/7 51/10 66/13
#> [2,] 11/2 26/5 41/8 56/11 71/14
#> [3,] 16/3 31/6 46/9 61/12 76/15
rational_matrix(x + x, nrow = 5)
#> [,1] [,2] [,3]
#> [1,] 2/1 1/3 2/11
#> [2,] 1/1 2/7 1/6
#> [3,] 2/3 1/4 2/13
#> [4,] 1/2 2/9 1/7
#> [5,] 2/5 1/5 2/15
然而,为了让它工作,无论如何我们都必须添加带有属性的额外类,所以我的感觉是,如果你想要一个与矩阵等一起工作的理性类,你应该在本机 S3 或其他面向对象方法之一中进行在 R 中可用,而不是使用vctrs
包。
值得一提的是,上面的类还远未准备好生产,因为您需要添加测试相等/不等式的方法、描述矩阵运算的方法、转换为十进制的能力、绘图方法等。
推荐阅读
- opencv - openvino python推理api导入错误
- php - 如何将环境变量从 .env 加载到 getenv() PHP MVC
- reactjs - 从 React 中的功能组件传递值
- javascript - “禁止(CSRF 令牌丢失或不正确。):”使用 Django 和 JS
- kubernetes - 当 cronjobs 设置为 replace 时,Kubernetes 是否会等待前一个作业完成关闭后再启动新作业?
- javascript - 使用Javascript递归检查对象中的任何空/未定义键/值
- python - 为什么在 RTX 3070/cudnn8/CUDA11.1 上运行时添加卷积/池层会使 Keras/Tensorflow 模型崩溃?
- javascript - 为什么/在哪里多次调用这个 componentDidMount()?
- function - 什么是镜像位的数学(用函数描述)等价物?
- python - docker hub 你是否为每个图像创建一个存储库