【问题标题】:r matrix individual shift operations of elementsr 矩阵元素的单个移位操作
【发布时间】:2013-08-16 20:51:32
【问题描述】:

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

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

   [,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. 重复下一列

谢谢,

尼科斯

【问题讨论】:

  • 我不明白第 3 步(如果在目标点有另一个条目,则置零)...
  • 对于第 3 步:A[3,1] 和 A[4,1] 它们都指向 A[7,1],当这种情况发生时取消两者。
  • 假设您这样做。 下一步是什么?我只是想知道在给定输入数据的情况下是否有更简单/更好的方法来实现最终所需的输出?
  • “按该条目的绝对数字向下移动”.. 这是什么意思?

标签: r matrix shift


【解决方案1】:

在我的机器上使用您的示例,这更简洁一些,速度提高了大约 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)

【讨论】:

  • @Peyton。谢谢。我不确定是否有选择,因为操作的顺序在这里很重要。这种算法会极大地受益于 Rcpp 重写。
  • @flodel 感谢您的建议。我想我得看看 Rcpp 包。这个函数是一个更大的程序的一部分,它有一个 4000x200 矩阵并且需要大量时间来计算
  • @user2493820:我刚刚用A &lt;- matrix(sample(-2:2, 4000*200, replace = TRUE), 4000, 200) 进行了测试,耗时 5 秒。这对您的需要来说太长了吗?
  • 该程序运行一个投资组合,但不确定隔离此功能有多容易......我会尝试检查总时间。但 Rcpp 的建议很好。该函数的形式与我实际使用的形式相比有所简化
  • @flodel 计算时间有了很大的改进。主要是由于您从 sapply 到 apply 所做的更改。再次感谢
【解决方案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]

【讨论】:

  • 考虑x &lt;- c(3, 2, 1, 0),也许你会同意FUN 应该是function(x) {if (length(x) %% 2L) tail(x, 1) else 0L}。否则,很好的答案。我花了一段时间才同意它可以被矢量化。
  • @flodel 抱歉,我不明白 - 你能指出 x 的问题是什么吗? (也就是上面的x 应该是indexv)我不明白我为什么要在这里计算长度模2?
  • @flodel 哦,我想我明白你的意思了 - 我认为这归结为我如何理解 OP 写的内容,但我确实明白你的意思 - 我的理解是 - 如果有任何冲突点,放一个零,你的似乎是 - 如果只有顺序冲突,放一个零
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-28
  • 2011-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-25
相关资源
最近更新 更多