Cambie los valores en el dataframe de pandas de acuerdo con value_counts ()

Tengo el siguiente dataframe de pandas:

import pandas as pd from pandas import Series, DataFrame data = DataFrame({'Qu1': ['apple', 'potato', 'cheese', 'banana', 'cheese', 'banana', 'cheese', 'potato', 'egg'], 'Qu2': ['sausage', 'banana', 'apple', 'apple', 'apple', 'sausage', 'banana', 'banana', 'banana'], 'Qu3': ['apple', 'potato', 'sausage', 'cheese', 'cheese', 'potato', 'cheese', 'potato', 'egg']}) 

Me gustaría cambiar los valores en las columnas Qu1 , Qu1 , Qu3 acuerdo con value_counts() cuando el valor del recuento sea grande o igual a algún número

Por ejemplo para la columna Qu1

 >>> pd.value_counts(data.Qu1) >= 2 cheese True potato True banana True apple False egg False 

Me gustaría mantener los valores de cheese , potato , banana , porque cada valor tiene al menos dos apariciones.

A partir de valores de apple y egg me gustaría crear valor a others

Para la columna Qu2 no hay cambios:

 >>> pd.value_counts(data.Qu2) >= 2 banana True apple True sausage True 

El resultado final como en test_data adjunto

 test_data = DataFrame({'Qu1': ['other', 'potato', 'cheese', 'banana', 'cheese', 'banana', 'cheese', 'potato', 'other'], 'Qu2': ['sausage', 'banana', 'apple', 'apple', 'apple', 'sausage', 'banana', 'banana', 'banana'], 'Qu3': ['other', 'potato', 'other', 'cheese', 'cheese', 'potato', 'cheese', 'potato', 'other']}) 

Gracias !

Me gustaría crear un dataframe de la misma forma donde la entrada correspondiente es el valor del conteo:

 data.apply(lambda x: x.map(x.value_counts())) Out[229]: Qu1 Qu2 Qu3 0 1 2 1 1 2 4 3 2 3 3 1 3 2 3 3 4 3 3 3 5 2 2 3 6 3 4 3 7 2 4 3 8 1 4 1 

Y, use los resultados en df.where para devolver “otro” donde la entrada correspondiente es menor que 2:

 data.where(data.apply(lambda x: x.map(x.value_counts()))>=2, "other") Qu1 Qu2 Qu3 0 other sausage other 1 potato banana potato 2 cheese apple other 3 banana apple cheese 4 cheese apple cheese 5 banana sausage potato 6 cheese banana cheese 7 potato banana potato 8 other banana other 

Tú podrías:

 value_counts = df.apply(lambda x: x.value_counts()) Qu1 Qu2 Qu3 apple 1.0 3.0 1.0 banana 2.0 4.0 NaN cheese 3.0 NaN 3.0 egg 1.0 NaN 1.0 potato 2.0 NaN 3.0 sausage NaN 2.0 1.0 

Luego, cree un dictionary que contenga los reemplazos para cada columna:

 import cycle replacements = {} for col, s in value_counts.items(): if s[s<2].any(): replacements[col] = dict(zip(s[s < 2].index.tolist(), cycle(['other']))) replacements {'Qu1': {'egg': 'other', 'apple': 'other'}, 'Qu3': {'egg': 'other', 'apple': 'other', 'sausage': 'other'}} 

Usa el dictionary para reemplazar los valores:

 df.replace(replacements) Qu1 Qu2 Qu3 0 other sausage other 1 potato banana potato 2 cheese apple other 3 banana apple cheese 4 cheese apple cheese 5 banana sausage potato 6 cheese banana cheese 7 potato banana potato 8 other banana other 

o envolver el bucle en un dictionary comprensión:

 from itertools import cycle df.replace({col: dict(zip(s[s < 2].index.tolist(), cycle(['other']))) for col, s in value_counts.items() if s[s < 2].any()}) 

Sin embargo, esto no solo es más engorroso sino también más lento que usar .where . Pruebas con 3.000 columnas:

 df = pd.concat([df for i in range(1000)], axis=1)  RangeIndex: 9 entries, 0 to 8 Columns: 3000 entries, Qu1 to Qu3 dtypes: object(3000) 

Utilizando .replace() :

 %%timeit value_counts = df.apply(lambda x: x.value_counts()) df.replace({col: dict(zip(s[s < 2].index.tolist(), cycle(['other']))) for col, s in value_counts.items() if s[s < 2].any()}) 1 loop, best of 3: 4.97 s per loop 

vs .where() :

 %%timeit df.where(df.apply(lambda x: x.map(x.value_counts()))>=2, "other") 1 loop, best of 3: 2.01 s per loop