Pandas bedingte Erstellung einer Serien- / Datenrahmenspalte


314

Ich habe einen Datenrahmen in der folgenden Richtung:

    Type       Set
1    A          Z
2    B          Z           
3    B          X
4    C          Y

Ich möchte dem Datenrahmen eine weitere Spalte mit der gleichen Länge wie der Datenrahmen (= gleiche Anzahl von Datensätzen / Zeilen) hinzufügen (oder eine Reihe generieren), die eine Farbe grün setzt, wenn Set = 'Z' und 'rot', wenn Set = andernfalls .

Was ist der beste Weg, dies zu tun?

Antworten:


712

Wenn Sie nur zwei Möglichkeiten zur Auswahl haben:

df['color'] = np.where(df['Set']=='Z', 'green', 'red')

Zum Beispiel,

import pandas as pd
import numpy as np

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
df['color'] = np.where(df['Set']=='Z', 'green', 'red')
print(df)

ergibt

  Set Type  color
0   Z    A  green
1   Z    B  green
2   X    B    red
3   Y    C    red

Wenn Sie mehr als zwei Bedingungen haben, verwenden Sie np.select . Zum Beispiel, wenn Sie wollen , colorsein

  • yellow wann (df['Set'] == 'Z') & (df['Type'] == 'A')
  • Andernfalls blue wenn(df['Set'] == 'Z') & (df['Type'] == 'B')
  • Andernfalls purple wenn(df['Type'] == 'B')
  • Andernfalls black ,

dann benutze

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
conditions = [
    (df['Set'] == 'Z') & (df['Type'] == 'A'),
    (df['Set'] == 'Z') & (df['Type'] == 'B'),
    (df['Type'] == 'B')]
choices = ['yellow', 'blue', 'purple']
df['color'] = np.select(conditions, choices, default='black')
print(df)

was ergibt

  Set Type   color
0   Z    A  yellow
1   Z    B    blue
2   X    B  purple
3   Y    C   black

1
funktioniert nicht, wenn ich zwei Bedingungen in wo Klausel mit und
Amol Sharma

2
df ['color'] = list (np.where (df ['Set'] == 'Z', 'green', 'red')) unterdrückt die Pandas-Warnung: Auf einer Kopie wird versucht, einen Wert festzulegen eines Slice aus einem DataFrame. Versuchen Sie stattdessen, .loc [row_indexer, col_indexer] = value zu verwenden
denson

3
'grün' und 'rot' können auch durch Spaltenarithmetik ersetzt werden. zB ,df['foo'] = np.where(df['Set']=='Z', df['Set'], df['Type'].shift(1))
Alejandro

Erstellt np.where eine neue Spalte? Ich habe diesen Code verwendet und wenn ich df.color.head () mache, bekomme ich: 'numpy.ndarray' Objekt hat kein Attribut 'head'
vvv

3
Es ist eine Schande, dass ich das nicht mehrmals positiv bewerten kann. Eine Gegenstimme scheint nicht genug zu sein.
Harper

120

Das Listenverständnis ist eine weitere Möglichkeit, eine weitere Spalte bedingt zu erstellen. Wenn Sie wie in Ihrem Beispiel mit Objekt-dtypes in Spalten arbeiten, übertreffen Listenverständnisse normalerweise die meisten anderen Methoden.

Beispiel Listenverständnis:

df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]

% timeit Tests:

import pandas as pd
import numpy as np

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
%timeit df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
%timeit df['color'] = np.where(df['Set']=='Z', 'green', 'red')
%timeit df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')

1000 loops, best of 3: 239 µs per loop
1000 loops, best of 3: 523 µs per loop
1000 loops, best of 3: 263 µs per loop

4
Beachten Sie, dass bei viel größeren Datenrahmen (think- pd.DataFrame({'Type':list('ABBC')*100000, 'Set':list('ZZXY')*100000})size) die numpy.whereOutpaces überschritten werden map, das Listenverständnis jedoch von entscheidender Bedeutung ist (ca. 50% schneller als numpy.where).
Blacksite

