sktime 数据与文件格式权威指南:序列化规格与内存 scitype/mtype 体系详解
【免费下载链接】sktimeA unified framework for machine learning with time series项目地址: https://gitcode.com/GitHub_Trending/sk/sktime
导读
本文基于 sktime 官方 API 参考文档《Data and File Format Specifications》(docs/source/api_reference/data_format.rst),系统梳理 sktime 的两大类格式规格:一是磁盘上的序列化文件格式(模型序列化 ZIP 归档与.ts数据集文件格式 v1.0),二是 Python 内存中的数据容器规格(scitype/mtype 双层次抽象类型体系)。读完本文,你将掌握:如何用save/load正确持久化与恢复任意 estimator、如何手写一个完全兼容的.ts数据集文件、以及 Series/Panel/Hierarchical/Table 四类科学类型及其十余种机器类型(mtype)的约束与用途,并能结合仓库源码理解背后的实现原理。
一、两类规格体系的定位
sktime 的数据与文件格式规格分为两个正交的维度:
- 序列化文件格式(serialized file format):用于把对象或数据“落盘”的格式,包括
- 模型序列化文件(estimator 的
.zip归档); .ts数据集文件(用于存储时间序列数据集及其元数据)。
- 模型序列化文件(estimator 的
- 内存数据规格(in-memory data specification):Python 进程内表示时间序列数据的抽象容器类型体系,贯穿所有 estimator 的输入输出。
两份子规格文档分别位于 model_serialization.rst 与 ts.rst。数据格式的检查与转换工具(如check_raise、convert等)不在本文详述范围,相关 API 见 utils.rst。
二、序列化文件格式规格
2.1 模型序列化格式(Estimator Serialization Format)
sktime 的所有 estimator 继承自BaseObject,其序列化入口是sktime.base.BaseObject.save,恢复入口是sktime.base.load。规格文档明确:磁盘容器与内存容器均由基类实现产生;带有额外外部状态的 estimator 可以覆写加载钩子并在磁盘容器中追加文件。
2.1.1 磁盘容器:扁平 ZIP 归档
调用estimator.save(path)会在path处生成一个带.zip后缀的归档文件。例如estimator.save("model")会在当前工作目录创建model.zip。归档创建过程中使用的临时目录会在save返回前被清理。
基类向归档写入以下两个成员:
| 成员 | 内容 | 用途 |
|---|---|---|
_metadata | estimator 对象的类型,即type(self) | 供sktime.base.load选择对应的加载实现 |
_obj | 序列化后的 estimator 实例(若保存前已 fit,则包含拟合状态) | 真正的模型对象 |
归档是扁平的,两个成员位于归档根目录:
model.zip ├── _metadata └── _obj两个成员使用同一个序列化器写入。save的serialization_format参数控制选择"pickle"(默认)或"cloudpickle",其中cloudpickle是可选依赖(soft dependency)。
从源码可以印证这一结构(sktime/base/_base.py):
save会先校验serialization_format是否在SERIALIZATION_FORMATS中,path必须是str或Path;- 若选择
"cloudpickle",先执行_check_soft_dependencies("cloudpickle", severity="error")强校验依赖; - 把
type(self)与self分别 pickle/cloudpickle 写入临时目录下的_metadata、_obj,再调用shutil.make_archive(base_name=path, format="zip", root_dir=path)打包,最后shutil.rmtree(path)删除临时目录。
恢复归档有两种等价方式:传入不带.zip后缀的字符串路径,或传入指向归档的pathlib.Path对象。加载器先读取_metadata,然后委托给存储类的类方法load_from_path。这一调度逻辑在 sktime/base/_serialize.py 中实现:load对字符串入参会自动补.zip后缀,先解压读取_metadata得到类对象cls,再调用cls.load_from_path(path)。
完整示例(保存已拟合的预测器并恢复):
from pathlib import Path from zipfile import ZipFile from sktime.base import load from sktime.datasets import load_airline from sktime.forecasting.naive import NaiveForecaster y = load_airline() forecaster = NaiveForecaster(strategy="mean") forecaster.fit(y, fh=[1, 2, 3]) # 在当前工作目录创建 model.zip forecaster.save("model") ZipFile("model.zip").namelist() # ['_metadata', '_obj'] # 两种恢复方式等价 restored_from_string = load("model") restored_from_path = load(Path("model.zip")) restored_from_path.predict()2.1.2 内存容器:二元组
调用estimator.save()(不传path)返回一个二元组:
- 第一个元素是 estimator 对象的类型,即
type(self); - 第二个元素是包含序列化 estimator 实例的
bytes对象。
直接把这个二元组传给sktime.base.load即可恢复。加载器会委托给第一个元素(类)的类方法load_from_serial,并把第二个元素传给它:
from sktime.base import load from sktime.datasets import load_airline from sktime.forecasting.naive import NaiveForecaster y = load_airline() forecaster = NaiveForecaster(strategy="mean") forecaster.fit(y, fh=[1, 2, 3]) serial = forecaster.save() # (<class 'sktime.forecasting.naive._naive.NaiveForecaster'>, b'\x80\x05...') restored = load(serial) restored.predict()源码佐证:load对tuple入参要求长度为 2,否则抛出ValueError;随后执行cls.load_from_serial(stored)(sktime/base/_serialize.py)。而基类load_from_serial的实现就是pickle.loads(serial),load_from_path则是从 ZIP 中读取_obj后pickle.loads(sktime/base/_base.py)。
2.1.3 扩展点(Extension Points)
基类格式覆盖“状态可以放进单个序列化对象”的 estimator。对于带有额外资源的 estimator,可以覆写save、load_from_serial或load_from_path:
- 这些 estimator 可以在归档中追加成员,同时保留
_metadata和_obj,这样通用加载器仍能识别 estimator 类并正确分派到其加载钩子; - 例如深度学习类 estimator 会在基类成员之外额外存储
keras模型与训练历史,其归档结构为:
model.zip ├── _metadata ├── _obj ├── history └── keras/ └── model.keras- 这些 estimator 的内存容器仍然是二元组,额外状态被嵌套在第二个元素内部。
规格文档由此给出明确建议:使用序列化 estimator 的消费者应当只依赖公开接口save与load,而不要依赖上述文档之外的归档成员。
2.1.4 安全注意事项
两种受支持的序列化格式(pickle / cloudpickle)在反序列化时都可能执行任意代码。因此只能加载来自可信来源的归档或内存容器。这一警告同样在 sktime/base/_serialize.py 的文档字符串中被反复强调(“can do this on empty kernel”示例暗示其跨进程/跨机器传输用途)。
2.2.ts文件格式 v1.0
.ts文件格式 v1.0 于 2022-10-08 由作者 Sagar Mishra 定稿,规格见 ts.rst。它形式化定义了.ts文件中的字符串标识符(string identifiers)——即文件中以@开头的字符串。
.ts文件以utf-8编码,存储时间序列数据集及其元数据,可用记事本等任何基础编辑器打开进行可视化检查。sktime 的核心读写函数位于 sktime/datasets/_readers_writers/ts.py,仓库自带的真实.ts样例数据位于 sktime/datasets/data 目录。
2.2.1 文件的三个信息块
一个.ts文件按顺序包含三个信息块:
1. 描述块(Description Block,可选)
- 任意数量的、以
#开头的连续行; - 每个
#后跟任意(utf-8)符号序列; - 规格不强制描述块内容,但惯例是包含数据集说明,如完整的数据字典、引用信息等;
- 所有
#行都会被 sktime 的加载函数忽略。
2. 元数据块(Metadata Block)
- 包含连续以
@开头的行; - 每个
@后直接跟随字符串标识符(无空白),即@<identifier>,再接与标识符类型匹配的取值; - 除
@data必须位于本块末尾外,其余标识符没有严格的出现顺序; - 行数取决于数据集属性(例如多维数据集需要额外一行声明维度数)。
3. 数据集块(Dataset Block)
- 包含表示数据集的浮点值列表;
- 最简单情况(无时间戳)下,一条序列的值用逗号分隔列表表示,每个值的索引相对于其在列表中的位置(0, 1, ..., m);
- 一个实例可以包含 1 到多个维度:实例之间用换行分隔,同一实例内的维度之间用冒号(:)分隔;
- 若存在时间戳,单条数据用圆括号包裹为
(YYYY-MM-DD HH:mm:ss,<value>); - 响应变量位于每个实例的末尾,同样用冒号分隔。
来自 BasicMotions 数据集的节选展示了三个块:
#The data was generated as part of a student project where four students performed four activities whilst wearing a smart watch. #The watch collects 3D accelerometer and a 3D gyroscope It consists of four classes, which are walking, resting, running and #badminton. ... @problemName BasicMotions @timeStamps false @missing false ... @data -0.740653,-0.740653,10.208449,2.867009,-0.194301,-0.194301,-0.249618,0.516079,-0.255552:Standing -0.247409,-0.247409,-0.77129,-0.576154,-0.368484,-0.020851,-0.020851,-0.465607,-0.382975,-0.382975:Walking ...2.2.2 字符串标识符全集
元数据块中的每个字符串标识符格式为@<identifier> [value],唯独@data之后不带任何信息。标识符必须写在行首,且一行只含一个标识符-取值对。
元数据覆盖的信息包括:数据集名称、是否含时间戳、是否含缺失值、是否单维、多维时的维度数、各实例是否等长、类别标签等。
下表为标识符完整规格:
| 标识符 | 描述 | 取值 | 补充说明 | 示例 |
|---|---|---|---|---|
@problemname | 数据集名称 | 任意string | 取值不能含空格 | BasicMotions |
@timestamps | 是否存在时间戳 | true/false | 仅限这两个值 | false |
@missing | 是否存在缺失值 | true/false | 仅限这两个值 | false |
@univariate | 是否只有一维时间序列 | true/false | 仅限这两个值 | false |
@dimension | 变量数量 | 整数 > 0 | 仅当@univariate=false时出现 | 6 |
@equallength | 各实例长度是否相等 | true/false | 仅限这两个值 | true |
@serieslength | 每个实例的时间戳数量 | 整数 > 0 | 仅当@equallength=true时出现 | 100 |
@targetlabel | 是否存在目标标签 | true/false | 回归数据专用 | true |
@classlabel | 是否存在类别标签 | false/true<string-1> <string-2> .. | 分类数据专用;为true时还可跟空格分隔的整型/字符串标签 | true Standing Running Walking Badminton |
@data | 标记数据开始 | — | 数据从下一行开始 | — |
关于命名惯例,需要注意:由于这类数据集常来自不同来源(如 tsregression、timeseriesclassification.com),命名可能存在细微冲突(全小写 vs. camelCase,例如上表用@problemname,而文件样例中出现@problemName)。sktime 内部会自动处理此类不一致。
2.2.3 创建.ts文件的完整步骤
创建兼容 sktime 的.ts文件需遵循以下要点:
- 标识符的一般顺序无关紧要,但
@data必须是最后一个字符串标识符; - 一行只能包含一个标识符-取值对;
- 包含标识符的行必须以标识符开头;
- 文件中唯一允许出现空格的位置是标识符与其取值之间;
- 避免在行与行之间出现换行符;
- 遵循“注释、标识符、数据”的顺序。
具体操作流程:
- 创建空文件:用任意文本编辑器(记事本即可)新建文件;
- 编写描述性注释:文件开头几行用于描述数据集(可选),以
#开头; - 添加分类与回归数据共用的元数据:
- 问题名称:
@problemName Example - 缺失信息:
@missing false - 时间戳信息:
@timestamps true - 是否单维:
@univariate false - 因
@univariate为false,追加维度数:@dimension 3 - 是否等长:
@equallength true - 因等长为
true,追加实例长度:@serieslength 5
- 问题名称:
- 根据数据集类型追加:
- 回归:添加
@targetlabel标识符,响应变量存在则为true,否则false; - 分类:添加
@classlabel标识符。无响应变量取false;为true时可选择用空格分隔的类别标签,例如三字符串标签@classlabel true good bad neutral,或两整型标签@classlabel true 0 1;
- 回归:添加
- 添加标识符
@data,在下一行写入数值; - 将文件保存为
<CHOOSE_NAME>.ts,编码必须是utf-8。
小技巧:如果文件保存后显示为<CHOSEN_NAME>.ts.txt,可先改名为.txt,再在终端用mv <CHOSEN_NAME>.txt <CHOSEN_NAME>.ts重命名。
2.2.4 逐步搭建的完整示例
设样本为带时间戳的单实例多维回归数据(一个实例、三维、五时间点、响应值 3.2):
(2004-08-10 18:00:00,1130.0),(2004-08-10 19:00:00,1217.75),(2004-08-10 20:00:00,1134.75),(2004-08-10 21:00:00,1155.5), (2004-08-10 22:00:00,1151.0):(2004-08-10 18:00:00,1144.24),(2004-08-11 19:00:00,1111.25),(2004-08-11 20:00:00,1065.75), (2004-08-11 21:00:00,992.5),(2004-08-11 22:00:00,905.76):(2004-08-11 18:00:00,903.35),(2004-08-11 19:00:00,941.0), (2004-08-11 20:00:00,1073.6666666667),(2004-08-11 21:00:00,1113.5),(2004-08-11 22:00:00,1100.6):3.2第 1 步:添加描述上下文(可选):
# The following dataset is generated using sensor S in the apparatus A as shown in the following # link: https://example.com/. We receive three individual variables, collected within the time duration of 4 hours. # There are no missing values in the dataset and timestamps are also included. # For more information about how data was collected, visit the datacollection.com.第 2 步:添加分类与回归共用的元数据:
@problemName Example @missing false @timestamps true @univariate false @dimension 3 @equallength true @serieslength 5第 3 步:因为这是回归数据集,追加@targetlabel true:
@targetlabel true第 4 步:添加@data并在新行写入数据:
@data (2004-08-10 18:00:00,1130.0),(2004-08-10 19:00:00,1217.75),(2004-08-10 20:00:00,1134.75),(2004-08-10 21:00:00,1155.5), (2004-08-10 22:00:00,1151.0):(2004-08-10 18:00:00,1144.24),(2004-08-11 19:00:00,1111.25),(2004-08-11 20:00:00,1065.75), (2004-08-11 21:00:00,992.5),(2004-08-11 22:00:00,905.76):(2004-08-11 18:00:00,903.35),(2004-08-11 19:00:00,941.0), (2004-08-11 20:00:00,1073.6666666667),(2004-08-11 21:00:00,1113.5),(2004-08-11 22:00:00,1100.6):3.2第 5 步:保存为sample.ts后即可通过 sktime 直接加载。
三、内存数据规格:scitype 与 mtype 体系
sktime 用一套双层次抽象类型体系描述进程内的数据容器,这也是其“科学类型系统”(scientific typing)设计的核心。
3.1 核心概念
- scitype(scientific type,科学类型):抽象数据类型。文档中对 Series、Panel、Hierarchical、Table 的定义由 sktime/datatypes/_series/_base.py、sktime/datatypes/_panel/_base.py、sktime/datatypes/_hierarchical/_base.py、sktime/datatypes/_table/_base.py 中的
Scitype*类实现; - mtype(machine type,机器类型):上述抽象类型的具体实现,例如
pandas.DataFrame、numpy二维数组、xarray 对象等; - 子类型属性:每个 scitype 用属性字段继续细分,如
is_univariate(是否单变量)、n_instances(面板/层级集合中的实例数)等; - 注册表:scitype 与 mtype 的完整清单由 sktime/datatypes/_registry.py 维护,其中
generate_mtype_cls_list还会根据当前环境是否满足 soft dependency 过滤可用 mtype。
3.2 四大核心 scitype
sktime 的核心 scitype 为:
| scitype | 含义 | 核心属性字段(节选) |
|---|---|---|
Series | 单条时间序列 | is_univariate、is_equally_spaced、is_empty、has_nans、n_features、feature_names |
Panel | 扁平的时序集合(panel data) | is_univariate、is_equal_length、is_empty、is_one_series、has_nans、n_instances、n_features |
Hierarchical | 层级化的时序集合 | 层级索引为 H 元组,H ≥ 2;H = 1 时归入 Panel 以避免类型重复 |
Table | 非时序的数据帧表格 | 类似 pandas.DataFrame 的实现 |
从源码可精确印证抽象定义。例如ScitypeSeries(sktime/datatypes/_series/_base.py)规定:抽象 Series 具有索引t_1,...,t_T(整数或可排序的日期时间类型)与取值y_1,...,y_T(每项为定长向量,元素为数值或类别),索引必须互异且有序(t_{i-1} < t_i)。ScitypePanel(sktime/datatypes/_panel/_base.py)则定义 Panel 为 N 条 Series 的索引化集合,实例索引互异但不必有序。
3.3 Series mtype 规格
Series的 mtype 代表单条时间序列,规格文档列出 8 种,实现在 sktime/datatypes/_series/_check.py:
| mtype 类 | 名称 | 说明要点 |
|---|---|---|
SeriesPdDataFrame | "pd.DataFrame" | 行 = 时间点、列 = 变量;索引需单调,且为 Int64/Range/Datetime/Period 之一(源码) |
SeriesPdSeries | "pd.Series" | pandas Series 表示的单变量序列 |
SeriesNp2D | "np.ndarray"二维 | 二维数组:轴 0 为时间、轴 1 为变量 |
SeriesXarray | "xarray.DataArray" | xarray 表示,需安装 xarray |
SeriesDask | "dask" | 分布式/惰性计算表示 |
SeriesPolarsEager | "polars" | Polars 惰性/即时 DataFrame 表示 |
SeriesGluontsList | "gluonts"list | GluonTS 列表表示 |
SeriesGluontsPandas | "gluonts"pandas | GluonTS 的 pandas 表示 |
以最常用的SeriesPdDataFrame为例(sktime/datatypes/_series/_check.py),其能力标签(capability tags)显示:支持多变量(capability:multivariate: True)、支持不等距序列(capability:unequally_spaced: True)、支持缺失值(capability:missing_values: True);同时它的文档字符串明确“cannot represent multivariate series”的表述有误、实际以能力标签为准——各 mtype 以_tags字典声明自身能力,这是判断“某个容器能否表达某类数据”的权威依据。
3.4 Panel mtype 规格
Panel的 mtype 代表扁平的时间序列集合,规格文档列出 7 种,实现在 sktime/datatypes/_panel/_check.py:
| mtype 类 | 说明要点 |
|---|---|
PanelPdMultiIndex | 多重索引 DataFrame,索引层含实例 id 与时间 |
PanelNp3D | 三维 numpy 数组:轴 0 实例、轴 1 时间、轴 2 变量 |
PanelDfList | DataFrame 列表,每个元素是一条序列 |
PanelDask | dask 分布式表示 |
PanelPolarsEager | Polars 表示 |
PanelGluontsList | GluonTS 列表表示 |
PanelGluontsPandas | GluonTS 的 pandas 表示 |
3.5 Hierarchical mtype 规格
Hierarchical的 mtype 代表层级化的时间序列集合,实现在 sktime/datatypes/_hierarchical/_check.py,共 3 种:
HierarchicalPdMultiIndexHierarchicalDaskHierarchicalPolarsEager
其抽象定义(sktime/datatypes/_hierarchical/_base.py)强调:层级索引的每个元素是 H 元组,域为固定笛卡尔积S = S_1 × ... × S_H;至少需要两个层级(H ≥ 2),H = 1 时归入 Panel 类型。
3.6 Table mtype 规格
Table的 mtype 代表非时序数据帧表格,实现在 sktime/datatypes/_table/_check.py,共 6 种:
TablePdDataFrameTablePdSeriesTableNp1DTableNp2DTableListOfDictTablePolarsEager
四、格式检查与转换工具
数据格式规格文档明确指引:检查与转换数据格式的工具见 utils API 参考(utils.rst)。在源码层面,这些工具位于 sktime/datatypes/_check.py(校验、check_raise等)与 sktime/datatypes/_convert.py(convert等)。
实际工程中的典型用法是:在把数据喂给 estimator 之前,用检查函数确认数据符合目标 scitype/mtype,必要时用转换函数在不同 mtype 之间迁移(例如把PanelNp3D转为PanelPdMultiIndex)。由于 mtype 清单会根据环境 soft dependency 动态过滤(见 sktime/datatypes/_registry.py 的generate_mtype_cls_list),转换目标 mtype 前应确认对应后端库已安装。
五、实践要点总结
- 模型持久化:默认用
save("name")生成name.zip(内含_metadata+_obj),跨环境传输可改用serialization_format="cloudpickle"(需安装 cloudpickle);恢复统一用sktime.base.load,它只认公开接口,不要依赖归档内部成员。 - 数据落盘:写
~/.ts文件时严格遵守“注释(#)、标识符(@,@data最后)、数据”的三块顺序,实例换行分隔、维度冒号分隔、响应变量置于实例末尾;分类用@classlabel,回归用@targetlabel,并按@univariate/@equallength的取值决定是否追加@dimension/@serieslength。 - 内存类型选择:先判断数据属于哪种 scitype(单条、扁平集合、层级集合、还是非时序表格),再选择合适的 mtype;mtype 的能力标签(如是否支持多变量、不等距、缺失值)决定了容器能否表达你的数据。
- 安全底线:pickle 与 cloudpickle 反序列化可执行任意代码,只从可信来源加载模型归档或内存容器。
【免费下载链接】sktimeA unified framework for machine learning with time series项目地址: https://gitcode.com/GitHub_Trending/sk/sktime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考