NumPy o Panda: mantenere il tipo di array come intero pur avendo un valore NaN


160

Esiste un modo preferito per mantenere numpyfisso il tipo di dati di un array come int( int64o qualunque cosa), pur avendo un elemento all'interno elencato come numpy.NaN?

In particolare, sto convertendo una struttura di dati interna in un DataFrame Pandas. Nella nostra struttura, abbiamo colonne di tipo intero che hanno ancora NaN (ma il tipo di colonna è int). Sembra rifondere tutto come float se lo trasformiamo in un DataFrame, ma ci piacerebbe davvero esserlo int.

Pensieri?

Le cose hanno provato:

Ho provato a usare la from_records()funzione in pandas.DataFrame, con coerce_float=Falsee questo non ha aiutato. Ho anche provato ad usare array mascherati NumPy, con NaN fill_value, che non ha funzionato. Tutto ciò ha fatto sì che il tipo di dati della colonna diventasse mobile.


Potresti usare un array mascherato intorpidito?
mgilson,

Lo proverò. Ho anche provato la from_recordsfunzione in panda.DataFrame, con coerce_float=False, ma senza fortuna ... rende ancora nuovi i tipi di dati float64.
ely,

1
Sì, niente fortuna. Anche con array mascherato, si converte comunque in float. Sembra che Panda vada in questo modo: "C'è un NaN da qualche parte? ... Quindi tutto è un galleggiante." Spero che ci sia un modo per aggirare questo.
ely,

1
Il supporto Nullable Integer opzionale è ora ufficialmente aggiunto su Panda 0.24.0 - finalmente :) - per favore, trova una risposta aggiornata qui sotto. panda 0.24.x note sulla versione
mork

Risposte:


70

Questa funzionalità è stata aggiunta ai panda (a partire dalla versione 0.24): https://pandas.pydata.org/pandas-docs/version/0.24/whatsnew/v0.24.0.html#optional-integer-na-support

A questo punto, richiede l'uso dell'estensione dtype Int64 (in maiuscolo), piuttosto che il predefinito dtype int64 (minuscolo).


1
Per ora devi specificare un tipo speciale come 'Int64'farlo funzionare. Sarà ancora meglio quando sarà abilitato per impostazione predefinita.
Jean Paul,

Questo è fantastico! C'è un piccolo problema, tuttavia, che PyCharm non riesce a visualizzare il frame di dati nella finestra di debug se utilizzato in questo modo. Puoi vedere la mia risposta per un'altra domanda su come forzare la visualizzazione: stackoverflow.com/questions/38956660/… (il problema originale è diverso, ma la soluzione per visualizzare il frame di dati funziona)
Alaa M.

Devo usare 'Int64'o c'è qualcosa del genere 'Int8'? Usa una quantità folle di memoria rispetto a np.float.
Superdooperhero,

'Int8'sembra funzionare, ma np.floatsembra ancora caricare molto più velocemente. Il problema sembra essere che non sta rilasciando memoria tra di loro. Supponiamo che alla fine verrà eseguito il Garbage Collector.
Superdooperhero,

103

NaNnon può essere archiviato in un array intero. Questa è una limitazione nota dei panda al momento; Ho aspettato che venissero fatti progressi con i valori NA in NumPy (simile a NA in R), ma ci vorranno almeno 6 mesi o un anno prima che NumPy ottenga queste funzionalità, sembra:

http://pandas.pydata.org/pandas-docs/stable/gotchas.html#support-for-integer-na

