当我在R 3.1.2中创建给定POSIXct向量的矩阵时,矩阵的条目是数字而不是POSIXct:
x <- as.POSIXct("2012-02-25 19:00:00")
x
attributes(x)
m <- matrix(x, nrow=2, ncol=3)
m
attributes(m)
创建POSIXct值矩阵的最佳方法是什么?
答案 0 :(得分:9)
我以为我以前从未见过有人创建POSIXct值的矩阵,尽管不难想象这样一个对象的用例。
R似乎并不能很好地支持这种类型的对象。 S3对象系统非常有限,并且创建POSIXct矩阵需要设置矩阵和POSIXct(以及POSIXt,它似乎总是与POSIXct一起标记)S3类。事实上,根据我的经验,任何对象从多个显式S3类继承都是很不寻常的,可能除了POSIXct + POSIXt和POSIXlt + POSIXt之外。
我通过创建一个新的矩阵构造函数matrix.POSIXct()
来试图填充这种类型的对象。为方便起见,为了提供S3调度,我还创建了一个新的通用matrix()
和默认matrix.default()
,它委托给普通的base::matrix()
。请注意,matrix()
的这种通用化有时由R包完成,例如gmp。他们将通用化功能限制在他们的包环境中,但我只是将这些功能转移到全局环境中。
不幸的是,默认的POSIXct打印函数print.POSIXct()
不够智能,无法处理也被归类为矩阵的POSIXct向量,因此任何此类矩阵都将打印为普通的旧向量。为了解决这个问题,我还创建了一个新的print.POSIXct()
函数,它拦截任何POSIXct类对象的打印,并检查它是否也被归类为矩阵,在这种情况下,提供一个合理的实现最小的工作,我建立一个新的矩阵,其数据值包含POSIXct值的字符表示,然后我打印该矩阵。如果它不被归类为矩阵,我只需将参数传递给普通base::print.POSIXct()
函数即可打印普通的旧矩阵POSIXct向量。
我试图尽可能密切地遵循base::matrix()
关于matrix.POSIXct()
中默认缺失论点的设计。
matrix <- function(x,...) UseMethod('matrix');
matrix.default <- function(...) base::matrix(...);
matrix.POSIXct <- function(data=NA,nrow,ncol,byrow=F,dimnames=NULL,...) {
if (missing(nrow)) {
if (missing(ncol)) {
nrow <- length(data);
ncol <- 1L;
} else {
nrow <- ceiling(length(data)/ncol);
}; ## end if
} else {
if (missing(ncol))
ncol <- ceiling(length(data)/nrow);
}; ## end if
data <- rep(as.POSIXct(data,tz=attr(data,'tzone'),...),len=nrow*ncol);
if (byrow) {
dim(data) <- c(ncol,nrow);
data <- t(data);
} else
dim(data) <- c(nrow,ncol);
if (!is.null(dimnames))
base::dimnames(data) <- dimnames;
class(data) <- c(class(data),'matrix');
data;
}; ## end matrix.POSIXct()
print.POSIXct <- function(x,...) {
if (inherits(x,'matrix') && !is.null(nrow(x))) {
print(matrix(as.character(x,usetz=T),nrow(x),dimnames=dimnames(x)),...);
invisible(x);
} else
base::print.POSIXct(x,...);
}; ## end print.POSIXct()
演示您的数据:
x <- as.POSIXct('2012-02-25 19:00:00');
m <- matrix(x,2L,3L);
m;
## [,1] [,2] [,3]
## [1,] "2012-02-25 19:00:00 EST" "2012-02-25 19:00:00 EST" "2012-02-25 19:00:00 EST"
## [2,] "2012-02-25 19:00:00 EST" "2012-02-25 19:00:00 EST" "2012-02-25 19:00:00 EST"
attributes(m);
## $class
## [1] "POSIXct" "POSIXt" "matrix"
##
## $tzone
## [1] ""
##
## $dim
## [1] 2 3
这里是format.POSIXct()
:
format.POSIXct <- function(x,...) {
if (inherits(x,'matrix') && !is.null(nrow(x)))
matrix(base::format.POSIXct(x,...),nrow(x),dimnames=dimnames(x))
else
base::format.POSIXct(x,...);
}; ## end format.POSIXct()
是的,忘了索引了。这是另一个有问题的案例。默认的base::`[.POSIXct`()
索引函数有点便宜(有点像上面的一些我的垫片代码,不可否认)因为它只是暂时删除了向量的类,将它传递给下一个特定的S3,然后恢复原班级。这意味着遵循矩阵的drop
参数,如果设置为TRUE(默认值)并且下标使得矩阵性被删除,则意味着从返回的对象中删除dim
属性。
问题是廉价包装器中的类恢复会恢复我们的矩阵类,因此,当廉价包装器返回时,我们会收到一个没有dim
属性的矩阵类对象。
我们遇到的确切错误,当我们尝试打印子集向量时,print.POSIXct()
垫片实际发出的错误(&#34;评估参数的错误&#39; x&#39;在选择函数的方法&#39; print&#39 ;: base :: matrix(...)中的错误:非数字矩阵范围&#34;)是由nrow(x)
返回NULL引起的,因此matrix()
调用接收nrow = NULL。
我已经做了两件事来解决这个问题。首先,我改进了print.POSIXct()
函数以防止nrow(x)
返回NULL的情况,在这种情况下,它不会将对象打印作为矩阵处理。因此,如果它收到一个没有dim
属性的矩阵分类对象(尽管这不应该发生),它会将其打印为普通的旧POSIXct向量。
其次,我编写了另一个索引函数来检测dim
属性的删除,并在这种情况下删除矩阵类。
这个新函数的创建因为廉价包装器使用NextMethod()
来调用下一个S3特定而变得复杂,如果从直接调用的调用中调用它,则无效,与S3调度无关处理。因此,正如您在下面的代码中所看到的,我使用了一些hack来&#34;插入&#34;廉价包装器的主体进入我们的shim函数,从而将NextMethod()
调用移动到我们的垫片中,因此必须通过通用`[`()
调用(像往常一样):
`[.POSIXct` <- function(x,...) {
res <- blah;
if (inherits(x,'matrix') && !'dim'%in%names(attributes(res)))
class(res) <- class(res)[class(res)!='matrix'];
res;
};
body(`[.POSIXct`)[[2]][[3]] <- body(base::`[.POSIXct`);
演示:
x <- as.POSIXct('2016-02-05 00:00:00')+0:8;
m <- matrix(x,3L,byrow=T);
m;
## [,1] [,2] [,3]
## [1,] "2016-02-05 00:00:00 EST" "2016-02-05 00:00:01 EST" "2016-02-05 00:00:02 EST"
## [2,] "2016-02-05 00:00:03 EST" "2016-02-05 00:00:04 EST" "2016-02-05 00:00:05 EST"
## [3,] "2016-02-05 00:00:06 EST" "2016-02-05 00:00:07 EST" "2016-02-05 00:00:08 EST"
m[1];
## [1] "2016-02-05 EST"
m[1:3];
## [1] "2016-02-05 00:00:00 EST" "2016-02-05 00:00:03 EST" "2016-02-05 00:00:06 EST"
m[1:3,1];
## [1] "2016-02-05 00:00:00 EST" "2016-02-05 00:00:03 EST" "2016-02-05 00:00:06 EST"
m[1:3,1,drop=F];
## [,1]
## [1,] "2016-02-05 00:00:00 EST"
## [2,] "2016-02-05 00:00:03 EST"
## [3,] "2016-02-05 00:00:06 EST"
m[1:3,1:2];
## [,1] [,2]
## [1,] "2016-02-05 00:00:00 EST" "2016-02-05 00:00:01 EST"
## [2,] "2016-02-05 00:00:03 EST" "2016-02-05 00:00:04 EST"
## [3,] "2016-02-05 00:00:06 EST" "2016-02-05 00:00:07 EST"
这里是as.data.frame.POSIXct()
:
as.data.frame.POSIXct <- function(x,...) {
if (inherits(x,'matrix') && !is.null(dim(x))) {
class(x) <- class(x)[!class(x)%in%c('POSIXct','POSIXt')];
res <- as.data.frame(x,...);
for (ci in seq_along(res))
res[[ci]] <- as.POSIXct(res[[ci]],tz=attr(x,'tzone'),origin='1970-01-01');
res;
} else
base::as.data.frame.POSIXct(x,...);
}; ## end as.data.frame.POSIXct()
演示:
m <- matrix(as.POSIXct('2016-02-05 00:00:00')+0:8,3);
m;
## [,1] [,2] [,3]
## [1,] "2016-02-05 00:00:00 EST" "2016-02-05 00:00:03 EST" "2016-02-05 00:00:06 EST"
## [2,] "2016-02-05 00:00:01 EST" "2016-02-05 00:00:04 EST" "2016-02-05 00:00:07 EST"
## [3,] "2016-02-05 00:00:02 EST" "2016-02-05 00:00:05 EST" "2016-02-05 00:00:08 EST"
as.data.frame(m);
## V1 V2 V3
## 1 2016-02-05 00:00:00 2016-02-05 00:00:03 2016-02-05 00:00:06
## 2 2016-02-05 00:00:01 2016-02-05 00:00:04 2016-02-05 00:00:07
## 3 2016-02-05 00:00:02 2016-02-05 00:00:05 2016-02-05 00:00:08
这里是summary.POSIXct()
:
summary.POSIXct <- function(x,...) {
if (inherits(x,'matrix') && !is.null(dim(x)))
summary(as.data.frame(x),...)
else
base::summary.POSIXct(x,...);
}; ## end summary.POSIXct()
答案 1 :(得分:0)
粗略的方法是将类和属性重新分配给矩阵:
x <- as.POSIXct("2012-02-25 19:00:00")
m <- matrix(x, nrow=2, ncol=3)
assignPOSIXct <- function(m,x){
class(m) <- c("matrix",class(x))
attr(m,"tzone") <- attr(x,"tzone")
return(m)
}
m <- assignPOSIXct(m,x)
m
但这很麻烦且容易出错。在循环中,我必须检查条目是否为POSIXct。
答案 2 :(得分:0)
另一种方法是在存储到矩阵和数组中时接受丢失S3信息,并在需要时转换为POSIXct。这可以通过以下功能
完成asPOSIXctFromNumeric <- function(
### convert numeric to POSIXct with default origin and time zone
x ##<< numeric vector to convert
,origin='1970-01-01' ##<< default origin
,tz='GMT' ##<< default time zone
){
##details<<
## Sometime POSIXct becomes converted to numeric, e.g. when stored
## in a matrix.
## The defaults of this routing convert it back to POSIXct with
## the same origin, and a default time zone
as.POSIXct(as.numeric(x),origin=origin, tz=tz)
}
答案 3 :(得分:0)
我通过重新排序class属性来调整@bgoldst的答案,以便矩阵首先出现:
matrix <- function(x,...) UseMethod('matrix');
matrix.default <- function(...) base::matrix(...);
matrix.POSIXct <- function(data=NA,nrow,ncol,byrow=F,dimnames=NULL,...) {
if (missing(nrow)) {
if (missing(ncol)) {
nrow <- length(data);
ncol <- 1L;
} else {
nrow <- ceiling(length(data)/ncol);
}; ## end if
} else {
if (missing(ncol))
ncol <- ceiling(length(data)/nrow);
}; ## end if
data <- rep(as.POSIXct(data,tz=attr(data,'tzone'),...),len=nrow*ncol);
if (byrow) {
dim(data) <- c(ncol,nrow);
data <- t(data);
} else
dim(data) <- c(nrow,ncol);
if (!is.null(dimnames))
base::dimnames(data) <- dimnames;
class(data) <- c('matrix',class(data));
data;
}; ## end matrix.POSIXct()
as.data.frame.matrix <- function (x, ...)
{
value <- base::as.data.frame.matrix(x,...)
if( inherits(x,"POSIXct") ) {
for (i in 1:ncol(value)){
attributes(value[[i]])$tzone <- attributes(x)$tzone
class(value[[i]]) <- c("POSIXct","POSIXt")
}
}
value
}
结果更符合我的预期。但是,一些原始功能仍然存在问题。以下结果用于向量而不是矩阵:
t(m)
m[1, ,drop=FALSE]
因此,它的使用仍然非常不安全。