2016-07-29 18 views
3

Ich mache ein Paket, das eine Binärdatei liest und Daten, die verwendet werden, um eine DataFrame initialisieren kann, frage ich mich jetzt, ob es am besten ist, eine zurückzugeben dict oder zwei Listen (eine, die die Schlüssel enthält und eine, die die Werte enthält).Speicherauslastung, Füllung Pandas DataFrame mit Dict vs mit Schlüssel-und Wertlisten

Das Paket, das ich mache, sollte nicht vollständig auf ein Objekt DataFrame angewiesen sein, weshalb mein Paket derzeit die Daten als dict (für einfachen Zugriff) ausgibt. Wenn es einige Speicher- und Geschwindigkeitseinsparungen geben könnte (was für meine Anwendung von größter Bedeutung ist, da es sich um Millionen von Datenpunkten handelt), möchte ich stattdessen die Schlüssel- und die Werteliste ausgeben. Diese Iterables würden dann verwendet werden, um eine DataFrame zu initialisieren.

Hier ist ein einfaches Beispiel:

In [1]: d = {(1,1,1): '111', 
    ...: (2,2,2): '222', 
    ...: (3,3,3): '333', 
    ...: (4,4,4): '444'} 

In [2]: keyslist=[(1,1,1),(2,2,2),(3,3,3),(4,4,4)] 

In [3]: valslist=['111','222','333','444'] 

In [4]: import pandas as pd 

In [5]: dfdict=pd.DataFrame(d.values(), index=pd.MultiIndex.from_tuples(d.keys(), names=['a','b','c'])) 

In [6]: dfdict 
Out[6]: 
     0 
a b c  
3 3 3 333 
2 2 2 222 
1 1 1 111 
4 4 4 444 

In [7]: dflist=pd.DataFrame(valslist, index=pd.MultiIndex.from_tuples(keyslist, names=['a','b','c'])) 

In [8]: dfpair 
Out[8]: 
     0 
a b c  
1 1 1 111 
2 2 2 222 
3 3 3 333 
4 4 4 444 

Es ist mein Verständnis, dass d.values() und d.keys() eine neue Kopie der Daten zu schaffen. Wenn wir die Tatsache ignorieren, dass die a dict mehr Speicher als eine list nimmt, führt die Verwendung von d.values() und d.keys() zu mehr Speicherverbrauch als die list Paarimplementierung?

+0

Warum nicht stattdessen numpige Arrays verwenden? Sie haben einen viel geringeren Speicherbedarf als Listen und Wörterbücher. –

+0

Ich benutze keine numpy, da ich die Größe der Daten nicht kenne, also muss ich eine Liste oder ein dict auffüllen und dann ein numpy Array oder Pandas Dataframe initialisieren. – snowleopard

+0

Ich schreibe einen Benchmark der Speicherbelegung von Listen vs dicts –

Antwort

3

Ich machte Speicher Profiling von 1M Zeilen. Die gewinnende Struktur besteht darin, array.array für jeden numerischen Index und eine Liste für Strings (147MB Daten und 310MB Umwandlung in Pandas) zu verwenden.

Nach Python Handbuch

Arrays sind Sequenztypen und verhalten sich sehr ähnlich wie Listen, außer dass die Art von Objekten in ihnen gespeichert ist eingeschränkt.

Sie haben sogar Append-Methode und höchstwahrscheinlich haben sehr schnelle Append-Geschwindigkeit.

Der zweite Platz geht an zwei separate Listen. (308MB und 450MB)

Die beiden anderen Optionen, die ein dict verwenden und eine Liste mit vier Tupeln verwenden, waren am schlimmsten. Dict: 339 MB, 524 MB. Liste von vier: 308MB, 514MB.

Hier ist die Verwendung von Array.Array:

In [1]: from array import array 
In [2]: import gc 
In [3]: import pandas as pd 
In [4]: %load_ext memory_profiler 
In [5]: a1=array("l",range(1000000)) 
In [6]: a2=array("l",range(1000000)) 
In [7]: a3=array("l",range(1000000)) 
In [8]: b=[str(x*111) for x in list(range(1000000))] 
In [9]: gc.collect() 
Out[9]: 0 
In [10]: %memit a1,a2,a3,b 
peak memory: 147.64 MiB, increment: 0.32 MiB 
In [11]: %memit dfpair=pd.DataFrame(b, index=pd.MultiIndex.from_arrays([a1,a2,a3], names=['a','b','c'])) 
peak memory: 310.60 MiB, increment: 162.91 MiB 

Hier ist der Rest des Codes (sehr langen):

Liste von Tupeln von vier:

In [1]: import gc 
In [2]: import pandas as pd 
In [3]: %load_ext memory_profiler 
In [4]: a=list(zip(list(range(1000000)),list(range(1000000)),list(range(1000000)))) 
In [5]: b=[str(x*111) for x in list(range(1000000))] 
In [6]: d2=[x+(b[i],) for i,x in enumerate(a)] 
In [7]: del a 
In [8]: del b 
In [9]: gc.collect() 
Out[9]: 0 
In [10]: %memit d2 
peak memory: 308.40 MiB, increment: 0.28 MiB 
In [11]: %memit df = pd.DataFrame(d2, columns=['a','b','c','d']).set_index(['a','b','c']) 
peak memory: 514.21 MiB, increment: 205.80 MiB 

