Importa file / array di grandi dimensioni con mathematica

Lavoro con mathematica 8.0.1.0 su una piattaforma Windows7 a 32 bit. Provo a importare i dati con

Import[file,”Table”] 

che funziona bene finché il file (la matrice nel file) è abbastanza piccolo. Ma per i file più grandi (38MB) / array (9429 volte 2052) ottengo il messaggio:

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

Sulla mia piattaforma Windows 7 64 bit con più memoria principale posso importare file più grandi, ma penso che avrò lo stesso problema un giorno quando il file è cresciuto / l’array ha più righe.

Quindi, cerco di trovare una soluzione per importare file di grandi dimensioni. Dopo aver cercato per un po ‘di tempo, ho visto una domanda simile: un modo per gestire file di dati di grandi dimensioni in Wolfram Mathematica . Ma sembra che la mia conoscenza matematica non sia sufficiente per adattare OpenRead, ReadList o simili ai miei dati (vedi qui il file di esempio). Il problema è che ho bisogno del resto delle informazioni sul programma della matrice nel file, come Dimensioni, Max / Min in alcune colonne e righe, e sto facendo operazioni su alcune colonne e ogni riga. Ma quando sto usando, ad esempio, ReadList, non ottengo mai le stesse informazioni dell’array che ho ottenuto con Import (probabilmente perché lo sto facendo nel modo sbagliato).

Qualcuno potrebbe darmi qualche consiglio? Gradirei ogni supporto!

Per qualche ragione, l’attuale implementazione di Import per il tipo Table (dati tabulari) è abbastanza memoria – inefficiente. Di seguito ho cercato di rimediare in qualche modo a questa situazione, continuando a riutilizzare le capacità di importazione ad alto livello di Mathematica (tramite ImportString ). Per le tabelle sparse viene presentata una soluzione separata, che può portare a risparmi di memoria molto significativi.

Soluzione di memoria generale efficiente

Ecco una funzione molto più efficiente in termini di memoria:

 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] 

Qui lo confronto con l’ Import standard, per il tuo file:

 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 

Puoi vedere che il mio codice è circa 10 volte più efficiente della memoria rispetto Import , pur non essendo molto più lento. È ansible controllare il consumo di memoria regolando il parametro chunkSize . La tabella risultante occupa circa 150 – 200 MB di RAM.

MODIFICARE

Ottenere ancora più efficiente per tabelle sparse

Voglio illustrare come si possa rendere questa funzione ancora 2-3 volte più efficiente in termini di memoria durante l’importazione, oltre a un altro ordine di grandezza più efficiente in termini di memoria in termini di memoria finale occupata dalla tabella, utilizzando SparseArray -s. Il grado in cui otteniamo guadagni di efficienza della memoria dipende molto da quanto sparsa è la tua tabella. Nel tuo esempio, la tabella è molto scarsa.

L’anatomia degli array sparsi

Iniziamo con un’API generalmente utile per la costruzione e la decostruzione di oggetti 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] := [email protected][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}}; 

Alcuni brevi commenti sono in ordine. Ecco un array sparse di esempio:

 In[15]:= [email protected]@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}}]] 

(Ho usato ToStringToHeldExpression per convertire List[...] ecc nel FullForm nuovo in {...} per la facilità di lettura). Qui, {3,5} sono ovviamente dimensioni. Il prossimo è 0 , l’elemento predefinito. Il prossimo è un elenco annidato, che possiamo indicare come {1,{ic,jr}, sparseData} . Qui, ic fornisce un numero totale di elementi diversi da zero mentre aggiungiamo le righe – quindi è il primo 0, quindi 2 dopo la prima riga, il secondo ne aggiunge altri 2 e l’ultimo ne aggiunge altri 3. L’elenco successivo, jr , fornisce posizioni di elementi diversi da zero in tutte le righe, quindi sono 3 e 5 per la prima riga, 1 e 5 per il secondo e 2 , 4 e 5 per l’ultimo. Non c’è confusione su dove inizia e finisce la riga qui, poiché questo può essere determinato dalla lista ic . Infine, abbiamo sparseData , che è una lista di elementi diversi da zero, letti riga per riga da sinistra a destra (l’ordine è lo stesso della lista jr ). Questo spiega il formato interno in cui gli SparseArray memorizzano i loro elementi e si spera che chiarisca il ruolo delle funzioni sopra.

Il codice

 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, [email protected][dataChunk] + [email protected]]; jr = Join[jr, getJR[dataChunk]]; sparseData = Join[sparseData, getSparseData[dataChunk]]; dims[[1]] += First[Dimensions[dataChunk]]; ], (* clean - up *) Close[stream] ]; makeSparseArray[dims, ic, jr, sparseData]] 

Benchmarks e confronti

Ecco la quantità iniziale di memoria utilizzata (kernel nuovo):

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

Chiamiamo la nostra funzione:

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

Quindi, è la stessa velocità di readTable . E l’utilizzo della memoria?

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

Penso che questo sia davvero notevole: abbiamo sempre usato solo il doppio della memoria di quanto il file sul disco occupi da solo. Ma, ancor più notevolmente, l’utilizzo finale della memoria (dopo che il calcolo è terminato) è stato drasticamente ridotto:

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

Questo perché utilizziamo SparseArray :

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

Quindi, la nostra tabella richiede solo 12 MB di RAM. Possiamo confrontarlo con la nostra funzione più generale:

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

I risultati sono gli stessi una volta convertiti alla normale tabella sparsa:

 In[20]:= [email protected]==t Out[20]= True 

mentre la tabella normale occupa molto più spazio (sembra che ByteCount superi la memoria occupata circa 3-4 volte, ma la vera differenza è ancora almeno di ordine di grandezza):

 In[21]:= ByteCount[t] Out[21]= 619900248