引言
时间序列数据是数据分析中最常见的类型之一,从股票价格到传感器读数,从网站流量到销售数据,几乎所有领域都会产生时间序列数据。Pandas的DataFrame提供了强大而灵活的时间序列处理能力,使得清洗、转换和分析时间数据变得高效而直观。本文将系统介绍DataFrame中时间序列操作的核心方法,帮助你掌握时间数据处理的全流程。
一、时间序列基础:创建与索引
1. 创建时间序列DataFrame
python
import pandas as pd
import numpy as np
# 方法1:直接指定索引为DatetimeIndex
dates = pd.date_range('2023-01-01', periods=6, freq='D')
df = pd.DataFrame({'value': [10, 21, 19, 27, 33, 30]}, index=dates)
# 方法2:从字符串列转换为时间索引
data = {'date': ['2023-01-01', '2023-01-02', '2023-01-03'],
'value': [10, 21, 19]}
df = pd.DataFrame(data)
df['date'] = pd.to_datetime(df['date']) # 转换为datetime类型
df.set_index('date', inplace=True) # 设置为索引
2. 常用时间频率
Pandas支持丰富的频率字符串:
'D':日历日'B':工作日'H':小时'T'或'min':分钟'S':秒'W':周'M':月末'Q':季末'Y'或'A':年末
二、时间序列重采样与频率转换
1. 降采样(Downsampling)
将高频数据转换为低频数据,通常需要聚合操作:
python
# 创建每日数据
daily_dates = pd.date_range('2023-01-01', periods=30, freq='D')
daily_data = pd.DataFrame({'value': np.random.randint(10, 50, 30)},
index=daily_dates)
# 转换为周数据(取每周平均值)
weekly_data = daily_data.resample('W').mean()
# 转换为月数据(取每月最大值)
monthly_data = daily_data.resample('M').max()
2. 升采样(Upsampling)
将低频数据转换为高频数据,通常需要填充或插值:
python
# 创建月度数据
monthly_dates = pd.date_range('2023-01-01', periods=6, freq='M')
monthly_data = pd.DataFrame({'value': [10, 20, 15, 25, 30, 18]},
index=monthly_dates)
# 转换为日数据(前向填充)
daily_upsampled = monthly_data.resample('D').ffill()
# 使用线性插值
daily_interpolated = monthly_data.resample('D').interpolate(method='linear')
三、时间偏移与滚动计算
1. 时间偏移操作
python
# 创建时间序列
ts = pd.Series(range(5),
index=pd.date_range('2023-01-01', periods=5, freq='D'))
# 时间偏移
print(ts.shift(2)) # 向前移动2天(未来值变为NaN)
print(ts.shift(-1)) # 向后移动1天(过去值变为NaN)
print(ts.tshift(1, freq='B')) # 按工作日偏移
2. 滚动计算(Rolling Window)
python
# 创建股票价格数据
dates = pd.date_range('2023-01-01', periods=10, freq='B')
prices = pd.DataFrame({'price': [100, 102, 101, 105, 107,
106, 108, 110, 109, 112]},
index=dates)
# 计算5日移动平均
prices['5_day_avg'] = prices['price'].rolling(window=5).mean()
# 计算带权重的移动平均
weights = np.array([0.1, 0.2, 0.2, 0.2, 0.3])
prices['weighted_avg'] = prices['price'].rolling(5).apply(
lambda x: np.sum(x * weights), raw=True
)
# 滚动标准差
prices['5_day_std'] = prices['price'].rolling(5).std()
四、时间序列的日期/时间组件提取
python
# 创建时间序列
ts = pd.Series(pd.date_range('2023-01-01', periods=5, freq='D'),
name='dates')
# 提取日期组件
date_info = pd.DataFrame({
'year': ts.dt.year,
'month': ts.dt.month,
'day': ts.dt.day,
'weekday': ts.dt.weekday, # 星期几(0=周一)
'quarter': ts.dt.quarter, # 季度
'day_of_year': ts.dt.dayofyear, # 一年中的第几天
'is_month_end': ts.dt.is_month_end, # 是否月末
'hour': ts.dt.hour, # 如果包含时间部分
'minute': ts.dt.minute
})
五、高级时间序列操作
1. 时间感知的分组聚合
python
# 创建带时间戳的销售数据
sales_data = pd.DataFrame({
'amount': np.random.randint(50, 200, 200),
'category': np.random.choice(['A', 'B', 'C'], 200)
}, index=pd.date_range('2023-01-01', periods=200, freq='H'))
# 按小时和类别分组统计
hourly_sales = sales_data.groupby([
sales_data.index.hour, # 按小时分组
'category'
]).agg({
'amount': ['sum', 'mean', 'count']
})
# 展平多级列索引
hourly_sales.columns = ['_'.join(col).strip() for col in hourly_sales.columns.values]
2. 时间差计算
python
# 创建事件时间序列
events = pd.Series([
pd.Timestamp('2023-01-01 09:00'),
pd.Timestamp('2023-01-01 09:30'),
pd.Timestamp('2023-01-01 10:15')
], name='event_time')
# 计算时间差
time_diffs = events.diff() # 相邻事件时间差
total_diff = events.iloc[-1] - events.iloc[0] # 总时间跨度
# 转换为分钟数
print(time_diffs.dt.total_seconds() / 60)
3. 处理时区
python
# 创建无时区时间序列
naive_ts = pd.Series(pd.date_range('2023-01-01', periods=3), name='dates')
# 添加时区信息
localized_ts = naive_ts.dt.tz_localize('UTC') # 设置为UTC时区
# 转换时区
ny_ts = localized_ts.dt.tz_convert('America/New_York')
tokyo_ts = localized_ts.dt.tz_convert('Asia/Tokyo')
六、时间序列可视化
python
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# 创建带趋势的时间序列
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=100, freq='D')
values = np.cumsum(np.random.randn(100)) + 50
df = pd.DataFrame({'value': values}, index=dates)
# 绘制时间序列图
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['value'], label='Daily Value')
# 添加7日移动平均线
rolling_avg = df['value'].rolling(7).mean()
plt.plot(df.index, rolling_avg, 'r-', label='7-Day Avg')
# 格式化x轴日期显示
plt.gca().xaxis.set_major_locator(mdates.WeekdayLocator(interval=2))
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.gcf().autofmt_xdate() # 自动旋转日期标签
plt.title('Time Series Analysis')
plt.xlabel('Date')
plt.ylabel('Value')
plt.legend()
plt.grid(True)
plt.show()
七、性能优化技巧
- 使用DatetimeIndex:确保时间列作为索引而非普通列,可显著提高查询性能
- 选择适当频率:重采样时选择最接近实际需求的频率,避免不必要的计算
- 并行处理 :对于大型时间序列,考虑使用
dask或modin等并行计算库 - 使用类别类型 :对于重复的字符串标签(如股票代码),转换为
category类型 - 内存优化 :对于长时间序列,考虑使用
int96或int64存储时间戳而非datetime64[ns]
总结
Pandas的DataFrame为时间序列分析提供了全面而强大的工具集,从基础的时间创建和索引,到复杂的重采样、滚动计算和时区处理,几乎覆盖了所有常见的时间数据处理需求。掌握这些技术后,你可以高效地处理金融数据、传感器数据、日志数据等各种时间序列场景。
实际应用中,建议从简单操作开始,逐步掌握高级功能。记住,时间序列分析的关键在于理解数据的内在时间结构,合理选择频率和处理缺失值,这些往往比技术实现本身更重要。随着经验的积累,你会发现Pandas的时间序列功能能够优雅地解决越来越复杂的数据分析问题。