proyectos ejemplos python list python-3.x memory integer

python - ejemplos - django



La forma más eficiente de almacenar la lista de enteros (3)

Recientemente he estado haciendo un proyecto en el que uno de los objetivos es utilizar la menor cantidad de memoria posible para almacenar una serie de archivos usando Python 3. Casi todos los archivos ocupan muy poco espacio, aparte de una lista de enteros que es aproximadamente 333,000 enteros de largo y tiene enteros de hasta aproximadamente 8000 en tamaño.

Actualmente uso pickle para almacenar la lista, que ocupa alrededor de 7 7mb , pero creo que debe haber una forma más eficiente de hacerlo.

Intenté almacenarlo como un archivo de texto y csv , pero ambos se usaron en exceso de 10mb de espacio.


Aquí hay una pequeña demostración, que usa el módulo Pandas:

import numpy as np import pandas as pd import feather # let''s generate an array of 1M int64 elements... df = pd.DataFrame({''num_col'':np.random.randint(0, 10**9, 10**6)}, dtype=np.int64) df.info() %timeit -n 1 -r 1 df.to_pickle(''d:/temp/a.pickle'') %timeit -n 1 -r 1 df.to_hdf(''d:/temp/a.h5'', ''df_key'', complib=''blosc'', complevel=5) %timeit -n 1 -r 1 df.to_hdf(''d:/temp/a_blosc.h5'', ''df_key'', complib=''blosc'', complevel=5) %timeit -n 1 -r 1 df.to_hdf(''d:/temp/a_zlib.h5'', ''df_key'', complib=''zlib'', complevel=5) %timeit -n 1 -r 1 df.to_hdf(''d:/temp/a_bzip2.h5'', ''df_key'', complib=''bzip2'', complevel=5) %timeit -n 1 -r 1 df.to_hdf(''d:/temp/a_lzo.h5'', ''df_key'', complib=''lzo'', complevel=5) %timeit -n 1 -r 1 feather.write_dataframe(df, ''d:/temp/a.feather'')

Información de DataFrame:

In [56]: df.info() <class ''pandas.core.frame.DataFrame''> RangeIndex: 1000000 entries, 0 to 999999 Data columns (total 1 columns): num_col 1000000 non-null int64 dtypes: int64(1) memory usage: 7.6 MB

Resultados (velocidad):

In [49]: %timeit -n 1 -r 1 df.to_pickle(''d:/temp/a.pickle'') 1 loop, best of 1: 16.2 ms per loop In [50]: %timeit -n 1 -r 1 df.to_hdf(''d:/temp/a.h5'', ''df_key'', complib=''blosc'', complevel=5) 1 loop, best of 1: 39.7 ms per loop In [51]: %timeit -n 1 -r 1 df.to_hdf(''d:/temp/a_blosc.h5'', ''df_key'', complib=''blosc'', complevel=5) 1 loop, best of 1: 40.6 ms per loop In [52]: %timeit -n 1 -r 1 df.to_hdf(''d:/temp/a_zlib.h5'', ''df_key'', complib=''zlib'', complevel=5) 1 loop, best of 1: 213 ms per loop In [53]: %timeit -n 1 -r 1 df.to_hdf(''d:/temp/a_bzip2.h5'', ''df_key'', complib=''bzip2'', complevel=5) 1 loop, best of 1: 1.09 s per loop In [54]: %timeit -n 1 -r 1 df.to_hdf(''d:/temp/a_lzo.h5'', ''df_key'', complib=''lzo'', complevel=5) 1 loop, best of 1: 32.1 ms per loop In [55]: %timeit -n 1 -r 1 feather.write_dataframe(df, ''d:/temp/a.feather'') 1 loop, best of 1: 3.49 ms per loop

Resultados (tamaño):

{ temp } » ls -lh a* /d/temp -rw-r--r-- 1 Max None 7.7M Sep 20 23:15 a.feather -rw-r--r-- 1 Max None 4.1M Sep 20 23:15 a.h5 -rw-r--r-- 1 Max None 7.7M Sep 20 23:15 a.pickle -rw-r--r-- 1 Max None 4.1M Sep 20 23:15 a_blosc.h5 -rw-r--r-- 1 Max None 4.0M Sep 20 23:15 a_bzip2.h5 -rw-r--r-- 1 Max None 4.1M Sep 20 23:15 a_lzo.h5 -rw-r--r-- 1 Max None 3.9M Sep 20 23:15 a_zlib.h5

Conclusión: preste atención a HDF5 ( blosc + blosc o lzo ) si necesita velocidad y tamaño razonable o en formato Feather si solo le importa la velocidad: ¡es 4 veces más rápido que Pickle!


Una solución stdlib que puede usar son las matrices de una array , de los documentos:

Este módulo define un tipo de objeto que puede representar de forma compacta una matriz de valores básicos: caracteres, números enteros, números en coma flotante. Las matrices son tipos de secuencia y se comportan de forma muy similar a las listas, excepto que el tipo de objetos almacenados en ellas está restringido.

Esto generalmente arroja un poco de memoria de listas grandes, por ejemplo, con una lista de 10 millones de elementos, la matriz recorta 11mb :

import pickle from array import array l = [i for i in range(10000000)] a = array(''i'', l) # tofile can also be used. with open(''arrfile'', ''wb'') as f: pickle.dump(a, f) with open(''lstfile'', ''wb'') as f: pickle.dump(l, f)

Tamaños:

!du -sh ./* 39M arrfile 48M lstfile


Me gusta la sugerencia de Jim de usar el módulo de array . Si sus valores numéricos son lo suficientemente pequeños como para caber en el tipo int nativo de la máquina, esta es una buena solución. (Preferiría serializar la matriz con el método array.tofile lugar de usar pickle , sin embargo). Si una int es de 32 bits, entonces esto usa 4 bytes por número.

Sin embargo, me gustaría cuestionar cómo hiciste tu archivo de texto. Si creo un archivo con 333 000 enteros en el rango [0, 8 000] con un número por línea,

import random with open(''numbers.txt'', ''w'') as ostr: for i in range(333000): r = random.randint(0, 8000) print(r, file=ostr)

sale a un tamaño de solo 1.6 MiB, que no es tan malo en comparación con el 1.3 MiB que usaría la representación binaria. Y si sucede que tiene un valor fuera del rango del tipo int nativo un día, el archivo de texto lo manejará felizmente sin desbordamiento.

Además, si comprimo el archivo usando gzip, el tamaño del archivo se reduce a 686 KiB. ¡Eso es mejor que copiar los datos binarios! Al usar bzip2, el tamaño del archivo es de solo 562 KiB. La biblioteca estándar de Python tiene soporte tanto para gzip como para bz2 por lo que es posible que desee probar el formato de texto sin formato más la compresión.