Wolfram mathematica 如何在非常大的Mathematica 2D数组中加速和/或并行更新选择值?

Wolfram mathematica 如何在非常大的Mathematica 2D数组中加速和/或并行更新选择值?,wolfram-mathematica,Wolfram Mathematica,我有一个{{int,int,real,…,string,real,…},…}形式的数组,维数约为1000000 x 400 我的目标是尽可能减少更新此数组中大量选择性值所需的时间 如果值是相邻的,我可以做如下操作 arr[[...]] = ParallelMap[ updateFunc,arr[[...]] ] 但是Part[]不接受选择性值,比如说Extract[]可以。因此,arr[[{{1,2},{5,7},…}]不是一个选项(它执行完全不同的操作),更新Extract不会将值放回数组中

我有一个{{int,int,real,…,string,real,…},…}形式的数组,维数约为1000000 x 400

我的目标是尽可能减少更新此数组中大量选择性值所需的时间

如果值是相邻的,我可以做如下操作

arr[[...]] = ParallelMap[ updateFunc,arr[[...]] ]
但是
Part[]
不接受选择性值,比如说
Extract[]
可以。因此,
arr[[{{1,2},{5,7},…}]
不是一个选项(它执行完全不同的操作),更新Extract不会将值放回数组中。相信我,尽管我有更好的判断,我已经尝试过:
Set::write:“Extract[{1,2,3,4,5},{{1},{3},{5}]中的标签Extract受到保护。”

我尝试了
SetSharedVariable[arr]
,然后在各个更新中使用ParallelMap,但是holy cow使用共享变量非常耗时

我终于找到了最快的方法,那就是

arr=ParallelTable[updateFunc[row],{row,arr}];
它仍然非常慢,我知道有一种更好的方法,比(a)润色每个值,(b)在内存中创建一个全新的临时表更好


救命啊

除非我误解了你,否则你可以试试
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}}
我使用Map first映射
ReplacePart
将矩阵的某些部分替换为零

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

现在使用
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
但当我使用
ParallelMap
时,由于某些原因,它不会更新矩阵:

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

和以前一样的垫子。我现在不知道为什么,如果我能弄明白的话,会更新,因为这是我目前最好的。祝你好运

明天会回来查看你的更多信息,但是如果你有办法确定你想“更新”哪些职位,那你呢

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


这假设您的更新取决于以前的值(从您对Nasser答案的评论来看似乎就是这样),并且您知道必须更新的位置。我认为对于这种大小的列表,替换规则会比较慢,因此
部分
似乎更可取。

我能想到的最快的方法是预先处理一个职位列表,将同一列中的职位分组,然后用
部分
逐列更新。这使用了数组是矩形(不是参差不齐)这一事实。代码如下:

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]];
编辑

这假定更新不依赖于以前更新的值。如果是这样的话,我们可以编写一个更详细的代码版本,该版本将考虑到这一点,但可能会稍微慢一些

结束编辑

下面是一个小测试示例,以了解其工作原理:

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
可列出的
,并且这使得更新速度更快,那么您可以将可选的第三个参数设置为
True
,使其可能运行得更快

您可以使用更多技巧来节省时间/内存消耗。例如,如果您知道原始大数组的某些列仅填充了某些可打包的数字类型(整数、实数或复数),则可以将
Developer`ToPackedArray
映射到这些特定列上,以显著减少数组占用的内存。打包阵列的代码为:

tstPacked = Table[0, {Length[tst]}];
Do[tstPacked [[i]] = Developer`ToPackedArray[tst[[All, i]]], {i, Length@First@tst}]; 
例如,如果您使用上述代码和参数
len=100000生成了
tst
;poslen=50000;n=100;m=10
,应用
字节计数
为数组
tst
提供
700800040
字节,但仅为
tst打包的
182028872
字节(请注意,尝试
转置
,然后
映射
Developer`ToPackedArray
,然后再次
转置
将失败,因为第二次
转置
将解压所有列)。还要注意,只有当
updateFunc
函数为每种列类型生成与原始列元素相同类型的值时,列才会保持压缩状态

除此之外,您可能还可以使用say
ParallelMap
MapThread
更改为一些代码,以利用并行功能


我有点担心您描述的完整数组的维度。您的完整数组可能不适合内存-但我想,这是另一个问题。

您可以在这个构造中找到实用程序:

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


一点也不坏,但我如何实际使用被替换位置的旧值来创建新值。例如,在您的示例中,如果值为x…还有一件事…您实际上没有并行运行任何东西吗?您发送`{mat,pos->0.}}`到ParallelMap,它将ReplacePart映射到此列表的1个元素上[List[map,Rule[]]。因此,您没有并行化替换部件,只需启动它一次。请参阅我的原始帖子“为什么ParallelMap不更新”-arr不是一个共享变量。要使更新工作正常,更新应并行创建一个值列表,并将其放在并行之外的所需单元格中,否则它将尝试不更新o将更新后的arr值重新分配给所有内核,这会带来很大的开销。您能提供一些关于“更新”实际含义的更多信息吗?对于这样一个大小的列表,我会尽力弄清楚
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}}
tstPacked = Table[0, {Length[tst]}];
Do[tstPacked [[i]] = Developer`ToPackedArray[tst[[All, i]]], {i, Length@First@tst}]; 
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