Confronta due colonne usando i panda


104

Usando questo come punto di partenza:

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

Voglio usare qualcosa di simile a ifun'affermazione all'interno dei panda.

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

Fondamentalmente, controlla ogni riga tramite l' ifistruzione, crea una nuova colonna.

I documenti dicono di usare .allma non c'è esempio ...


Quale dovrebbe essere il valore se l' ifaffermazione è False?
Alex Riley

3
@Merlin: se hai dati numerici in una colonna, è meglio non mescolarli con le stringhe. In questo modo il dtype della colonna viene modificato in object. Ciò consente di memorizzare oggetti Python arbitrari nella colonna, ma ha il costo di un calcolo numerico più lento. Pertanto, se la colonna sta memorizzando dati numerici, è preferibile utilizzare NaN per non numeri.
unutbu

1
Avendo interi come stringhe e cercando di fare il confronto su di loro sembra strano: a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]. Questo crea risultati confusi con il codice "corretto": df['que'] = df['one'][(df['one'] >= df['two']) & (df['one'] <= df['three'])] restituisce 10la prima riga, mentre dovrebbe restituire NaNse l'input fosse stato un numero intero.
Primer

Risposte:


147

Potresti usare np.where . Se condè un array booleano e Ae Bsono array, allora

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

definisce C come uguale a Adove condè True e Bdove condè False.

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)

rendimenti

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

Se hai più di una condizione, puoi invece usare np.select . Ad esempio, se desideri df['que']eguagliare df['two']quando df['one'] < df['two'], allora

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)

rendimenti

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

Se possiamo presumere che df['one'] >= df['two']quando df['one'] < df['two']è False, le condizioni e le scelte potrebbero essere semplificate

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

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

(L'assunzione potrebbe non essere vera se df['one']o df['two']contengono NaN.)


Nota che

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

definisce un DataFrame con valori stringa. Dal momento che sembrano numerici, potrebbe essere meglio convertire quelle stringhe in float:

df2 = df.astype(float)

Ciò cambia i risultati, tuttavia, poiché le stringhe confrontano carattere per carattere, mentre i float vengono confrontati numericamente.

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

In [62]: 10 <= 4.2
Out[62]: False

73

Puoi usare .equalsper colonne o interi dataframe.

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

Se sono uguali, quell'affermazione tornerà True, altrimenti False.


22
Nota: questo confronta solo l'intera colonna con un'altra. Questo non confronta l'elemento colonne saggio
guerda

1
Che ne dici se vuoi vedere se una colonna ha sempre un valore "maggiore di" o "minore di" le altre colonne?
rrlamichhane

28

Potresti usare apply () e fare qualcosa di simile

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

o se preferisci non usare un lambda

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

2
Sospetto che questo sia probabilmente un po 'più lento degli altri approcci pubblicati, poiché non sfrutta le operazioni vettorializzate consentite dai panda.
Marius

@BobHaffner: lambda non sono leggibili quando si utilizzano istruzioni complesse if / then / else.
Merlin

@Merlin potresti aggiungere un elseif e sarei d'accordo con te su lambdas e condizioni multiple
Bob Haffner

c'è un modo per generalizzare la funzione non lambda in modo tale da poter passare colonne di dataframe e non modificare il nome?
AZhao

@AZhao potresti generalizzare con iloc in questo modo df ['que'] = df.apply (lambda x: x.iloc [0] if x.iloc [0]> = x.iloc [1] e x.iloc [0 ] <= x.iloc [2] else "", axis = 1) È questo che intendi? Ovviamente. l'ordine delle tue colonne è importante
Bob Haffner

9

Un modo è utilizzare una serie booleana per indicizzare la colonna df['one']. Questo ti dà una nuova colonna in cui le Truevoci hanno lo stesso valore della stessa riga di df['one']e i Falsevalori sono NaN.

La serie booleana è data solo dalla tua ifdichiarazione (sebbene sia necessario usare al &posto di 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

Se desideri che i NaNvalori vengano sostituiti da altri valori, puoi utilizzare il fillnametodo sulla nuova colonna que. Ho usato al 0posto della stringa vuota qui:

>>> 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

4

Racchiudi ogni singola condizione tra parentesi, quindi utilizza l' &operatore per combinare le condizioni:

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

Puoi riempire le righe non corrispondenti semplicemente usando ~(l'operatore "not") per invertire la corrispondenza:

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

È necessario utilizzare &and ~anziché ande notperché gli operatori &e ~funzionano elemento per elemento.

Il risultato finale:

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

1

Utilizzare np.selectse si devono controllare più condizioni dal dataframe e visualizzare una scelta specifica in una colonna diversa

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

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

Nota: il numero di condizioni e il numero di scelte devono corrispondere, ripetere il testo a scelta se per due condizioni diverse si hanno le stesse scelte


0

Penso che il più vicino all'intuizione dell'OP sia un'istruzione if inline:

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

Il tuo codice mi dà un erroredf['que'] = (df['one'] if ((df['one'] >= df['two']) and (df['one'] <= df['three'])) ^ SyntaxError: unexpected EOF while parsing
vasili111
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.