前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一个时间序列可视化神器:Plotnine

一个时间序列可视化神器:Plotnine

作者头像
数据STUDIO
发布2024-07-24 09:59:43
2140
发布2024-07-24 09:59:43
举报
文章被收录于专栏:数据STUDIO
一图胜千言,可视化是一种从数据中快速有效获取有效信息的方法。本文云朵君和大家一起学习使用图形探索时间序列数据。

我们将利用6种不同的图表来揭示时间序列数据的各个方面。重点介绍Python中的plotnine库,这是一种基于图形语法(Grammar of Graphics)的绘图工具。

探索性数据分析(Exploratory Data Analysis,EDA)旨在揭示数据集内在的结构和模式。这个过程几乎总是涉及使用可视化技术来呈现数据。

对于时间序列数据,使用图形进行分析可以帮助我们快速发现:

  • 基本模式,如趋势或周期性规律
  • 异常情况,包括缺失值或异常值
  • 数据分布的变化

本文需要用到的python库如下:

代码语言:javascript
复制
from datasetsforecast.m3 import M3
import plotnine as p9
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import acf
from sklearn.model_selection import train_test_split
from mizani.breaks import date_breaks
from mizani.formatters import date_format
from numerize import numerize
from statsmodels.tsa.seasonal import STL

探索时间序列

我们首先加载一个时间序列。这里我们将使用 M3 数据集中提供的月度时间序列。直接从datasetsforecast库中获取它:

代码语言:javascript
复制
from datasetsforecast.m3 import M3
dataset, *_ = M3.load('./data', 'Monthly')
series = dataset.query(f'unique_id=="M400"')

注意,使用该方法是需要满足pandas版本要求,否则会报错。直接安装对应新版即可。

现在云朵君将和大家一起学习如何使用 plotnine 创建图形。这个库是 Python 的一种 ggplot2。

如果你还没有安装,直接安装即可。

从设置主题开始:

代码语言:javascript
复制
import plotnine as p9

MY_THEME = p9.theme_538(base_family='Palatino', base_size=12) + \
           p9.theme(plot_margin=.025,
                    axis_text_y=p9.element_text(size=10),
                    panel_background=p9.element_rect(fill='white'),
                    plot_background=p9.element_rect(fill='white'),
                    strip_background=p9.element_rect(fill='white'),
                    legend_background=p9.element_rect(fill='white'),
                    axis_text_x=p9.element_text(size=10))

我们将使用基于theme_538的主题并进行一些额外的修改。

时间序列图

绘制时间序列图是时间序列分析的第一步。时间序列图是一种线形图,用于展示数据值随时间的变化趋势。

代码语言:javascript
复制
import plotnine as p9

time_plot = p9.ggplot(data=series) + \
            p9.aes(x='ds', y='y') + \
            MY_THEME + \
            p9.geom_line(color='#58a63e', size=1) + \
            p9.labs(x='Datetime', y='value')

time_plot + p9.theme(figure_size=(8,3))

时间序列图

通过观察时间序列图,我们可以快速发现数据中存在的一些基本模式,如趋势、周期性等。同时,如果数据的均值或方差出现明显变化,在图上也能一目了然。

示例数据表现出一种随机趋势,数据值先是上升到一个拐点,之后开始下降。同时周期性的波动表明数据中可能存在季节性成分。

还可以使用分解数据构建时间图。首先,我们使用 STL 分解时间序列:

代码语言:javascript
复制
import pandas as pd
from statsmodels.tsa.seasonal import STL

ts_decomp = STL(series['y'], period=12).fit()

components = {
    'Trend': ts_decomp.trend,
    'Seasonal': ts_decomp.seasonal,
    'Residuals': ts_decomp.resid,
}

components_df = pd.DataFrame(components).reset_index()
melted_data = components_df.melt('index')

然后,我们使用facet_grid为每个部分创建时间图,如下所示:

代码语言:javascript
复制
from numerize import numerize 

# 一个在图形中总结大值的好技巧
labs = lambda lst: [numerize.numerize(x) for x in lst] 

