Aplicar un método de una lista de métodos para el dataframe pandas

Esta es mi primera pregunta aquí, así que ten paciencia conmigo.

Mi problema es el siguiente:

Supongamos que tenemos un Dataframe de pandas y queremos aplicar dinámicamente algunos métodos pd.Series a un conjunto de columnas de este Dataframe. ¿Por qué el siguiente ejemplo no funciona?

testframe=pd.DataFrame.from_dict({'col1': [1,2] ,'col2': [3,4] }) funcdict={'col1':[pd.Series.astype,str.replace],'col2':[pd.Series.astype,str.replace]} argdict= {'col1':[['str'],['1','A']],'col2':[['str'],['3','B']]} for col in testframe.columns: for func in funcdict[col]: idx=funcdict[col].index(func) testframe[col]=testframe[col].func(*argdict[col][idx]) 

El resultado esperado sería

  col1 col2 0 'A' 'B' 1 '1' '4' 

Pero en cambio me dan

 AttributeError: 'Series' object has no attribute 'func' 

Notablemente

 testframe['col1']=testframe['col1'].astype(*argdict['col1'][0]) 

funciona como se esperaba, por lo que, de alguna manera, Python parece tener un problema con la syntax ‘.func’ a pesar del hecho de que

print(func)

produce la salida deseada: ‘function NDFrame.astype en 0x00000186954EB840’ etc.

Tu syntax para llamar a un método es incorrecta. Hay 2 formas de llamar a un método en Python.

Directo

Como has encontrado, esto funcionará. Tenga en cuenta que astype no hace referencia a ningún otro objeto, es el nombre real del método que pertenece a pd.Series .

 testframe['col1'] = testframe['col1'].astype(*argdict['col1'][0]) 

Funcional

El método funcional demuestra explícitamente que astype es el nombre del método.

 from operator import methodcaller testframe['col1'] = methodcaller('astype', *argdict['col1'][0])(testframe[col]) 

Probar testframe[col].func(...) nunca funcionará ya que func no es el nombre de un método pd.Series .

Podría usar rgettattr para obtener atributos de la serie, testframe[col] : Por ejemplo,

 In [74]: s = pd.Series(['1','2']) In [75]: rgetattr(s, 'str.replace')('1', 'A') Out[75]: 0 A 1 2 dtype: object 

 import functools import pandas as pd def rgetattr(obj, attr, *args): def _getattr(obj, attr): return getattr(obj, attr, *args) return functools.reduce(_getattr, [obj] + attr.split('.')) testframe = pd.DataFrame.from_dict({'col1': [1, 2], 'col2': [3, 4]}) funcdict = {'col1': ['astype', 'str.replace'], 'col2': ['astype', 'str.replace']} argdict = {'col1': [['str'], ['1', 'A']], 'col2': [['str'], ['3', 'B']]} for col in testframe.columns: for attr, args in zip(funcdict[col], argdict[col]): testframe[col] = rgetattr(testframe[col], attr)(*args) print(testframe) 

rendimientos

  col1 col2 0 AB 1 2 4 

getattr es la función en la biblioteca estándar de Python utilizada para obtener un atributo con nombre de un objeto cuando el nombre se da en forma de una cadena. Por ejemplo, dado

 In [92]: s = pd.Series(['1','2']); s Out[92]: 0 1 1 2 dtype: object 

podemos obtener s.str utilizando

 In [85]: getattr(s, 'str') Out[85]:  In [91]: s.str == getattr(s, 'str') Out[91]: True 

Para obtener su lugar de s.str.replace , necesitaríamos

 In [88]: getattr(getattr(s, 'str'), 'replace') Out[88]: > In [90]: s.str.replace == getattr(getattr(s, 'str'), 'replace') Out[90]: True 

Sin embargo, si especificamos

 funcdict = {'col1': ['astype', 'str.replace'], 'col2': ['astype', 'str.replace']} 

luego necesitamos alguna forma de manejar los casos en los que necesitamos una llamada para obtener getattr , (por ejemplo, getattr(testframe[col], 'astype') ) en comparación con aquellos casos en los que necesitamos múltiples llamadas para getattr ([por ejemplo, getattr(getattr(testframe[col], 'str'), 'replace') .

Para unificar los dos casos en una syntax simple, podemos usar rgetattr , un reemplazo recursivo y getattr para getattr que puede manejar cadenas punteadas de nombres de atributos de cadenas como 'str.replace' .

La recursión es manejada por reduce . Los documentos dan como ejemplo que reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calcula ((((1+2)+3)+4)+5) . De forma similar, puede imaginar que el + sea ​​reemplazado por getattr modo que rgetattr(s, 'str.replace') calcule getattr(getattr(s, 'str'), 'replace') .