关于python:将方法列表中的方法应用于pandas数据帧

Apply a method from a list of methods to pandas dataframe

这是我第一个问题,请耐心等待。

我的问题是:

假设我们有一个pandas数据帧,并且我们想要动态地将一些pd.series方法应用到这个数据帧的一组列中。为什么下面的例子不起作用?

1
2
3
4
5
6
7
8
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])

预期结果是

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

但是我得到了

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

显著地

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

按预期工作,所以尽管事实上

print(func)

生成所需的输出:"function ndframe.astype at 0x00000186954eb840"等。


您可以使用rgettattr从系列中获取属性,testframe[col]:例如,

1
2
3
4
5
6
7
In [74]: s = pd.Series(['1','2'])

In [75]: rgetattr(s, 'str.replace')('1', 'A')
Out[75]:
0    A
1    2
dtype: object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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)

产量

1
2
3
  col1 col2
0    A    B
1    2    4

getattr是python标准库中的函数,用于在以字符串形式给出名称时从对象获取命名属性。例如,给定

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

我们可以使用

1
2
3
4
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,我们需要

1
2
3
4
5
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

但是,如果我们指定

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

然后,我们需要某种方式来处理需要一次呼叫getattr的情况(例如getattr(testframe[col], 'astype')),而那些需要多次呼叫getattr的情况(例如getattr(getattr(testframe[col], 'str'), 'replace'))。

为了将这两种情况统一为一种简单的语法,我们可以使用rgetattr,这是一种递归的drop替换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')


调用方法的语法不正确。在Python中有两种方法可以调用方法。

直接的

正如你所发现的,这是可行的。注意,astype没有引用其他对象,它是属于pd.Series的方法的实际名称。

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

功能性

函数方法显式地证明astype是方法的名称。

1
2
3
from operator import methodcaller

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

尝试testframe[col].func(...)永远不会奏效,因为func不是pd.Series方法的名称。