Pandas: el valor de verificación existe en una columna, que se almacen

Pandas: el valor de verificación existe en una columna, que se almacena como lista

Tengo el siguiente marco de datos. La columna de estado almacena el valor como una lista.

df

   STATUS     
1 [REQUESTED, RECEIVED]
2 [XYZ]
3 [RECEIVED]

Cuando pruebo la siguiente lógica:

df['STATUS'].str.upper().isin(['RECEIVED'])

Me da

1 False
2 False
3 False

Pero estoy esperando

1 True
2 False
3 True

ya que tenemos el valor RECEIVED en las filas 1 y 3

Mostrar la mejor respuesta

Parece que tiene un error tipográfico en 3. - Debe ser RECIBIDO y no RECIBIDO.

Es posible que te refieras a algo como

>>> df.STATUS.astype(str).str.upper().str.contains('RECEIVED')
1 True
2 False
3 False

(Por cierto, su ejemplo tiene un error tipográfico: 1. ha RECIBIDO y 3. ha RECIBIDO).

como isin es lo contrario de lo que significa tu ejemplo.

me da el error

¿Estás seguro de que colocaste .str después de upper()?

pero en esta pregunta vi algo similar sobre isin stackoverflow.com/questions/19960077/…

@TomJMuthirenthi Sí, eso es exactamente lo contrario de esta pregunta, ¿no? En esa pregunta, cada uno de los elementos de la serie es una sola cadena, y el objetivo es verificar si esta cadena está en una lista. Aquí cada elemento de la serie es una lista, y el objetivo es verificar si la lista contiene una cadena. LMK si me estoy perdiendo algo.

Entonces, ¿cómo podemos escribir el equivalente para un df select * from df where col in (5,6)? Por favor ayuda.

@TomJMuthirenthi Eso sería usar isin, pero es el caso opuesto de su pregunta aquí, AFAIU. En SELECT * .. WHERE col IN (5, 6), col es implícitamente una columna de escalares.

Entonces, ¿quiere decir que, dado que la columna STATUS contiene la lista, está causando la confusión?

@TomJMuthirenthi Sí. En este caso, la columna STATUS contiene listas, y está comprobando si cada una contiene algo, no si está contenida en algo, AFAIU.

En ese caso, debería imprimir verdadero para el caso 3, ¿verdad?

Es difícil operar directamente con valores de lista. Puede concatenar las cadenas en una, usando algún carácter separador y luego verificar la condición:

import pandas as pd

df = pd.DataFrame({'STATUS': [['REQUESTED', 'RECEIVED'], ['XYZ'], ['RECEIVED']]},
                  index=[1, 2, 3])
print(df['STATUS'].str.join('|').str.contains('RECEIVED'))

Salida:

1     True
2    False
3     True
Name: STATUS, dtype: bool

Una opción más eficiente sería reemplazar las cadenas con banderas numéricas. Esto se puede hacer muy bien desde Python 3.6 usando enum.Flag.

import enum
import pandas as pd

class Status(enum.Flag):
    REQUESTED = enum.auto()
    RECEIVED = enum.auto()
    XYZ = enum.auto()

df = pd.DataFrame({'STATUS': [Status.REQUESTED | Status.RECEIVED, Status.XYZ, Status.RECEIVED]}, index=[1, 2, 3])
print(df['STATUS'] & Status.RECEIVED)

O, si ya tiene un marco de datos con cadenas:

import enum
import pandas as pd
from functools import reduce

class Status(enum.Flag):
    REQUESTED = enum.auto()
    RECEIVED = enum.auto()
    XYZ = enum.auto()

df = pd.DataFrame({'STATUS': [['REQUESTED', 'RECEIVED'], ['XYZ'], ['RECEIVED']]}, index=[1, 2, 3])
df['STATUS_ENUM'] = df['STATUS'].apply(lambda v: reduce(lambda a, b: a | Status[b], v, Status(0)))
print(df['STATUS_ENUM'] & Status.RECEIVED)

Estoy reconsiderando el uso de contains ya que no distingue entre RECEIVED y RECEIVED CASH

@TomJMuthirenthi Sí, este enfoque tiene ese tipo de limitaciones... Podría hacer algo como contains('RECEIVED|'), pero necesitaría agregar un | a cada fila después de join...

Para una verificación simple como esta, puede unirse a la lista de cadenas y usar contains.

EDITAR: Para tener en cuenta la diferencia entre RECEIVED y RECEIVED CASH, puede unir las listas con un carácter único (como '=') Y rodear la cadena resultante con el mismo y luego verifique =RECEIVED=.

('=' + df['STATUS'].str.join('=') + '=').str.contains('=RECEIVED=')

Estoy reconsiderando el uso de contiene ya que no distingue entre ~RECIBIDO~ y ~RECIBIDO EN EFECTIVO~

Datos de jde

df = pd.DataFrame({'STATUS': [['REQUESTED', 'RECEIVED'], ['XYZ'], ['RECEIVED']]},
                  index=[1, 2, 3])
df.STATUS.apply(lambda x : 'RECEIVED' in x)
Out[11]: 
1     True
2    False
3     True
Name: STATUS, dtype: bool