Vergleichen Sie zwei Spalten mit Pandas

97

Verwenden Sie dies als Ausgangspunkt:

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

Out[8]: 
  one  two three
0   10  1.2   4.2
1   15  70   0.03
2    8   5     0

Ich möchte so etwas wie eine ifAussage innerhalb von Pandas verwenden.

if df['one'] >= df['two'] and df['one'] <= df['three']:
    df['que'] = df['one']

Überprüfen Sie grundsätzlich jede Zeile über die ifAnweisung und erstellen Sie eine neue Spalte.

Die Dokumente sagen zu verwenden, .allaber es gibt kein Beispiel ...

Merlin
quelle
Was sollte der Wert sein, wenn die ifAussage ist False?
Alex Riley
3
@ Merlin: Wenn Sie numerische Daten in einer Spalte haben, ist es am besten, diese nicht mit Zeichenfolgen zu mischen. Dadurch wird der dtype der Spalte in geändert object. Dies ermöglicht das Speichern beliebiger Python-Objekte in der Spalte, geht jedoch zu Lasten einer langsameren numerischen Berechnung. Wenn in der Spalte numerische Daten gespeichert sind, ist die Verwendung von NaNs für Nicht-Zahlen vorzuziehen.
Unutbu
1
Ganzzahlen als Zeichenfolgen zu haben und zu versuchen, sie zu vergleichen, sieht seltsam aus : a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]. Dies führt zu verwirrenden Ergebnissen mit "korrektem" Code: df['que'] = df['one'][(df['one'] >= df['two']) & (df['one'] <= df['three'])] ergibt 10für die erste Zeile, während es ergeben sollte, NaNwenn die Eingabe ganze Zahlen gewesen wäre.
Grundierung

Antworten:

139

Sie könnten np.where verwenden . Wenn condes sich um ein boolesches Array handelt und Aund BArrays sind, dann

C = np.where(cond, A, B)

definiert C als gleich Awo condist wahr und Bwo condist falsch.

import numpy as np
import pandas as pd

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

df['que'] = np.where((df['one'] >= df['two']) & (df['one'] <= df['three'])
                     , df['one'], np.nan)

ergibt

  one  two three  que
0  10  1.2   4.2   10
1  15   70  0.03  NaN
2   8    5     0  NaN

Wenn Sie mehr als eine Bedingung haben, können Sie stattdessen np.select verwenden. Wenn Sie beispielsweise df['que']gleich df['two']wann möchten df['one'] < df['two'], dann

conditions = [
    (df['one'] >= df['two']) & (df['one'] <= df['three']), 
    df['one'] < df['two']]

choices = [df['one'], df['two']]

df['que'] = np.select(conditions, choices, default=np.nan)

ergibt

  one  two three  que
0  10  1.2   4.2   10
1  15   70  0.03   70
2   8    5     0  NaN

Wenn wir davon ausgehen können, dass df['one'] >= df['two']wenn df['one'] < df['two']Falsch ist, könnten die Bedingungen und Auswahlmöglichkeiten vereinfacht werden

conditions = [
    df['one'] < df['two'],
    df['one'] <= df['three']]

choices = [df['two'], df['one']]

(Die Annahme ist möglicherweise nicht wahr, wenn sie NaNs enthält df['one']oder df['two']enthält.)


Beachten Sie, dass

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

Definiert einen DataFrame mit Zeichenfolgenwerten. Da sie numerisch aussehen, ist es möglicherweise besser, diese Zeichenfolgen in Floats umzuwandeln:

df2 = df.astype(float)

Dies ändert jedoch die Ergebnisse, da Zeichenfolgen zeichenweise verglichen werden, während Gleitkommazahlen numerisch verglichen werden.

In [61]: '10' <= '4.2'
Out[61]: True

In [62]: 10 <= 4.2
Out[62]: False
unutbu
quelle
70

Sie können .equalsfür Spalten oder ganze Datenrahmen verwenden.

df['col1'].equals(df['col2'])

Wenn sie gleich sind, kehrt diese Aussage True, sonst False.

ccook5760
quelle
22
Hinweis: Dadurch wird nur die gesamte Spalte mit einer anderen verglichen. Dies vergleicht nicht das columsn Element weise
Guerda
Wie wäre es, wenn Sie sehen möchten, ob eine Spalte immer den Wert "größer als" oder "kleiner als" der anderen Spalten hat?
Rrlamichhane
27

Sie könnten apply () verwenden und so etwas tun

