我需要对矩阵进行排序,以便所有元素都保留在列中,每列都按升序排列。对于R中的矩阵或数据帧,是否存在矢量化列式排序? (我的矩阵是全正的并且由B
限制,因此我可以将j*B
添加到列j
中的每个单元格并进行常规的一维排序:
> set.seed(100523); m <- matrix(round(runif(30),2), nrow=6); m
[,1] [,2] [,3] [,4] [,5]
[1,] 0.47 0.32 0.29 0.54 0.38
[2,] 0.38 0.91 0.76 0.43 0.92
[3,] 0.71 0.32 0.48 0.16 0.85
[4,] 0.88 0.83 0.61 0.95 0.72
[5,] 0.16 0.57 0.70 0.82 0.05
[6,] 0.77 0.03 0.75 0.26 0.05
> offset <- rep(seq_len(5), rep(6, 5)); offset
[1] 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 5 5 5 5 5 5
> m <- matrix(sort(m + offset), nrow=nrow(m)) - offset; m
[,1] [,2] [,3] [,4] [,5]
[1,] 0.16 0.03 0.29 0.16 0.05
[2,] 0.38 0.32 0.48 0.26 0.05
[3,] 0.47 0.32 0.61 0.43 0.38
[4,] 0.71 0.57 0.70 0.54 0.72
[5,] 0.77 0.83 0.75 0.82 0.85
[6,] 0.88 0.91 0.76 0.95 0.92
但是有没有更漂亮的东西?)否则,如果我的矩阵有大约1M(10M,100M)条目(大致是方阵),那么最快的方法是什么?我担心apply
和朋友的性能损失。
实际上,我不需要“排序”,只需要“前n”,n大约是30或100。我正在考虑使用apply
和partial
的{{1}}参数,但我想知道这是否比仅进行矢量化排序更便宜。所以,在我自己做基准测试之前,我想请有经验的用户提出建议。
答案 0 :(得分:4)
如果您想使用排序,?sort
表示method = "quick"
的速度可以是默认方法的两倍,大约为100万个元素。
从apply(m, 2, sort, method = "quick")
开始,看看是否提供足够的速度。
请注意?sort
中对此的评论;领带以不稳定的方式排序。
答案 1 :(得分:4)
我已经为目前提出的解决方案制定了快速测试框架。
library(rbenchmark)
sort.q <- function(m) {
sort(m, method='quick')
}
sort.p <- function(m) {
mm <- sort(m, partial=TOP)[1:TOP]
sort(mm)
}
sort.all.g <- function(f) {
function(m) {
o <- matrix(rep(seq_len(SIZE), rep(SIZE, SIZE)), nrow=SIZE)
matrix(f(m+o), nrow=SIZE)[1:TOP,]-o[1:TOP,]
}
}
sort.all <- sort.all.g(sort)
sort.all.q <- sort.all.g(sort.q)
apply.sort.g <- function(f) {
function(m) {
apply(m, 2, f)[1:TOP,]
}
}
apply.sort <- apply.sort.g(sort)
apply.sort.p <- apply.sort.g(sort.p)
apply.sort.q <- apply.sort.g(sort.q)
bb <- NULL
SIZE_LIMITS <- 3:9
TOP_LIMITS <- 2:5
for (SIZE in floor(sqrt(10)^SIZE_LIMITS)) {
for (TOP in floor(sqrt(10)^TOP_LIMITS)) {
print(c(SIZE, TOP))
TOP <- min(TOP, SIZE)
m <- matrix(runif(SIZE*SIZE), floor(SIZE))
if (SIZE < 1000) {
mr <- apply.sort(m)
stopifnot(apply.sort.q(m) == mr)
stopifnot(apply.sort.p(m) == mr)
stopifnot(sort.all(m) == mr)
stopifnot(sort.all.q(m) == mr)
}
b <- benchmark(apply.sort(m),
apply.sort.q(m),
apply.sort.p(m),
sort.all(m),
sort.all.q(m),
columns= c("test", "elapsed", "relative",
"user.self", "sys.self"),
replications=1,
order=NULL)
b$SIZE <- SIZE
b$TOP <- TOP
b$test <- factor(x=b$test, levels=b$test)
bb <- rbind(bb, b)
}
}
ftable(xtabs(user.self ~ SIZE+test+TOP, bb))
到目前为止的结果表明,对于除了最大的矩阵以外的所有矩阵,apply
确实会伤害性能,除非做一个“前n”。对于“小”矩阵&lt; 1e6,只是在没有apply
的情况下对整个事物进行排序是有竞争力的。对于“巨大”矩阵,对整个数组进行排序变得比apply
慢。使用partial
最适合“巨大”矩阵,对于“小”矩阵来说只是轻微损失。
请随意添加自己的排序程序: - )
TOP 10 31 100 316
SIZE test
31 apply.sort(m) 0.004 0.012 0.000 0.000
apply.sort.q(m) 0.008 0.016 0.000 0.000
apply.sort.p(m) 0.008 0.020 0.000 0.000
sort.all(m) 0.000 0.008 0.000 0.000
sort.all.q(m) 0.000 0.004 0.000 0.000
100 apply.sort(m) 0.012 0.016 0.028 0.000
apply.sort.q(m) 0.016 0.016 0.036 0.000
apply.sort.p(m) 0.020 0.020 0.040 0.000
sort.all(m) 0.000 0.004 0.008 0.000
sort.all.q(m) 0.004 0.004 0.004 0.000
316 apply.sort(m) 0.060 0.060 0.056 0.060
apply.sort.q(m) 0.064 0.060 0.060 0.072
apply.sort.p(m) 0.064 0.068 0.108 0.076
sort.all(m) 0.016 0.016 0.020 0.024
sort.all.q(m) 0.020 0.016 0.024 0.024
1000 apply.sort(m) 0.356 0.276 0.276 0.292
apply.sort.q(m) 0.348 0.316 0.288 0.296
apply.sort.p(m) 0.256 0.264 0.276 0.320
sort.all(m) 0.268 0.244 0.213 0.244
sort.all.q(m) 0.260 0.232 0.200 0.208
3162 apply.sort(m) 1.997 1.948 2.012 2.108
apply.sort.q(m) 1.916 1.880 1.892 1.901
apply.sort.p(m) 1.300 1.316 1.376 1.544
sort.all(m) 2.424 2.452 2.432 2.480
sort.all.q(m) 2.188 2.184 2.265 2.244
10000 apply.sort(m) 18.193 18.466 18.781 18.965
apply.sort.q(m) 15.837 15.861 15.977 16.313
apply.sort.p(m) 9.005 9.108 9.304 9.925
sort.all(m) 26.030 25.710 25.722 26.686
sort.all.q(m) 23.341 23.645 24.010 24.073
31622 apply.sort(m) 201.265 197.568 196.181 196.104
apply.sort.q(m) 163.190 160.810 158.757 160.050
apply.sort.p(m) 82.337 81.305 80.641 82.490
sort.all(m) 296.239 288.810 289.303 288.954
sort.all.q(m) 260.872 249.984 254.867 252.087
答案 2 :(得分:3)
apply(m, 2, sort)
做这个工作? :)
或者对于前10名,比方说,使用:
apply(m, 2 ,function(x) {sort(x,dec=TRUE)[1:10]})
性能很强 - 对于1e7行和5个cols(总共5e7个数字),我的电脑花了大约9或10秒钟。
答案 3 :(得分:3)
R在矩阵计算中非常快。在1e4列中具有1e7个元素的矩阵在我的机器上在3秒内排序
set.seed(1)
m <- matrix(runif(1e7), ncol=1e4)
system.time(sm <- apply(m, 2, sort))
user system elapsed
2.62 0.14 2.79
前5列:
sm[1:15, 1:5]
[,1] [,2] [,3] [,4] [,5]
[1,] 2.607703e-05 0.0002085913 9.364448e-05 0.0001937598 1.157424e-05
[2,] 9.228056e-05 0.0003156713 4.948019e-04 0.0002542199 2.126186e-04
[3,] 1.607228e-04 0.0003988042 5.015987e-04 0.0004544661 5.855639e-04
[4,] 5.756689e-04 0.0004399747 5.762535e-04 0.0004621083 5.877446e-04
[5,] 6.932740e-04 0.0004676797 5.784736e-04 0.0004749235 6.470268e-04
[6,] 7.856274e-04 0.0005927107 8.244428e-04 0.0005443178 6.498618e-04
[7,] 8.489799e-04 0.0006210336 9.249109e-04 0.0005917936 6.548134e-04
[8,] 1.001975e-03 0.0006522120 9.424880e-04 0.0007702231 6.569310e-04
[9,] 1.042956e-03 0.0007237203 1.101990e-03 0.0009826915 6.810103e-04
[10,] 1.246256e-03 0.0007968422 1.117999e-03 0.0009873926 6.888523e-04
[11,] 1.337960e-03 0.0009294956 1.229132e-03 0.0009997757 8.671272e-04
[12,] 1.372295e-03 0.0012221676 1.329478e-03 0.0010375632 8.806398e-04
[13,] 1.583430e-03 0.0012781983 1.433513e-03 0.0010662393 8.886999e-04
[14,] 1.603961e-03 0.0013518191 1.458616e-03 0.0012068383 8.903167e-04
[15,] 1.673268e-03 0.0013697683 1.590524e-03 0.0013617468 1.024081e-03
答案 4 :(得分:1)
他们说天才与疯狂之间存在着微妙的界限......看看这个,看看你对这个想法的看法。在问题中,目标是找到可能很长的向量vec
的前30个元素(1e7,1e8或更多元素)。
topn = 30
sdmult = max(1,qnorm(1-(topn/length(vec))))
sdmin = 1e-5
acceptmult = 10
calcsd = max(sd(vec),sdmin)
calcmn = mean(vec)
thresh = calcmn + sdmult*calcsd
subs = which(vec > thresh)
while (length(subs) > topn * acceptmult) {
thresh = thresh + calcsd
subs = which(vec > thresh)
}
while (length(subs) < topn) {
thresh = thresh - calcsd
subs = which(vec > thresh)
}
topvals = sort(vec[subs],dec=TRUE)[1:topn]
基本思想是,即使我们对vec
的分布知之甚少,我们当然希望vec
中的最高值比平均值高几个标准偏差。如果vec
是正态分布的,那么第2行的qnorm
表达式可以粗略地了解我们需要查找最高topn
值的平均值(例如,如果vec包含1e8值,前30个值可能位于从均值上方5 sd开始的区域。)即使vec
不正常,这个假设也不太可能远离事实。
好的,我们计算vec
的均值和sd,并使用这些来提出一个阈值来看上面 - 一定数量的sd高于均值。我们希望在这个上尾中找到略高于topn
值的子集。如果我们这样做,我们可以对其进行排序并轻松识别最高topn
值 - 这将是topn
总体中vec
值最高的值。
现在这里的确切规则可能会稍微调整一下,但我们的想法是,由于某种原因,我们需要防止原来的阈值“出局”。因此,我们利用了这样一个事实,即可以快速检查有多少元素超过某个阈值。因此,我们首先以calcsd
为增量提高阈值,直到阈值以上的10 * topn
个元素少于thresh
个元素。然后,如果需要的话。我们减少calcsd
(再次以topn
为步骤),直到我们确实至少有topn
个元素高于阈值。这种双向搜索应始终导致“阈值集”,其大小非常接近topn
(希望在10或100之内)。由于topn
相对较小(典型值为30),因此对此阈值集进行排序的速度非常快,这当然会立即为我们提供原始向量vec
中最高的{{1}}元素。
我的主张是,生成一个合适的阈值集所涉及的计算都是快速的R,所以如果只需要一个非常大的向量的前30个元素,这种间接方法将击败任何涉及排序的方法整个载体。
你怎么看?!如果您认为这是一个有趣的想法,请喜欢/投票:)我会考虑做一些正确的时间,但我对随机生成的数据的初步测试真的很有希望 - 在“真实”数据上测试它会很棒虽然...!
干杯:)