Konvertieren Sie die Pandas-Spalte mit NaNs in den Typ "int"

174

Ich lese Daten aus einer CSV-Datei in einen Pandas-Datenrahmen wie folgt. Für eine der Spalten idmöchte ich nämlich den Spaltentyp als angeben int. Das Problem ist, dass die idSerie fehlende / leere Werte hat.

Wenn ich versuche, die idSpalte beim Lesen der CSV-Datei in eine Ganzzahl umzuwandeln, wird Folgendes angezeigt:

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

Alternativ habe ich versucht, den Spaltentyp nach dem Lesen wie folgt zu konvertieren, aber diesmal erhalte ich:

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

Wie kann ich das angehen?

Zhubarb
quelle
3
Ich denke, dass ganzzahlige Werte nicht konvertiert oder in einer Reihe / einem Datenrahmen gespeichert werden können, wenn / NaN-Werte fehlen. Dies hat meiner Meinung nach mit der Numpy-Kompatibilität zu tun (ich vermute hier). Wenn Sie eine Kompatibilität mit fehlenden Werten wünschen, würde ich die Werte als Floats speichern
EdChum
1
siehe hier: pandas.pydata.org/pandas-docs/dev/… ; Sie müssen einen Float-Typ haben, wenn Sie fehlende Werte haben (oder einen technischen Objekt-Typ, aber das ist ineffizient). Was ist Ihr Ziel bei der Verwendung von int type?
Jeff
6
Ich glaube, dies ist ein NumPy-Problem, das nicht spezifisch für Pandas ist. Es ist eine Schande, da es so viele Fälle gibt, in denen ein int-Typ, der die Möglichkeit von Nullwerten zulässt, viel effizienter ist als eine große Spalte von Floats.
ely
1
Ich habe auch ein Problem damit. Ich habe mehrere Datenrahmen, die ich basierend auf einer Zeichenfolgendarstellung mehrerer "Ganzzahl" -Spalten zusammenführen möchte. Wenn jedoch eine dieser ganzzahligen Spalten eine np.nan hat, erzeugt das String-Casting eine ".0", die die Zusammenführung aufhebt. Macht die Sache nur etwas komplizierter, wäre schön, wenn es eine einfache Umgehung gäbe.
Dermen
1
@Rhubarb, Optionale Nullable Integer-Unterstützung wurde jetzt offiziell für Pandas 0.24.0 hinzugefügt - endlich :) - eine aktualisierte Antwort finden Sie unten. Pandas 0.24.x Versionshinweise
Mork

Antworten:

168

Das Fehlen von NaN rep in ganzzahligen Spalten ist ein Pandas "Gotcha" .

Die übliche Problemumgehung besteht darin, einfach Floats zu verwenden.

Andy Hayden
quelle
13
Gibt es noch andere Problemumgehungen, als sie wie Schwimmer zu behandeln?
NumenorForLife
3
@ jsc123 Sie können das Objekt dtype verwenden. Dies kommt mit einer kleinen Gesundheitswarnung, funktioniert aber größtenteils gut.
Andy Hayden
1
Können Sie ein Beispiel für die Verwendung des Objekttyps geben? Ich habe die Pandas-Dokumente durchgesehen und gegoogelt und gelesen, dass dies die empfohlene Methode ist. Ich habe jedoch kein Beispiel für die Verwendung des Objekttyps gefunden.
MikeyE
28
In Version 0.24 können Sie dies jetzt tun df = df.astype(pd.Int32Dtype())(um den gesamten Datenrahmen zu konvertieren, oder) df['col'] = df['col'].astype(pd.Int32Dtype()). Andere akzeptierte nullbare Ganzzahltypen sind pd.Int16Dtypeund pd.Int64Dtype. Wähle dein Gift.
CS95
1
Es ist NaN-Wert, aber isnan Überprüfung funktioniert überhaupt nicht :(
Winston
115

In Version 0.24 hat + pandas die Fähigkeit erhalten, ganzzahlige d-Typen mit fehlenden Werten zu halten.

Nullable Integer Datentyp .

Pandas können mit ganzzahligen Daten mit möglicherweise fehlenden Werten darstellen arrays.IntegerArray. Dies ist ein Erweiterungstyp, der in Pandas implementiert ist. Es ist nicht der Standard-D-Typ für Ganzzahlen und wird nicht abgeleitet. Sie müssen den dtype explizit an array()oder übergeben Series:

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

0      1
1      2
2    NaN
dtype: Int64

Verwenden Sie zum Konvertieren von Spalten in nullfähige Ganzzahlen:

df['myCol'] = df['myCol'].astype('Int64')
jezrael
quelle
4
Ich mag diese Antwort.
CS95
7
Beachten Sie, dass dtype sein muss "Int64"und nicht "int64"(zuerst muss 'i' großgeschrieben werden )
Viacheslav Z
2
df.myCol = df.myCol.astype('Int64')oderdf['myCol'] = df['myCol'].astype('Int64')
LoMaPh
43

Mein Anwendungsfall ist das Munging von Daten vor dem Laden in eine DB-Tabelle:

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)

Entfernen Sie NaNs, konvertieren Sie sie in int, konvertieren Sie sie in str und fügen Sie NANs erneut ein.

Es ist nicht schön, aber es erledigt den Job!

