如何在pandas的map函数中传递多个参数
我有一个这样的数据框
mn = pd.DataFrame({'fld1': [2.23, 4.45, 7.87, 9.02, 8.85, 3.32, 5.55],'fld2': [125000, 350000,700000, 800000, 200000, 600000, 500000],'lType': ['typ1','typ2','typ3','typ1','typ3','typ1','typ2'], 'counter': [100,200,300,400,500,600,700]})
映射函数
def getTag(rangeAttribute):
sliceDef = {'tag1': [1, 4], 'tag2': [4, 6], 'tag3': [6, 9],
'tag4': [9, 99]}
for sl in sliceDef.keys():
bounds = sliceDef[sl]
if ((float(rangeAttribute) >= float(bounds[0]))
and (float(rangeAttribute) <= float(bounds[1]))):
return sl
def getTag1(rangeAttribute):
sliceDef = {'100-150': [100000, 150000],
'150-650': [150000, 650000],
'650-5M': [650000, 5000000]}
for sl in sliceDef.keys():
bounds = sliceDef[sl]
if ((float(rangeAttribute) >= float(bounds[0]))
and (float(rangeAttribute) <= float(bounds[1]))):
return sl
我想根据fld1和fld2的标签来计算总和。目前我必须为不同类型的字段写不同的函数,并且这些函数里面的值是固定的。MAP函数只接受一个参数。有没有其他的函数可以接受sliceDef作为输入参数呢?
mn.groupby([mn['fld1'].map(getTag),mn['fld2'].map(getTag1),'lType'] ).sum()
1 个回答
5
counter fld1 fld2
lType
tag1 100-150 typ1 100 2.23 125000
150-650 typ1 600 3.32 600000
tag2 150-650 typ2 900 10.00 850000
tag3 150-650 typ3 500 8.85 200000
650-5M typ3 300 7.87 700000
tag4 650-5M typ1 400 9.02 800000
与其使用 map,你可以试试 pd.cut(感谢 DSM 和 Jeff 提醒我这一点):
import numpy as np
import pandas as pd
mn = pd.DataFrame(
{'fld1': [2.23, 4.45, 7.87, 9.02, 8.85, 3.32, 5.55],
'fld2': [125000, 350000, 700000, 800000, 200000, 600000, 500000],
'lType': ['typ1', 'typ2', 'typ3', 'typ1', 'typ3', 'typ1', 'typ2'],
'counter': [100, 200, 300, 400, 500, 600, 700]})
result = mn.groupby(
[pd.cut(mn['fld1'], [1,4,6,9,99], labels=['tag1', 'tag2', 'tag3', 'tag4']),
pd.cut(mn['fld2'], [100000, 150000, 650000, 5000000],
labels=['100-150', '150-650', '650-5M']),
'lType']).sum()
print(result)
这样做会更快,因为它只需要一次调用就能处理整个数据系列,而不是对每个值都调用一次 getTag
或 getTag1
。pd.cut
使用的是 np.searchsorted,这个方法能一次性返回所有的索引(而且,searchsorted
是用 C 语言写的 O(log n) 二分查找,比用 Python 写的 O(n) 循环要快)。
一个细微的点: sliceDef.keys()
返回的键值并不保证有特定的顺序。即使是同样的代码,每次运行的顺序也可能不同(至少在 Python3 中是这样)。你的条件使用的是完全闭合的区间:
if ((float(rangeAttribute) >= float(bounds[0]))
and (float(rangeAttribute) <= float(bounds[1]))):
所以如果 rangeAttribute
恰好落在 bounds
中的某个值上,先测试哪个键可能会有影响。
因此,你当前的代码是非确定性的。
pd.cut
使用的是半开区间,这样每个值只会落入一个类别,从而避免了这个问题。
关于一般性问题的回答:是的,有办法传递额外的参数——使用 apply 而不是 map(感谢 Andy Hayden 提醒我这一点):
import numpy as np
import pandas as pd
def getTag(rangeAttribute, sliceDef):
for sl in sliceDef.keys():
bounds = sliceDef[sl]
if ((float(rangeAttribute) >= float(bounds[0]))
and (float(rangeAttribute) <= float(bounds[1]))):
return sl
sliceDef = {'tag1': [1, 4], 'tag2': [4, 6], 'tag3': [6, 9],
'tag4': [9, 99]}
sliceDef1 = {'100-150': [100000, 150000],
'150-650': [150000, 650000],
'650-5M': [650000, 5000000]}
mn = pd.DataFrame(
{'fld1': [2.23, 4.45, 7.87, 9.02, 8.85, 3.32, 5.55],
'fld2': [125000, 350000, 700000, 800000, 200000, 600000, 500000],
'lType': ['typ1', 'typ2', 'typ3', 'typ1', 'typ3', 'typ1', 'typ2'],
'counter': [100, 200, 300, 400, 500, 600, 700]})
result = mn.groupby([mn['fld1'].apply(getTag, args=(sliceDef, ))
,mn['fld2'].apply(getTag, args=(sliceDef1, )),
'lType'] ).sum()
print(result)
不过,我不建议在这个特定问题上使用 apply
,因为 pd.cut
更快、更简单,并且避免了字典键顺序不确定的问题。但知道 apply
可以接受额外的位置参数,可能对你将来会有帮助。