Как ускорить замену матричных элементов Mathematica

У меня есть несколько матриц 100x15; один из них - это расстояние. Когда элементы этой матрицы превышают границу, я хочу, чтобы reset эти элементы равны нулю, а также reset соответствующие элементы трех других матриц равны нулю. Вот мой глупый способ (но он работает):

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  

Ответ 1

Как насчет этого:

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

Если вы хотите написать быстрый код, нужно убедиться, что вкл. [ "Упаковка" ] не дает сообщений; или, по крайней мере, что вы их понимаете и знаете, что это не проблема.

Изменить комментарий к OP:

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

Надеемся, что это поможет, вам все равно нужно установить лимит.

Ответ 2

Следующее будет основано на 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 = [email protected]},
     copy[[flatZeroPositions]] = If[type === Integer, 0, 0.];
     Partition[copy, Last[Dimensions[mat]]]];

getFlatZeroDistancePositions[distanceMat_, lim_] :=
  With[{flat = Flatten[distanceMat]},
     With[{originalZPos = [email protected] positionExtr[flat , 0]},
       If[originalZPos  === {}, #, Complement[#, originalZPos ]] &@
         [email protected][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}

Обратите внимание, что в процессе не было распаковки, но вы должны убедиться, что у вас есть свои матрицы, упакованные с самого начала, и что вы выбираете правильный тип (Integer или Real) для функции replaceWithZero.

Ответ 3

Еще один метод, который кажется быстрым

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

Основываясь на ограниченном тестировании в настройке Nasser, кажется, что он работает так же быстро, как маска SparseArray.

Изменить: может сочетаться с SparseArray, чтобы получить небольшую скорость

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

Edit 2: Вдохновленный решением ruebenko, еще одна встроенная функция (не так быстро, как UnitStep, но намного быстрее, чем другие):

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

Ответ 4

может быть

(*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) Я использовал размер матрицы 2000 на 2000.

Таким образом, приведенные ниже номера синхронизации не включают распределение данных.

Я запускаю тесты один раз.

Вот что я вижу:

Для 2000 на 2000 матриц:

  • Билл (цикл): 16 секунд
  • me (ReplacPart): 21 секунд
  • Brett (SparseArray): 7.27 секунд
  • Verbeia (MapThread): 32 секунды

Для 3000 на 3000 матриц:

  • Билл (цикл): 37 секунд
  • me (ReplacPart): 48 секунд
  • Бретт (SparseArray): 16 секунд
  • Verbeia (MapThread): 79 секунд

Итак, кажется, что 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, например, у Билла был R, и я тоже получаю гораздо более низкие тайминги. Я надеюсь, что эксперт может придумать гораздо более быстрый метод, потому что теперь я не слишком доволен этим.

Для 3000 на 3000 матриц я получаю

Elapsed time is 0.607026 seconds.

Это более чем в 20 раз быстрее, чем метод SparseArray, и это всего лишь цикл!

%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() дает аналогичные значения. tic/toc.

Ответ 5

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}

Ответ 6

Этот подход работает для вас?

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

Основная идея состоит в том, чтобы создать матрицу, которая равна нулю, где пересечен порог, и везде. Затем мы используем элементное умножение для нулевого выделения соответствующих элементов в различных матрицах.