Как ускорить и / или распараллелить обновление выборочных значений в ОЧЕНЬ большом массиве Mathematica 2D? - PullRequest
3 голосов
/ 07 января 2012

У меня есть массив в форме {{int, int, real, ..., string, real, ...}, ...} с размерами приблизительно 1 000 000 x 400.

Моя цельминимизировать время, необходимое для обновления большого числа выборочных значений в этом массиве.

Если бы значения были смежными, я мог бы сделать что-то вроде

arr[[...]] = ParallelMap[ updateFunc,arr[[...]] ]

, но Part[]не принимает выборочные значения, как, скажем, Extract[] can.Так что arr[[{{1,2},{5,7},...}]] не вариант (он делает что-то совсем другое), и обновление Extract не помещает значения обратно в массив.Поверьте, вопреки здравому смыслу, я попробовал: Set::write: "Tag Extract in Extract[{1,2,3,4,5},{{1},{3},{5}}] is Protected.".

Я попробовал SetSharedVariable[arr], а затем использовал ParallelMap для отдельных обновлений, но святая корова использует общие переменные, требующие много времени!

Я, наконец, остановился на самом быстром методе, который я нашел, а именно:

arr=ParallelTable[updateFunc[row],{row,arr}];

Он все еще мучительно медленный, и я знаю, что есть лучший способ, чем (a) ретушировать каждое значение, (b) создать в памяти новую временную таблицу.

Помогите, пожалуйста!

Ответы [ 4 ]

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

Самый быстрый способ сделать это, о котором я мог подумать, - это предварительно обработать список позиций, чтобы сгруппировать позиции в одном столбце, а затем обновить столбец за столбцом с помощью Part.При этом используется тот факт, что ваш массив прямоугольный (не рваный).Вот код:

