我有一个带有一列权重和一个值的数据框。我需要:
有没有简单的方法来实现这一目标?我找到了一种方法,但是似乎有点麻烦:
基本上,我正在寻找一种更好的方法来产生更平滑的曲线。
我的代码(带有一些随机数据)是:
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')
答案 0 :(得分:1)
问题的第一部分很容易做到。
我不确定您对第二部分的意思。您是否想要(简化)代码复制或更适合您需要的新方法?
无论如何,我必须查看您的代码以了解您对加权值的含义。我认为人们通常会期望与该术语有所不同(仅作为警告)。
这是您的方法的简化版本:
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
答案 1 :(得分:1)
通过将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/2
和k=2
,因为它可以减少数据变形。希望能帮助到你。
答案 2 :(得分:0)
如果我正确理解,您正在尝试重新创建滚动平均值。
使用rolling
函数,这已经是熊猫数据框的功能:
dataframe.rolling(n).mean()
其中n
是平均值在“窗口”或“箱”中使用的相邻点的数量,因此您可以对其进行调整以获得不同程度的平滑度。
您可以在此处找到示例:
答案 3 :(得分:0)
我认为这是您正在寻找的解决方案。正如其他人所建议的,它使用滚动窗口。要使其正常工作,还需要做更多的工作。
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()