测试矩阵中的行是否排序的有效方法是什么? [更新:见Aaron的Rcpp答案 - 直截了当的&非常快。]
我移植了一些使用issorted(,'rows')
from Matlab的代码。由于is.unsorted
似乎没有超出向量,我正在写或寻找其他东西。天真的方法是检查矩阵(或数据框)的排序版本是否与原始版本相同,但这显然效率低下。
注意:为了排序la sortrows()
in Matlab,我的代码基本上使用SortedDF <- DF[do.call(order, DF),]
(它包含在一个更大的函数中,它将矩阵转换为数据帧,将参数传递给order
等。 )。如果有更快的实现(我想到数据表),我不会感到惊讶。
更新1:澄清一下:我没有测试排序行内或列内。 (这种排序通常会产生代数不同的矩阵。)
作为创建未排序矩阵的示例:
set.seed(0)
x <- as.data.frame(matrix(sample(3, 60, replace = TRUE), ncol = 6, byrow = TRUE))
其排序版本为:
y <- x[do.call(order, x),]
正确的测试,例如testSorted
,将为FALSE
返回testSorted(x)
,为TRUE
返回testSorted(y)
。
更新2: 下面的答案都很好 - 它们很简洁并且可以进行测试。关于效率,看起来这些都是对数据进行排序。
我已经尝试使用相当大的矩阵,例如1M x 10,(只是更改上面x
的创建),并且所有矩阵都具有相同的时间和内存成本。特别之处在于它们对未分类对象(1Mx10大约5.5秒)消耗的时间比分类对象(y
大约0.5秒)消耗更多时间。这表明他们在测试前进行了分类。
我通过创建z
矩阵进行了测试:
z <- y
z[,2] <- y[,1]
z[,1] <- y[,2]
在这种情况下,所有方法都需要大约0.85秒才能完成。无论如何,在5.5秒内完成并不可怕(事实上,对于对象排序所需的时间似乎是正确的),但是知道排序矩阵快11倍表明没有排序的测试可能是均匀的快点。在1M行矩阵的情况下,x
的前三行是:
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1 3 1 2 2 3 1 3 3 2 2
2 1 1 1 3 2 3 2 3 3 2
3 3 3 1 2 1 1 2 1 2 3
没有必要超越第2行,尽管矢量化并不是一个坏主意。
(我还为byrow
的创建添加了x
参数,因此行值不依赖于x
的大小。)
更新3:
可以使用Linux中的sort -c
命令找到此测试的另一个比较。如果文件已经写入(使用write.table()
),包含1M行,那么time sort -c myfile.txt
对于未排序数据需要0.003秒,对于排序数据需要0.101秒。我不打算写出文件,但这是一个有用的比较。
更新4:
Aaron的Rcpp方法击败了这里提供的所有其他方法,并且我已经尝试过(包括上面的sort -c
比较:内存中预计将击败磁盘)。至于相对于其他方法的比例,很难说:分母太小而无法给出准确的测量,我没有广泛探索microbenchmark
。对于某些矩阵(例如用rnorm
制作的矩阵),加速可能非常大(4-5个数量级),但这是误导性的 - 检查可以在仅几行之后终止。我已经加速了大约25-60的示例矩阵和未分类的大约1.1X,因为如果数据被排序,竞争方法已经非常快。
由于这是正确的事情(即没有排序,只是测试),并且很快就完成了,这是公认的答案。
答案 0 :(得分:6)
如果y被排序,则do.call(order,y)返回1:nrow(y)。
testSorted = function(y){all(do.call(order,y)==1:nrow(y))}
请注意,这不会比较矩阵,但一旦发现不匹配就不会消失。
答案 1 :(得分:6)
那么,你为什么不使用:
all(do.call(order, y)==seq(nrow(y)))
这可以避免创建有序矩阵,并确保它检查您的订购方式。
答案 2 :(得分:4)
嗯,蛮力方法是循环和比较,一旦发现违规就中止。
这种方法可以在R中轻松实现和测试,然后转移到一个简单的C ++函数,我们可以通过inline和Rcpp(如果必须的话,还是普通的C)连接到R循环是一种真正受益于编译语言实现的东西。
否则,您是否可以使用diff()
之类的内容并检查所有增量是否为非负数?
答案 3 :(得分:4)
更新:我决定使用Rcpp练习......
library(Rcpp)
library(inline)
isRowSorted <- cxxfunction(signature(A="numeric"), body='
Rcpp::NumericMatrix Am(A);
for(int i = 1; i < Am.nrow(); i++) {
for(int j = 0; j < Am.ncol(); j++) {
if( Am(i-1,j) < Am(i,j) ) { break; }
if( Am(i-1,j) > Am(i,j) ) { return(wrap(false)); }
}
}
return(wrap(true));
', plugin="Rcpp")
rownames(y) <- NULL # because as.matrix is faster without rownames
isRowSorted(as.matrix(y))
新:这个仅限R的黑客攻击速度与所有矩阵相同;它对于排序矩阵来说肯定更快;对于未分类的,它取决于未分类的性质。
iss3 <- function(x) {
x2 <- sign(do.call(cbind, lapply(x, diff)))
x3 <- t(x2)*(2^((ncol(x)-1):0))
all(colSums(x3)>=0)
}
原文:对于某些未排序的矩阵,这个速度更快。多快将取决于未分类元素的位置;这会逐列地查看矩阵,因此左侧的未排序将比右侧的未排序快得多,而顶部/底部几乎不重要。
iss2 <- function(y) {
b <- c(0,nrow(y))
for(i in 1:ncol(y)) {
z <- rle(y[,i])
b2 <- cumsum(z$lengths)
sp <- split(z$values, cut(b2, breaks=b))
for(spi in sp) {
if(is.unsorted(spi)) return(FALSE)
}
b <- c(0, b2)
}
return(TRUE)
}
答案 4 :(得分:2)
您可以将do.call
声明与is.unsorted
:
issorted.matrix <- function(A) {!is.unsorted(do.call("order",data.frame(A)))}