Cómo almacenar un marco de datos usando Pandas


317

En este momento estoy importando un CSVmarco de datos bastante grande cada vez que ejecuto el script. ¿Existe una buena solución para mantener ese marco de datos constantemente disponible entre ejecuciones para que no tenga que pasar todo el tiempo esperando que se ejecute el script?


2
Sí, esta es una de mis principales quejas con Python: no hay una manera simple de guardar y recuperar marcos de datos. R y SAS son mucho más fáciles de usar a este respecto.
RobertF

Respuestas:


481

La forma más fácil es encurtirlo usando to_pickle:

df.to_pickle(file_name)  # where to save it, usually as a .pkl

Luego puedes volver a cargarlo usando:

df = pd.read_pickle(file_name)

Nota: antes de 0.11.1 savey loaderan la única forma de hacerlo (ahora están en desuso a favor to_pickley read_picklerespectivamente).


Otra opción popular es usar HDF5 ( pytables ) que ofrece tiempos de acceso muy rápidos para grandes conjuntos de datos:

store = HDFStore('store.h5')

store['df'] = df  # save it
store['df']  # load it

Las estrategias más avanzadas se discuten en el libro de cocina .


Desde 0.13 también hay msgpack que puede ser mejor para la interoperabilidad, como una alternativa más rápida a JSON, o si tiene datos de Python / texto pesado (vea esta pregunta ).


8
@geekazoid save está en desuso a to_pickle (que crea un pickle en lugar de un csv, que es un objeto mucho más rápido / diferente).
Andy Hayden

99
@geekazoid En caso de que los datos necesiten transformarse después de la carga (es decir, cadena / objeto a datetime64), esto debería hacerse nuevamente después de cargar un csv guardado, lo que resulta en una pérdida de rendimiento. pickle guarda el marco de datos en su estado actual, por lo que se conservan los datos y su formato. Esto puede conducir a aumentos masivos de rendimiento.
Harbun

44
Tanto Pickle como HDFStore no pueden guardar el marco de datos de más de 8 GB. ¿Hay alternativas?
user1700890

1
@ user1700890 intenta generar datos aleatorios (texto y matrices) y publica una nueva pregunta. No creo que esto pueda ser correcto / sospecho que nos estamos perdiendo algo. La nueva pregunta obtendrá más ojos, pero intente incluir / generar un DataFrame que reproduzca :)
Andy Hayden

1
@YixingLiu puede cambiar el modo después del hecho stackoverflow.com/a/16249655/1240268
Andy Hayden

100

Aunque ya hay algunas respuestas, encontré una buena comparación en la que intentaron varias formas de serializar Pandas DataFrames: almacenar eficientemente Pandas DataFrames .

Ellos comparan:

  • pickle: formato de datos ASCII original
  • cPickle, una biblioteca de C
  • pickle-p2: usa el formato binario más nuevo
  • json: biblioteca standardlib json
  • json-no-index: como json, pero sin índice
  • msgpack: alternativa binaria JSON
  • CSV
  • hdfstore: formato de almacenamiento HDF5

En su experimento, serializan un DataFrame de 1,000,000 de filas con las dos columnas probadas por separado: una con datos de texto y la otra con números. Su descargo de responsabilidad dice:

No debe confiar en que lo que sigue generaliza sus datos. Debe mirar sus propios datos y ejecutar puntos de referencia usted mismo

El código fuente de la prueba a la que se refieren está disponible en línea . Como este código no funcionó directamente, hice algunos cambios menores, que puede obtener aquí: serialize.py Obtuve los siguientes resultados:

resultados de comparación de tiempo

También mencionan que con la conversión de datos de texto a categóricos , la serialización es mucho más rápida. En su prueba aproximadamente 10 veces más rápido (también vea el código de prueba).

Editar : los tiempos más altos para pickle que CSV pueden explicarse por el formato de datos utilizado. Por defecto pickleusa una representación ASCII imprimible, que genera conjuntos de datos más grandes. Sin embargo, como se puede ver en el gráfico, utilice el formato de datos binarios más reciente (versión 2,pickle-p2 ) tiene tiempos de carga mucho más bajos.

Algunas otras referencias:


1
Actualicé mi respuesta para explicar tu pregunta. Para resumir: por defecto, pickle almacena datos en un formato ASCII.
agold

1
Ah, gracias por esa explicación! Como nota, el pandas DataFrame .to_pickle parece estar usando el pkl.HIGHEST_PROTOCOL (debería ser 2)
ntg

