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!
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