(Questa funzione è stata aggiunta a partire dalla versione 0.24 dei panda, ma nota che richiede l'uso dell'estensione d64 tipo Int64 (in maiuscolo), piuttosto che il predefinito dtype int64 (minuscolo): https://pandas.pydata.org/pandas- docs / version / 0.24 / whatsnew / v0.24.0.html # optional-integer-na-support )


7
Ciao Wes, ci sono aggiornamenti su questo? Incontriamo problemi in cui le colonne di join vengono convertite in ints o float, in base all'esistenza di un valore NA nell'elenco originale. (Creazione di problemi in seguito quando si tenta di unire questi frame di dati)
Carst,


8

Se le prestazioni non sono il problema principale, puoi invece archiviare le stringhe.

df.col = df.col.dropna().apply(lambda x: str(int(x)) )

Quindi puoi mescolarlo con tutto NaNquello che vuoi. Se vuoi davvero avere numeri interi, a seconda della tua applicazione, puoi usare -1, o 0, o 1234567890, o qualche altro valore dedicato da rappresentare NaN.

Puoi anche duplicare temporaneamente le colonne: una come hai, con float; l'altro sperimentale, con ints o stringhe. Quindi inserisce assertsin ogni luogo ragionevole verificando che i due siano sincronizzati. Dopo un numero sufficiente di prove puoi lasciar andare i galleggianti.


5

Questa non è una soluzione per tutti i casi, ma la mia (coordinate genomiche) ho fatto ricorso all'uso di 0 come NaN

a3['MapInfo'] = a3['MapInfo'].fillna(0).astype(int)

Ciò consente almeno di utilizzare il tipo di colonna "nativo" corretto, le operazioni come sottrazione, confronto ecc. Funzionano come previsto


5

Panda v0.24 +

Le funzionalità da supportare NaNin serie di numeri interi saranno disponibili dalla v0.24 in poi. Ci sono informazioni al riguardo nella sezione "Novità" v0.24 e ulteriori dettagli in Tipo di dati integer nullable .

Panda v0.23 e precedenti

In generale, è meglio lavorare con le floatserie laddove possibile, anche quando le serie vengono aggiornate da inta floatcausa dell'inclusione dei NaNvalori. Ciò consente calcoli vettoriali basati su NumPy in cui, altrimenti, verrebbero elaborati loop a livello Python.

I documenti suggeriscono : "Una possibilità è utilizzare dtype=objectinvece le matrici". Per esempio:

s = pd.Series([1, 2, 3, np.nan])

print(s.astype(object))

0      1
1      2
2      3
3    NaN
dtype: object

Per motivi estetici, ad esempio l'output in un file, questo può essere preferibile.

Panda v0.23 e precedenti: sfondo

NaNè considerato afloat . I documenti attualmente (a partire dalla v0.23) specificano il motivo per cui le serie di numeri interi vengono inviate a float:

In assenza del supporto NA ad alte prestazioni integrato in NumPy da zero, la principale vittima è la capacità di rappresentare i NA in matrici intere.

Questo compromesso è in gran parte per motivi di memoria e prestazioni, e anche in modo che la serie risultante continui ad essere "numerica".

I documenti forniscono anche regole per l'upgrade a causa NaNdell'inclusione:

Typeclass   Promotion dtype for storing NAs
floating    no change
object      no change
integer     cast to float64
boolean     cast to object


1

Volevo solo aggiungere che nel caso in cui si stia tentando di convertire un vettore float (1.143) in numero intero (1) che ha la conversione NA nel nuovo tipo 'Int64', verrà visualizzato un errore. Per risolvere questo problema devi arrotondare i numeri e poi fare ".astype ('Int64')"

s1 = pd.Series([1.434, 2.343, np.nan])
#without round() the next line returns an error 
s1.astype('Int64')
#cannot safely cast non-equivalent float64 to int64
##with round() it works
s1.round().astype('Int64')
0      1
1      2
2    NaN
dtype: Int64

Il mio caso d'uso è che ho una serie float che voglio arrotondare a int, ma quando fai .round () rimane un '* .0' alla fine del numero, quindi puoi eliminare quello 0 dalla fine di convertendo in int.


0

Se ci sono spazi vuoti nei dati di testo, le colonne che normalmente sarebbero numeri interi verranno lanciate su float come dtype float64 perché il dtype int64 non può gestire i null. Ciò può causare uno schema incoerente se si caricano più file alcuni con spazi vuoti (che finiranno come float64 e altri senza i quali finirà come int64

Questo codice tenterà di convertire qualsiasi colonna di tipo numero in Int64 (anziché in int64) poiché Int64 può gestire valori null

import pandas as pd
import numpy as np

#show datatypes before transformation
mydf.dtypes

for c in mydf.select_dtypes(np.number).columns:
    try:
        mydf[c] = mydf[c].astype('Int64')
        print('casted {} as Int64'.format(c))
    except:
        print('could not cast {} to Int64'.format(c))

#show datatypes after transformation
mydf.dtypes
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.