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

Wolfram mathematica 转换嵌套列表而不复制或丢失精度,wolfram-mathematica,Wolfram Mathematica,我正在使用Mathematica 7处理一个大型数据集。数据集是有符号整数的三维数组。这三个级别可能被认为是对应于每个快照X点、每个扫描Y点和每个集合Z点扫描 我还有一个“归零”快照(包含X个点,它们是整数的有符号分数),我想从数据集中的每个快照中减去它。之后,我将不再需要原始数据集 如何在不创建数据集或其部分的新副本的情况下执行此转换?从概念上讲,数据集位于内存中,我希望扫描每个元素,并在内存中的该位置对其进行更改,而无需将其永久复制到其他内存位置 以下自包含的代码捕获了我尝试执行的操作的所有

我正在使用Mathematica 7处理一个大型数据集。数据集是有符号整数的三维数组。这三个级别可能被认为是对应于每个快照X点、每个扫描Y点和每个集合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的帽尖为。)

在我的机器上(Intel Core2 Duo CPU 3.17 GHz,4 GB RAM,32位Windows 7),这三种方法都使用大约1.25 GB的内存,2和3的光顺效果稍好一些


如果我不介意丢失精度,那么在创建
myData
myZero
时,将
N[]
的内部包装起来,最初会将它们的内存大小增加150 MB,但会将归零所需的内存量(通过上面的方法#1-#3)从1.25 GB减少到300 MB!这是我的有效解决方案,但如果知道处理这个问题的最佳方法,那就太好了。

不幸的是,我现在时间不多了,所以我必须简明扼要

在处理大数据时,您需要注意Mathematica有一种不同的存储格式,称为压缩数组,它比常规数组更紧凑、速度更快,但只适用于机器实数或整数

请评估
?开发人员`*packeted*
,看看有哪些函数可以直接转换成或转换成它们,如果这不是自动发生的

因此,我的解决方案之所以快速高效,原因在于它使用了压缩阵列。我使用
Developer`PackedArrayQ
测试了我的数组永远不会被解包,并且我使用了machine reals(我将
N[]
应用于所有东西)


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

首先让我指出,这个答案必须被@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方法中,原始列表在
myData
被分配
Map
的结果后被垃圾收集,因此最终内存使用量是相同的,但是在计算过程中,
Map
会维护一个额外的
myData
大小的数组)


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

似乎不是myData中的前缀
N@
显著减少了映射所需的内存。在长列表的限制下,映射浮点数和分数真的会显著提高内存效率吗?@higgy情况是
myZero
不会打包为有理数。因此映射速度快或慢并不重要er本身,但如果不转换为实数,
myZero
将被解包。打包仅适用于头
Integer
Real
Complex
为什么我在[2]中没有看到
的任何时间差异
以上,当使用
ToPackedArray@
与根本不使用它相比?我在Windows上使用的是V8.04。感谢您指出我的解决方案中的转换不到位。我在发布答案后意识到了这一点,但我没有时间回来详述它。@Szabolcs我会在评论中这样做,但我无法压缩我所看到的所有内容我想说一两句。此外,无论如何,你的解决方案对这个问题来说比原地解决方案要好。如果你喜欢,请相应地编辑你的答案。我正试图尽可能精确(而Mathematica并没有让这成为一项容易的任务),冒着学究气和烦人的风险,因为新用户很容易感到困惑,并让我自己保持清醒的头脑(后者是不确定的)
In[5]:= 
Do[myData[[All,All,i]]=myData[[All,All,i]]- myZero[[i]],
     {i,Last@Dimensions@myData}];//Timing

Out[5]= {4.734,Null}