Compara dos columnas usando pandas

104

Usando esto como punto de partida:

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

Quiero usar algo como una ifdeclaración dentro de pandas.

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

Básicamente, verifique cada fila a través de la ifdeclaración, cree una nueva columna.

Los documentos dicen usar .allpero no hay ejemplo ...

Esmerejón
fuente
¿Cuál debería ser el valor si la ifdeclaración es False?
Alex Riley
3
@Merlin: si tiene datos numéricos en una columna, es mejor no mezclarlos con cadenas. Al hacerlo, el dtype de la columna cambia a object. Esto permite almacenar objetos de Python arbitrarios en la columna, pero tiene el costo de un cálculo numérico más lento. Por lo tanto, si la columna almacena datos numéricos, es preferible utilizar NaN para no números.
unutbu
1
Tener enteros como cadenas y tratando de hacer la comparación en ellos parece extraño: a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]. Esto crea resultados confusos con el código "correcto": df['que'] = df['one'][(df['one'] >= df['two']) & (df['one'] <= df['three'])] cede 10la primera línea, mientras que debería ceder NaNsi la entrada hubiera sido números enteros.
Primer

Respuestas:

147

Podrías usar np.where . Si condes una matriz booleana y Ay Bson matrices, entonces

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

define C como igual a Adonde condes Verdadero y Bdonde condes Falso.

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)

rendimientos

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

Si tiene más de una condición, puede usar np.select en su lugar. Por ejemplo, si desea df['que']igualar df['two']cuándo df['one'] < df['two'], entonces

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)

rendimientos

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

Si podemos suponer que df['one'] >= df['two']when df['one'] < df['two']es False, entonces las condiciones y opciones podrían simplificarse a

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

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

(La suposición puede no ser cierta si df['one']o df['two']contiene NaN).


Tenga en cuenta que

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

define un DataFrame con valores de cadena. Dado que parecen numéricos, es mejor que convierta esas cadenas en flotantes:

df2 = df.astype(float)

Sin embargo, esto cambia los resultados, ya que las cadenas comparan carácter por carácter, mientras que los flotantes se comparan numéricamente.

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

In [62]: 10 <= 4.2
Out[62]: False
unutbu
fuente
73

Puede usarlo .equalspara columnas o marcos de datos completos.

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

Si son iguales, esa declaración volverá True, de lo contrario False.

ccook5760
fuente
22
Nota: esto solo compara la columna completa con otra. Esto no se puede comparar el elemento columsn sabia
Guerda
1
¿Qué tal si desea ver si una columna siempre tiene un valor "mayor que" o "menor que" las otras columnas?
rrlamichhane
28

Podría usar apply () y hacer algo como esto

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

o si prefieres no usar una 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)
Bob Haffner
fuente
2
Sospecho que esto es probablemente un poco más lento que los otros enfoques publicados, ya que no aprovecha las operaciones vectorizadas que permiten los pandas.
Marius
@BobHaffner: lambda no se puede leer cuando se usan declaraciones complejas if / then / else.
Merlín
@Merlin, podría agregar un elseif y estaría de acuerdo con usted en lambdas y múltiples condiciones
Bob Haffner
¿Hay alguna manera de generalizar la función no lambda de modo que pueda pasar columnas de marco de datos y no cambiar el nombre?
AZhao
@AZhao podrías generalizar con iloc así df ['que'] = df.apply (lambda x: x.iloc [0] if x.iloc [0]> = x.iloc [1] y x.iloc [0 ] <= x.iloc [2] else "", eje = 1) ¿Eso es lo que quieres decir? Obviamente. el orden de las columnas importa
Bob Haffner
9

Una forma es utilizar una serie booleana para indexar la columna df['one']. Esto le da una nueva columna donde las Trueentradas tienen el mismo valor que la misma fila df['one']y los Falsevalores son NaN.

La serie booleana solo la da su ifdeclaración (aunque es necesario usar en &lugar de 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

Si desea que los NaNvalores sean reemplazados por otros valores, puede usar el fillnamétodo en la nueva columna que. He usado en 0lugar de la cadena vacía aquí:

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

Envuelva cada condición individual entre paréntesis y luego use el &operador para combinar las condiciones:

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

Puede completar las filas que no coinciden con solo usar ~(el operador "no") para invertir la coincidencia:

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

Necesita usar &y en ~lugar de andy notporque los operadores &y ~trabajan elemento por elemento.

El resultado final:

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

Úselo np.selectsi tiene varias condiciones para verificar desde el marco de datos y generar una opción específica en una columna diferente

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

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

Nota: El número de condiciones y el número de opciones deben coincidir, repita el texto en la opción si tiene las mismas opciones para dos condiciones diferentes

psn1997
fuente
0

Creo que lo más cercano a la intuición del OP es una declaración if en línea:

df['que'] = (df['one'] if ((df['one'] >= df['two']) and (df['one'] <= df['three'])) 
Nic Scozzaro
fuente
Tu código me da un errordf['que'] = (df['one'] if ((df['one'] >= df['two']) and (df['one'] <= df['three'])) ^ SyntaxError: unexpected EOF while parsing
vasili111