熊猫加权统计

Posted

技术标签:

【中文标题】熊猫加权统计【英文标题】:Pandas Weighted Stats 【发布时间】:2021-12-15 09:44:52 【问题描述】:

我有一个如下图所示的数据框。

权重列本质上表示每个项目的频率,因此对于每个位置,权重总和将等于 1

请记住,这是一个简化的数据集,实际上有超过 100 列,如 value

d = 'location': ['a', 'a', 'b', 'b'],'item': ['x', 'y', 's', 'v'], 'value': [1, 5, 3, 7], 'weight': [0.9, 0.1, 0.8, 0.2]
df = pd.DataFrame(data=d)
df
  location item value weight
0     a     x     1     0.9
1     a     y     5     0.1
2     b     s     3     0.8
3     b     v     7     0.2

我目前有代码可以计算未加权数据的分组中位数、标准差、偏斜和分位数,我正在使用以下代码:

df = df[['location','value']]

df1 = df.groupby('location').agg(['median','skew','std']).reset_index()

df2 = df.groupby('location').quantile([0.1, 0.9, 0.25, 0.75, 0.5]).unstack(level=1).reset_index()

dfs = df1.merge(df2, how = 'left', on = 'location')

结果如下:

  location   value
             median skew      std  0.1  0.9 0.25 0.75  0.5
0      a         3  NaN  2.828427  1.4  4.6  2.0  4.0  3.0
1      b         5  NaN  2.828427  3.4  6.6  4.0  6.0  5.0

我想生成与上述完全相同的结果数据框,但使用weight 列进行加权统计。我该怎么做?

需要注意的另一个重要注意事项,value 经常为空,但它具有与之相关的权重。

【问题讨论】:

我的回答是否按预期工作?或者您认为复制观察结果有什么问题吗? @Mark,据我了解,是的,这是应用权重的正确方法,只是出于好奇,您能否提供任何参考来表明这实际上是最佳实践/正确方法?跨度> 请给我一些时间进行研究以提供最佳答案。 您能否添加更多信息,例如它们是什么类型的权重(频率权重、概率权重或仅仅是重要性权重)?您可以参考this page 了解有关每种重量的更多信息。根据您的回答,可以决定我的方法是有效还是不准确。 嗯,是的!如果这些是频率权重,那么我的方法是正确的。只需要进行一些调整。但我想更多地了解这些空值。 values = [3, nan] 与相应的权重 [0.1, 0.9] 的含义是什么?是不是 3 好像nan 被忽略了? 【参考方案1】:

因为权重是频率权重,所以最准确的方法是根据权重复制观测值。

调整权重

通常,频率是整数。然而,这里的频率仅仅显示了一个项目相对于同一组的其他项目出现的频率。在这种情况下,您可以将所有权重乘以一个使权重为整数的值,并在整个数据集中一致地使用该值。

这是一个函数,可帮助您选择尽可能小的权重集以最小化内存使用量并将权重作为整数返回。

def adjust(weights):
    base = 10 ** max([len(str(i).split(".")[1]) for i in weights])
    scalar = base / np.gcd.reduce((weights * base).astype(int))
    weights = weights * scalar

    return weights

您可以参考以下问题来了解此功能的工作原理。

Multiply a Numpy array by a scalar to make every element an integer
df = pd.DataFrame(
    "location": ["a", "a", "b", "b"],
    "values": [1, 5, 3, 7],
    "weights": [0.9, 0.1, 0.8, 0.2]
)

df.loc[:, "weights"] = adjust(df["weights"])

这是调整后的权重。

>>> df
  location  value  weights
0        a      1      9.0
1        a      5      1.0
2        b      3      8.0
3        b      7      2.0

复制观察结果

调整权重后,需要根据权重复制观测值。

df = df.loc[df.index.repeat(df["weights"])] \
    .reset_index(drop=True).drop("weights", axis=1)

您可以参考以下答案来了解此过程的工作原理。

Duplicate rows, according to value in a column

让我们计算重复后的观察次数。

>>> df.count()
location    20
values      20

执行统计操作

现在,您可以使用groupby 并使用任何统计操作进行聚合。数据现在已加权。

df1 = df.groupby("location").agg(["median", "skew", "std"]).reset_index()
df2 = df.groupby("location").quantile([0.1, 0.9, 0.25, 0.75, 0.5]) \
    .unstack(level=1).reset_index()

