-2

我正在尝试优化我编写的一些代码,因为它对于大型数据集来说非常慢。我不确定是否可以通过矩阵运算完成以下操作,如果有人有任何建议可以使其更快,我将不胜感激。

我有一个带有零和整数的矩阵,我想将各个列的条目向下移动条目中整数的绝对数。

   [,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

每一列的规则如下:

  1. 从顶部开始查找不为零的第一个条目
  2. 按该条目的绝对数字向下移动
  3. 如果在目标点有另一个条目,则置零
  4. 重复下一列

谢谢,

尼科斯

4

2 回答 2

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)
于 2013-08-16T21:26:30.610 回答
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

然后,您可以将 then above 放入一个函数中,然后lapply放在您的data.frameapply矩阵的列上。

不出所料,上面最大的瓶颈是ave函数,您可以用以下data.table构造替换该行(不要忘记require(data.table)某处)以大大加快速度:

index = data.table(index)[, index := if(.N > 1) 0 else index, by = index][, index]
于 2013-08-16T22:31:37.593 回答