Как ускорить замену Mathematica матричных элементов - PullRequest
10 голосов
/ 04 января 2012

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

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.Посоветуйте, пожалуйста, как сделать это достаточно быстрым способом, так как этот calc находится внутри другого цикла (со временем), который выполняется много-много раз.Общий расчет, конечно, сейчас очень медленный.Заранее спасибо.


Вот как я это сделал в 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 ]

11 голосов
/ 04 января 2012

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

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}

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

8 голосов
/ 04 января 2012

Следующее будет основано на 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}

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

3 голосов
/ 04 января 2012

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

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: Вдохновленная решением Руебенко, еще одна встроенная функция (не так быстро, как UnitStep, но намного быстрее, чем другие):

umask3 = Clip[xnow, {limit, limit}, {1, 0}];
xnow = xnow*umask3; a = a*umask3; b = b*umask3; c = c*umask3;
2 голосов
/ 04 января 2012

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

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

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

2 голосов
/ 04 января 2012

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}
2 голосов
/ 04 января 2012

может быть

(*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, а затем Бретта, меня и Вербейю. Может быть, кто-то может перепроверить их. Я использовал одни и те же данные для всех. создал случайные данные один раз, а затем использовал их для каждого теста. Тот же предел (называемый L) Я использовал размер матрицы 2000 на 2000.

Значения приведенных ниже скоростных временных параметров не включают распределение данных.

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

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

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

  1. Счет (цикл): 16 секунд
  2. я (ReplacPart): 21 секунда
  3. Бретт (SparseArray): 7,27 секунды
  4. Вербея (MapThread): 32 секунды

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

  1. Счет (цикл): 37 секунд
  2. я (ReplacPart): 48 секунд
  3. Бретт (SparseArray): 16 секунд
  4. Вербея (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 test

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...