578 Stimmen

Erneuern Sie Werte in der Pandas-Spalte mit einem Dictionary und bewahren Sie NaNs.

Ich habe ein Wörterbuch, das so aussieht: di = {1: "A", 2: "B"}

Ich möchte es auf die col1-Spalte eines ähnlichen Dataframes anwenden:

     col1   col2
0       w      a
1       1      2
2       2    NaN

um folgendes zu erhalten:

     col1   col2
0       w      a
1       A      2
2       B    NaN

Wie kann ich das am besten machen?

601voto

DSM Punkte 317386

Sie können .replace verwenden. Zum Beispiel:

>>> df = pd.DataFrame({'col2': {0: 'a', 1: 2, 2: np.nan}, 'col1': {0: 'w', 1: 1, 2: 2}})
>>> di = {1: "A", 2: "B"}
>>> df
  col1 col2
0    w    a
1    1    2
2    2  NaN
>>> df.replace({"col1": di})
  col1 col2
0    w    a
1    A    2
2    B  NaN

oder direkt auf der Series, d.h. df["col1"].replace(di, inplace=True).

542voto

JohnE Punkte 27183

map kann viel schneller sein als replace

Wenn Ihr Wörterbuch mehr als ein paar Schlüssel hat, kann die Verwendung von map viel schneller sein als replace. Es gibt zwei Versionen dieses Ansatzes, je nachdem, ob Ihr Wörterbuch alle möglichen Werte erschöpfend zuordnet (und ob Sie Nichtübereinstimmungen ihre Werte behalten oder in NaNs umwandeln möchten):

Erschöpfende Zuordnung

In diesem Fall ist die Form sehr einfach:

df['col1'].map(di)       # Hinweis: Wenn das Wörterbuch nicht alle Einträge erschöpfend zuordnet, werden Nichtübereinstimmungen in NaNs geändert

Obwohl map in der Regel eine Funktion als Argument erhält, kann es alternativ ein Wörterbuch oder eine Serie erhalten: Dokumentation für Pandas.series.map

Nicht-erschöpfende Zuordnung

Wenn Sie eine nicht-erschöpfende Zuordnung haben und die vorhandenen Variablen für Nichtübereinstimmungen beibehalten möchten, können Sie fillna hinzufügen:

df['col1'].map(di).fillna(df['col1'])

wie in @jpp's Antwort hier: Werte in einer Pandas-Serie effizient über ein Wörterbuch ersetzen

Leistungsmessungen

Mit den folgenden Daten und pandas-Version 0.23.1:

di = {1: "A", 2: "B", 3: "C", 4: "D", 5: "E", 6: "F", 7: "G", 8: "H" }
df = pd.DataFrame({ 'col1': np.random.choice( range(1,9), 100000 ) })

und testen mit %timeit, scheint map etwa 10x schneller als replace zu sein.

Beachten Sie, dass Ihr Geschwindigkeitsgewinn mit map je nach Daten variieren wird. Der größte Geschwindigkeitsgewinn scheint bei großen Wörterbüchern und erschöpfenden Ersetzungen zu sein. Siehe die Antwort von @jpp (oben verlinkt) für umfangreichere Leistungsmessungen und Diskussionen.

87voto

unutbu Punkte 769083

Es gibt eine gewisse Mehrdeutigkeit in Ihrer Frage. Es gibt mindestens drei zwei Interpretationen:

  1. die Schlüssel in di beziehen sich auf Indexwerte
  2. die Schlüssel in di beziehen sich auf df['col1'] Werte
  3. die Schlüssel in di beziehen sich auf Indexpositionen (nicht die Frage des OP, aber zur Unterhaltung hinzugefügt.)

Unten finden Sie eine Lösung für jeden Fall.


Fall 1: Wenn die Schlüssel von di auf Indexwerte verweisen sollen, könnten Sie die Methode update verwenden:

df['col1'].update(pd.Series(di))

Zum Beispiel,

import pandas as pd
import numpy as np

df = pd.DataFrame({'col1':['w', 10, 20],
                   'col2': ['a', 30, np.nan]},
                  index=[1,2,0])
#   col1 col2
# 1    w    a
# 2   10   30
# 0   20  NaN

