Ersetzen von Leerwerten (Leerzeichen) durch NaN in Pandas

150

Ich möchte alle Werte in einem Pandas-Datenrahmen finden, die Leerzeichen (eine beliebige Menge) enthalten, und diese Werte durch NaNs ersetzen.

Irgendwelche Ideen, wie dies verbessert werden kann?

Grundsätzlich möchte ich das umdrehen:

                   A    B    C
2000-01-01 -0.532681  foo    0
2000-01-02  1.490752  bar    1
2000-01-03 -1.387326  foo    2
2000-01-04  0.814772  baz     
2000-01-05 -0.222552         4
2000-01-06 -1.176781  qux     

Das mögen:

                   A     B     C
2000-01-01 -0.532681   foo     0
2000-01-02  1.490752   bar     1
2000-01-03 -1.387326   foo     2
2000-01-04  0.814772   baz   NaN
2000-01-05 -0.222552   NaN     4
2000-01-06 -1.176781   qux   NaN

Ich habe es mit dem folgenden Code geschafft, aber Mann, ist es hässlich. Es ist nicht Pythonic und ich bin sicher, es ist auch nicht die effizienteste Verwendung von Pandas. Ich durchlaufe jede Spalte und ersetze sie boolesch gegen eine Spaltenmaske, die durch Anwenden einer Funktion generiert wird, die eine Regex-Suche für jeden Wert durchführt und mit Leerzeichen übereinstimmt.

for i in df.columns:
    df[i][df[i].apply(lambda i: True if re.search('^\s*$', str(i)) else False)]=None

Es könnte ein wenig optimiert werden, indem nur Felder durchlaufen werden, die leere Zeichenfolgen enthalten könnten:

if df[i].dtype == np.dtype('object')

Aber das ist keine große Verbesserung

Und schließlich setzt dieser Code die Zielzeichenfolgen auf Keine, was mit Pandas 'Funktionen wie funktioniert fillna(), aber der Vollständigkeit halber wäre es schön, wenn ich tatsächlich eine NaNdirekt anstelle von einfügen könnte None.

Chris Clark
quelle
2
Was Sie wirklich wollen, ist, replacemit einem regulären Ausdruck arbeiten zu können ... (vielleicht sollte dies als Funktion angefordert werden).
Andy Hayden
3
Ich habe ein Github-Problem für diese Funktion erstellt: github.com/pydata/pandas/issues/2285 . Wäre dankbar für PRs! :)
Chang She
Für diejenigen, die genau ein einzelnes leeres Zeichen zum Fehlen bringen möchten, siehe diese einfache Lösung unten
Ted Petrou

Antworten:

198

Ich denke df.replace()macht den Job, seit Pandas 0.13 :

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

# replace field that's entirely space (or empty) with NaN
print(df.replace(r'^\s*$', np.nan, regex=True))

Produziert:

                   A    B   C
2000-01-01 -0.532681  foo   0
2000-01-02  1.490752  bar   1
2000-01-03 -1.387326  foo   2
2000-01-04  0.814772  baz NaN
2000-01-05 -0.222552  NaN   4
2000-01-06 -1.176781  qux NaN

Verwenden Sie, wie Temak betonte, den df.replace(r'^\s+$', np.nan, regex=True)Fall, dass Ihre gültigen Daten Leerzeichen enthalten.

Patricksurry
quelle
1
Regex ist eine boolesche Flagge. Vielleicht meinst du pd.Series(["1", "#", "9", " .", None]).replace(r"( +\.)|#", "X", regex=True).valueswas gibt['1', 'X', '9', 'X', None]
patricksurry
2
2 Jahre später habe ich die akzeptierte Antwort darauf geändert, jetzt, wo Pandas sie unterstützt. Vielen Dank!
Chris Clark
35
HINWEIS : Wenn Sie in der Mitte Aufnahmeraum kein Element wollen mit NaN Verwendung ersetzt werdendf.replace(r'^\s+$', np.nan, regex=True)
TeMaK
7
Ich habe versucht, dies zu verwenden, aber herausgefunden, dass r '^ \ s * $' der zu verwendende Ausdruck sein sollte. ohne ^ und $ wird jede Zeichenfolge mit zwei aufeinander folgenden Leerzeichen abgeglichen. Auch + in * geändert, um die leere Zeichenfolge "" in die Liste der Dinge aufzunehmen, die in NaN konvertiert werden sollen
Master Yogurt
1
Ich versuche Ihre Lösung in meinem Code, aber sie hat keine Auswirkungen. Ich versuche "Energie [" Energieversorgung "]. Ersetzen (to_replace =" ... ", Wert = np.NaN)". Sie möchten die Zeichenfolge "..." in NaN-Werte ändern, tun jedoch nichts und geben denselben Datenrahmen zurück.
Archan Joshi
48

Wenn Sie eine leere Zeichenfolge und Datensätze durch Leerzeichen ersetzen möchten, lautet die richtige Antwort !

df = df.replace(r'^\s*$', np.nan, regex=True)

Die akzeptierte Antwort

df.replace(r'\s+', np.nan, regex=True)

Ersetzt keine leere Zeichenfolge!, Können Sie sich mit dem angegebenen Beispiel leicht aktualisieren versuchen:

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'fo o', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', ''],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

Beachten Sie auch, dass 'fo o' nicht durch Nan ersetzt wird, obwohl es ein Leerzeichen enthält. Weitere Anmerkung, dass eine einfache:

df.replace(r'', np.NaN)

Funktioniert auch nicht - probieren Sie es aus.

Philipp Schwarz
quelle
33

Wie wäre es mit:

d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)

Die applymapFunktion wendet eine Funktion auf jede Zelle des Datenrahmens an.