Wörterbuch:

In [1]: import gc 
In [2]: import pandas as pd 
In [3]: %load_ext memory_profiler 
In [4]: a=list(zip(list(range(1000000)),list(range(1000000)),list(range(1000000)))) 
In [5]: b=[str(x*111) for x in list(range(1000000))] 
In [6]: d = dict(zip(a, b)) 
In [7]: del a 
In [8]: del b 
In [9]: gc.collect() 
Out[9]: 0 
In [10]: %memit d 
peak memory: 339.14 MiB, increment: 0.23 MiB 
In [11]: %memit dfdict=pd.DataFrame(list(d.values()), index=pd.MultiIndex.from_tuples(d.keys(), names=['a','b','c'])) 
peak memory: 524.10 MiB, increment: 184.95 MiB 

Zwei Arrays:

In [1]: import gc 
In [2]: import pandas as pd 
In [3]: %load_ext memory_profiler 
In [4]: a=list(zip(list(range(1000000)),list(range(1000000)),list(range(1000000)))) 
In [5]: b=[str(x*111) for x in list(range(1000000))] 
In [6]: gc.collect() 
Out[6]: 0 
In [7]: %memit a,b 
peak memory: 307.75 MiB, increment: 0.19 MiB 
In [8]: %memit dfpair=pd.DataFrame(b, index=pd.MultiIndex.from_tuples(a, names=['a','b','c'])) 
peak memory: 459.94 MiB, increment: 152.19 MiB 
Hier
+0

unterstützen möchte Danke! Ich habe auch verstanden, dass das Diktat nicht so gut funktioniert wie Listen. Ich werde mehr in Array schauen, danke für all die tollen Zeigern. Ich werde meine Ergebnisse auch veröffentlichen. – snowleopard

0

sind die Benchmarks memory_profiler:

Filename: testdict.py 

Line # Mem usage Increment Line Contents 
================================================ 
    4  66.2 MiB  0.0 MiB @profile 
    5        def testdict(): 
    6 
    7  66.2 MiB  0.0 MiB  d = {} 
    8 
    9 260.6 MiB 194.3 MiB  for i in xrange(0,1000000): 
    10 260.6 MiB  0.0 MiB    d[(i,i,i)]=str(i)*3 
    11 
    12 400.2 MiB 139.6 MiB  dfdict=pd.DataFrame(d.values(), index= 
pd.MultiIndex.from_tuples(d.keys(), names=['a','b','c'])) 

Filename: testlist.py 

Line # Mem usage Increment Line Contents 
================================================ 
    4  66.5 MiB  0.0 MiB @profile 
    5        def testlist(): 
    6 
    7  66.5 MiB  0.0 MiB  keyslist=[] 
    8  66.5 MiB  0.0 MiB  valslist=[] 
    9 
    10 229.3 MiB 162.8 MiB  for i in xrange(0,1000000): 
    11 229.3 MiB  0.0 MiB    keyslist.append((i,i,i)) 
    12 229.3 MiB  0.0 MiB    valslist.append(str(i)*3) 
    13 
    14 273.6 MiB  44.3 MiB  dflist=pd.DataFrame(valslist, index= 
pd.MultiIndex.from_tuples(keyslist, names=['a','b','c'])) 

für die gleiche Aufgabe und Speichertypen, es scheint die Wörterbuch-Implementierung ist nicht als Speicher effizient.

bearbeiten

Aus irgendeinem Grund, wenn ich die Werte auf Arrays von Zahlen (mehr Vertreter meiner Daten) ändern bekomme ich sehr ähnliche Leistung, weiß jemand, warum dies geschieht?

Filename: testdict.py 

Line # Mem usage Increment Line Contents 
================================================ 
    4  66.9 MiB  0.0 MiB @profile 
    5        def testdict(): 
    6 
    7  66.9 MiB  0.0 MiB  d = {} 
    8 
    9 345.6 MiB 278.7 MiB  for i in xrange(0,1000000): 
    10 345.6 MiB  0.0 MiB    d[(i,i,i)]=[0]*9 
    11 
    12 546.2 MiB 200.6 MiB  dfdict=pd.DataFrame(d.values(), index= 
pd.MultiIndex.from_tuples(d.keys(), names=['a','b','c'])) 

Filename: testlist.py 

Line # Mem usage Increment Line Contents 
================================================ 
    4  66.3 MiB  0.0 MiB @profile 
    5        def testlist(): 
    6 
    7  66.3 MiB  0.0 MiB  keyslist=[] 
    8  66.3 MiB  0.0 MiB  valslist=[] 
    9 
    10 314.7 MiB 248.4 MiB  for i in xrange(0,1000000): 
    11 314.7 MiB  0.0 MiB    keyslist.append((i,i,i)) 
    12 314.7 MiB  0.0 MiB    valslist.append([0]*9) 
    13 
    14 515.2 MiB 200.6 MiB  dflist=pd.DataFrame(valslist, index= 
pd.MultiIndex.from_tuples(keyslist, names=['a','b','c'])) 
+0

Die Zeichenfolge in Python merkt sich frühere Zeichenfolgen. Damit belegt "000" den gleichen Speicher wie ein anderes "000". Und es könnte sein, dass "1000" tatsächlich ein Byte ist, das mit "000" verknüpft ist. Zahlen konnten das nicht. –