di = {0: "A", 2: "B"}

# Der Wert des Index 0 wird auf 'A' abgebildet, der Wert des Index 2 wird auf 'B' abgebildet
df['col1'].update(pd.Series(di))
print(df)

ergibt

  col1 col2
1    w    a
2    B   30
0    A  NaN

Ich habe die Werte aus Ihrem Originalbeitrag geändert, sodass klarer wird, was update macht. Beachten Sie, wie die Schlüssel in di mit Indexwerten verknüpft sind. Die Reihenfolge der Indexwerte - das heißt, die Index Positionen - spielt keine Rolle.


Fall 2: Wenn die Schlüssel in di auf df['col1'] Werte verweisen, zeigen @DanAllan und @DSM, wie dies mit replace erreicht werden kann:

import pandas as pd
import numpy as np

df = pd.DataFrame({'col1':['w', 10, 20],
                   'col2': ['a', 30, np.nan]},
                  index=[1,2,0])
print(df)
#   col1 col2
# 1    w    a
# 2   10   30
# 0   20  NaN

di = {10: "A", 20: "B"}

# Die Werte 10 und 20 werden durch 'A' und 'B' ersetzt
df['col1'].replace(di, inplace=True)
print(df)

ergibt

  col1 col2
1    w    a
2    A   30
0    B  NaN

Beachten Sie, wie in diesem Fall die Schlüssel in di geändert wurden, um mit Werten in df['col1'] übereinzustimmen.


Fall 3: Wenn die Schlüssel in di auf Indexpositionen verweisen, könnten Sie

df['col1'].put(di.keys(), di.values())

verwenden, da

df = pd.DataFrame({'col1':['w', 10, 20],
                   'col2': ['a', 30, np.nan]},
                  index=[1,2,0])
di = {0: "A", 2: "B"}

# Die Werte an den Indexpositionen 0 und 2 werden durch 'A' und 'B' ersetzt
df['col1'].put(di.keys(), di.values())
print(df)

ergibt

  col1 col2
1    A    a
2   10   30
0    B  NaN

Hier wurden die erste und dritte Zeile geändert, weil die Schlüssel in di 0 und 2 sind, die mit der Python 0-basierten Indizierung auf die ersten und dritten Positionen verweisen.

14voto

wordsforthewise Punkte 10875

DSM hat die akzeptierte Antwort, aber der Code scheint nicht für alle zu funktionieren. Hier ist einer, der mit der aktuellen Version von pandas (0.23.4 vom 8/2018) funktioniert:

import pandas as pd

df = pd.DataFrame({'col1': [1, 2, 2, 3, 1],
            'col2': ['negativ', 'positiv', 'neutral', 'neutral', 'positiv']})

conversion_dict = {'negativ': -1, 'neutral': 0, 'positiv': 1}
df['converted_column'] = df['col2'].replace(conversion_dict)

print(df.head())

Sieht so aus:

   col1      col2  converted_column
0     1  negativ                -1
1     2  positiv                 1
2     2   neutral                 0
3     3   neutral                 0
4     1  positiv                 1

Die Dokumentation für pandas.DataFrame.replace finden Sie hier.

8voto

ALollz Punkte 58015

Angegeben map ist schneller als replace (@JohnE's Lösung) müssen Sie vorsichtig sein mit Nicht-erschöpfenden Zuordnungen, bei denen Sie bestimmte Werte auf NaN zuordnen möchten. Die geeignete Methode in diesem Fall erfordert, dass Sie die Serie mask, wenn Sie .fillna verwenden, sonst heben Sie die Zuordnung zu NaN auf.

import pandas as pd
import numpy as np

d = {'m': 'männlich', 'f': 'weiblich', 'fehlend': np.NaN}
df = pd.DataFrame({'geschlecht': ['m', 'f', 'fehlend', 'männlich', 'U']})

keep_nan = [k for k,v in d.items() if pd.isnull(v)]
s = df['geschlecht']

df['zugeordnet'] = s.map(d).fillna(s.mask(s.isin(keep_nan)))

    geschlecht  zugeordnet
0           m     männlich
1           f     weiblich
2     fehlend        NaN
3    männlich     männlich
4           U            U

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X