BrenBarn
quelle
Was für eine schöne Verbesserung! Ich hätte im Nachhinein darüber nachdenken sollen, war aber aus irgendeinem Grund daran gehindert, boolesche Ersetzungen vorzunehmen. Eine Frage: Gibt es einen Vorteil bei der Durchführung des Basisstring-Checks gegenüber nur str (x) .isspace ()?
Chris Clark
1
@ ChrisClark: Beides ist in Ordnung, obwohl ich vermuten würde, dass das isinstanceetwas schneller sein wird.
BrenBarn
13
Der Verweis auf "basestring" im obigen Code funktioniert in Python 3 nicht. Versuchen Sie in diesem Fall stattdessen "str".
Spike Williams
4
Beachten Sie, dass diese Lösung keine leeren Zeichenfolgen ersetzt ''. Um auch leere Zeichenfolgen zu berücksichtigen, verwenden Sie:d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and (not x or x.isspace()) else x)
Tuomastik
18

Ich werde das tun:

df = df.apply(lambda x: x.str.strip()).replace('', np.nan)

oder

df = df.apply(lambda x: x.str.strip() if isinstance(x, str) else x).replace('', np.nan)

Sie können alle str abisolieren und dann leere str durch ersetzen np.nan.

Xiaorong Liao
quelle
Lambda x: x.str.strip () sollte Lambda x: x.strip () sein? kleiner Vorschlag: Fügen Sie .astype (str) vor, dies löst andere Datenprobleme für mich. Das funktioniert bei mir: df = df.apply ['column']. Astype (str) .apply (lambda x: x.strip ()). Replace ('', np.nan)
Wouter
Die zweite Codezeile behandelt sowohl int / float- als auch string-artige Spalten. Nett. Tks!
Kate Stohr
6

Einfachste aller Lösungen:

df = df.replace(r'^\s+$', np.nan, regex=True)
Gil Baggio
quelle
5

Wenn Sie die Daten aus der CSV-Datei exportieren, kann dies so einfach sein:

df = pd.read_csv(file_csv, na_values=' ')

Dadurch wird der Datenrahmen erstellt und leere Werte als Na ersetzt

ibrahim rupawala
quelle
2
Eine andere Option .. using skipinitialspace=Trueentfernt auch Leerzeichen nach dem Trennzeichen, die dazu führen würden, dass leere Zeichenfolgen mit beliebiger Länge von Leerzeichen gelesen werden nan. Wenn Sie jedoch die anfänglichen Leerzeichen aus irgendeinem Grund beibehalten möchten, ist diese Option keine gute Wahl.
Rajshekar Reddy
1
@ RajshekarReddy kannst du das bitte irgendwo als Antwort setzen, das war genial!
User2321
2

Für eine sehr schnelle und einfache Lösung, bei der Sie die Gleichheit mit einem einzelnen Wert vergleichen, können Sie die maskMethode verwenden.

df.mask(df == ' ')
Ted Petrou
quelle
1

Diese sind alle nahe an der richtigen Antwort, aber ich würde nicht sagen, dass sie das Problem lösen, während sie für andere, die Ihren Code lesen, am besten lesbar bleiben. Ich würde sagen, dass die Antwort eine Kombination aus BrenBarns Antwort und Tuomasttiks Kommentar unter dieser Antwort ist . Die Antwort von isspaceBrenBarn verwendet integrierte Funktionen, unterstützt jedoch nicht das Entfernen leerer Zeichenfolgen, wie von OP angefordert, und ich würde dies eher als Standardanwendungsfall für das Ersetzen von Zeichenfolgen durch null angeben.

Ich habe es mit umgeschrieben .apply, damit du es auf einem pd.Seriesoder aufrufen kannst pd.DataFrame.


Python 3:

So ersetzen Sie leere Zeichenfolgen oder Zeichenfolgen mit vollständig Leerzeichen:

df = df.apply(lambda x: np.nan if isinstance(x, str) and (x.isspace() or not x) else x)

So ersetzen Sie Zeichenfolgen mit vollständig Leerzeichen:

df = df.apply(lambda x: np.nan if isinstance(x, str) and x.isspace() else x)

Um dies zu verwenden in Python 2, müssen Sie ersetzen strmit basestring.

Python 2:

So ersetzen Sie leere Zeichenfolgen oder Zeichenfolgen mit vollständig Leerzeichen:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and (x.isspace() or not x) else x)

So ersetzen Sie Zeichenfolgen mit vollständig Leerzeichen:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)
spen.smith
quelle
1

Das hat bei mir funktioniert. Beim Importieren meiner CSV-Datei habe ich na_values ​​= '' hinzugefügt. Leerzeichen sind in den Standard-NaN-Werten nicht enthalten.

df = pd.read_csv (Dateipfad, na_values ​​= '')

Sambrowne
quelle
0

Sie können dazu auch einen Filter verwenden.

df = PD.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '])
    df[df=='']='nan'
    df=df.astype(float)
ERIC
quelle
Jede Zeile dieses Codes (ohne Daten) ist fehlerhaft.
Julius
0
print(df.isnull().sum()) # check numbers of null value in each column

modifiedDf=df.fillna("NaN") # Replace empty/null values with "NaN"

# modifiedDf = fd.dropna() # Remove rows with empty values

print(modifiedDf.isnull().sum()) # check numbers of null value in each column
Jayantha
quelle
0

Dies ist keine elegante Lösung, aber was zu funktionieren scheint, ist das Speichern in XLSX und das anschließende Importieren. Die anderen Lösungen auf dieser Seite haben bei mir nicht funktioniert, ich weiß nicht warum.

data.to_excel(filepath, index=False)
data = pd.read_excel(filepath)
David Kong
quelle