Hibernado
quelle
1
Ich habe mir die Haare ausgezogen und versucht, Seriennummern zu laden, bei denen einige null und der Rest schwebend sind. Das hat mich gerettet.
Chris Decker
1
Das OP möchte eine Spalte mit ganzen Zahlen. Das Konvertieren in einen String erfüllt die Bedingung nicht.
Rishab Gupta
1
Funktioniert nur, wenn col noch nicht -1 hat. Andernfalls wird es mit den Daten
durcheinander bringen
wie komme ich dann zurück zu int .. ??
Abdoulsn
5

Es ist jetzt möglich, eine Pandas-Spalte zu erstellen, die NaNs als dtype enthält int, da sie jetzt offiziell zu pandas 0.24.0 hinzugefügt wird

Versionshinweise zu pandas 0.24.x Zitat: " Pandas hat die Fähigkeit erlangt, ganzzahlige d-Typen mit fehlenden Werten zu halten

mork
quelle
4

Wenn Sie unbedingt Ganzzahlen und NaNs in einer Spalte kombinieren möchten, können Sie den Datentyp 'Objekt' verwenden:

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

Dadurch werden NaNs durch eine Ganzzahl ersetzt (egal welche), in int konvertiert, in Objekt konvertiert und schließlich NaNs erneut eingefügt.

jmenglund
quelle
3

Wenn Sie Ihre gespeicherten Daten ändern können, verwenden Sie einen Sentinel-Wert für das Fehlen id. Ein häufiger Anwendungsfall, der durch den Spaltennamen abgeleitet wird, idist eine Ganzzahl, die streng größer als Null ist. Sie können sie 0als Sentinel-Wert verwenden, damit Sie schreiben können

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

Sie können verwenden, .dropna()wenn es in Ordnung ist, die Zeilen mit den NaN-Werten zu löschen.

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

Alternativ können Sie das NaN verwenden .fillna()und .astype()durch Werte ersetzen und diese in int konvertieren.

Ich bin auf dieses Problem gestoßen, als ich eine CSV-Datei mit großen Ganzzahlen verarbeitet habe, während einige davon fehlten (NaN). Die Verwendung von float als Typ war keine Option, da ich möglicherweise die Genauigkeit verlieren würde.

Meine Lösung bestand darin , str als Zwischentyp zu verwenden . Dann können Sie die Zeichenfolge später im Code nach Belieben in int konvertieren. Ich habe NaN durch 0 ersetzt, aber Sie können einen beliebigen Wert auswählen.

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

Zur Veranschaulichung ist hier ein Beispiel, wie Schwimmer die Präzision verlieren können:

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

Und die Ausgabe ist:

1.2345678901234567e+19 12345678901234567168 12345678901234567890
elomage
quelle
2

Die meisten Lösungen hier zeigen Ihnen, wie Sie eine Platzhalter-Ganzzahl verwenden, um Nullen darzustellen. Dieser Ansatz ist nicht hilfreich, wenn Sie sich nicht sicher sind, ob in Ihren Quelldaten keine Ganzzahl angezeigt wird. Meine Methode mit will formatiert Floats ohne ihre Dezimalwerte und konvertiert Nullen in Keine. Das Ergebnis ist ein Objektdatentyp, der beim Laden in eine CSV wie ein ganzzahliges Feld mit Nullwerten aussieht.

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

Ich bin auf dieses Problem gestoßen, als ich mit pyspark gearbeitet habe. Da dies ein Python-Frontend für Code ist, der auf einem JVM ausgeführt wird, ist Typensicherheit erforderlich, und die Verwendung von float anstelle von int ist keine Option. Ich habe das Problem umgangen, indem ich die Pandas pd.read_csvin eine Funktion eingeschlossen habe, die benutzerdefinierte Spalten mit benutzerdefinierten Füllwerten füllt, bevor sie in den erforderlichen Typ umgewandelt werden. Folgendes habe ich letztendlich verwendet:

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
Neuneck
quelle
0

Entfernen Sie zuerst die Zeilen, die NaN enthalten. Führen Sie dann eine Ganzzahlkonvertierung für die verbleibenden Zeilen durch. Zuletzt die entfernten Zeilen wieder einfügen. Hoffe es wird funktionieren

Kamran Kausar
quelle
0
import pandas as pd

df= pd.read_csv("data.csv")
df['id'] = pd.to_numeric(df['id'])
Monaheng Ramochele
quelle
3
Gibt es einen Grund, warum Sie diese Formulierung der in der akzeptierten Antwort vorgeschlagenen vorziehen? In diesem Fall wäre es hilfreich, Ihre Antwort zu bearbeiten, um diese Erklärung bereitzustellen - und insbesondere, da es zehn zusätzliche Antworten gibt, die um Aufmerksamkeit konkurrieren.
Jeremy Caney
Während dieser Code das Problem des OP beheben kann, ist es am besten, eine Erklärung beizufügen, wie / warum Ihr Code es behebt. Auf diese Weise können zukünftige Besucher aus Ihrem Beitrag lernen und ihn auf ihren eigenen Code anwenden. SO ist kein Codierungsdienst, sondern eine Ressource für Wissen. Es ist auch wahrscheinlicher, dass qualitativ hochwertige, vollständige Antworten positiv bewertet werden. Diese Funktionen sowie die Anforderung, dass alle Beiträge in sich geschlossen sind, sind einige der Stärken von SO, da eine Plattform es von Foren unterscheidet. Sie können editzusätzliche Informationen hinzufügen und / oder Ihre Erklärungen durch Quellendokumentation ergänzen.
SherylHohman
-1

Angenommen, Ihre mit DateColumn formatierte 3312018.0 sollte als Zeichenfolge in den 31.03.2008 konvertiert werden. Und einige Datensätze fehlen oder 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'))
Justin Malinchak
quelle