# import package
import pandas as pd

El problema

Tengo un marco de datos:

data = {'row1': ['a', 'A', 'B', 'b'],
        'row2': ['a', 'b', 'c', 'd'],
        'row3': ['a', 'b', 'd', 'D']}
df = pd.DataFrame.from_dict(data, orient='index', columns=['col'+str(x) for x in range(4)])

Que se parece a:

enter image description here

También tengo una lista de clases de equivalencia. Cada clase de equivalencia consta de elementos que se toman como equivalentes.

equivalenceClasses={'classA':['a','A'],
                    'classB':['b','B'],
                    'classC':['c','C'],
                    'classD':['d','D']}

Me gustaría crear un marco de datos en el que las filas del marco de datos anterior se reemplacen por los nombres de las clases de equivalencia a las que pertenecen las letras de la fila . (Cada clase de equivalencia no debería aparecer más de una vez en una fila, y deberíamos usar NaN s para rellenar filas en las que no todas las columnas están rellenas con el nombre de una clase de equivalencia). Es decir, quiero esta salida:

enter image description here


Mi metodo

Logro la meta al:

def differentClasses(colvalues):
    return list(set([equivalenceClassName for colvalue in colvalues
                                          for equivalenceClassName, equivalenceClass in zip(equivalenceClasses.keys(),
                                                                                   equivalenceClasses.values())
                                          if colvalue in equivalenceClass]))

(En la comprensión de la lista, en la comprensión de la lista anidada).

df['classes'] = df.apply(lambda row : differentClasses(row['col'+str(x)] for x in range(4)), axis = 1) 

(Influenciado por esto. )

El df en este punto se ve así:

enter image description here

Terminar por:

result_df = pd.DataFrame(df['classes'].tolist(),index=df.index,columns=['classcol'+str(x) for x in range(4)])

result_df es el resultado deseado arriba.


La pregunta

¿Existe una forma más estándar de hacer esto? Algo como:

df.equivalenceClassify(equivalenceClassList)

Y obtengo mi salida?

2
zabop 4 ago. 2020 a las 00:14

1 respuesta

La mejor respuesta

Necesitamos crear el nuevo diccionario basado en tu equivalenceClasses original, luego hazlo replace

from collections import ChainMap
d = dict(ChainMap(*[dict.fromkeys(y,x) for x , y in equivalenceClasses.items()]))
df = df.replace(d)
Out[299]: 
        col0    col1    col2    col3
row1  classA  classA  classB  classB
row2  classA  classB  classC  classD
row3  classA  classB  classD  classD

Entonces

df = df.mask(df.apply(pd.Series.duplicated,1))
Out[307]: 
        col0    col1    col2    col3
row1  classA     NaN  classB     NaN
row2  classA  classB  classC  classD
row3  classA  classB  classD     NaN
2
BENY 3 ago. 2020 a las 21:20