2012-03-30 11 views
17

Ich habe eine Liste von sagen 100k floats und ich möchte es in einen Bytes-Puffer konvertieren.Schnellste Möglichkeit, eine Liste von Fließkommazahlen in Python in Bytes zu packen

buf = bytes() 
for val in floatList: 
    buf += struct.pack('f', val) 
return buf 

Dies ist ziemlich langsam. Wie kann ich es schneller machen, wenn nur Standard-Python 3.x-Bibliotheken verwendet werden?

+1

'' f'' bekommt Sie ein C * f * loat (32 Bit); Sie wollen zweifellos einen Python float alias C * d * ouble (64 bit), also sollten Sie und Ihre Follower '' '' '' ' –

+0

verwenden. Sie haben Recht, ich war unvorsichtig mit meinen Datentypen. Aber ich wollte tatsächlich einzelne Präzisionswagen. – MxyL

Antwort

37

Sagen Sie einfach struct wie viele float s Sie haben. 100k Floats dauert etwa eine 1/100 einer Sekunde auf meinem langsamen Laptop.

import random 
import struct 

floatlist = [random.random() for _ in range(10**5)] 
buf = struct.pack('%sf' % len(floatlist), *floatlist) 
+1

FWIW: Arbeitet mit 'array.array', aber es ist bemerkenswert, dass' array.tobytes() 'das gleiche wie' struct.pack (...) 'zurückgibt. Man kann also ein 'array ('f', [...])' 'verwenden und' append', indexer accessibility, etc. Array.array hat nicht alle die gleichen Methoden wie 'list', könnte aber einfacher sein in vielen Fällen zu implementieren. – IAbstract

0

Die meisten der Langsamkeit wird sein, dass Sie wiederholt an eine Bytestring anhängen. Das kopiert die Bytezeichenfolge jedes Mal. Stattdessen sollten Sie b''.join() verwenden:

import struct 
packed = [struct.pack('f', val) for val in floatList] 
return b''.join(packed) 
+3

Dies ist ungefähr sechsmal langsamer als das Aufrufen von 'struct' für 100k' float's. – agf

1

, die funktionieren sollte:

return struct.pack('f' * len(floatList), *floatList) 
5

Sie ctypes verwenden können, und verfügen über ein Doppel-Array (oder Array-float) genau so, wie Sie‘ d haben in C, anstatt Ihre Daten in einer Liste zu halten. Dies ist ein faires niedriges Niveau, aber es ist eine Empfehlung, wenn Sie eine gute Leistung benötigen und wenn Ihre Liste eine feste Größe hat.

Sie können das Äquivalent eines C erstellen double array[100]; in Python, indem Sie:

array = (ctypes.c_double * 100)() 

Der ctypes.c_double * 100 Ausdruck ergibt lange eine Python-Klasse für eine Reihe von Doppelzimmer, 100 Einzelteile. Um es zu einer Datei zu verbinden, können Sie einfach buffer verwenden, um seinen Inhalt zu erhalten:

>>> f = open("bla.dat", "wb") 
>>> f.write(buffer(array)) 

Wenn Ihre Daten bereits in einer Python-Liste sind, es in ein Doppel Array Verpackung kann oder nicht sein können schneller als Aufruf struct als in AGF akzeptierte Antwort - ich Mess verlassen wird, die schneller als Hausaufgaben, aber der gesamte Code Sie benötigen, ist dieses:

>>> import ctypes 
>>> array = (ctypes.c_double * len(floatlist))(*floatlist) 

Um es als String zu sehen, tun gerade: str(buffer(array)) - die man hier den Nachteil, dass Sie ist muss auf Float-Größe (float vs. double) und CPU-abhängigen Float-Typ achten - das struct-Modul kann sich darum kümmern. Der große Gewinn ist, dass man mit einem Float-Array die Elemente immer noch als Zahlen verwenden kann, indem man dann genauso wie bei einer einfachen Python-Liste darauf zugreift, während man dann als Planare Speicherregion mit buffer zur Verfügung steht.

0

Wie Sie sagen, dass Sie wirklich wollen, Single-Precision 'f' schwimmt, möchten Sie vielleicht versuchen, die array module (in der Standardbibliothek seit 1.x).

>>> mylist = [] 
>>> import array 
>>> myarray = array.array('f') 
>>> for guff in [123.45, -987.654, 1.23e-20]: 
... mylist.append(guff) 
... myarray.append(guff) 
... 
>>> mylist 
[123.45, -987.654, 1.23e-20] 
>>> myarray 
array('f', [123.44999694824219, -987.6539916992188, 1.2299999609665927e-20]) 
>>> import struct 
>>> mylistb = struct.pack(str(len(mylist)) + 'f', *mylist) 
>>> myarrayb = myarray.tobytes() 
>>> myarrayb == mylistb 
True 
>>> myarrayb 
b'f\xe6\xf6B\xdb\xe9v\xc4&Wh\x1e' 

Dies kann Ihnen eine Tasche Last der Erinnerung speichern, während immer noch ein variabler Länge Behälter mit den meisten der Liste Methoden mit. Das Array.Array-Ansatz dauert 4 Bytes pro Float mit einfacher Genauigkeit. Der Listenansatz verwendet einen Zeiger auf ein Python-Float-Objekt (4 oder 8 Byte) plus die Größe dieses Objekts; auf einer 32-Bit-CPython Implementierung, dh 16:

>>> import sys 
>>> sys.getsizeof(123.456) 
16 

Gesamt: 20 Bytes pro Artikel besten Fall für ein list, 4 Bytes pro Artikel immer für ein array.array('f').

0

Für Arrays mit einfacher Präzisionspose gibt es zwei Optionen: struct oder array zu verwenden.

In[103]: import random 
import struct 
from array import array 

floatlist = [random.random() for _ in range(10**5)] 

In[104]: %timeit struct.pack('%sf' % len(floatlist), *floatlist) 
100 loops, best of 3: 2.86 ms per loop 

In[105]: %timeit array('f', floatlist).tostring() 
100 loops, best of 3: 4.11 ms per loop 

So ist struct schneller.

Verwandte Themen