Estoy tratando de usar numpy.select para reemplazar los valores de cadena dentro de una columna; si la cadena contiene una palabra clave, necesito que toda la cadena sea reemplazada por otra palabra clave (hay + - 25 combinaciones).

df["new_col"] = np.select(
    condlist=[
        df["col"].str.contains("cat1", na=False, case=False),
        df["col"].str.contains("cat2", na=False, case=False),
        df["col"].str.contains("cat3", na=False, case=False),
        df["col"].str.contains("cat4", na=False, case=False),
        # ...
        df["col"].str.contains("cat25", na=False, case=False),
    ],
    choicelist=[
        "NEW_cat1",
        "NEW_cat2",
        "NEW_cat3",
        "NEW_cat4",
        # ...
        "NEW_cat25"
    ],
    default="DEFAULT_cat",
)

¿Hay una forma más concisa, o debería repetir str.contains(...) dentro de condlist 25 veces ?; ¿es numpy.select la forma correcta de hacerlo, en absoluto?

Supongo que dict podría usarse aquí, pero no veo cómo exactamente.

df["col"].map(d) donde d es un dict con valores antiguos y nuevos como {"cat1":"NEW_cat1"} no funcionaría (?) Ya que no puedo codificar valores exactos que necesitan ser reemplazados (y es por eso que estoy usando str.contains).

0
political scientist 9 oct. 2019 a las 20:22

3 respuestas

La mejor respuesta

El contenido que está pasando como los parámetros condlist y choicelist son listas de Python normales. El contenido de la lista se puede producir de manera concisa en el lenguaje mediante el uso de las comprensiones de la lista, es decir, la sintaxis: [expression_using_item for item in sequence]

En otras palabras, su código se puede escribir como:

df["new_col"] = np.select(
    condlist=[
       df["col"].str.contains(f"cat{i}", na=False, case=False) for i in range(1, 26)],        
    choicelist=[f"NEW_cat{i}" for i in range(1, 26)],
    default="DEFAULT_cat",
)

(y si los nombres de categoría no son una secuencia numérica, y está dando estos nombres aquí solo como un ejemplo, crea una secuencia (lista) con todos los nombres explícitos de categoría y la conecta en lugar de la llamada range() en el fragmento de arriba)

2
jsbueno 9 oct. 2019 a las 18:19

Basado en esta respuesta a una pregunta similar, y este, una solución simple:

import pandas as pd
import string

# Preparing test data
test_cont = [f"cat_{i}" for i in string.ascii_lowercase]
test_rep = [f"cat_{i}" for i in range(27)]

kv = zip(test_cont, test_rep)

test_df_data = zip(range(27), test_cont)

test_df = pd.DataFrame(data=test_df_data, columns=["some_col", "str_vals"])


# The solution itself
for (cont, rep) in kv:
    cont_mask = test_df["str_vals"].str.contains(cont, na=False, case=False)
    test_df.loc[cont_mask, "str_vals"] = rep
2
AMC 9 oct. 2019 a las 21:13

Debería poder utilizar str.extract y luego asignar las coincidencias.

Preparar

import pandas as pd
import re

df = pd.DataFrame({'col': ['foo', 'foOBar', 'oRange', 'manGo', 'i LIKE PIZZA', 
                           'some sentence with foo', 'foo and PizzA']})

cat_list = ['foo', 'orange', 'pizza']  # all lower case
label_l = ['Label_foo', 'Label_orange', 'Label_pizza']

Código

patt = re.compile('('+'|'.join(cat_list)+')', re.IGNORECASE)

df['new_col'] = (df.col.str.extract(patt)[0]  # First label in str if multiple
                   .str.lower()
                   .map(dict(zip(cat_list, label_l)))
                   .fillna('DEFAULT_LABEL'))

                      col        new_col
0                     foo      Label_foo
1                  foOBar      Label_foo
2                  oRange   Label_orange
3                   manGo  DEFAULT_LABEL
4            i LIKE PIZZA    Label_pizza
5  some sentence with foo      Label_foo
6           foo and PizzA      Label_foo

Si existe la posibilidad de múltiples coincidencias y necesitamos implementar una jerarquía en la que la 'pizza' debe tener prioridad sobre 'foo', podemos agregar algunos pasos más usando un tipo de categoría ordenado.

cat_list = ['pizza', 'orange', 'foo']  # ordered in priority
label_l = ['Label_pizza', 'Label_orange', 'Label_foo']

my_cat = pd.api.types.CategoricalDtype(categories=cat_list, ordered=True)

s = (df.col.str.extractall(patt)[0]
       .str.lower()
       .astype(my_cat))

df['new_col'] = (s.to_frame().groupby(level=0).min()[0]  # min gets priority
                  .map(dict(zip(cat_list, label_l))))
df['new_col'] = df['new_col'].astype(str).replace('nan', 'DEFAULT_LABEL')
#                      col        new_col
#0                     foo      Label_foo
#1                  foOBar      Label_foo
#2                  oRange   Label_orange
#3                   manGo  DEFAULT_LABEL
#4            i LIKE PIZZA    Label_pizza
#5  some sentence with foo      Label_foo
#6           foo and PizzA    Label_pizza
2
ALollz 9 oct. 2019 a las 18:23
58309250