In [1]:
# 导入必要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import font_manager
import warnings
warnings.filterwarnings('ignore')
# 设置中文字体
font_path = '/System/Library/Fonts/STHeiti Medium.ttc'
plt.rcParams['font.family'] = font_manager.FontProperties(fname=font_path).get_name()
plt.rcParams['axes.unicode_minus'] = False
# 设置颜色
COLORS = {
'primary': '#00d4ff',
'secondary': '#00ff88',
'accent': '#ffd700',
'purple': '#a855f7'
}
print("✅ 库导入成功!")
✅ 库导入成功!
1. 数据加载与预处理¶
In [2]:
# 加载数据
df = pd.read_csv('assets/data/time_series.csv', encoding='utf-8-sig')
print("="*60)
print("数据概览")
print("="*60)
print(f"\n数据形状: {df.shape[0]} 行 × {df.shape[1]} 列")
print(f"\n列名: {list(df.columns)}")
df.head(10)
============================================================ 数据概览 ============================================================ 数据形状: 57 行 × 2 列 列名: ['日期', '客户数']
Out[2]:
| 日期 | 客户数 | |
|---|---|---|
| 0 | Jan-09 | 2,644,539 |
| 1 | Feb-09 | 2,359,800 |
| 2 | Mar-09 | 2,925,918 |
| 3 | Apr-09 | 3,024,973 |
| 4 | May-09 | 3,177,100 |
| 5 | Jun-09 | 3,419,595 |
| 6 | Jul-09 | 3,649,702 |
| 7 | Aug-09 | 3,650,668 |
| 8 | Sep-09 | 3,191,526 |
| 9 | Oct-09 | 3,249,428 |
In [3]:
# 数据预处理
# 1. 清理客户数列(移除逗号)
df['客户数'] = df['客户数'].astype(str).str.replace(',', '').astype(float)
# 2. 转换日期列为datetime
df['日期'] = pd.to_datetime(df['日期'], format='%b-%y')
# 3. 设置日期为索引
df.set_index('日期', inplace=True)
df = df.sort_index()
print("\n" + "="*60)
print("数据预处理完成")
print("="*60)
print(f"\n日期范围: {df.index.min()} 至 {df.index.max()}")
print(f"\n数据类型:")
print(df.dtypes)
df.head()
============================================================ 数据预处理完成 ============================================================ 日期范围: 2009-01-01 00:00:00 至 2013-09-01 00:00:00 数据类型: 客户数 float64 dtype: object
Out[3]:
| 客户数 | |
|---|---|
| 日期 | |
| 2009-01-01 | 2644539.0 |
| 2009-02-01 | 2359800.0 |
| 2009-03-01 | 2925918.0 |
| 2009-04-01 | 3024973.0 |
| 2009-05-01 | 3177100.0 |
2. 描述性统计¶
In [4]:
# 描述性统计
print("="*60)
print("描述性统计")
print("="*60)
print(df.describe().round(2))
# 计算年度汇总
df['年份'] = df.index.year
df['月份'] = df.index.month
yearly = df.groupby('年份')['客户数'].agg(['sum', 'mean', 'min', 'max']).round(0)
yearly.columns = ['年度总和', '月均', '最低月', '最高月']
print("\n" + "="*60)
print("年度汇总")
print("="*60)
yearly
============================================================
描述性统计
============================================================
客户数
count 57.00
mean 3432872.28
std 459150.88
min 2359800.00
25% 3139059.00
50% 3443039.00
75% 3766323.00
max 4356216.00
============================================================
年度汇总
============================================================
Out[4]:
| 年度总和 | 月均 | 最低月 | 最高月 | |
|---|---|---|---|---|
| 年份 | ||||
| 2009 | 37338942.0 | 3111578.0 | 2359800.0 | 3650668.0 |
| 2010 | 39253999.0 | 3271167.0 | 2515361.0 | 3771842.0 |
| 2011 | 40927786.0 | 3410649.0 | 2610667.0 | 3935589.0 |
| 2012 | 44399885.0 | 3699990.0 | 2998119.0 | 4356216.0 |
| 2013 | 33753108.0 | 3750345.0 | 2966477.0 | 4347059.0 |
3. 时间序列可视化¶
In [5]:
# 创建综合可视化图表
fig = plt.figure(figsize=(18, 14))
# 1. 原始时间序列
ax1 = fig.add_subplot(3, 2, 1)
ax1.plot(df.index, df['客户数'], color=COLORS['primary'], linewidth=2, marker='o', markersize=4)
ax1.set_title('客户数量时间序列', fontsize=14, fontweight='bold')
ax1.set_xlabel('日期')
ax1.set_ylabel('客户数')
ax1.grid(True, alpha=0.3)
ax1.tick_params(axis='x', rotation=45)
# 2. 年度趋势对比
ax2 = fig.add_subplot(3, 2, 2)
for year in df['年份'].unique():
year_data = df[df['年份'] == year]
ax2.plot(year_data['月份'], year_data['客户数']/1e6, marker='o', markersize=4, label=str(year))
ax2.set_title('各年度客户数量月度对比', fontsize=14, fontweight='bold')
ax2.set_xlabel('月份')
ax2.set_ylabel('客户数(百万)')
ax2.set_xticks(range(1, 13))
ax2.set_xticklabels(['1','2','3','4','5','6','7','8','9','10','11','12'])
ax2.legend(fontsize=9)
ax2.grid(True, alpha=0.3)
# 3. 年度总量柱状图
ax3 = fig.add_subplot(3, 2, 3)
yearly_sum = df.groupby('年份')['客户数'].sum() / 1e6
bars = ax3.bar(yearly_sum.index.astype(str), yearly_sum.values, color=COLORS['secondary'], edgecolor='white')
ax3.set_title('各年度客户总数', fontsize=14, fontweight='bold')
ax3.set_xlabel('年份')
ax3.set_ylabel('客户数(百万)')
for bar, val in zip(bars, yearly_sum.values):
ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
f'{val:.1f}M', ha='center', fontsize=10, fontweight='bold')
ax3.grid(True, alpha=0.3, axis='y')
# 4. 月度平均模式
ax4 = fig.add_subplot(3, 2, 4)
monthly_avg = df.groupby('月份')['客户数'].mean() / 1e6
ax4.bar(monthly_avg.index, monthly_avg.values, color=COLORS['accent'], edgecolor='white')
ax4.set_title('月度平均客户数(季节性模式)', fontsize=14, fontweight='bold')
ax4.set_xlabel('月份')
ax4.set_ylabel('平均客户数(百万)')
ax4.set_xticks(range(1, 13))
ax4.grid(True, alpha=0.3, axis='y')
# 5. 12个月移动平均
ax5 = fig.add_subplot(3, 2, 5)
df['MA12'] = df['客户数'].rolling(window=12).mean()
ax5.plot(df.index, df['客户数'], color='gray', alpha=0.5, linewidth=1, label='原始数据')
ax5.plot(df.index, df['MA12'], color=COLORS['primary'], linewidth=2, label='12个月移动平均')
ax5.set_title('趋势分析(12个月移动平均)', fontsize=14, fontweight='bold')
ax5.set_xlabel('日期')
ax5.set_ylabel('客户数')
ax5.legend()
ax5.grid(True, alpha=0.3)
ax5.tick_params(axis='x', rotation=45)
# 6. 年度增长率
ax6 = fig.add_subplot(3, 2, 6)
yearly_growth = yearly_sum.pct_change() * 100
colors = [COLORS['secondary'] if v > 0 else '#ff4757' for v in yearly_growth.dropna().values]
ax6.bar(yearly_growth.dropna().index.astype(str), yearly_growth.dropna().values, color=colors, edgecolor='white')
ax6.axhline(y=0, color='black', linestyle='-', linewidth=0.5)
ax6.set_title('年度增长率(%)', fontsize=14, fontweight='bold')
ax6.set_xlabel('年份')
ax6.set_ylabel('增长率(%)')
ax6.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig('assets/images/time_series_overview.png', dpi=150, bbox_inches='tight')
plt.show()
print("\n✅ 综合分析图表已保存")
✅ 综合分析图表已保存
4. 季节性分析¶
In [6]:
# 季节性分解
from statsmodels.tsa.seasonal import seasonal_decompose
# 进行季节性分解
result = seasonal_decompose(df['客户数'], model='multiplicative', period=12)
# 可视化分解结果
fig, axes = plt.subplots(4, 1, figsize=(14, 12))
result.observed.plot(ax=axes[0], title='原始数据', color=COLORS['primary'], linewidth=2)
axes[0].set_ylabel('客户数')
axes[0].grid(True, alpha=0.3)
result.trend.plot(ax=axes[1], title='趋势', color=COLORS['secondary'], linewidth=2)
axes[1].set_ylabel('趋势')
axes[1].grid(True, alpha=0.3)
result.seasonal.plot(ax=axes[2], title='季节性', color=COLORS['accent'], linewidth=2)
axes[2].set_ylabel('季节性因子')
axes[2].grid(True, alpha=0.3)
result.resid.plot(ax=axes[3], title='残差', color=COLORS['purple'], linewidth=2)
axes[3].set_ylabel('残差')
axes[3].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('assets/images/seasonal_decomposition.png', dpi=150, bbox_inches='tight')
plt.show()
print("\n✅ 季节性分解图表已保存")
✅ 季节性分解图表已保存
5. 分析结论¶
In [7]:
# 生成分析报告
print("="*70)
print("时间序列分析报告")
print("="*70)
# 整体趋势
first_year = df[df['年份'] == 2009]['客户数'].mean()
last_year = df[df['年份'] == 2014]['客户数'].mean()
growth_rate = (last_year / first_year - 1) * 100
print(f"\n📊 【整体趋势】")
print(f" 2009年月均客户数: {first_year:,.0f}")
print(f" 2014年月均客户数: {last_year:,.0f}")
print(f" 整体增长率: {growth_rate:.1f}%")
# 季节性模式
monthly_avg = df.groupby('月份')['客户数'].mean()
peak_month = monthly_avg.idxmax()
low_month = monthly_avg.idxmin()
print(f"\n📅 【季节性模式】")
print(f" 高峰月份: {peak_month}月 (平均 {monthly_avg[peak_month]:,.0f})")
print(f" 低谷月份: {low_month}月 (平均 {monthly_avg[low_month]:,.0f})")
# 增长最快的年份
yearly_growth = df.groupby('年份')['客户数'].sum().pct_change() * 100
best_year = yearly_growth.idxmax()
print(f"\n🚀 【增长情况】")
print(f" 增长最快年份: {best_year}年 (+{yearly_growth[best_year]:.1f}%)")
print(f"\n💡 【关键发现】")
print(f" 1. 客户数量整体呈上升趋势,6年增长约{growth_rate:.0f}%")
print(f" 2. 存在明显的季节性波动,{peak_month}月为高峰期,{low_month}月为低谷期")
print(f" 3. 趋势线显示持续稳定的增长态势")
print(f" 4. 每年年初(1-2月)通常是客户数的低点")
====================================================================== 时间序列分析报告 ====================================================================== 📊 【整体趋势】 2009年月均客户数: 3,111,578 2014年月均客户数: nan 整体增长率: nan% 📅 【季节性模式】 高峰月份: 8月 (平均 4,008,734) 低谷月份: 2月 (平均 2,690,085) 🚀 【增长情况】 增长最快年份: 2012年 (+8.5%) 💡 【关键发现】 1. 客户数量整体呈上升趋势,6年增长约nan% 2. 存在明显的季节性波动,8月为高峰期,2月为低谷期 3. 趋势线显示持续稳定的增长态势 4. 每年年初(1-2月)通常是客户数的低点