Ersetzen Sie ungültige Werte in Pandas DataFrame durch None


80

Gibt es eine Methode, durch die Werte Nonein Pandas in Python ersetzt werden können?

Sie können df.replace('pre', 'post')einen Wert verwenden und durch einen anderen ersetzen. Dies ist jedoch nicht möglich, wenn Sie ihn durch einen Wert ersetzen möchten. NoneWenn Sie dies versuchen, erhalten Sie ein seltsames Ergebnis.

Hier ist ein Beispiel:

df = DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df.replace('-', 0)

was ein erfolgreiches Ergebnis zurückgibt.

Aber,

df.replace('-', None)

Dies ergibt ein folgendes Ergebnis:

0
0   - // this isn't replaced
1   3
2   2
3   5
4   1
5  -5
6  -1
7  -1 // this is changed to `-1`...
8   9

Warum wird so ein seltsames Ergebnis zurückgegeben?

Da ich diesen Datenrahmen in die MySQL-Datenbank einfügen möchte, kann ich keine NaNWerte in ein Element in meinem Datenrahmen einfügen und möchte stattdessen Werte einfügen None. Sicherlich können Sie zuerst ändern konvertieren und dann zu , aber ich möchte wissen , warum die Datenrahmen in einer solchen schreckliche Art und Weise wirkt.'-'NaNNaNNone

Getestet auf Pandas 0.12.0 Dev unter Python 2.7 und OS X 10.8. Python ist eine vorinstallierte Version unter OS X, und ich habe Pandas zu Ihrer Information mithilfe des SciPy Superpack-Skripts installiert.


Analysiert das write_framenicht NaNs zu nones?
Andy Hayden

Jep. Sie stoßen auf einen InternalError: (1054, u"Unknown column 'nan' in 'field list'")Fehler. Ich weiß nicht , über alle Lösungen , die auf sie andere als die Umwandlung NaNzu Nonevor der Ausführung write_frameVerfahren.
Blaszard

Welche Version von Pandas benutzt du?
Andy Hayden

Scipy Super Pack gibt Entwickler aus? Ok, ich denke definitiv, dass Sie dies als ein Problem auf Github ansprechen sollten, sollte nicht zu schwer zu beheben sein.
Andy Hayden

Wenn Sie diese Daten aus CSV / Excel lesen, können Sie diese Werte mithilfe eines na_valuesArguments als NaN einlesen . Weitere Informationen in dieser Antwort.
CS95

Antworten:


111

In späteren Versionen von Pandas wird dies einen TypeError ergeben:

df.replace('-', None)
TypeError: If "to_replace" and "value" are both None then regex must be a mapping

Sie können dies tun, indem Sie entweder eine Liste oder ein Wörterbuch übergeben:

In [11]: df.replace('-', df.replace(['-'], [None]) # or .replace('-', {0: None})
Out[11]:
      0
0  None
1     3
2     2
3     5
4     1
5    -5
6    -1
7  None
8     9

Ich empfehle jedoch die Verwendung von NaNs anstelle von None:

In [12]: df.replace('-', np.nan)
Out[12]:
     0
0  NaN
1    3
2    2
3    5
4    1
5   -5
6   -1
7  NaN
8    9

15
Oder einfach eine Liste, zB df.replace(['-'], [None])oder df.replace({'-': None}), denke ich. Die Verwendung Noneals Sentinel schließt die Verwendung als Wert aus.
DSM

@ user2360798 replace ist eigentlich eine sehr funktionsreiche (lesekomplizierte ) Funktion, die (dev) docstring ist allerdings wirklich gut.
Andy Hayden

4
Ich weiß nicht , wenn es offensichtlich ist, aber zu assign hatte dfwie zu sich selbst zurück:df = df.replace({'?': np.nan})
luckyging3r

3
@AndyHayden df.replace('-', df.replace(['-'], [None])sieht funky aus, ist das ein Tippfehler?
lin_bug

2
@lin_bug Obwohl es scheint, dass das in den letzten Pandas-Versionen nicht mehr funktioniert. df.where (df! = '-', None) arbeitet
Andy Hayden

16

Ich bevorzuge die Lösung replacemit einem dictwegen seiner Einfachheit und Eleganz:

df.replace({'-': None})

Sie können auch mehr Ersatz haben:

df.replace({'-': None, 'None': None})

Und selbst bei größeren Ersetzungen ist immer klar und deutlich, was durch was ersetzt wird - was meiner Meinung nach für lange Listen viel schwieriger ist.


1
Es ist erwähnenswert, dass ein Teil der Gründe für diese Technik darin besteht, dass die Verwendung des dictTyps in dazu führt, to_replacedass der methodParameter nicht ausgewertet wird und der method='pad'Standardwert daher keine negativen Auswirkungen hat.
bsplosion

15

whereist wahrscheinlich das, wonach Sie suchen. Damit

data=data.where(data=='-', None) 

Aus den Panda-Dokumenten :

where [gibt] ein Objekt zurück, das dieselbe Form wie self hat und dessen entsprechende Einträge von self stammen, wobei cond True ist und ansonsten von other).


5
Das ist eigentlich ungenau. data = data.where (data == '-', None) ersetzt alles, was nicht gleich '-' ist, durch None. Die Pandas-Version von where behält den Wert des ersten Arguments (in diesem Fall data == '-') bei und ersetzt alles andere durch das zweite Argument (in diesem Fall None). Es ist ein bisschen verwirrend, da np.where insofern expliziter ist, als es die Bedingung im ersten Argument fragt, dann das if true im zweiten Argument und das if false im dritten Argument.
Clg4

7

Bevor Sie mit diesem Beitrag fortfahren, ist es wichtig, den Unterschied zwischen NaN und None zu verstehen . Einer ist ein Float-Typ, der andere ist ein Objekttyp. Pandas eignet sich besser für die Arbeit mit Skalartypen, da viele Methoden für diese Typen vektorisiert werden können. Pandas versucht zwar, None und NaN konsistent zu behandeln, NumPy jedoch nicht.

Mein Vorschlag ( und der von Andy ) ist, bei NaN zu bleiben.

Aber um Ihre Frage zu beantworten ...

pandas> = 0.18: na_values=['-']Argument mit verwendenread_csv

Wenn Sie diese Daten aus CSV / Excel geladen haben, habe ich gute Nachrichten für Sie. Sie können dies während des Ladens der Daten im Stammverzeichnis löschen, anstatt als nachfolgenden Schritt einen Fix mit Code schreiben zu müssen.

Die meisten pd.read_*Funktionen (wie read_csvund read_excel) akzeptieren ein na_valuesAttribut.

file.csv

A,B
-,1
3,-
2,-
5,3
1,-2
-5,4
-1,-1
-,0
9,0

-Um die Zeichen in NaNs umzuwandeln, tun Sie Folgendes:

import pandas as pd
df = pd.read_csv('file.csv', na_values=['-'])
df

     A    B
0  NaN  1.0
1  3.0  NaN
2  2.0  NaN
3  5.0  3.0
4  1.0 -2.0
5 -5.0  4.0
6 -1.0 -1.0
7  NaN  0.0
8  9.0  0.0

Ähnliches gilt für andere Funktionen / Dateiformate.

PS: In Version 0.24 + können Sie den Integer-Typ beibehalten, auch wenn Ihre Spalte NaNs enthält (ja, sprechen Sie davon, den Kuchen zu haben und ihn auch zu essen). Sie können angebendtype='Int32'

df = pd.read_csv('file.csv', na_values=['-'], dtype='Int32')
df

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

df.dtypes

A    Int32
B    Int32
dtype: object

Der dtype ist kein herkömmlicher int-Typ ... sondern ein nullable Integer Type. Es gibt andere Möglichkeiten.


Umgang mit numerischen Daten: pd.to_numericmiterrors='coerce

Wenn Sie mit numerischen Daten arbeiten, besteht eine schnellere Lösung darin, pd.to_numericdas errors='coerce'Argument zu verwenden, das ungültige Werte (Werte, die nicht in numerische Werte umgewandelt werden können) zu NaN zwingt.

pd.to_numeric(df['A'], errors='coerce')

0    NaN
1    3.0
2    2.0
3    5.0
4    1.0
5   -5.0
6   -1.0
7    NaN
8    9.0
Name: A, dtype: float64

Verwenden Sie, um den (nullbaren) ganzzahligen Typ beizubehalten

pd.to_numeric(df['A'], errors='coerce').astype('Int32')

0    NaN
1      3
2      2
3      5
4      1
5     -5
6     -1
7    NaN
8      9
Name: A, dtype: Int32 

Verwenden Sie zum Erzwingen mehrerer Spalten apply:

df[['A', 'B']].apply(pd.to_numeric, errors='coerce').astype('Int32')

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

... und ordnen Sie das Ergebnis danach wieder zu.

Weitere Informationen finden Sie in dieser Antwort .


2
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df = df.where(df!='-', None)

0

Das Setzen von Nullwerten kann erfolgen mit np.nan:

import numpy as np
df.replace('-', np.nan)

Vorteil ist, dass df.last_valid_index()diese als ungültig erkannt werden.


0

Verwenden von Ersetzen und Zuweisen eines neuen df:

import pandas as pd
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
dfnew = df.replace('-', 0)
print(dfnew)


(venv) D:\assets>py teste2.py
   0
0  0
1  3
2  2
3  5
4  1
5 -5

0
df.replace('-', np.nan).astype("object")

Dadurch wird sichergestellt, dass Sie isnull()Ihren Datenrahmen später verwenden können


0

Mit Pandas Version ≥1.0.0 würde ich verwenden DataFrame.replaceoder Series.replace:

df.replace(old_val, pd.NA, inplace=True)

Dies ist aus zwei Gründen besser:

  1. Es verwendet pd.NAanstelle von Noneoder np.nan.
  2. Er ersetzt den Wert an Ort und Stelle , die mehr Speicher effizient sein könnte.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.