print(df1.merge(df2, how="left", on="location"))

这给出了以下输出。

  location values
           median      skew       std  0.1  0.9 0.25 0.75  0.5
0        a    1.0  3.162278  1.264911  1.0  1.4  1.0  1.0  1.0
1        b    3.0  1.778781  1.686548  3.0  7.0  3.0  3.0  3.0

解释加权统计数据

让我们按照上面相同的过程,但不是给权重尽可能小的值,我们将逐渐复制权重 并查看结果。因为权重处于最小值,较大的权重集将是当前集的倍数。以下行将被更改。

df.loc[:, "weights"] = adjust(df["weights"])

adjust(df["weights"]) * 2

  location values
           median      skew       std  0.1  0.9 0.25 0.75  0.5
0        a    1.0  2.887939  1.231174  1.0  1.4  1.0  1.0  1.0
1        b    3.0  1.624466  1.641565  3.0  7.0  3.0  3.0  3.0

adjust(df["weights"]) * 3

  location values
           median     skew       std  0.1  0.9 0.25 0.75  0.5
0        a    1.0  2.80912  1.220514  1.0  1.4  1.0  1.0  1.0
1        b    3.0  1.58013  1.627352  3.0  7.0  3.0  3.0  3.0

adjust(df["weights"]) * 4

  location values
           median      skew       std  0.1  0.9 0.25 0.75  0.5
0        a    1.0  2.771708  1.215287  1.0  1.4  1.0  1.0  1.0
1        b    3.0  1.559086  1.620383  3.0  7.0  3.0  3.0  3.0

重复这个过程几次,我们将得到下图。此图表中的统计数据未分组,并且出于演示目的添加了一些其他统计数据。

无论我们重复观察多少次,样本均值、中位数和分位数等一些统计数据始终保持不变。

另一方面,一些统计数据会根据我们复制的次数给出不同的结果。我们暂时称它们为不一致的统计数据。

有两种不一致的统计数据。

    与样本量无关的不一致统计数据

    例如:任何统计矩(均值、方差、标准差、偏度、峰度)

    这里的独立并不意味着“等式中没有样本量”。请注意样本均值如何在其方程中也有样本量,但它仍然与样本量无关。

    对于这些类型的统计数据,您无法计算出确切的值,因为答案可能会因样本量的不同而不同。但是,您可以得出结论,例如,A 组的标准差通常高于 B 组的标准差。

    取决于样本大小的不一致统计数据

    例如:均值和总和的标准误

    然而,标准误差取决于样本量。让我们看看它的方程式。

    我们可以将标准误差视为样本量平方根的标准偏差,因此它取决于样本量。总和还取决于样本量。

    对于这些类型的统计数据,我们无法得出任何结论,因为我们缺少一个重要信息:样本量。

【讨论】:

【参考方案2】:

不要合并两个groupby操作,而是在加权值后使用named aggregation:

    使用assign 生成weighted 值。 使用output_col: (input_col, agg_function), ...聚合。
dfs = df.assign(weighted=df.value * df.weight).groupby('location').agg(**
    'median': ('weighted', 'median'),
    'skew': ('weighted', 'skew'),
    'std': ('weighted', 'std'),
    '0.1': ('weighted', lambda x: x.quantile(0.1)),
    '0.9': ('weighted', lambda x: x.quantile(0.9)),
    '0.25': ('weighted', lambda x: x.quantile(0.25)),
    '0.75': ('weighted', lambda x: x.quantile(0.75)),
    '0.5': ('weighted', lambda x: x.quantile(0.5)),
)

输出:

          median  skew       std   0.1   0.9  0.25  0.75  0.5
location                                                     
a            0.7   NaN  0.282843  0.54  0.86  0.60  0.80  0.7
b            1.9   NaN  0.707107  1.50  2.30  1.65  2.15  1.9

【讨论】:

以上是关于熊猫加权统计的主要内容,如果未能解决你的问题,请参考以下文章

熊猫滚动加权平均值

基于买卖的加权平均收购成本熊猫

使用熊猫/数据框计算加权平均值

如何计算熊猫中一行中所有元素的加权和?

熊猫数据框每一行的加权平均值

如何使用带有询价和出价的熊猫数据框计算体积加权平均价格(VWAP)?