Pandas erstellen eine neue Spalte basierend auf Werten aus anderen Spalten / wenden eine Funktion mehrerer Spalten zeilenweise an

316

Ich mag , dass meine benutzerdefinierte Funktion anzuwenden (es verwendet einen if-else - Leiter) zu diesen sechs Säulen ( ERI_Hispanic, ERI_AmerInd_AKNatv, ERI_Asian, ERI_Black_Afr.Amer, ERI_HI_PacIsl, ERI_White) in jeder Reihe meines Datenrahmen.

Ich habe andere Methoden als andere Fragen ausprobiert, kann aber immer noch nicht die richtige Antwort für mein Problem finden. Das Entscheidende dabei ist, dass die Person, wenn sie als Hispanic gezählt wird, nicht als etwas anderes gezählt werden kann. Selbst wenn sie eine "1" in einer anderen ethnischen Spalte haben, werden sie immer noch als Hispanic gezählt, nicht als zwei oder mehr Rassen. Wenn die Summe aller ERI-Spalten größer als 1 ist, werden sie als zwei oder mehr Rassen gezählt und können nicht als eindeutige ethnische Zugehörigkeit gezählt werden (außer Hispanic). Hoffentlich macht das Sinn. Jede Hilfe wird sehr geschätzt.

Es ist fast so, als würde man eine for-Schleife durch jede Zeile führen. Wenn jeder Datensatz ein Kriterium erfüllt, werden sie zu einer Liste hinzugefügt und aus dem Original entfernt.

Aus dem folgenden Datenrahmen muss ich eine neue Spalte basierend auf der folgenden Spezifikation in SQL berechnen:

========================= KRITERIEN ======================== =======

IF [ERI_Hispanic] = 1 THEN RETURN Hispanic
ELSE IF SUM([ERI_AmerInd_AKNatv] + [ERI_Asian] + [ERI_Black_Afr.Amer] + [ERI_HI_PacIsl] + [ERI_White]) > 1 THEN RETURN Two or More
ELSE IF [ERI_AmerInd_AKNatv] = 1 THEN RETURN A/I AK Native
ELSE IF [ERI_Asian] = 1 THEN RETURN Asian
ELSE IF [ERI_Black_Afr.Amer] = 1 THEN RETURN Black/AA
ELSE IF [ERI_HI_PacIsl] = 1 THEN RETURN Haw/Pac Isl.”
ELSE IF [ERI_White] = 1 THEN RETURN White

Kommentar: Wenn das ERI-Flag für Hispanic True ist (1), wird der Mitarbeiter als „Hispanic“ klassifiziert.

Kommentar: Wenn mehr als 1 nicht-hispanisches ERI-Flag wahr ist, geben Sie "Zwei oder mehr" zurück.

===================== DATAFRAME ============================

     lname          fname       rno_cd  eri_afr_amer    eri_asian   eri_hawaiian    eri_hispanic    eri_nat_amer    eri_white   rno_defined
0    MOST           JEFF        E       0               0           0               0               0               1           White
1    CRUISE         TOM         E       0               0           0               1               0               0           White
2    DEPP           JOHNNY              0               0           0               0               0               1           Unknown
3    DICAP          LEO                 0               0           0               0               0               1           Unknown
4    BRANDO         MARLON      E       0               0           0               0               0               0           White
5    HANKS          TOM         0                       0           0               0               0               1           Unknown
6    DENIRO         ROBERT      E       0               1           0               0               0               1           White
7    PACINO         AL          E       0               0           0               0               0               1           White
8    WILLIAMS       ROBIN       E       0               0           1               0               0               0           White
9    EASTWOOD       CLINT       E       0               0           0               0               0               1           White
Dave
quelle
Ihre spezielle Funktion ist nur eine lange If-else-Leiter, in der die Werte einiger Variablen Vorrang vor anderen haben. Es würde im Hardware-Engineering-Sprachgebrauch als Prioritätsdecoder bezeichnet .
smci

Antworten:

407

OK, zwei Schritte dazu - der erste besteht darin, eine Funktion zu schreiben, die die gewünschte Übersetzung ausführt - Ich habe ein Beispiel zusammengestellt, das auf Ihrem Pseudocode basiert:

def label_race (row):
   if row['eri_hispanic'] == 1 :
      return 'Hispanic'
   if row['eri_afr_amer'] + row['eri_asian'] + row['eri_hawaiian'] + row['eri_nat_amer'] + row['eri_white'] > 1 :
      return 'Two Or More'
   if row['eri_nat_amer'] == 1 :
      return 'A/I AK Native'
   if row['eri_asian'] == 1:
      return 'Asian'
   if row['eri_afr_amer']  == 1:
      return 'Black/AA'
   if row['eri_hawaiian'] == 1:
      return 'Haw/Pac Isl.'
   if row['eri_white'] == 1:
      return 'White'
   return 'Other'

