使用mathematica导入大文件/数组

par*_*l81 21 wolfram-mathematica

我在Windows7 32位平台上使用mathematica 8.0.1.0.我尝试导入数据

Import[file,”Table”]
Run Code Online (Sandbox Code Playgroud)

只要文件(文件中的数组)足够小,它就可以正常工作.但是对于更大的文件(38MB)/阵列(9429乘2052),我得到的信息是:

No more memory available. Mathematica kernel has shut down. Try quitting other applications and then retry.
Run Code Online (Sandbox Code Playgroud)

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

所以,我试图找到一个导入大文件的解决方案.在搜索了一段时间之后,我在这里看到了一个类似的问题:处理Wolfram Mathematica中的大数据文件的方法.但似乎我的mathematica知识不足以使建议的OpenRead,ReadList或类似的数据适应我的数据(参见这里的示例文件).问题是我需要文件中数组的其余程序信息,例如某些列和行中的Dimensions,Max/Min,我正在对某些列和每一行进行操作.但是当我使用例如ReadList时,我从来没有得到与我使用Import相同的数组信息(可能是因为我以错误的方式进行).

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

Leo*_*rin 34

由于某种原因,Import类型Table(表格数据)的当前实现是非常低效的内存.下面我试图在某种程度上解决这种情况,同时仍然重用Mathematica的高级导入功能(通过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]
Run Code Online (Sandbox Code Playgroud)

在这里,我将面对标准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
Run Code Online (Sandbox Code Playgroud)

你可以看到我的代码大约是内存效率的10倍Import,而速度并不慢.您可以通过调整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}};
Run Code Online (Sandbox Code Playgroud)

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

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}}]]
Run Code Online (Sandbox Code Playgroud)

(我使用ToString- ToHeldExpression循环转换后面List[...]等以方便阅读).这里显然是尺寸.接下来是默认元素.接下来是一个嵌套列表,我们可以将其表示为.这里,在我们添加行时给出非零元素的总数 - 因此它首先是0,然后是第一行之后的2,第二行再增加2,最后添加3.接下来的列表,,给所有行非零元素的位置,所以他们和第一行,并为第二,和,和最后一个.关于哪一行在此处开始和结束的位置没有混淆,因为这可以由列表确定.最后,我们有一个非零元素列表,从左到右逐行读取(排序与列表相同).这解释了-s存储其元素的内部格式,并希望阐明上述函数的作用.FullForm{...}{3,5}0{1,{ic,jr}, sparseData}icjr3515245icsparseDatajrSparseArray

代码

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]]
Run Code Online (Sandbox Code Playgroud)

基准和比较

这是已用内存的起始量(新鲜内核):

In[10]:= used = MemoryInUse[]
Out[10]= 17910208
Run Code Online (Sandbox Code Playgroud)

我们称之为功能:

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

所以,速度与之相同readTable.内存使用情况如何?

In[12]:= used = MaxMemoryUsed[]-used
Out[12]= 80863296
Run Code Online (Sandbox Code Playgroud)

我认为,这非常值得注意:我们只使用了两倍于磁盘占用自身文件的内存.但是,更值得注意的是,最终的内存使用量(计算完成后)已大大减少:

In[13]:= MemoryInUse[]
Out[13]= 26924456
Run Code Online (Sandbox Code Playgroud)

这是因为我们使用SparseArray:

In[15]:= {tsparse,ByteCount[tsparse]}
Out[15]= {SparseArray[<326766>,{9429,2052}],12103816}
Run Code Online (Sandbox Code Playgroud)

所以,我们的表只需要12 MB的RAM.我们可以将它与我们更通用的功能进行比较:

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

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

In[20]:= Normal@tsparse==t
Out[20]= True
Run Code Online (Sandbox Code Playgroud)

而普通表占用的空间要ByteCount大得多(看起来超过占用的内存大约3-4倍,但真正的差异仍然是至少数量级):

In[21]:= ByteCount[t]
Out[21]= 619900248
Run Code Online (Sandbox Code Playgroud)

  • 实际上,`Table`既缓慢又极其缺乏内存效率.对于同类(即每个表条目的相同数据类型)数据,我通常使用`ReadList`.它的速度更快,内存效率也更高 (2认同)
  • `WithLocalSettings`在这里工作,但它在一般使用中有局限性.例如,它不能正确处理`Throw`和`Catch`(例如``Catch [Internal`WithLocalSettings [Print ["start"],Throw [23],Print ["cleanup"]]]`` .清理工作正在进行,但"捕获"中断了.在MMa中处理堆栈展开是一件棘手的事情.请参阅[Mathematica中的可靠清理](http://stackoverflow.com/q/3365794/211232). (2认同)