如何平滑和绘制 x 与 y 的加权平均值,由 x 加权?

Posted

技术标签:

【中文标题】如何平滑和绘制 x 与 y 的加权平均值,由 x 加权?【英文标题】:How to smooth and plot x vs weighted average of y, weighted by x? 【发布时间】:2019-08-23 03:50:21 【问题描述】:

我有一个数据框,其中有一列权重和一个值。我需要:

离散权重,并为每个权重区间绘制 加权平均值,然后 将相同的逻辑扩展到另一个 变量:离散 z,并为每个区间绘制加权的 值的平均值,按权重加权

有没有简单的方法可以实现?我找到了方法,但是好像有点麻烦:

我用 pandas.cut() 离散化数据帧 进行分组并计算加权平均值 绘制每个 bin 的平均值与加权平均值的关系 我也尝试过用样条曲线平滑曲线,但效果不大

基本上我正在寻找一种更好的方法来产生更平滑的曲线。

我的输出如下所示:

我的代码和一些随机数据是:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.interpolate import make_interp_spline, BSpline

n=int(1e3)
df=pd.DataFrame()
np.random.seed(10)
df['w']=np.arange(0,n)
df['v']=np.random.randn(n)
df['ranges']=pd.cut(df.w, bins=50)
df['one']=1.
def func(x, df):
    # func() gets called within a lambda function; x is the row, df is the entire table
    b1= x['one'].sum()
    b2 = x['w'].mean()
    b3 = x['v'].mean()       
    b4=( x['w'] * x['v']).sum() / x['w'].sum() if x['w'].sum() >0 else np.nan

    cols=['# items','avg w','avg v','weighted avg v']
    return pd.Series( [b1, b2, b3, b4], index=cols )

summary = df.groupby('ranges').apply(lambda x: func(x,df))

sns.set(style='darkgrid')

fig,ax=plt.subplots(2)
sns.lineplot(summary['avg w'], summary['weighted avg v'], ax=ax[0])
ax[0].set_title('line plot')

xnew = np.linspace(summary['avg w'].min(), summary['avg w'].max(),100)
spl = make_interp_spline(summary['avg w'], summary['weighted avg v'], k=5) #BSpline object
power_smooth = spl(xnew)
sns.lineplot(xnew, power_smooth, ax=ax[1])
ax[1].set_title('not-so-interpolated plot')

【问题讨论】:

你的平均权重是多少? 想象一个具有 3 列的数据框:w、x、y。我离散 x;对于如此离散的 x 的每个桶,我想计算 y 的加权平均值,由 w 加权。 请注意,您的评论与问题不同(您要离散权重还是 x?)此外,关于平滑的句子也不清楚。计算加权平均值不一定会平滑任何东西,这取决于权重,对吧?那么目的是平滑的吗?还是计算加权平均值? 你说得对,我不清楚。实际上,我有时会通过权重进行离散化,有时会通过另一个变量进行离散化。加权平均值与平滑无关 - 平滑是一个单独的点。 @Pythonistaan​​onymous 你考虑过使用内核吗?对我来说,这似乎是正确的方法 【参考方案1】:

如果我的理解正确,您是在尝试重新创建滚动平均值。

这已经是 Pandas 数据帧的一项功能,使用 rolling 函数:

dataframe.rolling(n).mean()

其中n 是“窗口”或“箱”中用于平均值的相邻点的数量,因此您可以调整它以获得不同程度的平滑度。

您可以在此处找到示例:

https://www.datacamp.com/community/tutorials/time-series-analysis-tutorial

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html

【讨论】:

我不确定这是否相同。我没有时间序列,我正在寻找的是加权平均值。可以说,我应该选择条形图而不是线图,但是,基本上,我离散化一个连续变量,然后显示离散化产生的每个桶的加权平均值。例如,想象一次人口调查;我按年龄范围划分,对于每个年龄范围,我显示加权平均值,我不知道,按收入加权的储蓄率。类似的东西。【参考方案2】:

你问题的第一部分很容易做到。

我不确定你对第二部分的意思。您想要(简化的)代码复制还是更适合您需求的新方法?

无论如何,我必须查看您的代码以通过加权值来理解您的意思。我认为人们通常会期望与该术语不同的东西(仅作为警告)。

这是您的方法的简化版本:

df['prod_v_w'] = df['v']*df['w']
weighted_avg_v = df.groupby(pd.cut(df.w, bins=50))[['prod_v_w','w']].sum()\
                   .eval('prod_v_w/w')
print(np.allclose(weighted_avg_v, summary['weighted avg v']))
Out[18]: True

【讨论】:

@p-tillmann 人们如何理解“权重”,他们如何称呼我所说的权重?【参考方案3】:

我认为这是您所寻求的解决方案。正如其他人所建议的那样,它使用滚动窗口。需要做更多的工作才能使其正常工作。

df["w*v"] = df["w"] * df["v"]

def rolling_smooth(df,N):
    df_roll = df.rolling(N).agg("w":["sum","mean"],"v":["mean"],"w*v":["sum"])
    df_roll.columns = [' '.join(col).strip() for col in df_roll.columns.values]
    df_roll['weighted avg v'] = np.nan
    cond = df_roll['w sum'] > 0
    df_roll.loc[cond,'weighted avg v'] = df_roll.loc[cond,'w*v sum'] / df_roll.loc[cond,'w sum']
    return df_roll

df_roll_100 = rolling_smooth(df,100)
df_roll_200 = rolling_smooth(df,200)

plt.plot(summary['avg w'], summary['weighted avg v'],label='original')
plt.plot(df_roll_100["w mean"],df_roll_100["weighted avg v"],label='rolling N=100')
plt.plot(df_roll_200["w mean"],df_roll_200["weighted avg v"],label='rolling N=200')
plt.legend()

【讨论】:

【参考方案4】:

我认为您使用的插值很少,通过将 xnew = np.linspace(summary['avg w'].min(), summary['avg w'].max(),100) 更改为 xnew = np.linspace(summary['avg w'].min(), summary['avg w'].max(),500) 我得到以下信息:

并将样条度数更改为k=2 我得到以下结果:

我认为插值的一个很好的起点可能是n/2k=2,因为它呈现的数据变形较少。希望能帮助到你。

【讨论】:

以上是关于如何平滑和绘制 x 与 y 的加权平均值,由 x 加权?的主要内容,如果未能解决你的问题,请参考以下文章

条件期望与重期望法则

条件期望与重期望法则

曲线平滑(低通滤波和算数平均滤波)

彩色图像转为灰度图像

如何用matlab画平滑曲线?

使用 ggplot2 沿平滑曲线绘制直方图或密度