You probably need to create a new column called benchmark_mean
that has the mean per group?, then you could do as below:
df.groupby(["category","date"],as_index=False).agg(
benchmark_mean = pd.NamedAgg(column = 'fund_ret', aggfunc = 'mean'))
output:
category date benchmark_mean
0 x 2019-02 0.050
1 x 2019-03 0.025
2 y 2019-02 -0.010
3 y 2019-03 0.070
If you want a new column in the original dataframe with no change in rows, you can use groupby with transform as below:
df['benchmark_mean'] = df.groupby(['category', 'date'])['fund_ret'].transform('mean')
output of df
after :
date fund_ticker nav category fund_ret benchmark_mean
0 2019-02 AAA 1000.0 x 0.05 0.050
1 2019-03 AAA 1030.0 x 0.03 0.025
2 2019-02 BBB NaN y -0.01 -0.010
3 2019-03 BBB NaN y 0.07 0.070
4 2019-03 CCC NaN x 0.02 0.025
Why we cannot use "apply" instead of "transform"?
You can use apply instead of transform definitely and you can get the same result as below:
df_bm = df.groupby(['date', 'category']).apply(lambda x : pd.Series(x['fund_ret'].mean(), ['benchmark_mean'])).reset_index()
df = df.merge(df_bm, on = ['date', 'category'], how = 'left')
You may want to look at this beautiful answer to look at differences between apply and transform.
How to get weighted mean?
I m going to use apply because transform works only on one Series at a time so I cannot use [nav, fund_ret]
after groupby. The code will be :
def weighted_average(x):
x['nav'] = x['nav'].replace(np.nan, 0)
if x['nav'].nunique() == 1 and x['nav'].iloc[0] == 0:
return 0
return np.average(x['fund_ret'], weights=x['nav'])
df1 = df.groupby(['date', 'category'],as_index=False).apply(lambda x: pd.Series(weighted_average(x), ['weighted_mean']))['weighted_mean'].reset_index()
df.merge(df1, on = ['date', 'category'], how = 'left')
Output (after both the apply
s):
date fund_ticker nav category fund_ret benchmark_mean weighted_mean
0 2019-02 AAA 1000.0 x 0.05 0.050 0.05
1 2019-03 AAA 1030.0 x 0.03 0.025 0.03
2 2019-02 BBB NaN y -0.01 -0.010 NaN
3 2019-03 BBB NaN y 0.07 0.070 NaN
4 2019-03 CCC NaN x 0.02 0.025 0.03