将函数应用到pandas groupby
问题描述:
我有一个名为my_labels
的包含字符串的列的熊猫数据框:'A', 'B', 'C', 'D', 'E'
。我想要计算每个字符串的出现次数,然后将计数次数除以所有计数的总和。我试图做到这一点在熊猫这样的:将函数应用到pandas groupby
func = lambda x: x.size()/x.sum()
data = frame.groupby('my_labels').apply(func)
此代码抛出一个错误,“数据帧对象有没有属性‘大小’。我怎样才能应用一个函数来计算熊猫呢?
答
apply
具有适用于各个值的功能,而不是系列,并接受kwargs。 所以,这些值没有.size()
方法。
也许这会工作:
from pandas import *
d = {"my_label": Series(['A','B','A','C','D','D','E'])}
df = DataFrame(d)
def as_perc(value, total):
return value/float(total)
def get_count(values):
return len(values)
grouped_count = df.groupby("my_label").my_label.agg(get_count)
data = grouped_count.apply(as_perc, total=df.my_label.count())
的.agg()
方法这里需要被应用到的groupby object的所有值的功能。
答
尝试:
g = pd.DataFrame(['A','B','A','C','D','D','E'])
# Group by the contents of column 0
gg = g.groupby(0)
# Create a DataFrame with the counts of each letter
histo = gg.apply(lambda x: x.count())
# Add a new column that is the count/total number of elements
histo[1] = histo.astype(np.float)/len(g)
print histo
输出:
0 1
0
A 2 0.285714
B 1 0.142857
C 1 0.142857
D 2 0.285714
E 1 0.142857
答
只见嵌套函数技术,用于计算上S.O.加权平均有一次,改变这种技术可以解决你的问题。
def group_weight(overall_size):
def inner(group):
return len(group)/float(overall_size)
inner.__name__ = 'weight'
return inner
d = {"my_label": pd.Series(['A','B','A','C','D','D','E'])}
df = pd.DataFrame(d)
print df.groupby('my_label').apply(group_weight(len(df)))
my_label
A 0.285714
B 0.142857
C 0.142857
D 0.285714
E 0.142857
dtype: float64
这里是如何做到组内的加权平均
def wavg(val_col_name,wt_col_name):
def inner(group):
return (group[val_col_name] * group[wt_col_name]).sum()/group[wt_col_name].sum()
inner.__name__ = 'wgt_avg'
return inner
d = {"P": pd.Series(['A','B','A','C','D','D','E'])
,"Q": pd.Series([1,2,3,4,5,6,7])
,"R": pd.Series([0.1,0.2,0.3,0.4,0.5,0.6,0.7])
}
df = pd.DataFrame(d)
print df.groupby('P').apply(wavg('Q','R'))
P
A 2.500000
B 2.000000
C 4.000000
D 5.545455
E 7.000000
dtype: float64
答
Starting with Pandas version 0.22,也存在于apply
替代:pipe
,这可以大大快于使用apply
(你也可以检查this question以获得两种功能之间的更多差异)。
对于示例:
df = pd.DataFrame({"my_label": ['A','B','A','C','D','D','E']})
my_label
0 A
1 B
2 A
3 C
4 D
5 D
6 E
的apply
版本
df.groupby('my_label').apply(lambda grp: grp.count()/df.shape[0])
给
my_label
my_label
A 0.285714
B 0.142857
C 0.142857
D 0.285714
E 0.142857
和pipe
版本
df.groupby('my_label').pipe(lambda grp: grp.size()/grp.size().sum())
产生
my_label
A 0.285714
B 0.142857
C 0.142857
D 0.285714
E 0.142857
所以值是相同的,然而,定时相差相当多(至少对于这个小数据帧):
%timeit df.groupby('my_label').apply(lambda grp: grp.count()/df.shape[0])
100 loops, best of 3: 5.52 ms per loop
和
%timeit df.groupby('my_label').pipe(lambda grp: grp.size()/grp.size().sum())
1000 loops, best of 3: 843 µs per loop
将它包装成功能也很简单:
def get_perc(grp_obj):
gr_size = grp_obj.size()
return gr_size/gr_size.sum()
现在,您可以拨打
df.groupby('my_label').pipe(get_perc)
产生
my_label
A 0.285714
B 0.142857
C 0.142857
D 0.285714
E 0.142857
然而,对于这种特殊情况下,你甚至不需要一个groupby
,但你可以只使用value_counts
这样的:
df['my_label'].value_counts(sort=False)/df.shape[0]
收益率
A 0.285714
C 0.142857
B 0.142857
E 0.142857
D 0.285714
Name: my_label, dtype: float64
对于这个小数据帧是
%timeit df['my_label'].value_counts(sort=False)/df.shape[0]
1000 loops, best of 3: 770 µs per loop
你也可以使用'HISTO = gg.size()'为简单起见相当快 – Reservedegotist 2013-03-13 01:13:51