Come archiviare un frame di dati usando Panda


317

In questo momento sto importando un CSVframe di dati abbastanza grande ogni volta che eseguo lo script. Esiste una buona soluzione per mantenere quel frame di dati costantemente disponibile tra le esecuzioni in modo da non dover passare tutto il tempo ad aspettare l'esecuzione dello script?


2
Sì, questa è una delle mie principali lamentele con Python: non esiste un modo semplice per salvare e recuperare i frame di dati. R e SAS sono molto più facili da usare in questo senso.
RobertF,

Risposte:


481

Il modo più semplice è quello di sottaceto usando to_pickle:

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

Quindi è possibile ricaricarlo utilizzando:

df = pd.read_pickle(file_name)

Nota: prima di 0.11.1 saveed loaderano l'unico modo per farlo (ora sono deprecati a favore to_picklee read_picklerispettivamente).


Un'altra scelta popolare è quella di utilizzare HDF5 ( pytables ) che offre tempi di accesso molto rapidi per set di dati di grandi dimensioni:

store = HDFStore('store.h5')

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

Strategie più avanzate sono discusse nel ricettario .


Da 0.13 c'è anche msgpack che potrebbe essere migliore per l'interoperabilità, come alternativa più veloce a JSON, o se hai oggetti python / dati pesanti di testo (vedi questa domanda ).


8
Il salvataggio di @geekazoid è deprecato in to_pickle (che crea un sottaceto piuttosto che un csv, che è molto più veloce / oggetto diverso).
Andy Hayden,

9
@geekazoid Nel caso in cui i dati debbano essere trasformati dopo il caricamento (ovvero stringa / oggetto in datetime64), questo dovrebbe essere ripetuto dopo aver caricato un csv salvato, con conseguente perdita di prestazioni. pickle salva il frame di dati nel suo stato attuale, quindi i dati e il suo formato vengono conservati. Questo può portare a massicci aumenti delle prestazioni.
Harbun,

4
Sia pickle che HDFStore non possono salvare dataframe più di 8 GB. Ci sono alternative?
user1700890

1
@ user1700890 prova a generare da dati casuali (testo e array) e pubblica una nuova domanda. Non penso che questo possa essere giusto / sospetto che ci manchi qualcosa. La nuova domanda avrà più occhi, ma prova a includere / generare un DataFrame che si riproduca :)
Andy Hayden

1
@YixingLiu puoi cambiare la modalità dopo il fatto stackoverflow.com/a/16249655/1240268
Andy Hayden,

100

Sebbene ci siano già alcune risposte, ho trovato un bel confronto in cui hanno provato diversi modi per serializzare Pandas DataFrames: memorizzare in modo efficiente Pandas DataFrames .

Si confrontano:

  • pickle: formato dati ASCII originale
  • cPickle, una libreria C.
  • pickle-p2: usa il formato binario più recente
  • json: libreria json standardlib
  • json-no-index: come json, ma senza indice
  • msgpack: alternativa binaria JSON
  • CSV
  • hdfstore: formato di archiviazione HDF5

Nel loro esperimento, serializzano un DataFrame di 1.000.000 di righe con le due colonne testate separatamente: una con dati di testo, l'altra con numeri. Il loro disclaimer dice:

Non dovresti fidarti del fatto che quanto segue generalizza i tuoi dati. Dovresti guardare i tuoi dati ed eseguire tu stesso i benchmark

Il codice sorgente per il test a cui si riferiscono è disponibile online . Poiché questo codice non ha funzionato direttamente, ho apportato alcune piccole modifiche, che puoi ottenere qui: serialize.py Ho ottenuto i seguenti risultati:

risultati del confronto temporale

Dicono anche che con la conversione di dati di testo in dati categorici la serializzazione è molto più veloce. Nel loro test circa 10 volte più veloce (vedi anche il codice del test).