Möglicherweise möchten Sie dies durchgehen, aber es scheint den Trick zu tun - beachten Sie, dass der Parameter, der in die Funktion eingeht, als Serienobjekt mit der Bezeichnung "Zeile" betrachtet wird.

Verwenden Sie als Nächstes die Apply-Funktion in Pandas, um die Funktion anzuwenden - z

df.apply (lambda row: label_race(row), axis=1)

Beachten Sie den Bezeichner axis = 1, dh, die Anwendung erfolgt auf Zeilen- und nicht auf Spaltenebene. Die Ergebnisse sind hier:

0           White
1        Hispanic
2           White
3           White
4           Other
5           White
6     Two Or More
7           White
8    Haw/Pac Isl.
9           White

Wenn Sie mit diesen Ergebnissen zufrieden sind, führen Sie sie erneut aus und speichern Sie die Ergebnisse in einer neuen Spalte in Ihrem ursprünglichen Datenrahmen.

df['race_label'] = df.apply (lambda row: label_race(row), axis=1)

Der resultierende Datenrahmen sieht folgendermaßen aus (scrollen Sie nach rechts, um die neue Spalte anzuzeigen):

      lname   fname rno_cd  eri_afr_amer  eri_asian  eri_hawaiian   eri_hispanic  eri_nat_amer  eri_white rno_defined    race_label
0      MOST    JEFF      E             0          0             0              0             0          1       White         White
1    CRUISE     TOM      E             0          0             0              1             0          0       White      Hispanic
2      DEPP  JOHNNY    NaN             0          0             0              0             0          1     Unknown         White
3     DICAP     LEO    NaN             0          0             0              0             0          1     Unknown         White
4    BRANDO  MARLON      E             0          0             0              0             0          0       White         Other
5     HANKS     TOM    NaN             0          0             0              0             0          1     Unknown         White
6    DENIRO  ROBERT      E             0          1             0              0             0          1       White   Two Or More
7    PACINO      AL      E             0          0             0              0             0          1       White         White
8  WILLIAMS   ROBIN      E             0          0             1              0             0          0       White  Haw/Pac Isl.
9  EASTWOOD   CLINT      E             0          0             0              0             0          1       White         White
Thomas Kimber
quelle
69
Nur eine Anmerkung: Wenn Sie nur die Zeile in Ihre Funktion einspeisen, können Sie einfach df.apply(label_race, axis=1)
Paul H
1
Wenn ich etwas Ähnliches mit einer anderen Zeile machen wollte, könnte ich dieselbe Funktion verwenden? Zum Beispiel aus den Ergebnissen, wenn ['race_label'] == "White" 'White' zurückgibt und so weiter. Wenn jedoch ['race_label'] == 'Unknown' die Werte aus der Spalte ['rno_defined'] zurückgibt. Ich gehe davon aus, dass dieselbe Funktion funktionieren würde, aber ich kann anscheinend nicht herausfinden, wie die Werte aus der anderen Spalte abgerufen werden.
Dave
2
Sie könnten eine neue Funktion schreiben, die sich das Feld 'race_label' ansieht, und die Ergebnisse in ein neues Feld senden, oder - und ich denke, dies ist in diesem Fall besser, die ursprüngliche Funktion bearbeiten und die letzte return 'Other'Zeile ändern , in return row['rno_defined']die dies erfolgen soll Ersetzen Sie den Wert aus dieser Spalte in den Fällen, in denen die Menge der if / then-Anweisungen keine Übereinstimmung findet (dh wo derzeit "Andere" angezeigt wird).
Thomas Kimber
9
Sie können vereinfachen: df.apply(lambda row: label_race (row),axis=1)zudf.apply(label_race, axis=1)
Benutzer48956
5
Wenn Sie in neueren Versionen 'SettingWithCopyWarning' erhalten, sollten Sie sich die Methode 'assign' ansehen. Siehe: stackoverflow.com/a/12555510/3015186
np8
218

Da dies das erste Google-Ergebnis für "Pandas neue Spalte von anderen" ist, ist hier ein einfaches Beispiel:

import pandas as pd

# make a simple dataframe
df = pd.DataFrame({'a':[1,2], 'b':[3,4]})
df
#    a  b
# 0  1  3
# 1  2  4

# create an unattached column with an index
df.apply(lambda row: row.a + row.b, axis=1)
# 0    4
# 1    6

