636 Stimmen

Erstellen Sie eine neue Spalte basierend auf Werten aus anderen Spalten / wenden Sie eine Funktion auf mehrere Spalten in Pandas zeilenweise an

Ich möchte meine benutzerdefinierte Funktion (die eine if-else-Leiter verwendet) auf diese sechs Spalten (ERI_Hispanic, ERI_AmerInd_AKNatv, ERI_Asian, ERI_Black_Afr.Amer, ERI_HI_PacIsl, ERI_White) in jeder Zeile meines Dataframes anwenden.

Ich habe verschiedene Methoden aus anderen Fragen ausprobiert, aber finde immer noch keine richtige Lösung für mein Problem. Der entscheidende Punkt dabei ist, dass wenn die Person als Hispanic gezählt wird, sie nicht als etwas anderes gezählt werden kann. Selbst wenn sie eine "1" in einer anderen Ethnizitätsspalte haben, werden sie immer noch als Hispanic und nicht als zwei oder mehr Rassen gezählt. Ebenso, 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 eine eindeutige Ethnizität gezählt werden (außer Hispanic).

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

Aus dem unten stehenden Dataframe muss ich eine neue Spalte gemäß 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 True ist, wird “Two or More” zurückgegeben

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

5voto

Karl Knechtel Punkte 55450

Auswahl einer Methode entsprechend der Komplexität der Kriterien

Für die unten stehenden Beispiele - um multiple Arten von Regeln für die neue Spalte zu zeigen - nehmen wir an, dass ein DataFrame mit den Spalten 'red', 'green' und 'blue' existiert, die Gleitkommazahlen im Bereich von 0 bis 1 enthalten.

Allgemeiner Fall: .apply

Solange die notwendige Logik, um den neuen Wert zu berechnen, als eine Funktion von anderen Werten in derselben Zeile geschrieben werden kann, können wir die Methode .apply des DataFrame verwenden, um das gewünschte Ergebnis zu erhalten. Schreiben Sie die Funktion so, dass sie einen einzigen Parameter akzeptiert, der eine einzelne Zeile des Inputs ist:

def as_hex(wert):
    # Klemmen, um Rundungsfehler usw. zu vermeiden
    return min(max(0, int(wert * 256)), 255)

def hex_farbe(zeile):
    r, g, b = as_hex(zeile['red']), as_hex(zeile['green']), as_hex(zeile['blue'])
    return f'#{r:02x}{g:02x}{b:02x}'

Übergeben Sie die Funktion selbst (nicht die Klammern nach dem Namen) an .apply und geben Sie axis=1 an (was bedeutet, dass Zeilen der kategorisierenden Funktion übergeben werden, um eine Spalte zu berechnen - und nicht umgekehrt). Somit:

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

Beachten Sie, dass die Verwendung von lambda nicht notwendig ist, da wir keine Argumente binden oder anderweitig die Funktion modifizieren.

Der Schritt mit .apply ist notwendig, da die Konvertierungsfunktion selbst nicht vektorisiert ist. Daher wird ein naiver Ansatz wie df['farbe'] = hex_farbe(df) nicht funktionieren (Beispielfrage).

Dieses Tool ist leistungsstark, aber ineffizient. Zur bestmöglichen Leistung verwenden Sie bitte einen spezifischeren Ansatz, wo möglich.

Mehrere Auswahlmöglichkeiten mit Bedingungen: numpy.select oder wiederholte Zuweisung mit df.loc oder df.where

Angenommen, wir würden die Farbwerte thresholden und grobe Farbnamen wie folgt berechnen:

def additive_farbe(zeile):
    # Hier einfügen: Logik, die Werte aus der `Zeile` nimmt und
    # den gewünschten Zellenwert für die neue Spalte in dieser Zeile berechnet.
    # Die `Zeile` ist ein gewöhnliches `Series`-Objekt, das eine Zeile des
    # originalen `DataFrame` darstellt; es kann mit Spaltennamen indiziert werden, also:
    if zeile['red'] > 0.5:
        if zeile['green'] > 0.5:
            return 'weiß' if zeile['blue'] > 0.5 else 'gelb'
        else:
            return 'magenta' if zeile['blue'] > 0.5 else 'rot'
    elif zeile['green'] > 0.5:
        return 'cyan' if zeile['blue'] > 0.5 else 'grün'
    else:
        return 'blau' if zeile['blue'] > 0.5 else 'schwarz'

In Fällen wie diesem - wo die kategorisierende Funktion eine if/else-Leiter wäre oder match/case in 3.10 und höher - können wir mit numpy.select eine viel schnellere Leistung erzielen.

Dieser Ansatz funktioniert sehr anders. Zuerst erstellen Sie Masken auf den Daten, wo jede Bedingung zutrifft:

