Importer de gros fichiers / tableaux avec mathematica

je travaille avec mathematica 8.0.1.0 sur une plateforme Windows7 32bit. J'essaie d'importer des données avec

Import[file,”Table”]

qui fonctionne très bien tant que le fichier (le tableau dans le fichier) est assez petit. Mais pour les plus gros fichiers (38MB) / array (9429 fois 2052) je reçois le message:

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

sur ma plate-forme Windows7 64bit avec plus de mémoire principale, je peux importer des fichiers plus gros, mais je pense que j'aurai là le même problème un jour où le fichier a cultivé/le tableau a plus de lignes.

donc, j'essaie de trouver une solution pour importer de gros fichiers. Après avoir cherché pendant un certain temps, j'ai vu ici une question similaire: façon de traiter les grands fichiers de données dans Wolfram Mathematica . Mais il semble que mes connaissances en mathematica ne soient pas suffisantes pour adapter le fichier OpenRead, ReadList ou similaire à mes données (voir ici le fichier exemple). Le problème est que j'ai besoin pour le reste de mon programme informations du tableau dans le fichier, telles que les Dimensions, Max/Min dans certaines colonnes et lignes, et je fais des opérations sur certaines colonnes et chaque ligne. Mais quand j'utilise par exemple ReadList, Je n'obtiens jamais les mêmes informations du tableau que j'ai obtenu avec Import (probablement parce que je le fais de la mauvaise manière).

quelqu'un peut-il me donner un conseil? Je vous serais reconnaissant de tout le soutien!

20
demandé sur Community 2011-09-23 11:42:03

1 réponses

pour une raison quelconque, la mise en œuvre actuelle de Import pour le type Table (données tabulaires) est tout à fait inefficace mémoire. Ci-dessous, j'ai tenté de remédier quelque peu à cette situation, tout en réutilisant les capacités d'importation de haut niveau de Mathematica (par le biais de ImportString ). Pour les tables clairsemées, une solution séparée est présentée, ce qui peut conduire à des économies de mémoire très importantes.

Général efficace de la mémoire de la solution

Voici une fonction beaucoup plus économe en mémoire:

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]

ici je le confronte avec le standard Import , pour votre dossier:

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

vous pouvez voir que mon code est environ 10 fois plus efficace en mémoire que Import , tout en n'étant pas beaucoup plus lent. Vous pouvez contrôler la consommation de mémoire en ajustant le paramètre chunkSize . Votre table résultante occupe environ 150-200 MB de RAM.

MODIFIER

obtenir encore plus efficace pour les tables clairsemées

je veux illustrer comment on peut rendre cette fonction encore 2-3 fois plus efficace en mémoire pendant l'importation, plus un autre ordre de grandeur plus efficace en mémoire en termes de mémoire finale occupée par votre table, en utilisant SparseArray -S. Le degré auquel nous obtenons des gains d'efficacité de mémoire dépend beaucoup de la façon dont votre table. Dans votre exemple, le tableau est très clairsemée.

L'anatomie de matrices creuses

nous commençons avec une API généralement utile pour la construction et la déconstruction de SparseArray objets:

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}};

quelques brefs commentaires s'imposent. Voici un exemple de tableau clairsemé:

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}}]]

(j'ai utilisé ToString - ToHeldExpression cycle pour convertir List[...] etc dans le FullForm retour à {...} pour la facilité de lecture). Ici, {3,5} sont évidemment des dimensions. Suivant est 0 , l'élément par défaut. Ensuite, une liste imbriquée, que nous pouvons dénoter comme {1,{ic,jr}, sparseData} . Ici, ic donne un nombre total d'éléments non nuls comme nous ajoutons des lignes - il est donc d'abord 0, puis 2 après première ligne, la seconde ajoute 2 de plus, et la dernière Ajoute 3 de plus. La liste suivante, jr , donne les positions des éléments non-zéros dans toutes les lignes, donc ils sont 3 et 5 pour la première ligne, 1 et 5 pour le second, et 2 , 4 et 5 pour le dernier. Il n'y a pas de confusion quant à l'endroit où la rangée commence et se termine ici, puisque cela peut être déterminé par la liste ic . Enfin, nous avons le sparseData , qui est une liste des éléments non-zéros lus ligne par ligne de gauche à droite (l'ordre est le même que pour la liste jr ). Cela explique le format interne dans lequel SparseArray - s stockez leurs éléments et, espérons-le, clarifiez le rôle des fonctions ci-dessus.

le code

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]]
"1519650920 des" points de référence et de comparaison", 1519660920"

Voici la quantité initiale de mémoire utilisée (noyau frais):

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

nous appelons notre fonction:

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

donc, c'est la même vitesse que readTable . Comment au sujet de l'utilisation de la mémoire?

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

je pense, c'est assez remarquable: nous n'avons utilisé que deux fois plus de mémoire que le fichier sur disque qui s'occupe lui-même. Mais, fait encore plus remarquable, l'utilisation de la mémoire finale (après le calcul terminé) a été considérablement réduite:

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

C'est parce que nous utilisons le SparseArray :

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

donc, notre table ne prend que 12 Mo de RAM. Nous pouvons le comparer à notre fonction plus générale:

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

les résultats sont les mêmes une fois que nous convertissons notre table clairsemée de nouveau à la normale:

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

alors que la table normale occupe beaucoup plus d'espace (il semble que ByteCount surmonte la mémoire occupée environ 3-4 fois, mais la différence réelle est encore au moins ordre de grandeur):

In[21]:= ByteCount[t]
Out[21]= 619900248
34
répondu Leonid Shifrin 2011-09-26 06:48:56