第 2 篇:初探时间序列 - 可视化与基本概念
第 2 篇:初探时间序列 - 可视化与基本概念
(图片来源: Luke Chesser on Unsplash)
在上一篇《你好,时间序列!》中,我们了解了什么是时间序列数据以及学习它的重要性。现在,是时候卷起袖子,真正开始接触和探索这些按时间流动的数据了!
本篇我们将聚焦于:
- 如何使用 Python 加载和处理时间序列数据? (基础操作)
- 如何通过可视化让时间序列“说话”? (核心技能)
- 时间序列通常由哪些基本“成分”构成? (核心概念)
准备好你的 Python 环境 (Pandas, Matplotlib/Seaborn),让我们开始吧!
数据加载与基础操作:Pandas 的魔法
处理时间序列数据,Pandas 库是我们的得力助手。它提供了强大的 Timestamp
和 DatetimeIndex
对象,专门用于处理时间信息。
假设我们有一个 CSV 文件 my_time_series.csv
,内容如下:
Date,Value
2023-01-01,10
2023-01-02,12
2023-01-03,15
2023-01-04,13
2023-01-05,16
...
我们可以这样加载它,并确保日期列被正确识别为时间索引:
import pandas as pd# 加载数据
# parse_dates=['Date'] 告诉 Pandas 'Date' 列包含日期信息
# index_col='Date' 将 'Date' 列设置为 DataFrame 的索引
try:# 假设文件在当前目录下df = pd.read_csv('my_time_series.csv', parse_dates=['Date'], index_col='Date')print("数据加载成功!")
except FileNotFoundError:print("错误:找不到 my_time_series.csv 文件。请确保文件路径正确。")# 为了演示,我们创建一个简单的示例 DataFrameprint("将创建一个示例 DataFrame 继续演示...")dates = pd.to_datetime(['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04', '2023-01-05'])values = [10, 12, 15, 13, 16]df = pd.DataFrame({'Value': values}, index=dates)# 查看数据前几行和索引类型
print("\n数据概览:")
print(df.head())
print("\n索引类型:")
print(type(df.index))
关键点:
parse_dates=['列名']
:让 Pandas 尝试将指定列解析为日期时间格式。index_col='列名'
:将解析后的日期时间列设置为 DataFrame 的索引。这会创建一个DatetimeIndex
。
为什么 DatetimeIndex
很重要?
拥有 DatetimeIndex
后,我们可以方便地进行基于时间的各种操作,例如:
- 按时间范围选择数据 (Slicing):
# 选择 2023 年 1 月的数据
print("\n选择 2023 年 1 月数据:")
try:print(df['2023-01'])
except KeyError:print("示例数据中没有完整的 1 月数据,此为切片示例。")print(df['2023-01-01':'2023-01-03']) # 选择特定日期范围# 选择特定日期
print("\n选择 2023 年 1 月 2 日的数据:")
print(df.loc['2023-01-02'])
- 重采样 (Resampling): 改变时间序列的频率(例如,从日数据变为月度平均数据)。我们后面会更详细地接触。
# 简单示例:计算月度平均值 (如果数据足够多)
# 'M' 表示 月末 (Month End)
# .mean() 计算平均值
try:monthly_mean = df.resample('M').mean()print("\n月度平均值 (如果数据覆盖多月):")print(monthly_mean)
except Exception as e:print(f"\n计算月度平均值出错 (可能是数据量太少): {e}")
可视化:让数据“说话”
时间序列最直观的探索方式就是画图!折线图 (Line Plot) 是我们的首选,它能清晰地展示数据点如何随着时间变化。
我们将使用 Matplotlib 或 Seaborn (通常基于 Matplotlib 提供更美观的图形) 来绘图。
import matplotlib.pyplot as plt
import seaborn as sns# 设置绘图风格 (可选)
sns.set_style("whitegrid")# 创建图形
plt.figure(figsize=(12, 6)) # 设置图形大小# 绘制折线图
plt.plot(df.index, df['Value'], marker='o', linestyle='-')
# 或者使用 Seaborn
# sns.lineplot(data=df, x=df.index, y='Value', marker='o')# 添加标题和标签
plt.title('My Time Series Data Over Time')
plt.xlabel('Date')
plt.ylabel('Value')# 优化日期显示 (可选)
plt.xticks(rotation=45)
plt.tight_layout() # 调整布局防止标签重叠# 显示图形
plt.show()
(请运行上面的代码,生成的图就是这里应该展示的内容。一个简单的折线图,X 轴是时间,Y 轴是数值。)
如何解读图形?
盯着这张图,试着回答以下问题:
- 整体趋势 (Trend): 数据是长期来看是上升的?下降的?还是保持水平?(看大方向)
- 周期性模式 (Seasonality/Cycles): 是否存在固定时间间隔内(比如每年、每周、每天)反复出现的模式?(看重复的波峰波谷)
- 异常点 (Outliers): 是否有某个或某几个点显得特别“突兀”,远离整体模式?
- 波动性 (Volatility): 数据变化的幅度是大致恒定的,还是时大时小?
通过可视化,我们就能对时间序列的特性有一个初步的、直观的认识。
时间序列的基本成分 (概念引入)
刚才我们在解读图形时提到了“趋势”、“周期性模式”等,这些其实就是时间序列数据通常包含的基本成分。理解这些成分对于后续分析和建模至关重要。
一个时间序列 Y(t)
通常被认为可以分解为以下几个(并非所有序列都包含全部)主要部分:
-
趋势 (Trend, T(t)):
- 描述数据在长期内的主要走向或基本趋势。
- 可以是上升的(如经济增长)、下降的(如某种技术被淘汰)或水平的。
- 想象成一条贯穿数据中心的平滑曲线。
-
季节性 (Seasonality, S(t)):
- 指数据在一个固定且已知的时间周期内(如一年、一季度、一周、一天)表现出的、可预测的模式性波动。
- 例如:冰淇淋销量夏季高、冬季低(周期为年);工作日通勤交通量早晚高峰(周期为天)。
- 关键: 周期长度是固定的。
-
周期性 (Cyclicality, C(t)):
- 指数据在非固定长度的时期内出现的长期波动,通常与经济周期等宏观因素相关。
- 周期长度通常比季节性长(比如几年甚至几十年),且没有固定的时间间隔。
- 注意: 这个成分对于初学者来说比较难识别和处理,我们初期会更多关注趋势和季节性。
-
随机性 / 噪声 / 残差 (Noise / Irregular / Residual, R(t)):
- 剔除掉趋势、季节性和周期性成分后,数据中剩余的、不规则的、通常是随机的波动。
- 可以看作是模型无法解释的部分。
(这张图展示了一个序列 (Data) 被分解为趋势 (Trend)、季节性 (Seasonal) 和残差 (Remainder/Residual) 的经典示例)
我们目前只需要理解这些概念即可,下一篇我们将学习如何使用工具实际地将一个时间序列分解开来。
小结与代码示例回顾
今天我们:
- 学会了使用
pandas
加载时间序列数据,并理解了parse_dates
和index_col
的重要性,创建了DatetimeIndex
。 - 掌握了使用
matplotlib
或seaborn
绘制时间序列折线图这一核心可视化技能。 - 学习了如何从图中初步观察趋势和季节性等模式。
- 理解了时间序列通常包含的趋势 (T)、季节性 (S)、周期性 © 和随机性 ® 四个基本成分(概念层面)。
完整代码示例:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns# 1. 生成示例数据 (包含趋势和季节性)
np.random.seed(42) # for reproducibility
dates = pd.date_range(start='2022-01-01', periods=365 * 2, freq='D') # 2 年的日数据
trend = np.linspace(0, 10, len(dates)) # 线性上升趋势
seasonality = 5 * np.sin(2 * np.pi * dates.dayofyear / 365.25) # 年度季节性
noise = np.random.normal(0, 1, len(dates)) # 随机噪声
values = trend + seasonality + noise + 20 # 基线值为 20# 创建 DataFrame
df_generated = pd.DataFrame({'Value': values}, index=dates)print("生成的示例数据概览:")
print(df_generated.head())
print("\n索引类型:")
print(type(df_generated.index))# 2. 可视化
sns.set_style("whitegrid")
plt.figure(figsize=(14, 7))# 绘制折线图
sns.lineplot(data=df_generated, x=df_generated.index, y='Value')# 添加标题和标签
plt.title('Generated Time Series with Trend and Seasonality')
plt.xlabel('Date')
plt.ylabel('Value')# 优化日期显示
plt.xticks(rotation=30)
plt.tight_layout()# 显示图形
plt.show()# 3. 初步解读
print("\n观察上图:")
print("- 整体看,数据是不是有一个向上的大方向?(趋势)")
print("- 数据是不是每年都在重复相似的上下波动模式?(季节性)")
通过观察生成的图,你应该能比较清晰地看到我们定义的“趋势”(整体向上)和“季节性”(每年重复的波浪形)。
下一篇预告
现在我们对时间序列有了直观感受,也知道了它可能包含哪些成分。那么,如何更精确地把这些成分分离出来呢?下一篇,我们将深入探讨时间序列分解 (Time Series Decomposition) 技术,学习如何量化地识别和提取趋势与季节性。
敬请期待!