转换嵌套列表而不复制或丢失精度

时间:2012-01-07 18:57:26

标签: wolfram-mathematica

我正在使用Mathematica 7处理大型数据集。数据集是有符号整数的三维数组。这三个级别可以被认为对应于 X X 点, Y 每个扫描的镜头和< em> Z 每集扫描

我还有一个“归零”镜头(包含 X 点,它们是符号的整数),我想从数据中的每一个镜头中减去组。之后,我将永远不再需要原始数据集。

如何在不创建数据集的新副本或部分数据集的情况下执行此转换?从概念上讲,数据集位于内存中,我想扫描每个元素,并在内存中的该位置进行更改,而不是将其永久复制到其他内存位置。

以下自包含代码捕获了我尝试做的所有方面:

(* Create some offsetted data, and a zero data set. *)
myData = Table[Table[Table[RandomInteger[{1, 100}], {k, 500}], {j, 400}], {i, 200}];
myZero = Table[RandomInteger[{1, 9}]/RandomInteger[{1, 9}] + 50, {i, 500}];

(* Method 1 *)
myData = Table[
   f1 = myData[[i]];
   Table[
     f2 = f1[[j]];
     f2 - myZero, {j, 400}], {i, 200}];

(* Method 2 *)
Do[
 Do[
  myData[[i]][[j]] = myData[[i]][[j]] - myZero, {j, 400}], {i, 200}]

(* Method 3 *)
Attributes[Zeroing] = {HoldFirst};
Zeroing[x_] := Module[{}, 
   Do[
     Do[
       x[[i]][[j]] = x[[i]][[j]] - myZero, {j, Length[x[[1]]]}
       ], {i, Length[x]}
     ]
 ];

(注意:方法#3的帽子提示为Aaron Honecker。)

在我的机器上(Intel Core2 Duo CPU 3.17 GHz,4 GB RAM,32位Windows 7),所有这三种方法都使用大约1.25 GB的内存,#2和#3整流罩稍好一些。

如果我不介意失去精确度,那么N[ ]myData内部的myZero在创建它们时会将它们的内存大小增加150 MB,但会减少将(从上面的#1-#3方法)归零所需的内存量从1.25 GB降低到仅300 MB!这是我的工作解决方案,但知道处理这个问题的最佳方法会很棒。

2 个答案:

答案 0 :(得分:6)

不幸的是我现在没有时间,所以我必须简明扼要......

使用大数据时,您需要注意Mathematica具有不同的存储格式,称为压缩数组,它比常规存储格式更紧凑,更快,但仅适用于机器实数或整数。

请评估?Developer`*Packed*以了解哪些功能可用于直接转换为/来自哪些功能,如果这不是自动发生的话。

因此,为什么我的解决方案快速且内存有效的简要解释是它使用打包数组。我使用Developer`PackedArrayQ进行了测试,我的数组从未得到解压缩,并且我使用了机器实数(我将N[]应用于所有内容)

In[1]:= myData = N@RandomInteger[{1, 100}, {200, 400, 500}];

In[2]:= myZero = 
  Developer`ToPackedArray@
   N@Table[RandomInteger[{1, 9}]/RandomInteger[{1, 9}] + 50, {i, 500}];

In[3]:= myData = Map[# - myZero &, myData, {2}]; // Timing

Out[3]= {1.516, Null}

此外,您要求的操作(“我想扫描每个元素,并在内存中的该位置更改它”)称为映射(请参阅Map[]/@)。

答案 1 :(得分:3)

让我首先指出,这个答案必须被视为对@Szabolcs的补充,后者在我的结论中是更好的选择。虽然@Szabolcs的解决方案可能是最快且最好的整体,但它没有达到原始规范,Map返回原始列表的(修改过的)副本,而不是“扫描每个元素,并在记忆中的那个位置“。此类行为AFAIK仅由Part命令提供。我将使用他的想法(将所有内容转换为压缩数组),以显示对原始列表进行内存中更改的代码:

In[5]:= 
Do[myData[[All,All,i]]=myData[[All,All,i]]- myZero[[i]],
     {i,Last@Dimensions@myData}];//Timing

Out[5]= {4.734,Null}

这在概念上等同于问题中列出的方法3,但运行速度要快得多,因为这是部分矢量化解决方案,只需要一个循环。然而,这仍然比@Szabolcs的解决方案至少慢一个数量级。

理论上,这似乎是一个经典的速度/内存权衡:如果你需要速度并有一些备用内存,@ Szabolcs的解决方案是可行的方法。如果您的内存要求很难,理论上这种较慢的方法会节省中间内存消耗(在@Szabolcs的方法中,原始列表在{之后的垃圾收集后) {1}}被赋予myData的结果,因此最终的内存使用量是相同的,但计算期间,会保留一个额外的Map数组。由myData)。

在实践中,内存消耗似乎并不小,因为在两种情况下,Map变量中由于某种原因保留了列表的额外副本期间(或之后)计算,即使输出被抑制(也可能是这种效果在所有情况下都不会表现出来)。我还不太了解这一点,但我目前的结论是@Szabolcs的方法在中间内存消耗方面与基于就地列表修改的当前内存消耗一样好。因此,他的方法似乎是所有情况下的方法,但我仍然决定将这个答案作为补充。