ClearAll[updateByColumn];
SetAttributes[updateByColumn, HoldFirst];
updateByColumn[l_, positions_, updateFunc_, updateFuncListable : (True | False) : False] :=
  MapThread[
    (l[[##]] = If[updateFuncListable, updateFunc@l[[##]], updateFunc /@ l[[##]]]) &,
    {#[[All, 1, 1]], #[[All, All, 2]]} &@GatherBy[positions, First]];

РЕДАКТИРОВАТЬ

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

END EDIT

ВотНебольшой тестовый пример, чтобы увидеть, как это работает:

randomString[] := FromCharacterCode@RandomInteger[{97, 122}, 5];

In[131]:= 
len = 10;
poslen = 10;
n = 1;
m = 1;
tst = 
  Table[{
     Sequence @@ RandomInteger[10000, n],
     Sequence @@ Table[randomString[], {m}],
     Sequence @@ RandomReal[10000, n]}, {len}
]
testPositions  = 
  Table[{RandomInteger[{1, Length[tst]}],RandomInteger[{1, Length@First@tst}]}, 
     {len}]

Out[135]= {{320, "iwuwy", 3082.4}, {3108, "utuwf", 4339.14}, {5799, "dzjht", 8650.81}, 
{3177, "biyyl", 6239.64}, {7772, "bfawf",  6704.02}, {1679, "lrbro", 1873.57}, 
{9866, "gtprg", 4157.83}, {9720, "mtdnx", 4379.48}, {5399, "oxlhh", 2734.21}, 
{4409, "dbnlx",  955.428}}

Out[136]= {{1, 2}, {4, 1}, {3, 2}, {7, 2}, {8, 1}, {5, 2}, {2, 2},
{7, 2}, {2, 2}, {6, 2}}

Здесь мы вызываем функцию:

In[137]:= 
updateByColumn[tst, testPositions, f];
tst

Out[138]= {{320, f["iwuwy"], 3082.4}, {3108, f["utuwf"], 4339.14}, 
{5799, f["dzjht"], 8650.81}, {f[3177], "biyyl" 6239.64}, {7772, f["bfawf"], 6704.02},
{1679, f["lrbro"], 1873.57}, {9866, f["gtprg"], 4157.83}, {f[9720], "mtdnx", 4379.48}, 
{5399, "oxlhh", 2734.21}, {4409, "dbnlx", 955.428}}

Обратите внимание, что, поскольку функция HoldFirst, исходный массив изменяется,что позволяет нам сэкономить на памяти, которая была бы необходима для копии.

Теперь генерируем большую выборку с тем же кодом, что и выше, но с такими значениями параметров: len = 100000; poslen = 50000; n = 100; m = 100;, вызов updateByColumn[tst,testPositions, f]; работает за 0,15 с.на моей машине, и это без распараллеливания.Если ваша функция обновления updateFunc имеет значение Listable и она работает намного быстрее, вы можете установить необязательный третий параметр на True, чтобы сделать его потенциально более быстрым.

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

tstPacked = Table[0, {Length[tst]}];
Do[tstPacked [[i]] = Developer`ToPackedArray[tst[[All, i]]], {i, Length@First@tst}]; 

Если, например, вы произвели tst с приведенным выше кодом и параметрами len = 100000;poslen = 50000;n = 100;m = 10;, применение ByteCount даст 700800040 байт для массива tst, но только 182028872 байт для tstPacked (обратите внимание, что попытка Transpose, затем Map Developer`ToPackedArray, а затем Transpose снова не удастся, так как второй Transpose распакует всеколонны).Также обратите внимание, что столбцы останутся упакованными, только если ваша функция updateFunc выдает значения тех же типов, что и исходные элементы столбца, для каждого типа столбца.

Кроме того, вы, вероятно, можете изменить MapThread на некоторый код, используя, скажем, ParallelMap, чтобы использовать параллельные возможности.

Я немного беспокоюсь о ваших описанных измерениях полного массива.Ваш полный массив может не уместиться в памяти - но я думаю, это еще одна проблема.

1 голос
/ 07 января 2012

будет проверять завтра для получения дополнительной информации от вас, но если у вас есть способ определить, какие позиции вы хотите «обновить», то как насчет

(arr[[#]] = updateFunc[arr[[#]]]) & /@ positions

и

ParallelMap[(arr[[#]] = updateFunc[arr[[#]]]) &, positions]

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

0 голосов
/ 13 января 2012

Вы можете найти полезность в этой конструкции:

update = ReplacePart[#, Thread[#2 -> #3 /@ Extract[#, #2]]] &;

Использование:

table = Array[Times, {7, 7}];

parts = {{5, 1}, {7, 7}, {5, 2}, {4, 6}, {2, 3}, {4, 7}};

update[table, parts, Framed] // Grid

Mathematica graphics

0 голосов
/ 07 января 2012

Если я не понял вас, может быть, вы можете попробовать ReplacePart

(*make data once *)
$mat = Table[Random[], {3}, {3}]

, что

{{0.295376, 0.362912, 0.945531}, 
 {0.191438, 0.175706, 0.469595}, 
 {0.734491, 0.328592, 0.856225}}

Сначала я использую Карту для отображения ReplacePart, чтобы заменить некоторые части матрицы на ноль

mat = $mat;
pos = Position[mat, x_ /; x < .5]
(*---> {{1, 1}, {1, 2}, {2, 1}, {2, 2}, {2, 3}, {3, 2}, {3, 3}} *)

теперь используйте карту

mat = Map[ReplacePart[#[[1]], #[[2]]] &, {{mat, pos -> 0.}}];
mat

дает

{{0., 0., 0.945531}, {0., 0., 0.}, {0.734491, 0., 0.856225}}

теперь сделайте то же самое, используя ParallelMap

mat = $mat;
mat = ParallelMap[ReplacePart[#[[1]], #[[2]]] &, {{mat, pos -> 0.}}];
mat

и это дает тот же результат

{{{0., 0., 0.945531}, {0., 0., 0.}, {0.734491, 0., 0.856225}}}

редактирование (1)

Ну, я попробовал это:

сначала с помощью карты

$mat = {{0.295376, 0.362912, 0.945531}, {0.191438, 0.175706, 
    0.469595}, {0.734491, 0.328592, 0.856225}};
mat = $mat;
pos = Position[mat, x_ /; x < .5];
Map[(mat = ReplacePart[mat, # -> 0]) &, pos];
mat

дает

{{0, 0, 0.945531}, {0, 0, 0}, {0.734491, 0, 0.856225}}

Но когда я использую ParallelMap, по какой-то причине он не обновляет матрицу:

mat = $mat;
ParallelMap[(mat = ReplacePart[mat, # -> 0]) &, {{1, 1}}];
mat

тот же коврик, что и раньше. Сейчас я не уверен, почему, если я смогу это выяснить, обновлю, потому что это лучшее, что у меня есть на данный момент. удачи

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