2012-03-30 20 views
17

Mam listę powiedzmy 100k i chcę ją przekonwertować na bufor bajtów.Najszybszy sposób na spakowanie listy elementów pływających do bajtów w pythonie

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

To dość wolno. Jak mogę go przyspieszyć przy użyciu tylko standardowych bibliotek Python 3.x.

+1

'' f''ty dostaniesz C * f * loat (32 bity); bez wątpienia chcesz pływać w Pythonie aka C * d * podwójny (64 bity), więc ty i twoi zwolennicy powinniście używać '' d'' –

+0

Masz rację, byłem nieostrożny przy moich typach danych. Ale tak naprawdę chciałem pływać z jedną precyzją. – MxyL

Odpowiedz

37

Po prostu powiedz struct ile masz s masz. Pętle 100k zajmują około 1/100 sekundy na moim powolnym laptopie.

import random 
import struct 

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

FWIW: działa z 'array.array', ale warto zauważyć, że' array.tobytes() 'zwraca to samo, co' struct.pack (...) '. Tak więc można użyć 'tablica ('f', [...])' i mieć 'append', dostępność indeksatora, itp. Array.array nie ma wszystkich tych samych metod co' list', ale może być łatwiejsze do wdrożenia w wielu przypadkach. – IAbstract

0

Większość powolności będzie polegała na tym, że wielokrotnie będziesz dołączać do testu bytowego. To kopiuje test bytowy za każdym razem. Zamiast tego należy użyć b''.join():

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

Jest to około sześć razy wolniejsze od wywoływania 'struct' raz dla 100k' float's. – agf

1

To powinno działać:

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

Można użyć ctypes, i mają podwójne-array (lub float tablicy) dokładnie tak, jak” d mieć w C, zamiast trzymać swoje dane na liście. Jest to dość niski poziom, ale jest zalecany, jeśli potrzebujesz świetnej wydajności i jeśli twoja lista ma stały rozmiar.

Można stworzyć odpowiednik C double array[100]; w Pythonie, wykonując:

array = (ctypes.c_double * 100)() 

Wyrażenie ctypes.c_double * 100 daje klasę Pythona na tablicę podwójnych, 100 pozycji długich. Aby podłączyć go do pliku, można po prostu użyć buffer aby jego zawartość:

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

Jeśli dane są już na liście Pythona, pakowanie go w podwójnej tablicy może lub nie może być szybsze niż dzwonienie struct jako w AGF zaakceptowany odpowiedź - odejdę pomiarowy, który jest szybszy jak praca domowa, ale cały kod trzeba to:

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

Aby go zobaczyć jako ciąg znaków, po prostu zrobić: str(buffer(array)) - jedną wadą jest to, że ciebie musisz dbać o rozmiar float (float vs double) i typ float zależny od CPU - moduł struct może to zająć.

Duża wygrana jest to, że z tablicy pływaka można nadal wykorzystywać elementy jak numery, dzięki dostępowi potem po prostu tak, jakby gdzie zwykły lista Python, mając wtedy łatwo dostępny jako region płaska pamięci z buffer.

0

Tak jak mówisz, że naprawdę potrzebujesz pojedynczej precyzji "f", możesz spróbować array module (w standardowej bibliotece od 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' 

Można zaoszczędzić BAG-obciążenie pamięci, a jednocześnie posiadające pojemnik o zmiennej długości z większością metod listy. Tablica.Podejście macierzowe zajmuje 4 bajty na jedno wyrównanie jednostajne. Podejście do listy zużywa wskaźnik do obiektu float w języku Python (4 lub 8 bajtów) plus rozmiar tego obiektu; na realizację 32-bitowym CPython, czyli 16:

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

Razem: 20 bajtów za sztukę najlepszym przypadku dla list, 4 bajty za sztukę zawsze za array.array('f').

0

W przypadku macierzy spławików pojedynczej precyzji dostępne są dwie opcje: użycie struct lub array.

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 

Tak więc struct jest szybszy.

Powiązane problemy