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

Posted

技术标签:

【中文标题】使用熊猫/数据框计算加权平均值【英文标题】:Calculate weighted average using a pandas/dataframe 【发布时间】:2014-11-30 02:30:10 【问题描述】:

我有下表。我想根据以下公式计算按每个日期分组的加权平均值。我可以使用一些标准的常规代码来做到这一点,但假设这些数据在 pandas 数据框中,有没有比通过迭代更简单的方法来实现这一点?

Date        ID      wt      value   w_avg
01/01/2012  100     0.50    60      0.791666667
01/01/2012  101     0.75    80
01/01/2012  102     1.00    100
01/02/2012  201     0.50    100     0.722222222
01/02/2012  202     1.00    80

01/01/2012 w_avg = 0.5 * ( 60/ sum(60,80,100)) + .75 * (80/ sum(60,80,100)) + 1.0 * (100/sum(60,80,100))

01/02/2012 w_avg = 0.5 * ( 100/ sum(100,80)) + 1.0 * ( 80/ 总和(100,80))

【问题讨论】:

请注意,在您的示例中,“值”列实际上表示权重,“重量”列表示要平均的值... 【参考方案1】:

老问题的另一种选择(使用@kadeesample data):

(df.drop(columns='ID')
  .assign(wt = lambda df: df.prod(1)) # product of weight and value
  .groupby('Date').sum()
  .pipe(lambda df: df.wt / df.value) # weighted average computation
) 
Date
01/01/2012    0.791667
01/02/2012    0.722222

如果在不使用匿名函数的情况下完成计算,应该会快得多:

temp = df.drop(columns='ID')
temp = temp.assign(wt = temp.prod(1)).groupby('Date').sum()
temp.wt / temp.value

Date
01/01/2012    0.791667
01/02/2012    0.722222
dtype: float64

【讨论】:

【参考方案2】:

如果速度对您来说是一个重要因素,那么矢量化至关重要。因此,基于the answer by Andy Hayden,这是一个仅使用 Pandas 原生函数的解决方案:

def weighted_mean(df, values, weights, groupby):
    df = df.copy()
    grouped = df.groupby(groupby)
    df['weighted_average'] = df[values] / grouped[weights].transform('sum') * df[weights]
    return grouped['weighted_average'].sum(min_count=1) #min_count is required for Grouper objects

相比之下,使用自定义的lambda 函数代码更少,但速度更慢:

import numpy as np
def weighted_mean_by_lambda(df, values, weights, groupby):
    return df.groupby(groupby).apply(lambda x: np.average(x[values], weights=x[weights]))

速度测试:

import time
import numpy as np
import pandas as pd

n = 100000000

df = pd.DataFrame(
    'values': np.random.uniform(0, 1, size=n), 
    'weights': np.random.randint(0, 5, size=n),
    'groupby': np.random.randint(0, 10000, size=n), 
)

time1 = time.time()
weighted_mean(df, 'values', 'weights', 'groupby')
print('Time for `weighted_mean`:', time.time() - time1)

time2 = time.time()
weighted_mean_by_lambda(df, 'values', 'weights', 'groupby')
print('Time for `weighted_mean_by_lambda`:', time.time() - time2)

速度测试输出:

Time for `weighted_mean`: 3.4519572257995605
Time for `weighted_mean_by_lambda`: 11.41335940361023

【讨论】:

【参考方案3】:

我将表格保存在 .csv 文件中

df=pd.read_csv('book1.csv')

grouped=df.groupby('Date')
g_wavg= lambda x: np.average(x.wt, weights=x.value)
grouped.apply(g_wavg)

【讨论】:

你也可以把这个放在单行里..!!【参考方案4】:

我觉得以下是这个问题的优雅解决方案:(Pandas DataFrame aggregate function using multiple columns)

grouped = df.groupby('Date')

def wavg(group):
    d = group['value']
    w = group['wt']
    return (d * w).sum() / w.sum()

grouped.apply(wavg)

【讨论】:

【参考方案5】:

