【问题标题】:Pandas: Weighted median of grouped observations熊猫:分组观察的加权中位数
【发布时间】:2015-04-17 04:50:31
【问题描述】:

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

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

【问题讨论】:

  • 对不起,你想要INCAGG中值的索引吗?
  • 您似乎忽略了科学记数法...您看到 5 在右侧列中的值几乎是 3 的两倍,对吧?
  • 嗯,你收入的中位数是第 5 行的那个是熊猫告诉我的
  • 对不起各位,可能有多种混淆来源;其中之一:我没有注意到第 5 组以后是 e+09 而不是 e+08
  • 哦,第二列是权重。你只需要一个加权中位数。可能是这样的stackoverflow.com/questions/20601872/… 但总的来说,搜索“加权”任何东西都会得到很多结果。这在其他一些统计包(如 stata)中真的很容易,但我不知道它是否在 pandas 中那么容易。也许 scipy 或 statsmodels 有什么?

标签: python pandas scipy


【解决方案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

【讨论】:

    【解决方案2】:

    您可以使用来自 itertools 的链。我使用列表推导获取重复适当次数的聚合组列表,然后使用链将其放入单个列表中。最后,我将其转换为 Series 并计算了中位数:

    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
    

    【讨论】:

    • 嗯,我有数十亿的体重。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-16
    • 1970-01-01
    • 2020-01-22
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 2014-02-16
    相关资源
    最近更新 更多