Усреднение нескольких временных рядов вместе с доверительным интервалом (с тестовым кодом) - PullRequest
1 голос
/ 28 марта 2019

Звучит очень сложно, но простой сюжет облегчит понимание: enter image description here У меня есть три кривые кумулятивной суммы некоторых значений во времени, которые представляют собой синие линии.

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

Я попробовал одно простое решение - объединить все данные в одну кривую, усреднить их с помощьюфункция "прокатки" в пандах, получение стандартного отклонения для нее.Я изобразил их в виде пурпурной кривой с доверительным интервалом вокруг нее.

Проблема с моими реальными данными, и, как показано на графике выше, кривая вообще не гладкая, также есть резкие скачки вдоверительный интервал, который также не является хорошим представлением 3 отдельных кривых, поскольку в них нет скачков.

Существует ли лучший способ представить 3 разные кривые в одной гладкой кривой с хорошим доверительным интервалом?

Я предоставляю тестовый код, протестированный на python 3.5.1 с numpy и pandas (не меняйте начальное число для получения одинаковых кривых).

Существуют некоторые ограничения -увеличение количества баллов для функции «прокатки» не является для меня решением, потому что некоторые мои данные слишком коротки для этого.

Тестовый код:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
np.random.seed(seed=42)


## data generation - cumulative analysis over time
df1_time = pd.DataFrame(np.random.uniform(0,1000,size=50), columns=['time'])
df1_values = pd.DataFrame(np.random.randint(0,10000,size=100), columns=['vals'])
df1_combined_sorted =  pd.concat([df1_time, df1_values], axis = 1).sort_values(by=['time'])
df1_combined_sorted_cumulative = np.cumsum(df1_combined_sorted['vals'])

df2_time = pd.DataFrame(np.random.uniform(0,1000,size=50), columns=['time'])
df2_values = pd.DataFrame(np.random.randint(1000,13000,size=100), columns=['vals'])
df2_combined_sorted =  pd.concat([df2_time, df2_values], axis = 1).sort_values(by=['time'])
df2_combined_sorted_cumulative = np.cumsum(df2_combined_sorted['vals'])

df3_time = pd.DataFrame(np.random.uniform(0,1000,size=50), columns=['time'])
df3_values = pd.DataFrame(np.random.randint(0,4000,size=100), columns=['vals'])
df3_combined_sorted =  pd.concat([df3_time, df3_values], axis = 1).sort_values(by=['time'])
df3_combined_sorted_cumulative = np.cumsum(df3_combined_sorted['vals'])


## combining the three curves
df_all_vals_cumulative = pd.concat([df1_combined_sorted_cumulative,.
    df2_combined_sorted_cumulative, df3_combined_sorted_cumulative]).reset_index(drop=True)
df_all_time =  pd.concat([df1_combined_sorted['time'],
    df2_combined_sorted['time'], df3_combined_sorted['time']]).reset_index(drop=True)
df_all = pd.concat([df_all_time, df_all_vals_cumulative], axis = 1)


## creating confidence intervals 
df_all_sorted = df_all.sort_values(by=['time'])
ma = df_all_sorted.rolling(10).mean()
mstd = df_all_sorted.rolling(10).std()


## plotting
plt.fill_between(df_all_sorted['time'], ma['vals'] - 2 * mstd['vals'],
        ma['vals'] + 2 * mstd['vals'],color='b', alpha=0.2)
plt.plot(df_all_sorted['time'],ma['vals'], c='purple')
plt.plot(df1_combined_sorted['time'], df1_combined_sorted_cumulative, c='blue')
plt.plot(df2_combined_sorted['time'], df2_combined_sorted_cumulative, c='blue')
plt.plot(df3_combined_sorted['time'], df3_combined_sorted_cumulative, c='blue')
matplotlib.use('Agg')
plt.show()

1 Ответ

2 голосов
/ 28 марта 2019

Прежде всего, ваш пример кода может быть переписан, чтобы лучше использовать pd.Например,

np.random.seed(seed=42)

## data generation - cumulative analysis over time
def get_data(max_val, max_time=1000):
    times = pd.DataFrame(np.random.uniform(0,max_time,size=50), columns=['time'])
    vals = pd.DataFrame(np.random.randint(0,max_val,size=100), columns=['vals'])
    df =  pd.concat([times, vals], axis = 1).sort_values(by=['time']).\
            reset_index().drop('index', axis=1)
    df['cumulative'] = df.vals.cumsum()
    return df

# generate the dataframes
df1,df2,df3 = (df for df in map(get_data, [10000, 13000, 4000]))
dfs = (df1, df2, df3)

# join 
df_all = pd.concat(dfs, ignore_index=True).sort_values(by=['time'])

# render function
def render(window=10):
    # compute rolling means and confident intervals
    mean_val = df_all.cumulative.rolling(window).mean()
    std_val = df_all.cumulative.rolling(window).std()
    min_val = mean_val - 2*std_val
    max_val = mean_val + 2*std_val

    plt.figure(figsize=(16,9))
    for df in dfs:
        plt.plot(df.time, df.cumulative, c='blue')

    plt.plot(df_all.time, mean_val, c='r')
    plt.fill_between(df_all.time, min_val, max_val, color='blue', alpha=.2)
    plt.show()

Причина, по которой ваши кривые не такие плавные, может быть, ваше скользящее окно недостаточно велико.Вы можете увеличить этот размер окна, чтобы получить более плавные графики.Например, render(20) дает: enter image description here

, в то время как render(30) дает: enter image description here

Хотя лучший способ может бытьналожение каждого из df['cumulative'] на все временное окно и вычисление среднего / доверительного интервала для этих рядов.Имея это в виду, мы можем изменить код следующим образом:

np.random.seed(seed=42)

## data generation - cumulative analysis over time
def get_data(max_val, max_time=1000):
    times = pd.DataFrame(np.random.uniform(0,max_time,size=50), columns=['time'])
    vals = pd.DataFrame(np.random.randint(0,max_val,size=100), columns=['vals'])
    # note that we set time as index of the returned data
    df =  pd.concat([times, vals], axis = 1).dropna().set_index('time').sort_index()
    df['cumulative'] = df.vals.cumsum()
    return df

df1,df2,df3 = (df for df in map(get_data, [10000, 13000, 4000]))
dfs = (df1, df2, df3)

# rename column for later plotting
for i,df in zip(range(3),dfs):
    df.rename(columns={'cumulative':f'cummulative_{i}'}, inplace=True)

# concatenate the dataframes with common time index
df_all = pd.concat(dfs,sort=False).sort_index()

# interpolate each cumulative column linearly
df_all.interpolate(inplace=True)

# plot graphs
mean_val = df_all.iloc[:,1:].mean(axis=1)
std_val = df_all.iloc[:,1:].std(axis=1)
min_val = mean_val - 2*std_val
max_val = mean_val + 2*std_val

fig, ax = plt.subplots(1,1,figsize=(16,9))
df_all.iloc[:,1:4].plot(ax=ax)

plt.plot(df_all.index, mean_val, c='purple')
plt.fill_between(df_all.index, min_val, max_val, color='blue', alpha=.2)
plt.show()

и получим: enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...