2
Parece que el blog vinculado anteriormente ( Efficiently Store Pandas DataFrames ha sido eliminado. Hice mis propias comparaciones con .to_pickle()(que usa almacenamiento binario) contra .to_hdf()(sin compresión). El objetivo era la velocidad, el tamaño del archivo para HDF era 11x Pickle y el tiempo de carga fue 5x Pickle. Mis datos fueron ~ 5k archivos de ~ 7k filas x 6 cols cada uno, en su mayoría numéricos.
hamx0r

1
La página todavía existe, solo necesita eliminar la barra inclinada: Almacene eficientemente los marcos de datos de Pandas
IanSR

2
@Mike Williamson, en mi prueba, el pickle fue 5 veces más rápido de cargar que HDF y también tomó 1/11 del espacio en disco (es decir, hdf era 11 veces más grande en el disco y tardó 5 veces más tiempo en cargarse del disco que el pickle). Todo esto en Python 3 con pandas 0.22.0.
hamx0r

35

Si entiendo correctamente, ya está usando, pandas.read_csv()pero le gustaría acelerar el proceso de desarrollo para que no tenga que cargar el archivo cada vez que edite su script, ¿es así? Tengo algunas recomendaciones:

  1. puede cargar solo una parte del archivo CSV utilizando pandas.read_csv(..., nrows=1000)para cargar solo el bit superior de la tabla, mientras realiza el desarrollo

  2. use ipython para una sesión interactiva, de modo que mantenga la tabla de pandas en la memoria mientras edita y recarga su script.

  3. convertir el csv a una tabla HDF5

  4. uso actualizadoDataFrame.to_feather() y pd.read_feather()para almacenar datos en el formato binario de plumas compatible con R que es súper rápido (en mis manos, un poco más rápido que pandas.to_pickle()en datos numéricos y mucho más rápido en datos de cadena).

También puede interesarle esta respuesta en stackoverflow.


¿Sabes por to_featherqué funcionaría bien en datos de cadena? Yo como punto de referencia to_pickley to_featureen mi trama de datos numéricos y de la salmuera es aproximadamente 3 veces más rápido.
zyxue

@zyxue buena pregunta, honestamente no he jugado mucho con las cosas de plumas, así que no tengo una respuesta
Noah

20

¡Pickle funciona bien!

import pandas as pd
df.to_pickle('123.pkl')    #to save the dataframe, df to 123.pkl
df1 = pd.read_pickle('123.pkl') #to load 123.pkl back to the dataframe df

8
Tenga en cuenta que los archivos generados no son archivos csv, tal vez sea mejor usar la extensión .pklcomo se sugiere en la respuesta de @Andy Haydens.
Agold

5

Puede usar el archivo de formato de pluma. Es extremadamente rapido.

df.to_feather('filename.ft')

Y los datos se pueden usar directamente Rutilizando la featherbiblioteca.
James Hirschorn

4

Pandas DataFrames tiene la to_picklefunción que es útil para guardar un DataFrame:

import pandas as pd

a = pd.DataFrame({'A':[0,1,0,1,0],'B':[True, True, False, False, False]})
print a
#    A      B
# 0  0   True
# 1  1   True
# 2  0  False
# 3  1  False
# 4  0  False

a.to_pickle('my_file.pkl')

b = pd.read_pickle('my_file.pkl')
print b
#    A      B
# 0  0   True
# 1  1   True
# 2  0  False
# 3  1  False
# 4  0  False

4

Como ya se mencionó, hay diferentes opciones y formatos de archivo ( HDF5 , JSON , CSV , parquet , SQL ) para almacenar un marco de datos. Sin embargo, pickleno es un ciudadano de primera clase (dependiendo de su configuración), porque:

  1. pickleEs un riesgo potencial de seguridad. Forme la documentación de Python para pickle :

Advertencia El picklemódulo no es seguro contra datos erróneos o mal construidos. Nunca elimine los datos recibidos de una fuente no confiable o no autenticada.

  1. picklees lento. Encuentre aquí y aquí puntos de referencia.

Dependiendo de su configuración / uso, ambas limitaciones no se aplican, pero no recomendaría picklecomo la persistencia predeterminada para los marcos de datos de pandas.


1

Los formatos de archivo Numpy son bastante rápidos para datos numéricos

Prefiero usar archivos numpy ya que son rápidos y fáciles de trabajar. Aquí hay un punto de referencia simple para guardar y cargar un marco de datos con 1 columna de 1 millón de puntos.

import numpy as np
import pandas as pd

num_dict = {'voltage': np.random.rand(1000000)}
num_df = pd.DataFrame(num_dict)

usando la %%timeitfunción mágica de ipython

%%timeit
with open('num.npy', 'wb') as np_file:
    np.save(np_file, num_df)

la salida es

100 loops, best of 3: 5.97 ms per loop

cargar los datos nuevamente en un marco de datos

%%timeit
with open('num.npy', 'rb') as np_file:
    data = np.load(np_file)

data_df = pd.DataFrame(data)

la salida es

100 loops, best of 3: 5.12 ms per loop

¡NO ESTÁ MAL!

CONTRAS

Hay un problema si guarda el archivo numpy con python 2 y luego intenta abrirlo con python 3 (o viceversa).


66
tenga en cuenta que esta solución eliminará todos los nombres de sus columnas y cambiará todos sus datos enteros a flotantes :(
Joseph Garvin

0

https://docs.python.org/3/library/pickle.html

Los formatos de protocolo de pickle:

La versión 0 del protocolo es el protocolo original "legible para humanos" y es compatible con versiones anteriores de Python.

La versión 1 del protocolo es un formato binario antiguo que también es compatible con versiones anteriores de Python.

La versión 2 del protocolo se introdujo en Python 2.3. Proporciona una preparación mucho más eficiente de las clases de estilo nuevo. Consulte la PEP 307 para obtener información sobre las mejoras aportadas por el protocolo 2.

Se agregó la versión 3 del protocolo en Python 3.0. Tiene soporte explícito para objetos de bytes y Python 2.x no puede desenredarlo. Este es el protocolo predeterminado y el protocolo recomendado cuando se requiere compatibilidad con otras versiones de Python 3.

Se agregó la versión 4 del protocolo en Python 3.4. Agrega soporte para objetos muy grandes, decapado de más tipos de objetos y algunas optimizaciones de formato de datos. Consulte PEP 3154 para obtener información sobre las mejoras aportadas por el protocolo 4.


0

compatibilidad de pyarrow entre versiones

El movimiento general ha sido a pyarrow / feather (advertencias de desaprobación de pandas / msgpack). Sin embargo, tengo un desafío con pyarrow con transitorios en la especificación. Los datos serializados con pyarrow 0.15.1 no se pueden deserializar con 0.16.0 ARROW-7961 . Estoy usando la serialización para usar redis, así que tengo que usar una codificación binaria.

He vuelto a probar varias opciones (usando el cuaderno jupyter)

import sys, pickle, zlib, warnings, io
class foocls:
    def pyarrow(out): return pa.serialize(out).to_buffer().to_pybytes()
    def msgpack(out): return out.to_msgpack()
    def pickle(out): return pickle.dumps(out)
    def feather(out): return out.to_feather(io.BytesIO())
    def parquet(out): return out.to_parquet(io.BytesIO())

warnings.filterwarnings("ignore")
for c in foocls.__dict__.values():
    sbreak = True
    try:
        c(out)
        print(c.__name__, "before serialization", sys.getsizeof(out))
        print(c.__name__, sys.getsizeof(c(out)))
        %timeit -n 50 c(out)
        print(c.__name__, "zlib", sys.getsizeof(zlib.compress(c(out))))
        %timeit -n 50 zlib.compress(c(out))
    except TypeError as e:
        if "not callable" in str(e): sbreak = False
        else: raise
    except (ValueError) as e: print(c.__name__, "ERROR", e)
    finally: 
        if sbreak: print("=+=" * 30)        
warnings.filterwarnings("default")

Con los siguientes resultados para mi marco de datos (en outvariable jupyter)

pyarrow before serialization 533366
pyarrow 120805
1.03 ms ± 43.9 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
pyarrow zlib 20517
2.78 ms ± 81.8 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
msgpack before serialization 533366
msgpack 109039
1.74 ms ± 72.8 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
msgpack zlib 16639
3.05 ms ± 71.7 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
pickle before serialization 533366
pickle 142121
733 µs ± 38.3 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
pickle zlib 29477
3.81 ms ± 60.4 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
feather ERROR feather does not support serializing a non-default index for the index; you can .reset_index() to make the index into column(s)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
parquet ERROR Nested column branch had multiple children: struct<x: double, y: double>
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=

la pluma y el parquet no funcionan para mi marco de datos. Voy a seguir usando pyarrow. Sin embargo, lo complementaré con pepinillo (sin compresión). Al escribir en caché, almacene los formularios serializados de pyarrow y pickle. Al leer desde la memoria caché de reserva para encurtir si falla la deserialización de pyarrow.


Esto no responde la pregunta
Jason S

0

El formato depende de su caso de uso.

  • Guarde DataFrame entre sesiones de cuaderno ( pluma , si está acostumbrado a encurtir) , también está bien.
  • Guarde DataFrame en el tamaño de archivo más pequeño posible: parquet o pickle.gz (verifique qué es mejor para sus datos)
  • Guarde un DataFrame muy grande (más de 10 millones de filas) - hdf
  • Poder leer los datos en otra plataforma (no Python) que no sea compatible con otros formatos: csv , csv.gz , verifique si el parquet es compatible
  • Poder revisar con los ojos / usar Excel / Hojas de cálculo de Google / Git diff - csv
  • Guarde un DataFrame que tome casi toda la RAM - csv

La comparación de los formatos de archivo pandas se encuentra en este video .

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.