Как сгладить и построить x против средневзвешенного значения y, взвешенного по x? - PullRequest
6 голосов
/ 02 апреля 2019

У меня есть фрейм данных со столбцом весов и одним из значений.Мне нужно:

  • до дискретных весов и, для каждого интервала весов, построить средневзвешенное значение , затем
  • , чтобы расширить ту же логикук другой переменной: дискретизируйте z, и для каждого интервала выведите средневзвешенное значение, взвешенное по весам

Есть ли простой способ добиться этого? Я нашел способ, но, похоже,Немного громоздко:

  • Я делаю дискретный кадр данных с помощью pandas.cut ()
  • , делаю сгруппированную и вычисляю средневзвешенное значение
  • строит среднее значение каждого бина противсредневзвешенное значение
  • Я также пытался сгладить кривую с помощью сплайна, но это мало что дает

В основном я ищу лучший способ получения более сглаженногокривая.

Мой вывод выглядит следующим образом: enter image description here

и мой код с некоторыми случайными данными:

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')

Ответы [ 4 ]

1 голос
/ 11 апреля 2019

Я думаю, что вы используете несколько значений для интерполяции, изменив xnew = np.linspace(summary['avg w'].min(), summary['avg w'].max(),100) на xnew = np.linspace(summary['avg w'].min(), summary['avg w'].max(),500) Я получаю следующее:

enter image description here

И изменив степень сплайна на k=2, я получаю следующее:

enter image description here

Я думаю, что хорошей отправной точкой для интерполяции может быть n/2 иk=2, поскольку он представляет меньше деформации данныхНадеюсь, поможет.

1 голос
/ 05 апреля 2019

Первая часть вашего вопроса довольно проста.

Я не уверен, что вы имеете в виду под второй частью.Вы хотите (упрощенное) воспроизведение вашего кода или новый подход, который лучше соответствует вашим потребностям?

В любом случае мне пришлось посмотреть на ваш код, чтобы понять, что вы подразумеваете под взвешиванием значений.Я думаю, что люди обычно ожидают что-то отличное от термина (просто как предупреждение).

Вот упрощенная версия вашего подхода:

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
0 голосов
/ 11 апреля 2019

Я думаю, что это решение того, что вы ищете.Это использует скользящее окно, как другие предложили.Для правильной работы потребовалось немного больше работы.

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()

enter image description here

0 голосов
/ 05 апреля 2019

Если я правильно понимаю, вы пытаетесь воссоздать скользящее среднее.

Это уже возможность данных Pandas с использованием функции rolling:

dataframe.rolling(n).mean()

, где n - это число смежных точек, используемых в «окне» или «корзине» для среднего значения, так что вы можете настроить его для различных степеней гладкости.

Примеры вы можете найти здесь:

...