本文隶属加速数据分析系列教程,重点讲解如何利用 RAPIDS cuDF 高效处理时间序列数据。您将系统学习从数据格式化、重采样到滚动窗口分析的完整流程,并了解如何借助 GPU 加速将处理耗时缩短数倍,显著提升数据分析效率。
什么是时间序列数据?
时间序列数据是指按时间顺序排列的数据点集合,通常包含时间戳变量。在实际业务中,它的应用场景非常广泛:
- 金融行业欺诈检测
- 零售业销售预测
- 天气预报传感器读数
- 内容推荐系统
时间序列数据往往需要大量的转换操作,如重采样和滚动窗口平滑。在传统 pandas 中,这些操作受单线程限制,处理大型数据集时速度明显变慢。而 RAPIDS cuDF 提供了与 pandas 高度相似的 API,但通过 GPU 并行计算,可将处理速度提升 最高 40 倍。它特别适合那些“金发姑娘”数据集——既不太大也不太小,单机 pandas 处理吃力,但又无需引入分布式框架。
数据集:Meteonet
本教程选用 Meteonet 天气数据集,包含 2016 年至 2018 年巴黎各地气象站的读数(含缺失值和异常值),数据规模约为 12.5 GB。我们将模拟数据科学家首次接收该数据时的场景,进行探索性分析与格式化处理。
分析方法概述
整篇分析基于 Time Series Data Analysis Using cuDF 笔记本(可从 RAPIDS GitHub 仓库获取),主要任务包括:
- 格式化数据帧
- 对时间序列重采样
- 运行滚动窗口分析
本文聚焦上述三个核心步骤,完整笔记本还包含数据清洗等环节,此处不再赘述。
步骤 1:格式化数据帧
首先导入必要的库:
# Import the necessary packages
import cudf
import cupy as cp
import pandas as pd
读取 CSV 数据:
## Read in data
gdf = cudf.read_csv('./SE_data.csv')
接下来,我们只关注风速 (ff)、湿度 (hu) 和温度 (t) 这 3 个气象参数,删除其他无关列:
gdf = gdf.drop(columns=['dd','precip','td','psl'])
将日期列转换为 datetime 数据类型,并查看前五行和数据集形状:
# Change the date column to the datetime data type. Look at the DataFrame info
gdf['date'] = cudf.to_datetime(gdf['date'])
gdf.head()
Gdf.shape
| number_sta | lat | lon | height_sta | date | ff | hu | t | |
| 0 | 1027003 | 45.83 | 5.11 | 196.0 | 2016-01-01 | 98.0 | 279.05 | |
| 1 | 1033002 | 46.09 | 5.81 | 350.0 | 2016-01-01 | 0.0 | 99.0 | 278.35 |
| 2 | 1034004 | 45.77 | 5.69 | 330.0 | 2016-01-01 | 0.0 | 100.0 | 279.15 |
| 3 | 1072001 | 46.20 | 5.29 | 260.0 | 2016-01-01 | 276.55 | ||
| 4 | 1089001 | 45.98 | 5.33 | 252.0 | 2016-01-01 | 0.0 | 95.0 | 279.55 |
表 1. 数据集前五行
输出显示 DataFrame 形状为 (127515796, 8),即约 1275 万行、8 列。
接下来,我们检查采样频率:
## Investigate the sampling frequency with the diff() function to calculate the time diff
## dt.seconds, which is used to find the seconds value in the datetime frame. Then apply the
## max() function to calculate the maximum date value of the series.
delta_mins = gdf['date'].diff().dt.seconds.max()/60
print(f"The dataset collection covers from {gdf['date'].min()} to {gdf['date'].max()} with {delta_mins} minute sampling interval")
输出:数据集覆盖从 2016-01-01T00:00:00.000000000 到 2018-12-31T23:54:00.000000000,采样间隔为 6 分钟。
现在将时间增量分离为独立列(年、月、日、小时、分钟),方便后续切片:
gdf['year'] = gdf['date'].dt.year
gdf['month'] = gdf['date'].dt.month
gdf['day'] = gdf['date'].dt.day
gdf['hour'] = gdf['date'].dt.hour
gdf['mins'] = gdf['date'].dt.minute
gdf.tail
| number_sta | lat | lon | height_sta | date | ff | hu | t | year | month | day | hour | mins | |
| 127515791 | 84086001 | 43.811 | 5.146 | 672.0 | 2018-12-31 23:54:00 | 3.7 | 85.0 | 276.95 | 2018 | 12 | 31 | 23 | 54 |
| 127515792 | 84087001 | 44.145 | 4.861 | 55.0 | 2018-12-31 23:54:00 | 11.4 | 80.0 | 281.05 | 2018 | 12 | 31 | 23 | 54 |
| 127515793 | 84094001 | 44.289 | 5.131 | 392.0 | 2018-12-31 23:54:00 | 3.6 | 68.0 | 280.05 | 2018 | 12 | 31 | 23 | 54 |
| 127515794 | 84107002 | 44.041 | 5.493 | 836.0 | 2018-12-31 23:54:00 | 0.6 | 91.0 | 270.85 | 2018 | 12 | 31 | 23 | 54 |
| 127515795 | 84150001 | 44.337 | 4.905 | 141.0 | 2018-12-31 23:54:00 | 6.7 | 84.0 | 280.45 | 2018 | 12 | 31 | 23 | 54 |
表 2. 增加时间增量列后的结果
现在,我们可以选择特定时间范围和气象站来进一步分析:
# Use the cupy.logical_and(...) function to select the data from a specific time range.
import pandas as pd
start_time = pd.Timestamp('2017-02-01T00')
end_time = pd.Timestamp('2018-11-01T00')
station_id = 84086001
gdf_period = gdf.loc[cp.logical_and(cp.logical_and(gdf['date']>start_time,gdf['date']
此时 DataFrame 包含 13 个变量,共 146039 行,准备就绪。