schwarz = (df['red'] <= 0.5) & (df['green'] <= 0.5) & (df['blue'] <= 0.5)
weiß = (df['red'] > 0.5) & (df['green'] > 0.5) & (df['blue'] > 0.5)

Um numpy.select aufzurufen, benötigen wir zwei parallele Sequenzen - eine für die Bedingungen und eine andere für die entsprechenden Werte:

df['farbe'] = np.select(
    [weiß, schwarz],
    ['weiß', 'schwarz'],
    'bunt'
)

Das optionale dritte Argument gibt einen Wert an, der verwendet wird, wenn keine der Bedingungen erfüllt sind. (Als Übung: Füllen Sie die verbleibenden Bedingungen aus und versuchen Sie es ohne ein drittes Argument.)

Ein ähnlicher Ansatz besteht darin, wiederholte Zuweisungen basierend auf jeder Bedingung zu machen. Weisen Sie den Standardwert zunächst zu und verwenden Sie dann df.loc, um spezifische Werte für jede Bedingung zuzuweisen:

df['farbe'] = 'bunt'
df.loc[weiß, 'farbe'] = 'weiß'
df.loc[schwarz, 'farbe'] = 'schwarz'

Alternativ kann df.where verwendet werden, um die Zuweisungen durchzuführen. Jedoch, df.where, wenn es so verwendet wird, weist den angegebenen Wert dort zu, wo die Bedingung nicht erfüllt ist, also müssen die Bedingungen invertiert werden:

df['farbe'] = 'bunt'
df['farbe'] = df['farbe'].where(~weiß, 'weiß').where(~schwarz, 'schwarz')

Einfache mathematische Manipulationen: eingebaute mathematische Operatoren und Broadcasting

Zum Beispiel kann ein apply-basierter Ansatz wie:

def helligkeit(zeile):
    return zeile['red'] * .299 + zeile['green'] * .587 + zeile['blue'] * .114

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

anstatt dessen durch das Broadcasting der Operatoren für eine viel bessere Leistung (und einfacher) geschrieben werden:

df['helligkeit'] = df['red'] * .299 + df['green'] * .587 + df['blue'] * .114

Als Übung hier ist das erste Beispiel auf diese Weise neu gemacht:

def as_hex(spalte):
    skaliert = (spalte * 256).astype(int)
    geklemmt = skaliert.where(skaliert >= 0, 0).where(skaliert <= 255, 255)
    return geklemmt.apply(lambda i: f'{i:02x}')

df['hex_farbe'] = '#' + as_hex(df['red']) + as_hex(df['green']) + as_hex(df['blue'])

Ich konnte kein vektorisiertes Äquivalent finden, um die ganzen Zahlen als Hex-Zeichenfolgen zu formatieren, daher wird .apply hier immer noch intern verwendet - was bedeutet, dass die volle Geschwindigkeitsstrafe immer noch greift. Dennoch demonstriert dies einige allgemeine Techniken.

Weitere Details und Beispiele finden Sie in cottontails Antwort.

0voto

keepAlive Punkte 5720

Noch ein (leicht verallgemeinerbarer) Ansatz, dessen Eckpfeiler pandas.DataFrame.idxmax ist. Zuerst die leicht verallgemeinerbare Einleitung.

# Tatsächlich lässt sich all Ihre Bedingungen auf folgendes zurückführen
_gt_1_key = 'two_or_more'
_lt_1_key = 'other'

# Die "dictionary-basierten" if-else-Anweisungen
labels = {
    _gt_1_key     : 'Zwei oder mehr',
    'eri_hispanic': 'Hispanisch',
    'eri_nat_amer': 'A/I AK Native',
    'eri_asian'   : 'Asiatisch',
    'eri_afr_amer': 'Schwarz/Afroamerikanisch',
    'eri_hawaiian': 'Haw./Paz. Inselbew.',
    'eri_white'   : 'Weiß',  
    _lt_1_key     : 'Andere',
}

# Die ausgabesteuernde 1-0-Matrix
mat = df.filter(regex='^eri_').copy()  # `~.copy` um `SettingWithCopyWarning` zu vermeiden

... und schließlich, in einem _vektorisierter Weise_:

mat[_gt_1_key] = gt1 = mat.sum(axis=1)
mat[_lt_1_key] = gt1.eq(0).astype(int)
race_label     = mat.idxmax(axis=1).map(labels)

wo

>>> race_label
0           Weiß
1        Hispanisch
2           Weiß
3           Weiß
4           Andere
5           Weiß
6     Zwei oder mehr
7           Weiß
8    Haw./Paz. Inselbew.
9           Weiß
dtype: object

dies ist eine pandas.Series-Instanz, die Sie leicht innerhalb von df hosten können, d.h. indem Sie df['race_label'] = race_label ausführen.

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