df['que'] = df.apply(lambda x : x['one'] if x['one'] >= x['two'] and x['one'] <= x['three'] else "", axis=1)

oder wenn Sie kein Lambda verwenden möchten

def que(x):
    if x['one'] >= x['two'] and x['one'] <= x['three']:
        return x['one']
    return ''
df['que'] = df.apply(que, axis=1)
Bob Haffner
quelle
2
Ich vermute, dass dies wahrscheinlich etwas langsamer ist als die anderen veröffentlichten Ansätze, da es die von Pandas zugelassenen vektorisierten Operationen nicht ausnutzt.
Marius
@ BobHaffner: Lambda ist bei Verwendung komplexer if / then / else-Anweisungen nicht lesbar.
Merlin
@ Merlin Sie könnten ein elseif hinzufügen und ich würde Ihnen in Bezug auf Lambdas und mehrere Bedingungen zustimmen
Bob Haffner
Gibt es eine Möglichkeit, die Nicht-Lambda-Funktion so zu verallgemeinern, dass Sie Datenrahmenspalten übergeben und den Namen nicht ändern können?
AZhao
@AZhao Sie könnten mit iloc wie folgt verallgemeinern: df ['que'] = df.apply (Lambda x: x.iloc [0], wenn x.iloc [0]> = x.iloc [1] und x.iloc [0 ] <= x.iloc [2] else "", axis = 1) Meinst du das? Offensichtlich. Die Reihenfolge Ihrer Kolumnen ist wichtig
Bob Haffner
9

Eine Möglichkeit besteht darin, eine Boolesche Reihe zum Indizieren der Spalte zu verwenden df['one']. Dies gibt Ihnen eine neue Spalte, in der die TrueEinträge den gleichen Wert wie die gleiche Zeile haben df['one']und die FalseWerte sind NaN.

Die Boolesche Reihe wird nur durch Ihre ifAussage angegeben (obwohl es notwendig ist, &statt zu verwenden and):

>>> df['que'] = df['one'][(df['one'] >= df['two']) & (df['one'] <= df['three'])]
>>> df
    one two three   que
0   10  1.2 4.2      10
1   15  70  0.03    NaN
2   8   5   0       NaN

Wenn Sie möchten, dass die NaNWerte durch andere Werte ersetzt werden, können Sie die fillnaMethode für die neue Spalte verwenden que. Ich habe hier 0anstelle der leeren Zeichenfolge verwendet:

>>> df['que'] = df['que'].fillna(0)
>>> df
    one two three   que
0   10  1.2   4.2    10
1   15   70  0.03     0
2    8    5     0     0
Alex Riley
quelle
4

Schließen Sie jede einzelne Bedingung in Klammern ein und &kombinieren Sie die Bedingungen mit dem Operator:

df.loc[(df['one'] >= df['two']) & (df['one'] <= df['three']), 'que'] = df['one']

Sie können die nicht übereinstimmenden Zeilen füllen, indem Sie einfach ~(den Operator "nicht") verwenden, um die Übereinstimmung zu invertieren:

df.loc[~ ((df['one'] >= df['two']) & (df['one'] <= df['three'])), 'que'] = ''

Sie müssen &und ~anstelle von andund verwenden, notda die Operatoren &und ~Element für Element arbeiten.

Das Endergebnis:

df
Out[8]: 
  one  two three que
0  10  1.2   4.2  10
1  15   70  0.03    
2   8    5     0  
Marius
quelle
1

Verwenden np.selectSie diese Option, wenn Sie mehrere Bedingungen aus dem Datenrahmen überprüfen und eine bestimmte Auswahl in einer anderen Spalte ausgeben möchten

conditions=[(condition1),(condition2)]
choices=["choice1","chocie2"]

df["new column"]=np.select=(condtion,choice,default=)

Hinweis: Die Anzahl der Bedingungen und die Anzahl der Auswahlmöglichkeiten sollten übereinstimmen. Wiederholen Sie den Text in der Auswahl, wenn Sie für zwei verschiedene Bedingungen die gleichen Auswahlmöglichkeiten haben

psn1997
quelle
0

Ich denke, die Intuition des OP kommt einer Inline-if-Aussage am nächsten:

df['que'] = (df['one'] if ((df['one'] >= df['two']) and (df['one'] <= df['three'])) 
Nic Scozzaro
quelle
Ihr Code gibt mir Fehlerdf['que'] = (df['one'] if ((df['one'] >= df['two']) and (df['one'] <= df['three'])) ^ SyntaxError: unexpected EOF while parsing
vasili111