让我们首先创建示例熊猫数据框:

In [1]: import numpy as np

In [2]: import pandas as pd

In [3]: index = pd.Index(['01/01/2012','01/01/2012','01/01/2012','01/02/2012','01/02/2012'], name='Date')

In [4]: df = pd.DataFrame('ID':[100,101,102,201,202],'wt':[.5,.75,1,.5,1],'value':[60,80,100,100,80],index=index)

然后,得到“价值”加权并按指标分组的“重量”平均值为:

In [5]: df.groupby(df.index).apply(lambda x: np.average(x.wt, weights=x.value))
Out[5]: 
Date
01/01/2012    0.791667
01/02/2012    0.722222
dtype: float64

另外,也可以定义一个函数:

In [5]: def grouped_weighted_avg(values, weights, by):
   ...:     return (values * weights).groupby(by).sum() / weights.groupby(by).sum()

In [6]: grouped_weighted_avg(values=df.wt, weights=df.value, by=df.index)
Out[6]: 
Date
01/01/2012    0.791667
01/02/2012    0.722222
dtype: float64

【讨论】:

我更喜欢这个(由于可读性),这个和安迪·海登的解决方案之间有什么显着的表现吗? 这行是否有可能:在[5]中:df.groupby(df.index).apply(lambda x: np.average(x.wt, weights=x.value)) x.wt 和 x.value 应该切换? @prooffreader:正如我评论的above:在提问者给出的示例中,“值”列实际上表示权重,“重量”列表示要平均的值。跨度> 在处理大型数据帧时,此方法比公认的答案慢得多。 @dwitvliet “大”有多大?我正在处理每日频率的人口普查区块组数据。数据框有Cx365 行,其中 C 是人口普查区块组的数量。使用大约 600,000 行模拟数据,kadee 的方法实际上比 Andy 的答案快两倍。我假设您指的是具有大量 by_groups 的“大型数据框”?【参考方案6】:

我想我会用两个 groupbys 来做这个。

首先计算“加权平均值”:

In [11]: g = df.groupby('Date')

In [12]: df.value / g.value.transform("sum") * df.wt
Out[12]:
0    0.125000
1    0.250000
2    0.416667
3    0.277778
4    0.444444
dtype: float64

如果将其设置为列,则可以对其进行分组:

In [13]: df['wa'] = df.value / g.value.transform("sum") * df.wt

现在该列的总和是所需的:

In [14]: g.wa.sum()
Out[14]:
Date
01/01/2012    0.791667
01/02/2012    0.722222
Name: wa, dtype: float64

或可能:

In [15]: g.wa.transform("sum")
Out[15]:
0    0.791667
1    0.791667
2    0.791667
3    0.722222
4    0.722222
Name: wa, dtype: float64

【讨论】:

注意:我不是 100% 对在突变 df 时重用 g 有什么看法,前提是你没有改变 groupby 键,我认为这很整洁......这可能是有争议的吗?! IMO 大熊猫。 我能够通过类似的方式完成此操作,但我没有使用转换,而是使用了 groupby(..).sum()。使用转换有什么好处吗? @AndyHayden DataFrameGroupBy 对象反映一个变异的对象,但在这种情况下你没有变异,所以没什么大不了的。 当我尝试将它插入同一个数据帧时,这些值都是 NAN。我认为这是因为聚合是在日期上,但数据框是在日期和 ID 上编制索引的。所以这样做不起作用:df['w_avg'] = g.wa.sum().我该如何解决这个问题? @mike01010 转换将结果传播到整个组中,如果您以后使用它很有用。使用 NaN 插入正是如此 - 这就是需要转换的原因(它与原始索引匹配)。

以上是关于使用熊猫/数据框计算加权平均值的主要内容,如果未能解决你的问题,请参考以下文章

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

Pandas:交换一个数据框中的特定列值并计算其加权平均值

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

group的加权平均值不等于pandas groupby中的总平均值

熊猫移动平均线[重复]

使用 NumPy 函数计算 Pandas 中的加权平均值