Converti la colonna Panda contenente NaN in dtype `int`


175

Ho letto i dati da un file .csv in un frame di dati Pandas come di seguito. Per una delle colonne, vale a dire id, voglio specificare il tipo di colonna come int. Il problema è che la idserie ha valori mancanti / vuoti.

Quando provo a trasmettere la idcolonna a un numero intero durante la lettura di .csv, ottengo:

df= pd.read_csv("data.csv", dtype={'id': int}) 
error: Integer column has NA values

In alternativa, ho provato a convertire il tipo di colonna dopo aver letto come di seguito, ma questa volta ottengo:

df= pd.read_csv("data.csv") 
df[['id']] = df[['id']].astype(int)
error: Cannot convert NA to integer

Come posso affrontare questo?


3
Penso che i valori interi non possano essere convertiti o archiviati in una serie / frame di dati se ci sono valori mancanti / NaN. Questo penso che abbia a che fare con la compatibilità intorpidita (sto indovinando qui), se vuoi una mancanza di compatibilità dei valori, memorizzerei i valori come float
EdChum

1
vedi qui: pandas.pydata.org/pandas-docs/dev/… ; devi avere un dtype float quando hai valori mancanti (o tecnicamente oggetto dtype ma questo è inefficiente); qual è il tuo obiettivo di usare int type?
Jeff,

6
Credo che questo sia un problema di NumPy, non specifico per i panda. È un peccato dato che ci sono così tanti casi in cui avere un tipo int che consente la possibilità di valori null è molto più efficiente di una grande colonna di float.
ely,

1
Ho un problema anche con questo. Ho più frame di dati che voglio unire in base a una rappresentazione in stringa di più colonne "intere". Tuttavia, quando una di quelle colonne di numeri interi ha un np.nan, il casting della stringa produce un ".0", che elimina l'unione. Rende le cose un po 'più complicate, sarebbe bello se ci fosse una semplice soluzione.
dermen,

1
@Rhubarb, Nullable Integer Support 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:


169

La mancanza di rep NaN nelle colonne di interi è un "gotcha" di panda .

La solita soluzione consiste nell'utilizzare semplicemente i float.


13
Ci sono altre soluzioni alternative oltre a trattarle come galleggianti?
NumenorForLife,

3
@ jsc123 è possibile utilizzare il tipo di oggetto. Questo viene fornito con un piccolo avviso di salute, ma per la maggior parte funziona bene.
Andy Hayden,

1
Potete fornire un esempio di come usare il tipo di oggetto? Ho esaminato i documenti dei panda e ho cercato su Google, e ho letto che è il metodo raccomandato. Ma non ho trovato un esempio di come utilizzare il tipo di oggetto.
MikeyE,

