wolfram-mathematica – 用数学导入大文件/数组 - 代码日志

wolfram-mathematica – 用数学导入大文件/数组

我在Windows7 32位平台上与数学8.0.1.0一起工作。我尝试导入数据

Import[file,”Table”]

只要文件(文件中的数组)足够小就可以正常工作。但是对于较大的文件(38MB)/数组(9429次2052),我收到消息:

No more memory available. Mathematica kernel has shut down. Try quitting other applications and then retry.

在我拥有更多主内存的Windows7 64位平台上,我可以导入更大的文件,但是我认为当文件增长/数组有更多行时,我将会遇到同样的问题。

所以,我尝试找到导入大文件的解决方案。搜索一段时间后,我在这里看到一个类似的问题:Way to deal with large data files in Wolfram Mathematica
但是,似乎我的数学知识不够好,以适应OpenRead,ReadList或类似于我的数据(参见here示例文件)。
问题是我需要在文件中的其余数组的程序信息,如维,一些列和行中的最大/最小,我正在对某些列和每一行进行操作。
 但是当我使用例如ReadList,我从来没有得到与Import一样的数组的相同信息(可能是因为我以错误的方式进行)。

有人可以给我一些建议吗?我会感谢每一个支持!

由于某些原因,目前对类型表(表格数据)的导入执行效率相当高。下面我试图纠正这种情况,同时仍然重用数学的高级别导入功能(通过ImportString)。对于稀疏表,提供了一个单独的解决方案,这可以导致非常显着的内存节省。

一般记忆效率的解决方案

这是一个更加高效的记忆功能:

Clear[readTable];
readTable[file_String?FileExistsQ, chunkSize_: 100] :=
   Module[{str, stream, dataChunk, result , linkedList, add},
      SetAttributes[linkedList, HoldAllComplete];
      add[ll_, value_] := linkedList[ll, value];           
      stream  = StringToStream[Import[file, "String"]];
      Internal`WithLocalSettings[
         Null,
         (* main code *)
         result = linkedList[];
         While[dataChunk =!= {},
           dataChunk = 
              ImportString[
                 StringJoin[Riffle[ReadList[stream, "String", chunkSize], "\n"]], 
                 "Table"];
           result = add[result, dataChunk];
         ];
         result = Flatten[result, Infinity, linkedList],
         (* clean-up *)
         Close[stream]
      ];
      Join @@ result]

这里我用标准的Import来代替你的文件:

In[3]:= used = MaxMemoryUsed[]
Out[3]= 18009752

In[4]:= 
tt = readTable["C:\\Users\\Archie\\Downloads\\ExampleFile\\ExampleFile.txt"];//Timing
Out[4]= {34.367,Null}

In[5]:= used = MaxMemoryUsed[]-used
Out[5]= 228975672

In[6]:= 
t = Import["C:\\Users\\Archie\\Downloads\\ExampleFile\\ExampleFile.txt","Table"];//Timing
Out[6]= {25.615,Null}

In[7]:= used = MaxMemoryUsed[]-used
Out[7]= 2187743192

In[8]:= tt===t
Out[8]= True

您可以看到,我的代码比导入的内存效率高出10倍,而速度不会太慢。您可以通过调整chunkSize参数来控制内存消耗。您生成的表占用大约150 – 200 MB的RAM。

编辑

为稀疏表获得更高效

我想说明如何使这个功能在导入期间内存效率高出2-3倍,再加上使用SparseArray-s在表格占用的最终内存方面的内存效率更高一个数量级。我们获得记忆效率增益的程度在很大程度上取决于你的表的稀疏程度。在你的例子中,表很稀疏。

稀疏阵列的解剖学

我们从一个普遍有用的API开始构建和解构SparseArray对象:

ClearAll[spart, getIC, getJR, getSparseData, getDefaultElement, makeSparseArray];
HoldPattern[spart[SparseArray[s___], p_]] := {s}[[p]];
getIC[s_SparseArray] := spart[s, 4][[2, 1]];
getJR[s_SparseArray] := Flatten@spart[s, 4][[2, 2]];
getSparseData[s_SparseArray] := spart[s, 4][[3]];
getDefaultElement[s_SparseArray] := spart[s, 3];
makeSparseArray[dims : {_, _}, jc : {__Integer}, ir : {__Integer}, 
     data_List, defElem_: 0] :=
 SparseArray @@ {Automatic, dims, defElem, {1, {jc, List /@ ir}, data}};

一些简短的评论是有序的。这里是一个稀疏数组示例:

In[15]:= 
ToHeldExpression@ToString@FullForm[sp  = SparseArray[{{0,0,1,0,2},{3,0,0,0,4},{0,5,0,6,7}}]]

Out[15]= 
Hold[SparseArray[Automatic,{3,5},0,{1,{{0,2,4,7},{{3},{5},{1},{5},{2},{4},{5}}},
{1,2,3,4,5,6,7}}]]

(我使用ToString – ToHeldExpression循环将FullForm中的List […]等转换回{…}以便阅读)。在这里,{3,5}显然是维度。接下来是0,默认元素。接下来是一个嵌套列表,我们可以将其表示为{1,{ic,jr},sparseData}。这里,当我们添加行时,ic给出非零元素的总数 – 所以它是第一个0,然后是第二行之后的第二行,第二个添加2个,最后一个添加3个。下一个列表jr给出了所有行中非零元素的位置,因此第一行为3和5,第二行为1和5,最后一个为2,4和5。对于哪一行在这里开始和结束的位置没有任何混淆,因为这可以由ic列表确定。最后,我们有sparseData,它是从左到右依次读取非零元素的列表(排序与jr列表相同)。这解释了SparseArray存储其元素的内部格式,并希望澄清上述功能的作用。

代码

Clear[readSparseTable];
readSparseTable[file_String?FileExistsQ, chunkSize_: 100] :=
   Module[{stream, dataChunk, start, ic = {}, jr = {}, sparseData = {}, 
        getDataChunkCode, dims},
     stream  = StringToStream[Import[file, "String"]];
     getDataChunkCode := 
       If[# === {}, {}, SparseArray[#]] &@
         ImportString[
             StringJoin[Riffle[ReadList[stream, "String", chunkSize], "\n"]], 
             "Table"];
     Internal`WithLocalSettings[
        Null,
        (* main code *)
        start = getDataChunkCode;
        ic = getIC[start];
        jr = getJR[start];
        sparseData = getSparseData[start];
        dims = Dimensions[start];
        While[True,
           dataChunk = getDataChunkCode;
           If[dataChunk === {}, Break[]];
           ic = Join[ic, Rest@getIC[dataChunk] + Last@ic];
           jr = Join[jr, getJR[dataChunk]];
           sparseData = Join[sparseData, getSparseData[dataChunk]];
           dims[[1]] += First[Dimensions[dataChunk]];
        ],
        (* clean - up *)
        Close[stream]
     ];
     makeSparseArray[dims, ic, jr, sparseData]]

