如何加速Mathematica替换矩阵元素

时间:2012-01-04 04:03:21

标签: wolfram-mathematica

我有几个100x15矩阵;其中一个是距离。当该矩阵的元素超过一个边界时,我想将这些元素重置为零,并将其他三个矩阵的相应元素重置为零。这是我的愚蠢方式(但它有效):

Do[ If[ xnow[[i, j]] > L, xnow[[i, j]] = 0.;
                  cellactvA[[i, j ]]  = 0.;
                  cellactvB[[i, j ]]  = 0.;
                  cellactvC[[i, j ]]  = 0.;   ], (* endIF  *)
   { i, 1, nstrips}, {j, 1, ncells}       ];  (* endDO *)

我尝试了ReplacePart

 xnow = ReplacePart[ xnow, Position[ xnow, x_?(# > L &) ] ]

(类似这样的东西,我没有得心应用;它已经足够正确地执行了),但是它和循环一样慢并且没有在矩阵xnow中产生正确的替换结构。请告知如何以相当快的方式执行此操作,因为此计算是在执行多次的另一个循环(随着时间的推移)内。当然,整体计算现在非常缓慢。提前谢谢。


以下是我在R中的表现;非常简单快捷:

    # -- find indices of cells outside window
indxoutRW  <- which( xnow > L, arr.ind=T )

    # -- reset cells outside window
cellrateA[indxoutRW] <- 0 
cellrateB[indxoutRW] <- 0 
cellrateC[indxoutRW] <- 0 

    # -- move reset cells back to left side
 xnow[indxoutRW]    <- xnow[indxoutRW] - L  

6 个答案:

答案 0 :(得分:11)

这个怎么样:

Timing[
 matrixMask2 = UnitStep[limit - $xnow];
 xnow = $xnow*matrixMask2;
 cellactvA2 = $a*matrixMask2;
 cellactvB2 = $b*matrixMask2;
 cellactvC2 = $c*matrixMask2;
 ]

如果你想编写快速代码一件事,以确保检查On [“Packing”]没有给出消息;或者至少你理解它们并且知道它们不是问题。

编辑OP评论:

mask = UnitStep[limit - xnow];
{xnow*mask, cellactvA2*mask, cellactvB2*mask, cellactvC2*mask}

希望这有帮助,你仍然需要设置限制。

答案 1 :(得分:8)

以下将基于SparseArrays,避免无关紧要的东西,速度非常快:

extractPositionFromSparseArray[
   HoldPattern[SparseArray[u___]]] := {u}[[4, 2, 2]];
positionExtr[x_List, n_] := 
   extractPositionFromSparseArray[
     SparseArray[Unitize[x - n], Automatic, 1]]

replaceWithZero[mat_, flatZeroPositions_List, type : (Integer | Real) : Real] :=
  Module[{copy = Flatten@mat},
     copy[[flatZeroPositions]] = If[type === Integer, 0, 0.];
     Partition[copy, Last[Dimensions[mat]]]];

getFlatZeroDistancePositions[distanceMat_, lim_] :=
  With[{flat = Flatten[distanceMat]},
     With[{originalZPos = Flatten@ positionExtr[flat , 0]},
       If[originalZPos  === {}, #, Complement[#, originalZPos ]] &@
         Flatten@positionExtr[Clip[flat , {0, lim}, {0, 0}], 0]]];

现在,我们生成矩阵,确保它们已被打包:

{xnow, cellactvA, cellactvB, cellactvC} = 
   Developer`ToPackedArray /@ RandomReal[10, {4, 100, 15}];

这是1000次这样做的基准:

In[78]:= 
Do[
  With[{L = 5},
    With[{flatzpos = getFlatZeroDistancePositions[xnow,L]},
       Map[replaceWithZero[#,flatzpos ]&,{xnow,cellactvA,cellactvB,cellactvC}]]
  ],
  {1000}]//Timing

Out[78]= {0.203,Null}

请注意,此过程中没有解压缩,但您必须确保从一开始就打包矩阵,并为replaceWithZero函数选择正确的类型(整数或实数)。 / p>

答案 2 :(得分:3)

另一种似乎很快的方法

xnow = $xnow; a = $a; b = $b; c = $c;
umask = Unitize@Map[If[# > limit, 0, #] &, xnow, {2}];
xnow = xnow*umask; a = a*umask; b = b*umask; c = c*umask;

基于纳赛尔设置中的有限测试,它似乎与基于SparseArray的面具一样快。

编辑:可与SparseArray结合使用以获得轻微的加速

umask2=SparseArray[Unitize@Map[If[# > limit, 0, #] &, xnow, {2}]];
xnow = xnow*umask2; a = a*umask2; b = b*umask2; c = c*umask2;

编辑2:灵感来自ruebenko的解决方案,另一个内置函数(不如UnitStep快,但比其他函数快得多):

umask3 = Clip[xnow, {limit, limit}, {1, 0}];
xnow = xnow*umask3; a = a*umask3; b = b*umask3; c = c*umask3;

答案 3 :(得分:2)

可能是

(*data*)
nRow = 5; nCol = 5;
With[{$nRow = nRow, $nCol = nCol},
  xnow = Table[RandomReal[{1, 3}], {$nRow}, {$nCol}];
  cellactvA = cellactvB = cellactvC = Table[Random[], {$nRow}, {$nCol}]
  ];
limit = 2.0;

现在进行更换

pos = Position[xnow, x_ /; x > limit]; 

{cellactvA, cellactvB, cellactvC} = 
  Map[ReplacePart[#, pos -> 0.] &, {cellactvA, cellactvB, cellactvC}];

修改(1)

这是一个快速的速度比较上面的4种方法,LOOP,然后是Brett,me和Verbeia。也许有人可以仔细检查一下。我为所有人使用了相同的数据。创建一次随机数据,然后将其用于每个测试。相同的限制(称为L)我使用的矩阵大小为2,000乘2,000。

所以下面的速度计时数字不包括数据分配。

我运行一次测试。

这就是我所看到的:

2,000 x 2,000个矩阵:

  1. Bill(循环):16秒
  2. me(ReplacPart):21秒
  3. Brett(SparseArray):7.27秒
  4. Verbeia(MapThread):32秒
  5. 3,000 x 3,000个矩阵:

    1. 比尔(循环):37秒
    2. me(ReplacPart):48秒
    3. Brett(SparseArray):16秒
    4. Verbeia(MapThread):79秒
    5. 所以,似乎SparseArray是最快的。 (但请检查以确保我没有破坏某些东西)

      以下代码:

      数据生成

      (*data*)
      nRow = 2000;
      nCol = 2000;
      
      With[{$nRow = nRow, $nCol = nCol},
        $xnow = Table[RandomReal[{1, 3}], {$nRow}, {$nCol}];
        $a = $b = $c = Table[Random[], {$nRow}, {$nCol}]
        ];
      
      limit = 2.0;
      

      ReplacePart测试

      xnow = $xnow;
      a = $a;
      b = $b;
      c = $c;
      
      Timing[
        pos = Position[xnow, x_ /; x > limit];
        {xnow, a, b, c} = Map[ReplacePart[#, pos -> 0.] &, {xnow, a, b, c}]][[1]]
      

      SparseArray测试

      xnow = $xnow;
      a = $a;
      b = $b;
      c = $c;
      Timing[
        matrixMask = 
         SparseArray[Thread[Position[xnow, _?(# > limit &)] -> 0.], 
          Dimensions[xnow], 1.]; xnow = xnow*matrixMask;
        a = a*matrixMask;
        b = b*matrixMask;
        c = c*matrixMask
        ][[1]]
      

      MapThread测试

      xnow = $xnow;
      a = $a;
      b = $b;
      c = $c;
      Timing[
        {xnow, a, b, c} = 
         MapThread[Function[{x, y}, If[x > limit, 0, y]], {xnow, #}, 
            2] & /@ {xnow, a, b, c}
        ][[1]]
      

      循环测试

      xnow = $xnow;
      a = $a;
      b = $b;
      c = $c;
      Timing[
        Do[If[xnow[[i, j]] > limit,
          xnow[[i, j]] = 0.;
          a[[i, j]] = 0.;
          b[[i, j]] = 0.;
          c[[i, j]] = 0.
          ],
         {i, 1, nRow}, {j, 1, nCol}
         ]
        ][[1]]
      

      修改(2)

      所有这一切都让我感到非常困扰。我不明白为什么专门的命令可以更快地循环?

      我在Matlab中编写了一个简单的循环测试,就像Bill使用R一样,而且我的时序也低得多。我希望专家可以提出一个更快的方法,因为现在我对此并不满意。

      对于3,000乘3,000的矩阵,我正在

      Elapsed time is 0.607026 seconds.
      

      这比SparseArray方法快20多倍,它只是一个循环!

      %test, on same machine, 4GB ram, timing uses cpu timing using tic/toc
      %allocate data
      nRow = 3000;
      nCol = 3000;
      
      %generate a random matrix of real values
      %between 1 and 3
      xnow = 1 + (3-1).*rand(nRow,nRow);
      
      %allocate the other 3 matrices
      a=zeros(nRow,nCol);
      b=a;
      c=b;
      
      %set limit
      limit=2;
      
      %engine
      tstart=tic;
      
      for i=1:nRow
          for j=1:nCol
              if xnow(i,j) > limit
                  xnow(i,j) = 0;
                  a(i,j) = 0;
                  b(i,j) = 0;
                  c(i,j) = 0;
              end
          end
      end
      toc(tstart)
      

      fyi:使用cputime()给出类似的值.as tic / toc。

答案 4 :(得分:2)

ReplacePart出了名的慢。

MapThread应该做你想做的事 - 注意第三个论点。

{xnow, cellactvA, cellactvB, cellactvC} = 
  RandomReal[{0, 1}, {4, 10, 5}]
L = 0.6;
MapThread[If[#1 > L, 0, #2] &, {xnow, xnow}, 2]

对于所有四个矩阵

{xnow, cellactvA, cellactvB, cellactvC} =
 MapThread[Function[{x, y}, If[x > L, 0, y]], {xnow, #}, 
  2] & /@ {xnow, cellactvA, cellactvB, cellactvC}

答案 5 :(得分:2)

这种方法对你有用吗?

matrixMask = 
 SparseArray[Thread[Position[xnow, _?(# > 0.75 &)] -> 0.], 
  Dimensions[xnow], 1.]; 
xnow = xnow * matrixMask;
cellactvA = cellactvA * matrixMask;
cellactvB = cellactvB * matrixMask;
cellactvC = cellactvC * matrixMask;

基本思想是创建一个矩阵,在超过阈值时为零,在其他任何地方创建一个矩阵。然后我们使用逐元素乘法将各种矩阵中的相应元素归零。