2017-01-04 8 views
4

Hier ein Beispiel:Warum ergibt ein numpy Array mit dtype = object eine wesentlich kleinere Dateigröße als dtype = int?

import numpy as np 
randoms = np.random.randint(0, 20, 10000000) 

a = randoms.astype(np.int) 
b = randoms.astype(np.object) 

np.save('d:/dtype=int.npy', a)  #39 mb 
np.save('d:/dtype=object.npy', b) #19 mb! 

Sie können sehen, dass die Datei mit dtype = Objekt ist etwa halb so groß. Woher? Ich hatte den Eindruck, dass richtig definierte numpy dtypes streng besser sind als Objekt dtypes.

+1

Welche Versionen von Python und NumPy verwenden Sie? – user2357112

+0

Welche CPU-Architektur haben Sie? Und was genau ist der 'dtype' von' a'? 'np.int64'? oder 'np.int32?' –

+0

Wie auch immer, die Objekte dtypes werden streng * langsamer *, aber es gibt mehrere Möglichkeiten, warum Sie eine kleinere Dateigröße bekommen. –

Antwort

6

Bei einem Nicht-Objekt dtype besteht das meiste des NPY-Dateiformats aus einem Dump der Rohbytes der Daten des Arrays. Das wären 4 oder 8 Bytes pro Element, abhängig davon, ob Ihr NumPy standardmäßig 4- oder 8-Byte-Ganzzahlen verwendet. Aus der Dateigröße sieht es aus wie 4 Bytes pro Element.

Bei einem Objekt dtype besteht das meiste des npy-Dateiformats aus einer gewöhnlichen Beize des Arrays. Für kleine ganze Zahlen, wie sie in Ihrem Array verwendet die Gurke die K Beize Opcode, lange Namen BININT1 „dokumentiert“ im pickletools Modul:

I(name='BININT1', 
    code='K', 
    arg=uint1, 
    stack_before=[], 
    stack_after=[pyint], 
    proto=1, 
    doc="""Push a one-byte unsigned integer. 

    This is a space optimization for pickling very small non-negative ints, 
    in range(256). 
    """), 

Dies erfordert zwei Bytes pro ganzer Zahl, einen für den K Opcode und ein Byte vorzeichenloser ganzzahliger Daten.

Beachten Sie, dass Sie die Dateigröße noch weiter reduzieren könnten, indem Sie Ihr Array mit dtype oder numpy.uint8 für ungefähr 1 Byte pro Ganzzahl speichern.

+0

Interessant. Danke für diese weitere Analyse. Es passt gut zusammen mit Roberts Antwort. – Muppet

2

EDIT: Diese Analyse ist falsch. Siehe die Antwort von user2357112 für die richtige.

dtype=object Arrays werden als Python-Gurke in der NPY-Datei gespeichert. Python-Pickles bewahren die Identität von Objekten innerhalb ihres Objektgraphen; d. h. wenn b[i] is b[j] dann wird die Beize das Objekt, auf das durch b[i] und b[j] verwiesen wird, nur zum ersten Mal serialisieren und sich darauf beziehen, wenn es zum nächsten Auftreten kommt. Diese Referenz ist oft kleiner als das serialisierte Objekt selbst, selbst wenn die Objekte selbst bei der Serialisierung ziemlich klein sind.

Python optimiert kleine Ganzzahlen, so dass es immer das gleiche Objekt für Ganzzahlen von -5 bis 256 wieder verwendet, also alle range(0, 20), die die einzigen Werte in Ihrem Array sind. numpy kann auch entscheiden, Instanzen bei der Konvertierung über .astype(object) erneut zu verwenden.

Wenn Sie ein Array erstellt haben, in dem die meisten oder alle Werte eindeutig sind, wie beim Gleitkomma uniform(0.0, 1.0, 10000000), erhalten Sie die erwarteten relativen Größen.

+0

Große Antwort - macht sehr viel Sinn. Vielen Dank! – Muppet

+0

Beat mich dazu - Ich habe gerade unter der Decke suchen: https://github.com/numpy/numpy/blob/master/numpy/lib/format.py # L567 –

+0

Ich habe erwartet, dass dies die Antwort ist, aber in meinen eigenen Tests schien die Gurke die Memo-Operatoren nicht zu verwenden, oder zumindest wenn dies der Fall war, waren die Ergebnisse immer noch etwa 8-10 Bytes pro Integer. – user2357112

Verwandte Themen