decomposed_timeplot = \ 
    p9.ggplot(melted_data) + \ 
    p9.aes(x= 'index' , y= 'value' ) + \ 
    p9.facet_grid( 'variable ~.' , scales= 'free' ) + \ 
    MY_THEME + \ 
    p9.geom_line(color= '#58a63e' , size= 1 ) + \ 
    p9.labs(x= 'Datetime index' ) + \ 
    p9.scale_y_continuous(labels=labs)

时间序列分解图

此变体使检查每个组件变得更容易。在这种情况下,趋势和季节性影响变得清晰。

我们使用numerize使大数字更清晰易读。你也可以将此样式添加到任何其他绘图中。

滞后图

滞后散点图是将时间序列的当前值与前一个值(滞后值)画在平面坐标系上。

代码语言:javascript
复制
X = [series['y'].shift(i) for i in list(range(2, 0, -1))]
X = pd.concat(X, axis=1).dropna()
X.columns = ['t-1', 't']

lag_plot = p9.ggplot(X) + \
           p9.aes(x='t-1', y='t') + \
           MY_THEME + \
           p9.geom_point(color='#58a63e') + \
           p9.labs(x='Series at time t-1',
                   y='Series at time t') + \
           p9.scale_y_continuous(labels=labs) + \
           p9.scale_x_continuous(labels=labs)

滞后散点图可以帮助我们了解时间序列的内在结构。如果数据点沿对角线密集分布,说明该时间序列存在自相关性,点分布越集中则自相关性越强。如果数据点分散分布,则表明该序列是随机的,前值对后值没有预测作用。

时间序列滞后图

滞后散点图还可用于发现异常值,异常值点将远离数据点的密集区域。

示例数据的点倾向于沿对角线分布,但当值越大时,离散程度也越大。这种特征表明该序列可能存在自回归结构。

自相关图

自相关性是衡量时间序列在过去值(滞后)中观察到的与自身相关的程度的指标。绘制自相关系数图有助于了解时间序列的内部结构。

你可以使用statsmodels来计算自相关:

代码语言:javascript
复制
import numpy as np
from statsmodels.tsa.stattools import acf

acf_x = acf(
    series['y'],
    nlags=24,
    alpha=0.05,
    bartlett_confint=True
)

acf_vals, acf_conf_int = acf_x[:2]

acf_df = pd.DataFrame({
    'ACF': acf_vals,
    'ACF_low': acf_conf_int[:, 0],
    'ACF_high': acf_conf_int[:, 1],
})

acf_df['Lag'] = ['t'] + [f't-{i}' for i in range(1, 25)]
acf_df['Lag'] = pd.Categorical(acf_df['Lag'], categories=acf_df['Lag'])

然后,我们使用plotnine构建一个棒棒糖图:

代码语言:javascript
复制
significance_thr = 2 / np.sqrt(len(series['y']))

acf_plot = p9.ggplot(acf_df, p9.aes(x='Lag', y='ACF')) + \
           p9.geom_hline(yintercept=significance_thr,
                         linetype='dashed',
                         color='#58a63e',
                         size=.8) + \
           p9.geom_hline(yintercept=-significance_thr,
                         linetype='dashed',
                         color='#58a63e',
                         size=.8) + \
           p9.geom_hline(yintercept=0, linetype='solid', color='black', size=1) + \
           p9.geom_segment(p9.aes(x='Lag',
                                  xend='Lag',
                                  y=0, yend='ACF'),
                           size=1.5,
                           color='#58a63e'
                           ) + \
           p9.geom_point(size=4, color='darkgreen', ) + \
           MY_THEME

自相关图

如果自相关系数随滞后阶数的增加而缓慢衰减,表明数据可能存在趋势成分;如果自相关系数呈现出明显的波动模式,峰值出现在特定的滞后阶数上,则说明数据中可能存在明显的周期性。如果自相关系数始终接近于0,则表明该序列可能是白噪声序列,即随机序列。

季节子序列图

有些图形工具专门用于探究时间序列的季节性成分,如季节子序列图。

季节子序列图的绘制方法是:根据数据的季节周期,将整个序列分组,每组包含一个完整的季节周期。然后将每个周期的数据值绘制在同一张图上,从而可视化观察序列在不同季节的表现模式。如下所示:

代码语言:javascript
复制
grouped_df = series.groupby('Month')['y']
group_avg = grouped_df.mean()
group_avg = group_avg.reset_index()
series['Month'] = pd.Categorical(series['Month'], 
                                 categories=series['Month'].unique())
group_avg['Month'] = pd.Categorical(group_avg['Month'], 
                                    categories=series['Month'].unique())

seas_subseries_plot = \
    p9.ggplot(series) + \
    p9.aes(x='ds',
           y='y') + \
    MY_THEME + \
    p9.theme(axis_text_x=p9.element_text(size=8, angle=90),
             legend_title=p9.element_blank(),
             strip_background_x=p9.element_text(color='#58a63e'),
             strip_text_x=p9.element_text(size=11)) + \
    p9.geom_line() + \
    p9.facet_grid('. ~Month') + \
    p9.geom_hline(data=group_avg,
                  mapping=p9.aes(yintercept='y'),
                  colour='darkgreen',
                  size=1) + \
    p9.scale_y_continuous(labels=labs) + \
    p9.scale_x_datetime(breaks=date_breaks('2 years'), 
                        labels=date_format('%Y')) + \
    p9.labs(y='value')

seas_subseries_plot + p9.theme(figure_size=(10,4))

季节子序列图

此图对于揭示季节内和跨季节的模式很有用。

在示例时间序列中,我们可以看到平均值在 3 月份最低。在某些月份(例如 5 月),该序列显示出强劲的正趋势。

分组密度图

现实中的时间序列数据往往会受到各种因素的干扰和影响,导致数据模式产生变化。

我们可以利用分组密度图等可视化工具,来观察这些干扰事件对数据的影响。将数据按照干扰事件进行分组,每组对应一个不同的状态,然后分别绘制每组数据的密度曲线,从而比较不同状态下数据的分布差异。如下所示:

代码语言:javascript
复制
# 某些事件发生在索引 23
change_index = 23

before, after = train_test_split(series, train_size=change_index, shuffle=False)

n_bf, n_af = before.shape[0], after.shape[0]

p1_df = pd.DataFrame({'Series': before['y'], 'Id': range(n_bf)})
p1_df['Part'] = 'Before change'
p2_df = pd.DataFrame({'Series': after['y'], 'Id': range(n_af)})
p2_df['Part'] = 'After change'

df = pd.concat([p1_df, p2_df])
df['Part'] = pd.Categorical(df['Part'], categories=['Before change', 'After change'])

group_avg = df.groupby('Part').mean()['Series']

density_plot = \
    p9.ggplot(df) + \
    p9.aes(x='Series', fill='Part') + \
    MY_THEME + \
    p9.theme(legend_position='top') + \
    p9.geom_vline(xintercept=group_avg,
                  linetype='dashed',
                  color='steelblue',
                  size=1.1,
                  alpha=0.7) + \
    p9.geom_density(alpha=.2)

在这个特定的例子中,索引 23 处发生了一些事件。这里随意选择了这个特定的时间步骤。但是,你可以使用变化点检测方法来检测重要的时间步骤。

分组密度图

我们绘制了临界点前后的分布图。分布有明显的变化。

写在最后

探索性数据分析是时间序列分析和预测的基础环节。本文介绍了6种有助于探索时间序列内在模式和结构的可视化图形技术:

  • 时间序列图: 直观展示数据随时间的变化趋势,发现潜在的趋势和周期性。
  • 分解时间序列图: 将原始序列分解为趋势、周期、残差等不同成分,有助于进一步分析。
  • 滞后散点图: 将当前值与前若干滞后值绘制在散点图上,检验序列的自相关性。
  • 自相关系数图: 绘制不同滞后阶数下的自相关系数,判断序列中趋势和周期性的存在。
  • 季节子序列图: 根据季节周期对序列分组,展现不同季节下的数据模式。
  • 分组密度图: 根据干扰事件对数据进行分组,比较不同状态下数据分布的差异。本文使用Python的plotnine库进行可视化,它提供了丰富的统计绘图功能,是基于R的ggplot2设计的。实例代码可在plotnine官网查阅:https://plotnine.org/tutorials/。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据STUDIO 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 探索时间序列
    • 时间序列图
      • 滞后图
        • 自相关图
          • 季节子序列图
            • 分组密度图
            • 写在最后
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档