# do same but attach it to the dataframe
df['c'] = df.apply(lambda row: row.a + row.b, axis=1)
df
#    a  b  c
# 0  1  3  4
# 1  2  4  6

Wenn Sie das bekommen SettingWithCopyWarning, können Sie es auch so machen:

fn = lambda row: row.a + row.b # define a function for the new column
col = df.apply(fn, axis=1) # get column data with an index
df = df.assign(c=col.values) # assign values to column 'c'

Quelle: https://stackoverflow.com/a/12555510/243392

Und wenn Ihr Spaltenname Leerzeichen enthält, können Sie die folgende Syntax verwenden:

df = df.assign(**{'some column name': col.values})

Und hier ist die Dokumentation zum Anwenden und Zuweisen .

Brian Burns
quelle
1
Kurze Antwort, auf das Wesentliche reduziert!
Frode Akselsen
1
Ich bekomme das, SettingWithCopyWarningwenn ich es tue. df['c'] = df.apply(lambda row: row.a + row.b, axis=1) Ist das hier ein echtes Problem, oder sollte ich mir darüber keine Sorgen machen?
Nate
2
@Nate Ich habe diese Warnung nie erhalten - vielleicht hängt es von den Daten im Datenrahmen ab? Aber ich habe die Antwort basierend auf einer anderen Antwort aus dem Jahr 2017 geändert.
Brian Burns
57

Die obigen Antworten sind vollkommen gültig, aber es gibt eine vektorisierte Lösung in Form von numpy.select. Auf diese Weise können Sie Bedingungen definieren und dann Ausgaben für diese Bedingungen definieren, viel effizienter als mit apply:


Definieren Sie zunächst die Bedingungen:

conditions = [
    df['eri_hispanic'] == 1,
    df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1),
    df['eri_nat_amer'] == 1,
    df['eri_asian'] == 1,
    df['eri_afr_amer'] == 1,
    df['eri_hawaiian'] == 1,
    df['eri_white'] == 1,
]

Definieren Sie nun die entsprechenden Ausgänge:

outputs = [
    'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
]

Schließlich mit numpy.select:

res = np.select(conditions, outputs, 'Other')
pd.Series(res)

0           White
1        Hispanic
2           White
3           White
4           Other
5           White
6     Two Or More
7           White
8    Haw/Pac Isl.
9           White
dtype: object

Warum sollte numpy.selectüber verwendet werden apply? Hier einige Leistungsprüfungen:

df = pd.concat([df]*1000)

In [42]: %timeit df.apply(lambda row: label_race(row), axis=1)
1.07 s ± 4.16 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [44]: %%timeit
    ...: conditions = [
    ...:     df['eri_hispanic'] == 1,
    ...:     df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1),
    ...:     df['eri_nat_amer'] == 1,
    ...:     df['eri_asian'] == 1,
    ...:     df['eri_afr_amer'] == 1,
    ...:     df['eri_hawaiian'] == 1,
    ...:     df['eri_white'] == 1,
    ...: ]
    ...:
    ...: outputs = [
    ...:     'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
    ...: ]
    ...:
    ...: np.select(conditions, outputs, 'Other')
    ...:
    ...:
3.09 ms ± 17 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Durch numpy.selectdie Verwendung können wir die Leistung erheblich verbessern, und die Diskrepanz nimmt nur zu, wenn die Daten wachsen.

user3483203
quelle
8
Diese Lösung wird so unterschätzt. Ich wusste, dass ich mit apply etwas Ähnliches tun könnte, suchte aber nach einer Alternative, da ich diesen Vorgang für Tausende von Dateien ausführen muss. Ich bin so froh, dass ich deinen Beitrag gefunden habe.
mlx
Ich habe Probleme damit, etwas Ähnliches zu erstellen. Ich erhalte die Fehlermeldung "Der Wahrheitswert einer Serie ist mehrdeutig ...". Mein Code lautet Kansas_City = ['ND', 'SD', 'NE', 'KS', 'MN', 'IA', 'MO'] Bedingungen = [df_merge ['state_alpha'] in Kansas_City] output = [' Kansas City '] df_merge [' Region '] = np.select (Bedingungen, Ausgaben,' Andere ') Kann mir jemand helfen?
Shawn Schreier
3
Dies sollte die akzeptierte Antwort sein. Die anderen sind in Ordnung, aber sobald Sie mit größeren Datenmengen arbeiten, ist diese die einzige, die funktioniert, und sie funktioniert erstaunlich schnell.
TheProletariat
29

.apply()nimmt eine Funktion als ersten Parameter auf; Übergeben Sie die label_raceFunktion wie folgt:

df['race_label'] = df.apply(label_race, axis=1)

Sie müssen keine Lambda-Funktion erstellen, um eine Funktion zu übergeben.

Gabrielle Simard-Moore
quelle
12

Versuche dies,

df.loc[df['eri_white']==1,'race_label'] = 'White'
df.loc[df['eri_hawaiian']==1,'race_label'] = 'Haw/Pac Isl.'
df.loc[df['eri_afr_amer']==1,'race_label'] = 'Black/AA'
df.loc[df['eri_asian']==1,'race_label'] = 'Asian'
df.loc[df['eri_nat_amer']==1,'race_label'] = 'A/I AK Native'
df.loc[(df['eri_afr_amer'] + df['eri_asian'] + df['eri_hawaiian'] + df['eri_nat_amer'] + df['eri_white']) > 1,'race_label'] = 'Two Or More'
df.loc[df['eri_hispanic']==1,'race_label'] = 'Hispanic'
df['race_label'].fillna('Other', inplace=True)

O / P:

     lname   fname rno_cd  eri_afr_amer  eri_asian  eri_hawaiian  \
0      MOST    JEFF      E             0          0             0   
1    CRUISE     TOM      E             0          0             0   
2      DEPP  JOHNNY    NaN             0          0             0   
3     DICAP     LEO    NaN             0          0             0   
4    BRANDO  MARLON      E             0          0             0   
5     HANKS     TOM    NaN             0          0             0   
6    DENIRO  ROBERT      E             0          1             0   
7    PACINO      AL      E             0          0             0   
8  WILLIAMS   ROBIN      E             0          0             1   
9  EASTWOOD   CLINT      E             0          0             0   

   eri_hispanic  eri_nat_amer  eri_white rno_defined    race_label  
0             0             0          1       White         White  
1             1             0          0       White      Hispanic  
2             0             0          1     Unknown         White  
3             0             0          1     Unknown         White  
4             0             0          0       White         Other  
5             0             0          1     Unknown         White  
6             0             0          1       White   Two Or More  
7             0             0          1       White         White  
8             0             0          0       White  Haw/Pac Isl.  
9             0             0          1       White         White 

verwenden .locstatt apply.

es verbessert die Vektorisierung.

.loc funktioniert auf einfache Weise, maskiert Zeilen basierend auf der Bedingung, wendet Werte auf die Einfrierzeilen an.

Weitere Informationen finden Sie unter .loc docs

Leistungsmetriken:

Akzeptierte Antwort:

def label_race (row):
   if row['eri_hispanic'] == 1 :
      return 'Hispanic'
   if row['eri_afr_amer'] + row['eri_asian'] + row['eri_hawaiian'] + row['eri_nat_amer'] + row['eri_white'] > 1 :
      return 'Two Or More'
   if row['eri_nat_amer'] == 1 :
      return 'A/I AK Native'
   if row['eri_asian'] == 1:
      return 'Asian'
   if row['eri_afr_amer']  == 1:
      return 'Black/AA'
   if row['eri_hawaiian'] == 1:
      return 'Haw/Pac Isl.'
   if row['eri_white'] == 1:
      return 'White'
   return 'Other'

df=pd.read_csv('dataser.csv')
df = pd.concat([df]*1000)

%timeit df.apply(lambda row: label_race(row), axis=1)

1,15 s ± 46,5 ms pro Schleife (Mittelwert ± Standardabweichung von 7 Läufen, jeweils 1 Schleife)

Meine vorgeschlagene Antwort:

def label_race(df):
    df.loc[df['eri_white']==1,'race_label'] = 'White'
    df.loc[df['eri_hawaiian']==1,'race_label'] = 'Haw/Pac Isl.'
    df.loc[df['eri_afr_amer']==1,'race_label'] = 'Black/AA'
    df.loc[df['eri_asian']==1,'race_label'] = 'Asian'
    df.loc[df['eri_nat_amer']==1,'race_label'] = 'A/I AK Native'
    df.loc[(df['eri_afr_amer'] + df['eri_asian'] + df['eri_hawaiian'] + df['eri_nat_amer'] + df['eri_white']) > 1,'race_label'] = 'Two Or More'
    df.loc[df['eri_hispanic']==1,'race_label'] = 'Hispanic'
    df['race_label'].fillna('Other', inplace=True)
df=pd.read_csv('s22.csv')
df = pd.concat([df]*1000)

%timeit label_race(df)

24,7 ms ± 1,7 ms pro Schleife (Mittelwert ± Standardabweichung von 7 Läufen, jeweils 10 Schleifen)

Mohamed Thasin ah
quelle