基准和比较

这里是使用内存的开始量(新鲜内核):

In[10]:= used = MemoryInUse[]
Out[10]= 17910208

我们称之为我们的功能:

In[11]:= 
(tsparse= readSparseTable["C:\\Users\\Archie\\Downloads\\ExampleFile\\ExampleFile.txt"]);//Timing
Out[11]= {39.874,Null}

那么与readTable的速度是一样的。内存使用情况如何?

In[12]:= used = MaxMemoryUsed[]-used
Out[12]= 80863296

我认为,这是非常显着的:我们只使用了两倍于磁盘占用文件的内存。但是,更显着的是,最终的内存使用(计算完成后)已经大大减少:

In[13]:= MemoryInUse[]
Out[13]= 26924456

这是因为我们使用SparseArray:

In[15]:= {tsparse,ByteCount[tsparse]}
Out[15]= {SparseArray[<326766>,{9429,2052}],12103816}

所以我们的表只需要12 MB的RAM。我们可以将其与我们更一般的功能进行比较:

In[18]:= 
(t = readTable["C:\\Users\\Archie\\Downloads\\ExampleFile\\ExampleFile.txt"]);//Timing
Out[18]= {38.516,Null}

一旦我们将稀疏表转换回正常,结果是一样的:

In[20]:= Normal@tsparse==t
Out[20]= True

而正常表占用的空间更大(似乎ByteCount占用了大约3-4次的占用内存,但实际的差异至少仍然是数量级):

In[21]:= ByteCount[t]
Out[21]= 619900248
http://stackoverflow.com/questions/7525782/import-big-files-arrays-with-mathematica

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:wolfram-mathematica – 用数学导入大文件/数组