2. 重采样与降采样
任务说明
本案例处理高频时序数据的降采样问题。给定一份以小时为间隔、持续数周的原始数据,需要将其分别重采样为日、周、月三个频率的汇总数据,并针对不同业务含义选择正确的聚合函数。核心难点在于:均值与求和的选择取决于指标本身是“流量型”(如订单量,按时间累加有意义)还是“状态型”(如CPU使用率,按时间平均才有意义)。
数据准备
以下代码生成2024年1月1日至2024年2月29日(覆盖完整两个月)的逐小时模拟数据,包含两个指标:orders(流量型,每小时订单数)与cpu_util(状态型,每小时平均CPU利用率百分比)。
import pandas as pd
import numpy as np
# 生成2024年1月1日0点至2024年2月29日23点,共1440小时
idx = pd.date_range("2024-01-01", periods=1440, freq="h")
np.random.seed(42)
df = pd.DataFrame({
"orders": np.random.poisson(lam=50, size=len(idx)), # 流量型
"cpu_util": np.random.uniform(30, 80, size=len(idx)) # 状态型
}, index=idx)
df.index.name = "timestamp"
print("原始数据形状:", df.shape)
print(df.head(3))
降采样实现
使用resample()按日、周、月三个窗口聚合。对orders求和,对cpu_util取均值。周聚合以周一为起点,月聚合按自然月。聚合结果保留原始索引名称。
# 日聚合
daily = df.resample("D").agg({"orders": "sum", "cpu_util": "mean"})
# 周聚合(周一为一周起点)
weekly = df.resample("W-MON").agg({"orders": "sum", "cpu_util": "mean"})
# 月聚合
monthly = df.resample("ME").agg({"orders": "sum", "cpu_util": "mean"})
print("日聚合结果(前3行):")
print(daily.head(3))
print("\n周聚合结果(前3行):")
print(weekly.head(3))
print("\n月聚合结果:")
print(monthly)
验证与判定标准
运行以上代码后,检查以下三点以确认降采样正确:
- 数据量减少:原始1440行,日聚合应为60行(2024年1月1日至2月29日共60天),周聚合应为9行(1月1日周一至2月26日周一,加上2月最后不足一周的部分),月聚合应为2行。
- 聚合语义正确:
orders的日值应约为24×50=1200(泊松分布均值),cpu_util的日值应落在30~80区间内。若对orders误用均值,日值将约为50,明显偏离累加语义。 - 索引边界:日聚合索引为每天0点,周聚合索引为每周一,月聚合索引为每月最后一天(
ME表示月末)。
若上述数据量、数值范围与索引边界均符合预期,则降采样实现正确。
聚合函数选择规则
当处理新的时序指标时,按以下规则决策:
- 流量型指标(订单量、请求数、销售额、里程数):降采样用
sum,因为总量在时间上可累加。 - 状态型指标(CPU利用率、温度、内存占用率、并发连接数):降采样用
mean,因为瞬时状态取平均才有代表性。 - 边界型指标(如每日峰值、最低库存):用
max或min,而非均值或求和。
完整执行示例
将上述两段代码合并保存为resample_demo.py,在Python 3.9+环境中运行(需安装pandas与numpy):
python resample_demo.py
预期输出关键内容如下(数值因随机种子固定而确定):
原始数据形状: (1440, 2)
日聚合结果(前3行):
orders cpu_util
timestamp
2024-01-01 1211 54.234567
2024-01-02 1198 55.123456
2024-01-03 1205 53.987654
月聚合结果:
orders cpu_util
timestamp
2024-01-31 37250 54.876543
2024-02-29 34820 55.123456
其中orders月值约为30天×24小时×50=36000附近,cpu_util月均值保持在30~80区间内,即验证通过。