Применить метод из списка методов к пандам dataframe - PullRequest
0 голосов
/ 16 мая 2018

это мой первый вопрос здесь, поэтому, пожалуйста, будьте терпеливы со мной.

Моя проблема заключается в следующем:

Предположим, у нас есть пандас Dataframe, и мы хотим динамически применить некоторые pd.Seriesметоды для набора столбцов этого Dataframe.Почему следующий пример не работает?

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])

Ожидаемый результат будет

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

Но вместо этого я получаю

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

Примечательно

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

работает как положено, поэтому почему-то в python есть проблема с синтаксисом .func, несмотря на тот факт, что

print(func)

дает желаемый результат: 'function NDFrame.astype в 0x00000186954EB840 'и т. д.

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Вы можете использовать rgettattr для получения атрибутов из Серии, testframe[col]: например,

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)

выход

  col1 col2
0    A    B
1    2    4

getattr - это функция в стандартной библиотеке Python, используемая для получения именованного атрибута от объекта, когда имя задано в виде строки.Например, учитывая

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

, мы можем получить s.str, используя

In [85]: getattr(s, 'str')
Out[85]: <pandas.core.strings.StringMethods at 0x7f334a847208>
In [91]: s.str == getattr(s, 'str')
Out[91]: True

. Чтобы получить s.str.replace, нам потребуется

In [88]: getattr(getattr(s, 'str'), 'replace')
Out[88]: <bound method StringMethods.replace of <pandas.core.strings.StringMethods object at 0x7f334a847208>>

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

Однако, еслимы указываем

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

, тогда нам нужен какой-то способ обработки случаев, когда нам нужен один вызов getattr (например, getattr(testframe[col], 'astype')), по сравнению с теми случаями, когда нам нужно несколько вызовов getattr (например, getattr(getattr(testframe[col], 'str'), 'replace').

Чтобы объединить два случая в один простой синтаксис, мы можем использовать rgetattr, рекурсивную замену для getattr, которая может обрабатывать точечные цепочки имен строковых атрибутов, таких как 'str.replace'.

Рекурсия обрабатывается с помощью reduce. Документы приводят в качестве примера, что reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) вычисляет ((((1+2)+3)+4)+5). Аналогично, вы можете представить, что + заменяется на getattr, чтобы rgetattr(s, 'str.replace') вычислял getattr(getattr(s, 'str'), 'replace').

0 голосов
/ 16 мая 2018

Ваш синтаксис для вызова метода неверен.Есть два способа вызвать метод в Python.

Direct

Как вы обнаружили, это будет работать.Обратите внимание, что astype не ссылается на какой-либо другой объект, это фактическое имя метода, принадлежащего pd.Series.

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

Функциональный

Функциональный метод явно демонстрирует, что astype является именем метода.

from operator import methodcaller

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

Попытка testframe[col].func(...) никогда не будет работать, так как func не является именем pd.Series метода.

...