Erstellen Sie eine neue Spalte basierend auf Werten aus anderen Spalten / wenden Sie eine Funktion mehrerer Spalten zeilenweise in Pandas an
Lesezeit: 15 Minuten
David
Ich möchte meine benutzerdefinierte Funktion (sie verwendet eine if-else-Leiter) auf diese sechs Spalten anwenden (ERI_Hispanic, ERI_AmerInd_AKNatv, ERI_Asian, ERI_Black_Afr.Amer, ERI_HI_PacIsl, ERI_White) in jeder Zeile meines Datenrahmens.
Ich habe verschiedene Methoden aus anderen Fragen ausprobiert, kann aber immer noch nicht die richtige Antwort auf mein Problem finden. Der entscheidende Punkt dabei ist, dass, wenn die Person als Hispanoamerikaner gezählt wird, sie nicht als irgendetwas anderes gezählt werden kann. Selbst wenn sie eine “1” in einer anderen Ethnizitätsspalte haben, werden sie immer noch als Hispanoamerikaner gezählt, nicht als zwei oder mehr Rassen. Wenn die Summe aller ERI-Spalten größer als 1 ist, werden sie in ähnlicher Weise als zwei oder mehr Rassen gezählt und können nicht als eindeutige ethnische Zugehörigkeit gezählt werden (außer Hispanic).
Es ist fast so, als würde man jede Zeile mit einer for-Schleife durchlaufen, und 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 (1) ist, wird der Mitarbeiter als „Hispanic“ eingestuft.
Kommentar: Wenn mehr als 1 nicht-hispanisches ERI-Flag wahr ist, geben Sie „Zwei oder mehr“ zurück
DATENRAHMEN
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
Für diese Aufgabe apply scheint aber offensichtlich nicht Verwenden Sie es, weil es nur eine Schleife über die Zeilen ist. Es gibt bessere Möglichkeiten, dies zu tun. Beispiele: hier, hier, hier.
– Baumwollschwanz
17. November 2022 um 21:37 Uhr
Thomas Kimber
OK, zwei Schritte dazu – zuerst muss eine Funktion geschrieben werden, die die gewünschte Übersetzung durchfü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'
Vielleicht möchten Sie dies noch einmal durchgehen, aber es scheint zu funktionieren – beachten Sie, dass der Parameter, der in die Funktion geht, als ein Series-Objekt 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 Spezifizierer axis=1, was bedeutet, dass die Anwendung auf Zeilen- und nicht auf Spaltenebene erfolgt. 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 es erneut aus und speichern Sie die Ergebnisse in einer neuen Spalte in Ihrem ursprünglichen Datenrahmen.
Der resultierende Datenrahmen sieht so aus (scrollen Sie nach rechts, um die neue Spalte zu sehen):
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
Brian Burns
Da dies das erste Google-Ergebnis für „pandas neue Spalte von anderen“ 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 du die bekommst SettingWithCopyWarning du kannst 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'
Sehr interessante Antwort. Eine Frage, wie können wir tun df.apply(lambda row: row.a + row.b, axis=1) sondern auch die nächste Reihe mit einbeziehen? Ich habe hier eine Frage dazu stackoverflow.com/questions/74792731/…
– KansaiRobot
14. Dezember 2022 um 2:45 Uhr
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:
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 .loc anstatt apply.
es verbessert die Vektorisierung.
.loc funktioniert auf einfache Weise, maskiert Zeilen basierend auf der Bedingung, wendet Werte auf die eingefrorenen Zeilen an.
24,7 ms ± 1,7 ms pro Schleife (Mittelwert ± std. Abweichung von 7 Läufen mit jeweils 10 Schleifen)
Baumwollschwanz
Wenn wir es inspizieren Quellcode, apply() ist ein syntaktischer Zucker für eine Python for-Schleife (über die apply_series_generator() Methode der FrameApply Klasse). Weil es die Pandas über Kopf hat, ist es im Allgemeinen Langsamer als eine Python-Schleife.
Verwenden Sie möglichst optimierte (vektorisierte) Methoden. Wenn Sie eine Schleife verwenden müssen, verwenden Sie @numba.jit Dekorateur.
1. Nicht verwenden apply() für eine if-else-Leiter
df.apply() ist so ungefähr der langsamste Weg, dies bei Pandas zu tun. Wie in den Antworten von user3483203 und Mohamed Thasin ah gezeigt, je nach Datenrahmengröße, np.select() und df.loc kann 50-300 mal schneller sein als df.apply() um die gleiche Ausgabe zu erzeugen.
Zufällig ist eine Schleifenimplementierung (nicht unähnlich apply()) mit dem @jit Dekorateur aus numba Modul ist (ca. 50-60%) schneller als df.loc und np.select.1
Numba arbeitet mit numpy Arrays, also bevor Sie die jit decorator müssen Sie den Datenrahmen in ein numpy-Array konvertieren. Füllen Sie dann Werte in ein vorinitialisiertes leeres Array ein, indem Sie die Bedingungen in einer Schleife überprüfen. Da numpy-Arrays keine Spaltennamen haben, müssen Sie in der Schleife über ihren Index auf die Spalten zugreifen. Der unbequemste Teil der If-Else-Leiter in der Jitted-Funktion über der One-In apply() greift auf die Spalten über ihre Indizes zu. Ansonsten ist es fast die gleiche Implementierung.
import numpy as np
import numba as nb
@nb.jit(nopython=True)
def conditional_assignment(arr, res):
length = len(arr)
for i in range(length):
if arr[i][3] == 1:
res[i] = 'Hispanic'
elif arr[i][0] + arr[i][1] + arr[i][2] + arr[i][4] + arr[i][5] > 1:
res[i] = 'Two Or More'
elif arr[i][0] == 1:
res[i] = 'Black/AA'
elif arr[i][1] == 1:
res[i] = 'Asian'
elif arr[i][2] == 1:
res[i] = 'Haw/Pac Isl.'
elif arr[i][4] == 1:
res[i] = 'A/I AK Native'
elif arr[i][5] == 1:
res[i] = 'White'
else:
res[i] = 'Other'
return res
# the columns with the boolean data
cols = [c for c in df.columns if c.startswith('eri_')]
# initialize an empty array to be filled in a loop
# for string dtype arrays, we need to know the length of the longest string
# and use it to set the dtype
res = np.empty(len(df), dtype=f"<U{len('A/I AK Native')}")
# pass the underlying numpy array of `df[cols]` into the jitted function
df['rno_defined'] = conditional_assignment(df[cols].values, res)
2. Nicht verwenden apply() für numerische Operationen
Wenn Sie eine neue Zeile hinzufügen müssen, indem Sie zwei Spalten hinzufügen, ist Ihr erster Instinkt vielleicht, zu schreiben
Abhängig von der Datenrahmengröße, sum(1) kann 100 mal schneller sein als apply().
In der Tat werden Sie fast nie brauchen apply() für numerische Operationen auf einem Pandas-Datenrahmen, da er über optimierte Methoden für die meisten Operationen verfügt: Addition (sum(1)), Subtraktion (sub() oder diff()), Multiplikation (prod(1)), Einteilung (div() oder /), Energie (pow()), >, >=, ==, %, //, &, | usw. können alle auf dem gesamten Datenrahmen ohne ausgeführt werden apply().
Angenommen, Sie möchten eine neue Spalte mit der folgenden Regel erstellen:
IF [colC] > 0 THEN RETURN [colA] * [colB]
ELSE RETURN [colA] / [colB]
Unter Verwendung der optimierten Pandas-Methoden kann dies geschrieben werden als
Der Ansatz mit den optimierten Methoden ist 250-mal schneller als das Äquivalent apply() Ansatz für Datenrahmen mit 20.000 Zeilen. Diese Lücke nimmt nur zu, wenn die Datengröße zunimmt (für einen Datenrahmen mit 1 mil Zeilen ist es 365-mal schneller) und der Zeitunterschied wird immer deutlicher.2
1: Im folgenden Ergebnis zeige ich die Leistung der drei Ansätze unter Verwendung eines Datenrahmens mit 24-Mil-Zeilen (dies ist der größte Rahmen, den ich auf meinem Computer erstellen kann). Bei kleineren Frames läuft die numba-Jitted-Funktion auch durchgehend mindestens 50 % schneller als die anderen beiden (Sie können es selbst überprüfen).
2: Im folgenden Ergebnis zeige ich die Leistung der beiden Ansätze mit einem Datenrahmen mit 20.000 Zeilen und erneut mit 1 mil Zeilen. Bei kleineren Frames ist die Lücke kleiner, da der optimierte Ansatz einen zusätzlichen Overhead hat apply() ist eine Schleife. Wenn die Größe des Frames zunimmt, verringern sich die Overhead-Kosten der Vektorisierung in Bezug auf die Gesamtlaufzeit des Codes während apply() bleibt eine Schleife über dem Rahmen.
n = 20_000 # 1_000_000
df = pd.DataFrame(np.random.rand(n,3)-0.5, columns=['colA','colB','colC'])
%timeit df[['colA','colB']].prod(1).where(df['colC']>0, df['colA'] / df['colB'])
# n = 20000: 2.69 ms ± 23.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# n = 1000000: 86.2 ms ± 441 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit df.apply(lambda row: row.colA * row.colB if row.colC > 0 else row.colA / row.colB, axis=1)
# n = 20000: 679 ms ± 33.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# n = 1000000: 31.5 s ± 587 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Sai Pardhu
Wie @ user3483203 betonte, ist numpy.select der beste Ansatz
Speichern Sie Ihre bedingten Anweisungen und die entsprechenden Aktionen in zwei Listen
14406800cookie-checkErstellen Sie eine neue Spalte basierend auf Werten aus anderen Spalten / wenden Sie eine Funktion mehrerer Spalten zeilenweise in Pandas anyes
Für diese Aufgabe
apply
scheint aber offensichtlich nicht Verwenden Sie es, weil es nur eine Schleife über die Zeilen ist. Es gibt bessere Möglichkeiten, dies zu tun. Beispiele: hier, hier, hier.– Baumwollschwanz
17. November 2022 um 21:37 Uhr