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