锋哥原创的Pandas2 Python数据处理与分析 视频教程:
2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili
Pandas 提供了强大的时间序列处理功能,是金融分析、物联网数据处理、业务指标监控等领域的核心工具。下面我将全面介绍时间序列数据处理的核心知识点和方法:
一、时间序列基础
1. 时间数据类型
Pandas 提供了4种核心时间数据类型:
-
Timestamp:表示单个时间点(纳秒精度)
-
DatetimeIndex:时间戳索引
-
Period:表示时间区间
-
PeriodIndex:时间段索引
pandas.Timestamp()
用于创建单个时间戳对象,可表示特定的日期和时间点。它支持多种输入格式,包括字符串、整数(时间戳)、datetime
对象等。
方法定义:
pandas.Timestamp(ts_input, # 时间输入(字符串、整数、datetime等)freq=None, # 频率(如'D'表示天,'H'表示小时)tz=None, # 时区(如'Asia/Shanghai')unit=None, # 时间单位(当ts_input为整数时有效)year=None, # 年(单独指定时使用)month=None, # 月(1-12)day=None, # 日(1-31)hour=None, # 时(0-23)minute=None, # 分(0-59)second=None, # 秒(0-59)microsecond=None, # 微秒(0-999999)nanosecond=None # 纳秒(0-999999999)
)
pandas.DatetimeIndex
是 Pandas 中用于处理时间序列索引的核心类,它继承自 pandas.Index
,专门用于存储和操作时间戳数据。DatetimeIndex
支持高效的时间切片、频率推断、重采样等操作,是时间序列分析的基础工具。
方法定义:
pandas.DatetimeIndex(data=None, # 时间戳数据(列表、数组或可迭代对象)freq=None, # 时间频率(如'D'表示天,'H'表示小时)start=None, # 开始时间(与end/periods配合使用)end=None, # 结束时间(与start/periods配合使用)periods=None, # 时间点数量(与start/end配合使用)tz=None, # 时区(如'Asia/Shanghai')normalize=False, # 是否将时间规范化为午夜(00:00:00)closed=None, # 区间开闭性('left'、'right'或None)ambiguous='raise', # 处理时区歧义的方式dayfirst=False, # 是否将字符串解析为DD/MM格式yearfirst=False, # 是否将字符串解析为YYYY/MM格式dtype=None, # 数据类型(默认为datetime64[ns])copy=False, # 是否复制数据name=None # 索引名称
)
pandas.Period
是 Pandas 中用于表示固定时间区间(如 “2025 年 5 月”、“2025Q2”、“2025-05-31 一天”)的类,与 Timestamp
(表示时间点)不同,Period
表示的是一个时间段。它支持时间区间的算术运算、频率转换和周期比较,是处理周期性数据的重要工具。
方法定义:
pandas.Period(value=None, # 时间值(字符串、整数或datetime对象)freq=None, # 频率(如'A'=年,'Q'=季度,'M'=月,'D'=日等)year=None, # 年(单独指定时使用)month=None, # 月(1-12,仅对某些频率有效)quarter=None, # 季度(1-4,仅对季度频率有效)day=None, # 日(1-31,仅对某些频率有效)hour=None, # 时(0-23,仅对某些频率有效)minute=None, # 分(0-59,仅对某些频率有效)second=None # 秒(0-59,仅对某些频率有效)
)
pandas.PeriodIndex
是 Pandas 中用于处理固定时间区间序列的索引类,它继承自 pandas.Index
,专门用于存储和操作由 Period
对象组成的索引。PeriodIndex
支持高效的周期计算、频率转换和时间区间选择,是处理周期性数据(如月度销售额、季度 GDP 等)的核心工具。
方法定义:
pandas.PeriodIndex(data=None, # 周期数据(列表、数组或可迭代对象)freq=None, # 时间频率(如'A'=年,'Q'=季度,'M'=月等)start=None, # 开始周期(与end/periods配合使用)end=None, # 结束周期(与start/periods配合使用)periods=None, # 周期数量(与start/end配合使用)name=None, # 索引名称copy=False, # 是否复制数据dtype=None # 数据类型(默认为period[freq])
)
我们看下示例:
import pandas as pd
import numpy as np# 创建Timestamp
ts = pd.Timestamp('2023-06-15 14:30:00')
print(f"Timestamp: {ts}")# 创建DatetimeIndex
date_range = pd.date_range('2023-01-01', periods=5, freq='D')
print(f"\nDatetimeIndex:\n{date_range}")# 创建Period
p = pd.Period('2023-06')
print(f"\nPeriod: {p}")# 创建PeriodIndex
period_range = pd.period_range('2023-01', periods=3, freq='M')
print(f"\nPeriodIndex:\n{period_range}")
时间序列创建:
# 从字符串创建时间序列
dates = ['2023-01-01', '2023-01-02', '2023-01-03']
ts_list = pd.to_datetime(dates)
print(f"\n从字符串创建:\n{ts_list}")
# 创建带时间索引的DataFrame
np.random.seed(42)
df = pd.DataFrame({'temperature': np.random.randint(15, 30, 5),'humidity': np.random.uniform(40, 80, 5)
}, index=pd.date_range('2023-01-01', periods=5, freq='D'))
print(f"\n时间序列DataFrame:\n{df}")
二、时间序列索引与切片
2.1 基本索引方法
# 按日期字符串索引
print("\n按日期索引:")
print(df.loc['2023-01-03'])# 按日期范围索引
print("\n日期范围索引:")
print(df['2023-01-03':'2023-01-05'])
2.2 部分字符串索引
# 创建小时级数据
hourly_data = pd.DataFrame({'value': np.random.rand(24)},index=pd.date_range('2023-01-01', periods=24, freq='h')
)# 按时间段索引
print("\n9:00到12:00的数据:")
print(hourly_data.between_time('09:00', '12:00'))# 按特定时间点索引
print("\n15:30的数据:")
print(hourly_data.at_time('15:30'))
三、时间序列重采样(Resampling)
重采样(Resampling)是时间序列分析中的核心操作,它指的是将时间序列从一个频率转换到另一个频率的过程。Pandas 提供了强大的 resample()
方法来实现这一功能,特别适用于处理时间序列数据的聚合、转换和分析。
重采样的基本概念
-
重采样的两种类型 降采样 (Downsampling):从高频数据到低频数据(如日数据 → 月数据) 升采样 (Upsampling):从低频数据到高频数据(如月数据 → 日数据)
-
重采样的核心步骤 分组 (Grouping):将时间序列数据分成多个时间区间 聚合 (Aggregation):对每个时间区间内的数据进行聚合计算 填充 (Filling):对于升采样,可能需要填充缺失值
基本语法:
# 基本重采样语法
resampled = df.resample(rule, closed=None, label=None, convention='start', kind=None, on=None, level=None)
参数详解:
-
rule:重采样频率字符串(如 'D'、'M'、'5min')
-
closed:控制区间闭合方向('right' 或 'left')
-
label:设置聚合结果的标签('right' 或 'left')
-
convention:重采样约定('start' 或 'end')
-
kind:指定索引类型('timestamp' 或 'period')
-
on:指定时间列(如果索引不是时间类型)
-
level:用于多级索引的时间级别
我们看一个降采样(Downsampling)示例:
import pandas as pd
import numpy as np
# 创建日数据
dates = pd.date_range('2023-01-01', periods=90, freq='D')
daily_data = pd.DataFrame({'sales': np.random.randint(100, 500, 90),'visitors': np.random.randint(50, 200, 90)
}, index=dates)
# 按月重采样(求和)
monthly_sales = daily_data.resample('ME').sum()
print("\n按月重采样(求和):")
print(monthly_sales)
# 按周重采样(平均值)
weekly_avg = daily_data.resample('W-MON').mean()
print("\n按周重采样(平均值):")
print(weekly_avg.head())
我们在看一个升采样(Upsampling)的示例:
import pandas as pd# 创建月数据
monthly_data = pd.DataFrame({'revenue': [12000, 15000, 18000, 16000],
}, index=pd.date_range('2023-01-01', periods=4, freq='ME'))# 升采样到日频率
daily_upsampled = monthly_data.resample('D').asfreq()
print("\n升采样到日频率(不填充):")
print(daily_upsampled.head(10))# 前向填充
ffilled = monthly_data.resample('D').ffill()
print("\n前向填充:")
print(ffilled.head(10))# 线性插值
interpolated = monthly_data.resample('D').interpolate(method='linear')
print("\n线性插值:")
print(interpolated.head(10))
输出:
升采样到日频率(不填充):revenue
2023-01-31 12000.0
2023-02-01 NaN
2023-02-02 NaN
2023-02-03 NaN
2023-02-04 NaN
2023-02-05 NaN
2023-02-06 NaN
2023-02-07 NaN
2023-02-08 NaN
2023-02-09 NaN前向填充:revenue
2023-01-31 12000
2023-02-01 12000
2023-02-02 12000
2023-02-03 12000
2023-02-04 12000
2023-02-05 12000
2023-02-06 12000
2023-02-07 12000
2023-02-08 12000
2023-02-09 12000线性插值:revenue
2023-01-31 12000.000000
2023-02-01 12107.142857
2023-02-02 12214.285714
2023-02-03 12321.428571
2023-02-04 12428.571429
2023-02-05 12535.714286
2023-02-06 12642.857143
2023-02-07 12750.000000
2023-02-08 12857.142857
2023-02-09 12964.285714
重采样频率别名大全
别名 | 描述 | 别名 | 描述 |
---|---|---|---|
B | 工作日 | QS | 季度开始 |
C | 自定义工作日 | BQS | 工作季度开始 |
D | 日历日 | A, Y | 年结束 |
W | 周 | BA, BY | 工作年结束 |
M | 月结束 | AS, YS | 年开始 |
BM | 工作月结束 | BAS, BYS | 工作年开始 |
MS | 月开始 | BH | 工作小时 |
BMS | 工作月开始 | H | 小时 |
Q | 季度结束 | T, min | 分钟 |
BQ | 工作季度结束 | S | 秒 |
QS | 季度开始 | L, ms | 毫秒 |
BQS | 工作季度开始 | U, us | 微秒 |
A, Y | 年结束 | N | 纳秒 |
组合频率示例:
-
5min
:5分钟 -
1H30min
:1小时30分钟 -
W-MON
:以周一为起始的周 -
QS-JAN
:从一月开始的季度 -
BQ-MAR
:从三月开始的工作季度
Pandas 支持多种聚合函数,可以根据需要选择:
常用聚合函数
-
mean()
:平均值 -
sum()
:求和 -
min()
:最小值 -
max()
:最大值 -
median()
:中位数 -
first()
:第一个值 -
last()
:最后一个值 -
ohlc()
:开盘、最高、最低、收盘(金融分析常用) -
count()
:计数 -
std()
:标准差 -
var()
:方差
升采样中的填充方法
当进行升采样时,需要处理新增时间点的数据缺失问题:
方法 | 描述 | 适用场景 |
---|---|---|
ffill() | 前向填充 | 数据变化缓慢的场景 |
bfill() | 后向填充 | 近期数据更重要的场景 |
interpolate() | 插值 | 数据连续变化的场景 |
nearest() | 最近邻填充 | 分类数据或离散数据 |
asfreq() | 返回NaN | 仅改变频率,不填充数据 |
pad() | 同ffill | ffill的别名 |
fillna() | 自定义填充 | 需要特定填充逻辑的场景 |
四、时间序列窗口操作
窗口操作是时间序列分析的核心技术,它允许我们对时间序列数据的移动子集进行计算。Pandas 提供了三种主要的窗口操作类型:滚动窗口(rolling)、扩展窗口(expanding) 和 指数加权窗口(ewm)。
窗口操作类型概览
窗口类型 | 描述 | 特点 | 适用场景 |
---|---|---|---|
滚动窗口(Rolling) | 固定大小的窗口沿时间轴滑动 | 窗口大小固定 | 短期趋势分析、平滑数据 |
扩展窗口(Expanding) | 窗口从起始点扩展到当前点 | 窗口随时间增大 | 累积统计量、长期趋势分析 |
指数加权窗口(EWM) | 对历史数据赋予指数衰减权重 | 近期数据权重更高 | 时间序列预测、金融分析 |
rolling()方法:
DataFrame.rolling(window, min_periods=None, center=False, win_type=None, on=None, axis=0, closed=None)
参数说明:
参数 | 描述 | 默认值 | 示例 |
---|---|---|---|
window | 窗口大小(整数或时间字符串) | 必填 | 5 , '30D' , '2H' |
min_periods | 计算所需的最小观测值数量 | window | 1 , 3 |
center | 是否将结果居中(而非右对齐) | False | True |
win_type | 窗口类型(权重分配方式) | None | 'triang' , 'gaussian' |
on | 指定用于计算的时间列 | None | 'date' |
axis | 应用方向 | 0 | 0 (行方向) |
closed | 窗口闭合端 | None | 'left' , 'right' |
滚动窗口(Rolling Window)示例:
import pandas as pd
import numpy as np
# 创建时间序列数据
dates = pd.date_range('2023-01-01', periods=30, freq='D')
data = pd.DataFrame({'value': np.sin(np.linspace(0, 4 * np.pi, 30)) * 100 + 500
}, index=dates)
print(data)
# 7天滚动平均
data['rolling_7d'] = data['value'].rolling(window=7).mean()
# 5天时间窗口
data['rolling_5d_time'] = data['value'].rolling('5D').mean()
print("\n滚动窗口操作:")
print(data.head(10))
expanding()方法:
DataFrame.expanding(min_periods=1, center=False, axis=0)
参数说明:
参数 | 描述 | 默认值 | 示例 |
---|---|---|---|
min_periods | 计算所需的最小观测值数量 | 1 | 3 |
center | 是否将结果居中 | False | True |
axis | 应用方向 | 0(行方向) | 1(列方向) |
扩展窗口(Expanding Window)示例:
data # 扩展窗口计算
data['expanding_mean'] = data['value'].expanding().mean()
data['expanding_max'] = data['value'].expanding().max()print("\n扩展窗口操作:")
print(data[['value', 'expanding_mean', 'expanding_max']].head(10))
ewm()方法:
DataFrame.ewm(com=None, span=None, halflife=None, alpha=None, min_periods=0, adjust=True, ignore_na=False, axis=0, times=None)
参数说明:
参数 | 描述 | 默认值 | 相互关系 |
---|---|---|---|
com | 质心(Center of Mass) | None | $\alpha = 1/(1 + com)$ |
span | 衰减跨度 | None | $\alpha = 2/(span + 1)$ |
halflife | 半衰期 | None | $\alpha = 1 - \exp(\ln(0.5)/halflife)$ |
alpha | 平滑因子 | None | 直接指定 |
min_periods | 最小观测值数 | 0 | - |
adjust | 是否调整初始值 | True | - |
ignore_na | 是否忽略NaN | False | - |
axis | 应用方向 | 0 | - |
times | 时间间隔 | None | - |
指数加权移动平均(EWMA)
# 指数加权移动平均
data['ewma_span10'] = data['value'].ewm(span=10).mean()
data['ewma_alpha02'] = data['value'].ewm(alpha=0.2).mean()print("\n指数加权移动平均:")
print(data[['value', 'ewma_span10', 'ewma_alpha02']].head(10))