3
Kann die Listenverständnismethode verwendet werden, wenn die Bedingung Informationen aus mehreren Spalten benötigt? Ich suche so etwas (das funktioniert nicht):df['color'] = ['red' if (x['Set'] == 'Z') & (x['Type'] == 'B') else 'green' for x in df]
Mappi

2
Fügen Sie dem Datenrahmen Iterrows hinzu, und Sie können über die Zeile auf mehrere Spalten zugreifen: ['rot' if (Zeile ['Set'] == 'Z') & (Zeile ['Typ'] == 'B') else 'grün 'für Index, Zeile in df.iterrows ()]
Cheekybastard

1
Beachten Sie, dass diese nette Lösung nicht funktioniert, wenn Sie Ersatzwerte aus einer anderen Serie im df['color_type'] = np.where(df['Set']=='Z', 'green', df['Type'])
Datenrahmen übernehmen müssen

@cheekybastard Oder nicht, da dies .iterrows()notorisch träge ist und der DataFrame während der Iteration nicht geändert werden sollte.
AMC

21

Ein anderer Weg, wie dies erreicht werden könnte, ist

df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')

Guter Ansatz, dies kann für eine schnellere Effizienz (in größeren Datensätzen) gespeichert werden, würde jedoch einen zusätzlichen Schritt erfordern.
Yaakov Bressler

21

Hier ist noch eine andere Möglichkeit, diese Katze zu häuten, indem Sie mithilfe eines Wörterbuchs neue Werte auf die Schlüssel in der Liste abbilden:

def map_values(row, values_dict):
    return values_dict[row]

values_dict = {'A': 1, 'B': 2, 'C': 3, 'D': 4}

df = pd.DataFrame({'INDICATOR': ['A', 'B', 'C', 'D'], 'VALUE': [10, 9, 8, 7]})

df['NEW_VALUE'] = df['INDICATOR'].apply(map_values, args = (values_dict,))

Wie sieht es aus:

df
Out[2]: 
  INDICATOR  VALUE  NEW_VALUE
0         A     10          1
1         B      9          2
2         C      8          3
3         D      7          4

Dieser Ansatz kann sehr leistungsfähig sein, wenn Sie viele haben ifelse Anweisungen vom Typ geben müssen (dh viele eindeutige Werte, die ersetzt werden müssen).

Und das können Sie natürlich immer tun:

df['NEW_VALUE'] = df['INDICATOR'].map(values_dict)

Dieser Ansatz ist jedoch mehr als dreimal so langsam wie der apply auf meinem Computer Ansatz von oben.

Und Sie können dies auch tun, indem Sie dict.get:

df['NEW_VALUE'] = [values_dict.get(v, None) for v in df['INDICATOR']]

Ich mag diese Antwort, weil sie zeigt, wie man mehrere Werte ersetzt
Monica Heddneck

Dieser Ansatz ist jedoch mehr als dreimal so langsam wie der von oben angewendete Ansatz auf meinem Computer. Wie haben Sie diese bewertet? Nach meinen schnellen Messungen ist die .map()Lösung ~ 10-mal schneller als .apply().
AMC

Update: Bei 100.000.000 Zeilen .apply()dauert 52 Zeichenfolgenwerte 47 Sekunden, gegenüber nur 5,91 Sekunden .map().
AMC

19

Das Folgende ist langsamer als die hier zeitgesteuerten Ansätze , aber wir können die zusätzliche Spalte basierend auf dem Inhalt von mehr als einer Spalte berechnen, und es können mehr als zwei Werte für die zusätzliche Spalte berechnet werden.

Einfaches Beispiel mit nur der Spalte "Set":

def set_color(row):
    if row["Set"] == "Z":
        return "red"
    else:
        return "green"

df = df.assign(color=df.apply(set_color, axis=1))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C  green

Beispiel mit mehr Farben und mehr Spalten berücksichtigt:

def set_color(row):
    if row["Set"] == "Z":
        return "red"
    elif row["Type"] == "C":
        return "blue"
    else:
        return "green"

df = df.assign(color=df.apply(set_color, axis=1))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C   blue

Bearbeiten (21/06/2019): Verwenden von Plydata

Es ist auch möglich, Plydata zu verwenden, um diese Art von Dingen auszuführen (dies scheint jedoch noch langsamer zu sein als die Verwendung von assignund apply).