30
In v0.24, ora puoi farlo df = df.astype(pd.Int32Dtype())(per convertire l'intero dataFrame o) df['col'] = df['col'].astype(pd.Int32Dtype()). Altri tipi interi accettabili nullable sono pd.Int16Dtypee pd.Int64Dtype. Scegli il tuo veleno.
cs95,

1
È il valore NaN ma il controllo isnan non funziona affatto :(
Winston

119

Nella versione 0.24. + Panda ha acquisito la capacità di contenere tipi interi con valori mancanti.

Tipo di dati intero integrabile .

I panda possono rappresentare dati interi con valori eventualmente mancanti usando arrays.IntegerArray. Questo è un tipo di estensione implementato all'interno dei panda. Non è il dtype predefinito per gli interi e non verrà dedotto; è necessario passare esplicitamente il tipo in array()o Series:

arr = pd.array([1, 2, np.nan], dtype=pd.Int64Dtype())
pd.Series(arr)

0      1
1      2
2    NaN
dtype: Int64

Per convertire la colonna in numeri interi nullable utilizzare:

df['myCol'] = df['myCol'].astype('Int64')

4
Mi piace questa risposta.
cs95,

9
Si noti che il tipo deve essere "Int64"e non "int64"(il primo 'i' deve essere in maiuscolo)
Viacheslav Z

2
df.myCol = df.myCol.astype('Int64')oppuredf['myCol'] = df['myCol'].astype('Int64')
LoMaPh

43

Il mio caso d'uso è il munging dei dati prima del caricamento in una tabella DB:

df[col] = df[col].fillna(-1)
df[col] = df[col].astype(int)
df[col] = df[col].astype(str)
df[col] = df[col].replace('-1', np.nan)

Rimuovere NaNs, convertire in int, convertire in str e quindi reinserire i NAN.

Non è carino ma fa il lavoro!


1
Mi sono strappato i capelli cercando di caricare i numeri di serie in cui alcuni sono nulli e il resto sono float, questo mi ha salvato.
Chris Decker,

1
L'OP vuole una colonna di numeri interi. La conversione in stringa non soddisfa la condizione.
Rishab Gupta,

1
Funziona solo se col non ha già -1. Altrimenti,
rovinerà

allora come tornare a int .. ??
abdoulsn,

5

È ora possibile creare una colonna panda contenente NaN come dtype int, poiché ora è ufficialmente aggiunta su panda 0.24.0

panda 0.24.x note di rilascio Citazione: " Pandas ha acquisito la capacità di contenere tipi interi con valori mancanti


4

Se si desidera assolutamente combinare numeri interi e NaN in una colonna, è possibile utilizzare il tipo di dati "oggetto":

df['col'] = (
    df['col'].fillna(0)
    .astype(int)
    .astype(object)
    .where(df['col'].notnull())
)

Questo sostituirà i NaN con un numero intero (non importa quale), convertirà in int, convertirà in oggetto e infine reinserirà i NaN.


3

Se è possibile modificare i dati memorizzati, utilizzare un valore sentinella per mancante id. Un caso d'uso comune, dedotto dal nome della colonna, che idè un numero intero, strettamente maggiore di zero, è possibile utilizzare 0come valore sentinella in modo da poter scrivere

if row['id']:
   regular_process(row)
else:
   special_process(row)

3

È possibile utilizzare .dropna()se è corretto eliminare le righe con i valori NaN.

df = df.dropna(subset=['id'])

In alternativa, utilizzare .fillna()e .astype()per sostituire la NaN con i valori e convertirli in int.

Ho riscontrato questo problema durante l'elaborazione di un file CSV con numeri interi di grandi dimensioni, mentre alcuni di essi mancavano (NaN). L'uso di float come tipo non era un'opzione, perché potevo perdere la precisione.

La mia soluzione era usare str come tipo intermedio . Quindi puoi convertire la stringa in int come ti pare più avanti nel codice. Ho sostituito NaN con 0, ma è possibile scegliere qualsiasi valore.

df = pd.read_csv(filename, dtype={'id':str})
df["id"] = df["id"].fillna("0").astype(int)

Per l'illustrazione, ecco un esempio di come i galleggianti possano perdere la precisione:

s = "12345678901234567890"
f = float(s)
i = int(f)
i2 = int(s)
print (f, i, i2)

E l'output è:

1.2345678901234567e+19 12345678901234567168 12345678901234567890

2

La maggior parte delle soluzioni qui spiega come utilizzare un numero intero segnaposto per rappresentare i null. Tale approccio non è utile se non si è certi che numeri interi non vengano visualizzati nei dati di origine. Il mio metodo con formatterà i float senza i loro valori decimali e convertirà i valori null in Nessuno. Il risultato è un tipo di dati oggetto che apparirà come un campo intero con valori null quando caricato in un CSV.

keep_df[col] = keep_df[col].apply(lambda x: None if pandas.isnull(x) else '{0:.0f}'.format(pandas.to_numeric(x)))

1

Ho riscontrato questo problema lavorando con pyspark. Poiché si tratta di un frontend Python per il codice in esecuzione su una jvm, richiede la sicurezza del tipo e l'utilizzo di float anziché int non è un'opzione. Ho risolto il problema avvolgendo i panda pd.read_csvin una funzione che riempirà le colonne definite dall'utente con valori di riempimento definiti dall'utente prima di lanciarle nel tipo richiesto. Ecco cosa ho finito per usare:

def custom_read_csv(file_path, custom_dtype = None, fill_values = None, **kwargs):
    if custom_dtype is None:
        return pd.read_csv(file_path, **kwargs)
    else:
        assert 'dtype' not in kwargs.keys()
        df = pd.read_csv(file_path, dtype = {}, **kwargs)
        for col, typ in custom_dtype.items():
            if fill_values is None or col not in fill_values.keys():
                fill_val = -1
            else:
                fill_val = fill_values[col]
            df[col] = df[col].fillna(fill_val).astype(typ)
    return df

1
import pandas as pd

df= pd.read_csv("data.csv")
df['id'] = pd.to_numeric(df['id'])

4
C'è un motivo per cui preferisci questa formulazione rispetto a quella proposta nella risposta accettata? In tal caso, sarebbe utile modificare la tua risposta per fornire quella spiegazione, e soprattutto perché ci sono altre dieci risposte in competizione per attirare l'attenzione.
Jeremy Caney,

Sebbene questo codice possa risolvere il problema del PO, è meglio includere una spiegazione su come / perché il codice lo risolve. In questo modo, i futuri visitatori possono imparare dal tuo post e applicarlo al proprio codice. SO non è un servizio di codifica, ma una risorsa per la conoscenza. Inoltre, è più probabile che vengano votate risposte complete e di alta qualità. Queste caratteristiche, insieme al requisito secondo cui tutti i post sono autonomi, sono alcuni dei punti di forza di SO poiché una piattaforma lo differenzia dai forum. È possibile editaggiungere ulteriori informazioni e / o integrare le spiegazioni con la documentazione di origine.
SherylHohman,

0

Rimuovere innanzitutto le righe che contengono NaN. Quindi esegui la conversione Intero sulle righe rimanenti. Alla fine inserire nuovamente le righe rimosse. Spero che funzioni


-1

Supponendo che DateColumn formattato 3312018.0 dovrebbe essere convertito in 31/03/2018 come stringa. E, mancano alcuni record o 0.

df['DateColumn'] = df['DateColumn'].astype(int)
df['DateColumn'] = df['DateColumn'].astype(str)
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.zfill(8))
df.loc[df['DateColumn'] == '00000000','DateColumn'] = '01011980'
df['DateColumn'] = pd.to_datetime(df['DateColumn'], format="%m%d%Y")
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.strftime('%m/%d/%Y'))
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.