Python 使用数据帧/数据帧计算加权平均值

Python 使用数据帧/数据帧计算加权平均值,python,numpy,pandas,Python,Numpy,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

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

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
2012年1月1日w_平均值=0.5*(60/总和(60,80100))+0.75*(80/ 总和(60,80100))+1.0*(100/总和(60,80100))

2012年2月1日w_平均值=0.5*(100/总和(100,80))+1.0*(80/ 总数(100,80))


我想我会用两个GroupBy来做这件事

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

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

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

让我们首先创建示例数据帧:

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)
然后,通过“值”加权并按指数分组的“wt”平均值如下:

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

我觉得以下是解决这个问题的一个很好的方法:()


我将表格保存在.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)

如果速度对您来说是一个重要因素,那么矢量化是至关重要的。因此,基于,这里有一个仅使用本机函数的解决方案:

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

注意:我不是百分之百地认为在变异df时重用g,只要你没有变异groupby键,我认为这很好。。。这可能是有争议的?!我可以用类似的方法来完成这个任务,但是我没有使用转换,而是使用了groupby(..).sum()。使用transform有什么好处吗?@AndyHayden DataFrameGroupBy对象将反映一个变异的对象,但在这种情况下,您没有变异,所以没什么大不了的。当我尝试将其插入同一数据帧时,值都是NAN。我认为这是因为聚合是按日期进行的,但数据帧是按日期和ID进行索引的。因此这样做不起作用:df['w_avg']=g.wa.sum()。如何解决此问题?@mike01010 transform将结果传播到整个组,如果您以后使用它,这将非常有用。插入NaNs正是这一点-这就是为什么需要转换(它匹配原始索引)。请注意,在您的示例中,“value”列实际上表示权重,“wt”列表示要平均的值…我更喜欢这一列(由于可读性),这与Andy Hayden的解决方案之间是否有任何显著的性能?是否有可能在这行中:in[5]:df.groupby(df.index).apply(lambda x:np.average(x.wt,weights=x.value))x.wt和x.value应该切换?@proofreader:正如我所评论的:在asker给出的示例中,“value”列实际上表示权重,“wt”列表示要平均的值。“wt”列表示要平均的值。在处理大数据帧时,此方法比接受的答案慢得多。@dwitvliet“large”有多大?我每天都在处理人口普查分组数据。数据框中有
Cx365
行,其中C是普查区块组的数量。在大约600000行模拟数据的情况下,卡迪的方法实际上比安迪的答案快两倍。我想你指的是“大数据帧”,包含大量的by_组?你也可以把它放在一行中。。!!
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