熊猫:分组观察的加权中位数

时间:2015-04-16 14:42:33

标签: python pandas scipy

我有一个数据框,其中包含每组收入的观察数量:

INCAGG
1         6.561681e+08
3         9.712955e+08
5         1.658043e+09
7         1.710781e+09
9         2.356979e+09

我想计算收入中位数组。我的意思是什么? 让我们从一个更简单的系列开始:

INCAGG
1          6
3          9
5         16
7         17
9         23

它代表了这组数字:

1 1 1 1 1 1
3 3 3 3 3 3 3 3 3
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 
7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9

我可以重新订购

1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 7 7 7 7 7
7 7 7 7 7 7 7 7 7 7 7 7 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9

在视觉上我的意思是 - 这里的中位数是7

2 个答案:

答案 0 :(得分:1)

在看了一个numpy示例here之后,我认为cumsum()提供了一个很好的方法。假设你的计数列被称为'wt',这里有一个简单的解决方案,大部分时间都可以工作(以下是一个更通用的解决方案):

df = df.sort('incagg')

df['tmp'] = df.wt.cumsum() < ( df.wt.sum() / 2. )

df['med_grp'] = (df.tmp==False) & (df.tmp.shift()==True)

上面的第二个代码行分为中位数上方和下方的行。中位观察将在第一个False组中。

   incagg          wt    tmp med_grp
0       1   656168100   True   False
1       3   971295500   True   False
2       5  1658043000   True   False
3       7  1710781000  False    True
4       9  2356979000  False   False

df.ix[df.med_grp,'incagg']

3    7
Name: incagg, dtype: int64

当中位数是唯一的并且通常不是中位数时,这将正常工作。只有当中位数是非唯一的并且它落在组的边缘时才会出现问题。在这种情况下(有5个组,权重在数百万/十亿),这真的不是一个问题,但不过这是一个更通用的解决方案:

df['tmp1']    = df.wt.cumsum() == (df.wt.sum() / 2.)
df['tmp2']    = df.wt.cumsum() < (df.wt.sum() / 2.)
df['med_grp'] = (df.tmp2==False) & (df.tmp2.shift()==True)
df['med_grp'] = df.med_grp | df.tmp1.shift()

   incagg  wt   tmp1   tmp2 med_grp
0       1   1  False   True   False
1       3   1  False   True   False
2       5   1   True  False    True
3       7   2  False  False    True
4       9   1  False  False   False

df.ix[df.med_grp,'incagg']
2    5
3    7

df.ix[df.med_grp,'incagg'].mean()
6.0

答案 1 :(得分:0)

您可以使用itertools中的链。我使用list comprehension来获取重复组的列表,重复适当的次数,然后使用chain将其放入单个列表中。最后,我将其转换为系列并计算中位数:

from itertools import chain

df = pd.DataFrame([6, 9, 16, 17, 23], index=[1, 3, 5, 7, 9], columns=['counts'])

median = pd.Series([i for i in chain(*[[k] * v for k, v in zip(df.index, df.counts)])]).median()

>>> median
7.0