我正在尝试优化我编写的一些代码,因为它对于大型数据集来说非常慢。我不确定是否可以通过矩阵运算完成以下操作,如果有人有任何建议可以让它更快,我将不胜感激。
我有一个带有零和整数的矩阵,我想通过条目中整数的绝对数字向下移动各列的条目。
[,1] [,2] [,3]
[1,] 0 0 0
[2,] 0 -4 0
[3,] 4 0 0
[4,] -3 -2 0
[5,] 0 2 -1
[6,] 2 -2 0
[7,] 0 0 0
[8,] -3 -3 0
我使用的代码如下:
#data
A<-matrix(data=c(0,0,4,-3,0,2,0,-3,0,-4,0,-2,2,-2,0,-3,0,0,0,0,-1,0,0,0),nrow=8,ncol=3)
#shift function
shift<-function(x)
{
#create the output matrix
out<-matrix(data=0,nrow=8,ncol=1)
#for loop to create the shift matrix
for(i in seq(1,8,by=1))
{
if(i+abs(x[i])<=8)
{
#find the non zero
if(x[i]!=0)
{
#if there is already a number put zero
if(out[i+abs(x[i]),1]!=0)
{
out[i+abs(x[i]),1]=0
} else {
#shift
out[i+abs(x[i]),1]=x[i]
}
}
}
}
#return object
return(out)
}
#run the logic
shift_mat<-sapply(1:ncol(A),FUN=function(k) shift(A[,k]))
结果是:
[,1] [,2] [,3]
[1,] 0 0 0
[2,] 0 0 0
[3,] 0 0 0
[4,] 0 0 0
[5,] 0 0 0
[6,] 0 0 -1
[7,] 0 2 0
[8,] 2 -2 0
每列的规则如下:
谢谢,
尼科斯
答案 0 :(得分:2)
在我的机器上使用您的示例时,这样会更清晰,速度提高约40%。使用较大的数据可能会提高速度吗?
您应该使用整数矩阵。它使用更少的内存,一些操作更快:
A <- matrix(as.integer(c(0,0,4,-3,0,2,0,-3,0,-4,0,-2,2,
-2,0,-3,0,0,0,0,-1,0,0,0)), nrow = 8, ncol = 3)
每列都是一个向量,所以应该是你的输出。我用向量替换了矩阵。在没有硬编码行数的情况下,您的代码也更加健壮:
shift <- function(x) {
n <- length(x)
y <- rep(0L, n)
for(i in seq_len(n)) {
if (x[i] == 0L) next
j <- i + abs(x[i])
if (j > n) next
y[j] <- if (y[j] != 0L) 0L else x[i]
}
return(y)
}
您可以使用apply
:
shift_mat <- apply(A, 2, shift)
答案 1 :(得分:2)
可以对移位操作进行矢量化。我们只需要获取数据的第一列,看看如何:
v = c(0,0,4,-3,0,2,0,-3)
# index of the elements that could be non-zero in the final result
index = ifelse (v != 0 & abs(v) + seq_along(v) <= length(v),
abs(v) + seq_along(v), 0)
# [1] 0 0 7 7 0 8 0 0
# now just need to filter out the duplicated entries
index = ave(index, index, FUN = function(x) {if (length(x) > 1) 0 else x})
# [1] 0 0 0 0 0 8 0 0
# home at last
res = integer(length(v))
res[index] = v[which(index != 0)]
res
# [1] 0 0 0 0 0 0 0 2
然后,您可以将上面的内容添加到函数中,然后lapply
覆盖矩阵列上的data.frame
或apply
。
不出所料,上面的最大瓶颈是ave
函数,您可以使用以下data.table
构造替换该行(不要忘记require(data.table)
某处)以大大加快速度:
index = data.table(index)[, index := if(.N > 1) 0 else index, by = index][, index]