from plydata import define, if_else

Einfach if_else:

df = define(df, color=if_else('Set=="Z"', '"red"', '"green"'))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C  green

Verschachtelt if_else:

df = define(df, color=if_else(
    'Set=="Z"',
    '"red"',
    if_else('Type=="C"', '"green"', '"blue"')))

print(df)                            
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B   blue
3   Y    C  green

10

Vielleicht war dies mit neueren Updates von Pandas möglich, aber ich denke, das Folgende ist die kürzeste und vielleicht beste Antwort auf die Frage, die es bisher gab. Du kannst den ... benutzen.loc Methode verwenden und je nach Bedarf eine oder mehrere Bedingungen verwenden.

Code-Zusammenfassung:

df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))
df['Color'] = "red"
df.loc[(df['Set']=="Z"), 'Color'] = "green"

#practice!
df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"

Erläuterung:

df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))

# df so far: 
  Type Set  
0    A   Z 
1    B   Z 
2    B   X 
3    C   Y

Fügen Sie eine 'Farb'-Spalte hinzu und setzen Sie alle Werte auf "rot".

df['Color'] = "red"

Wenden Sie Ihre einzige Bedingung an:

df.loc[(df['Set']=="Z"), 'Color'] = "green"


# df: 
  Type Set  Color
0    A   Z  green
1    B   Z  green
2    B   X    red
3    C   Y    red

oder mehrere Bedingungen, wenn Sie möchten:

df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"

Sie können die logischen Operatoren und die bedingte Auswahl von Pandas hier nachlesen: Logische Operatoren für die boolesche Indizierung in Pandas


2
Das bisher beste. Sie könnten wahrscheinlich für weitere Bedingungen hinzufügen, die der Code wäredf.loc[(df['Set']=="Z") & (df['Type']=="A"), 'Color'] = "green"
Salvador Vigo

2
Dies sollte die akzeptierte Antwort sein. Eigentlich idiomatisch und erweiterbar.
AMC

1

Ein Liner mit .apply()Methode ist wie folgt:

df['color'] = df['Set'].apply(lambda set_: 'green' if set_=='Z' else 'red')

Danach dfsieht der Datenrahmen folgendermaßen aus:

>>> print(df)
  Type Set  color
0    A   Z  green
1    B   Z  green
2    B   X    red
3    C   Y    red

0

Wenn Sie mit massiven Daten arbeiten, ist ein gespeicherter Ansatz am besten:

# First create a dictionary of manually stored values
color_dict = {'Z':'red'}

# Second, build a dictionary of "other" values
color_dict_other = {x:'green' for x in df['Set'].unique() if x not in color_dict.keys()}

# Next, merge the two
color_dict.update(color_dict_other)

# Finally, map it to your column
df['color'] = df['Set'].map(color_dict)

Dieser Ansatz ist am schnellsten, wenn Sie viele wiederholte Werte haben. Meine allgemeine Faustregel lautet, sich zu merken, wann: data_size> 10**4& n_distinct<data_size/4

Ex Merken Sie sich in einem Fall 10.000 Zeilen mit 2.500 oder weniger unterschiedlichen Werten.


Okay, mit nur 2 unterschiedlichen Werten für die Zuordnung, 100.000.000 Zeilen, dauert es 6,67 Sekunden, um ohne "Memoization" ausgeführt zu werden, und 9,86 Sekunden mit.
AMC

100.000.000 Zeilen, 52 verschiedene Werte, wobei einer dieser Werte dem ersten Ausgabewert und die anderen 51 dem anderen entsprechen: 7,99 Sekunden ohne Speicherung, 11,1 Sekunden mit.
AMC

Sind Ihre Werte in zufälliger Reihenfolge? Oder sind sie Rücken an Rücken? Eine hohe Geschwindigkeit von Pandas könnte auf das Zwischenspeichern von @AMC
Yaakov Bressler

1
Sind Ihre Werte in zufälliger Reihenfolge? Oder sind sie Rücken an Rücken? Die Werte sind zufällig und werden mit ausgewähltrandom.choices() .
AMC
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.