Modifica : i tempi più alti per il sottaceto rispetto a CSV possono essere spiegati dal formato dei dati utilizzato. Per impostazione predefinita pickleutilizza una rappresentazione ASCII stampabile, che genera set di dati più grandi. Come si può vedere dal grafico, tuttavia, il pickle usando il più recente formato di dati binari (versione 2, pickle-p2) ha tempi di caricamento molto più bassi.

Alcuni altri riferimenti:


1
Ho aggiornato la mia risposta per spiegare la tua domanda. Riassumendo: per impostazione predefinita pickle memorizza i dati in un formato ASCII.
Agold

1
Ah, grazie per quella spiegazione! Come nota, Panda DataFrame .to_pickle sembra utilizzare pkl.HIGHEST_PROTOCOL (dovrebbe essere 2)
ntg

2
Sembra che il blog linkato sopra ( Store efficientemente Pandas DataFrames sia stato cancellato. Ho fatto i miei confronti con .to_pickle()(che utilizza l'archiviazione binaria) contro .to_hdf()(senza compressione). L'obiettivo era la velocità, la dimensione del file per HDF era 11x Pickle e il tempo di caricare era 5x Pickle. I miei dati erano ~ 5k file di ~ 7k righe x 6 cols ciascuno, per lo più numerici
hamx0r

1
La pagina esiste ancora, devi solo rimuovere la barra finale: Memorizza efficacemente Pandas DataFrames
IanSR

2
@ Mike Williamson, nel mio test, il pickle era 5 volte più veloce da caricare rispetto all'HDF e richiedeva anche 1/11 dello spazio su disco (ovvero hdf era 11x più grande sul disco e impiegava 5 volte più tempo per caricare dal disco come il pickle). questo era tutto su Python 3 con Panda 0.22.0.
hamx0r

35

Se ho capito bene, stai già utilizzando, pandas.read_csv()ma vorresti accelerare il processo di sviluppo in modo da non dover caricare il file ogni volta che modifichi lo script, giusto? Ho alcuni consigli:

  1. potresti caricare solo una parte del file CSV usando pandas.read_csv(..., nrows=1000)per caricare solo il bit superiore della tabella, mentre stai facendo lo sviluppo

  2. usa ipython per una sessione interattiva, in modo tale da mantenere in memoria la tabella dei panda mentre modifichi e ricarichi lo script.

  3. converti il ​​csv in una tabella HDF5

  4. uso aggiornatoDataFrame.to_feather() e pd.read_feather()per memorizzare i dati nel formato binario piuma compatibile con R che è super veloce (nelle mie mani, leggermente più veloce rispetto pandas.to_pickle()ai dati numerici e molto più veloce sui dati stringa).

Potresti anche essere interessato a questa risposta su StackOverflow.


Sai perché to_featherdovrebbe funzionare bene sui dati delle stringhe? Ho fatto un benchmark to_picklee to_featuresul mio frame di dati numerici e pickle è circa 3 volte più veloce.
zyxue,

@zyxue bella domanda, sinceramente non ho giocato molto con le cose di piume, quindi non ho una risposta
Noah

20

Pickle funziona bene!

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
Nota che i file generati non sono file CSV, forse è meglio usare l'estensione .pklcome suggerito nella risposta @Andy Haydens.
Agold

5

È possibile utilizzare il file in formato piuma. È estremamente veloce.

df.to_feather('filename.ft')

E i dati possono quindi essere utilizzati direttamente Rutilizzando la featherlibreria.
James Hirschorn,

4

Pandas DataFrames ha la to_picklefunzione che è utile per salvare 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

Come già accennato, ci sono diverse opzioni e formati di file ( HDF5 , JSON , CSV , parquet , SQL ) per memorizzare un frame di dati. Tuttavia, picklenon è un cittadino di prima classe (a seconda della configurazione), perché:

  1. pickleè un potenziale rischio per la sicurezza. Forma la documentazione di Python per pickle :

Avviso Il picklemodulo non è protetto da dati errati o costruiti in modo pericoloso. Non decomprimere mai i dati ricevuti da una fonte non attendibile o non autenticata.

  1. pickleè lento. Trova qui e qui i benchmark.

A seconda della configurazione / utilizzo non si applicano entrambe le limitazioni, ma non consiglierei picklecome persistenza predefinita per i frame di dati Panda.


1

I formati di file Numpy sono piuttosto veloci per i dati numerici

Preferisco usare file intorpiditi poiché sono veloci e facili da lavorare. Ecco un semplice benchmark per il salvataggio e il caricamento di un frame di dati con 1 colonna da 1 milione di punti.

import numpy as np
import pandas as pd

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

usando la %%timeitfunzione magica di ipython

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

l'uscita è

100 loops, best of 3: 5.97 ms per loop

per caricare nuovamente i dati in un frame di dati

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

data_df = pd.DataFrame(data)

l'uscita è

100 loops, best of 3: 5.12 ms per loop

NON MALE!

CONS

C'è un problema se salvi il file numpy usando python 2 e poi provi ad aprirlo usando python 3 (o viceversa).


6
nota che questa soluzione eliminerà tutti i nomi delle tue colonne e cambierà tutti i tuoi dati interi in float :(
Joseph Garvin

0

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

I formati di protocollo pickle:

La versione del protocollo 0 è il protocollo originale "leggibile dall'uomo" ed è retrocompatibile con le versioni precedenti di Python.

La versione 1 del protocollo è un vecchio formato binario compatibile anche con le versioni precedenti di Python.

Il protocollo versione 2 è stato introdotto in Python 2.3. Fornisce un decapaggio molto più efficiente delle classi di nuovo stile. Fare riferimento a PEP 307 per informazioni sui miglioramenti apportati dal protocollo 2.

La versione 3 del protocollo è stata aggiunta in Python 3.0. Ha un supporto esplicito per gli oggetti byte e non può essere rimosso da Python 2.x. Questo è il protocollo predefinito e il protocollo consigliato quando è richiesta la compatibilità con altre versioni di Python 3.

La versione 4 del protocollo è stata aggiunta in Python 3.4. Aggiunge supporto per oggetti molto grandi, decapando più tipi di oggetti e alcune ottimizzazioni del formato dei dati. Fare riferimento a PEP 3154 per informazioni sui miglioramenti apportati dal protocollo 4.


0

pyarrow compatibilità tra le versioni

La mossa generale è stata di piyarrow / piuma (avvertimenti di deprecazione da panda / msgpack). Tuttavia, ho una sfida con pyarrow con transiente nelle specifiche. I dati serializzati con pyarrow 0.15.1 non possono essere deserializzati con 0.16.0 ARROW-7961 . Sto usando la serializzazione per usare redis, quindi devo usare una codifica binaria.

Ho testato nuovamente varie opzioni (usando il notebook 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 i seguenti risultati per il mio frame di dati (nella outvariabile 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>
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=

piuma e parquet non funzionano per il mio frame di dati. Continuerò a usare Pyarrow. Comunque integrerò con sottaceto (nessuna compressione). Quando si scrive nella cache, memorizzare i moduli serializzati pyarrow e pickle. Quando si legge dal fallback della cache al decapaggio se la deserializzazione del piramide fallisce.


Questo non risponde alla domanda
Jason S,

0

Il formato dipende dal tuo caso d'uso

  • Salva DataFrame tra le sessioni di notebook - piuma , se sei abituato a decapare - anche ok.
  • Salva DataFrame nella dimensione del file più piccola possibile - parquet o pickle.gz (controlla cosa c'è di meglio per i tuoi dati)
  • Salva un DataFrame molto grande (10+ milioni di righe) - hdf
  • Essere in grado di leggere i dati su un'altra piattaforma (non Python) che non supporta altri formati - csv , csv.gz , controlla se il parquet è supportato
  • Essere in grado di rivedere con gli occhi / utilizzando Excel / Fogli Google / Git diff - csv
  • Salvare un DataFrame che richiede quasi tutta la RAM - CSV

Il confronto dei formati di file panda è in questo video .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.