摘要: 本笔记本介绍了 sktime 的内存数据容器和数据集,以及相关的功能,如内存格式验证、转换和数据集加载。
设置说明: 在binder上,这个笔记本应该可以开箱即用。
要按预期运行此笔记本,请确保在您的 Python 环境中安装了带有基本依赖项要求的 sktime。
要在本地开发版本的 sktime 上运行此笔记本,可以取消注释并运行以下内容,或者对 sktime main 分支的本地克隆执行 pip install -e。
[1]:
# from os import sys
# sys.path.append("..")
内存数据表示和数据加载#
sktime 提供了多个与时间序列相关的学习任务模块。
这些模块使用 sktime 特定的内存(即,python 工作区)表示来处理时间序列及相关对象,最重要的是单个时间序列和时间序列面板。sktime 的内存表示依赖于 pandas 和 numpy,并在这些对象上增加了额外的约定。
sktime 的用户应了解这些表示方式,因为以 sktime 兼容的表示方式呈现数据通常是使用任何 sktime 模块的第一步。
本笔记本介绍了 sktime 中使用的数据类型、相关的功能如转换器和有效性检查器,以及加载和转换的常见工作流程:
第1节 介绍了 sktime 中使用的内存数据容器格式,并附有示例。
第2节 介绍了内存数据容器的有效性检查器和转换功能。
第3节 介绍了加载预定义基准数据集的常见工作流程。
第4节 展示了从表格 csv 格式加载的常见工作流程。
第1节:内存数据容器#
本节提供了 sktime 中用于时间序列及相关对象的数据容器的参考。
从概念上讲,sktime 区分:
数据科学抽象数据类型 - 简称:scitype - 是指数据容器的一种类型,由数据表示的关系和统计属性以及对其的常见操作定义 - 例如,抽象的“时间序列”或抽象的“时间序列面板”,而不指定特定的机器实现,如在Python中。
数据容器的 机器实现类型 - 或者简称为 mtype - 对于定义的 scitype,指定了python类型以及python内存对象的结构和值的约定。例如,一个具体的(数学)时间序列在
sktime中由具体的pandas.DataFrame表示,并遵循pandas.DataFrame的某些约定。形式上,这些约定形成了一个特定的mtype,即表示(抽象)“时间序列”scitype的一种方式。
在 sktime 中,相同的科学类型可以通过多种 mtype 实现。例如,sktime 允许用户将时间序列指定为 pandas.DataFrame、pandas.Series 或 numpy.ndarray。这些是不同的 mtype,它们是相同科学类型“时间序列”的可接受表示。此外,并非所有 mtype 的元数据都同样丰富——例如,pandas.DataFrame 可以存储列名,而在 numpy.ndarray 中则不可能。
在 sktime 中,scitypes 和 mtypes 通过字符串进行编码,以便于引用。
本节介绍以下 scitypes 的 mtypes:
第1.1节:时间序列 - "Series" 科学类型#
sktime 中时间序列的主要表示形式有:
"pd.DataFrame"- 一个单变量或多变量的pandas.DataFrame,行 = 时间点,列 = 变量"pd.Series"- 一个(单变量)``pandas.Series``,其条目对应于不同的时间点"np.ndarray"- 一个二维numpy.ndarray,行 = 时间点,列 = 变量
pandas 对象必须具有以下 pandas 索引类型之一:Int64Index、RangeIndex、DatetimeIndex、PeriodIndex;如果为 DatetimeIndex,则必须设置 freq 属性。
numpy.ndarray 二维数组被解释为在行上具有 RangeIndex,并且通常等同于使用 pandas.DataFrame 构造函数进行默认强制转换后获得的 pandas.DataFrame。
[2]:
# import to retrieve examples
from sktime.datatypes import get_examples
第1.1.1节:时间序列 - "pd.DataFrame" mtype#
在 "pd.DataFrame" mtype 中,时间序列由内存中的容器 obj: pandas.DataFrame 表示,如下所示。
结构约定:
obj.index必须是单调的,并且是Int64Index、RangeIndex、DatetimeIndex、PeriodIndex之一。变量:
obj的列对应于不同的变量变量名:列名
obj.columns时间点:
obj的行对应于不同、明确的时间点时间索引:
obj.index被解释为时间索引。功能:可以表示多变量序列;可以表示不等间距序列
pd.DataFrame 表示中的单变量序列示例。单个变量名为 "a",并在四个时间点 0, 1, 2, 3 进行了观测。
[3]:
get_examples(mtype="pd.DataFrame", as_scitype="Series")[0]
[3]:
| a | |
|---|---|
| 0 | 1.0 |
| 1 | 4.0 |
| 2 | 0.5 |
| 3 | -3.0 |
"pd.DataFrame" 表示中的二元时间序列示例。该序列有两个变量,分别命名为 "a" 和 "b"。两者都在相同的时间点 0、1、2、3 进行了观测。
[4]:
get_examples(mtype="pd.DataFrame", as_scitype="Series")[1]
[4]:
| a | b | |
|---|---|---|
| 0 | 1.0 | 3.000000 |
| 1 | 4.0 | 7.000000 |
| 2 | 0.5 | 2.000000 |
| 3 | -3.0 | -0.428571 |
1.1.2 节:时间序列 - "pd.Series" mtype#
在 "pd.Series" mtype 中,时间序列由内存中的容器 obj: pandas.Series 表示,如下所示。
结构约定:
obj.index必须是单调的,并且是Int64Index、RangeIndex、DatetimeIndex、PeriodIndex之一。变量:这里有一个单一的变量,对应于
obj的值。只能表示单变量序列。变量名称:默认情况下,没有列名。如果需要,可以提供一个变量名称,如
obj.name。时间点:
obj的条目对应于不同、明确的时间点时间索引:
obj.index被解释为时间索引。功能:无法表示多变量序列;可以表示不等间距序列
pd.Series mtype 表示中的单变量序列示例。单个变量的名称为 "a",并在四个时间点 0、1、2、3 进行了观测。
[5]:
get_examples(mtype="pd.Series", as_scitype="Series")[0]
[5]:
0 1.0
1 4.0
2 0.5
3 -3.0
Name: a, dtype: float64
第1.1.3节:时间序列 - "np.ndarray" mtype#
在 "np.ndarray" mtype 中,时间序列由内存中的容器 obj: np.ndarray 表示,如下所示。
结构约定:
obj必须是二维的,即obj.shape的长度必须为 2。对于单变量时间序列也是如此。变量:变量对应于
obj的列。变量名称:
"np.ndarray"mtype 不能表示变量名称。时间点:
obj的行对应于不同、独特的时间点。时间索引:时间索引是隐式的,并且是约定俗成的。第
i行(对于整数i)被解释为在时间点i的观测。功能:可以表示多变量序列;不能表示不等间距序列
"np.ndarray" mtype 表示中的单变量序列示例。有一个单一(未命名)的变量,它在四个时间点 0、1、2、3 被观测。
[6]:
get_examples(mtype="np.ndarray", as_scitype="Series")[0]
[6]:
array([[ 1. ],
[ 4. ],
[ 0.5],
[-3. ]])
np.ndarray mtype 表示中的双变量序列示例。有两个(未命名)变量,它们都在四个时间点 0、1、2、3 被观测到。
[7]:
get_examples(mtype="np.ndarray", as_scitype="Series")[1]
[7]:
array([[ 1. , 3. ],
[ 4. , 7. ],
[ 0.5 , 2. ],
[-3. , -0.42857143]])
第1.2节:时间序列面板 - "Panel" 科学类型#
sktime 中时间序列面板的主要表示形式有:
"pd-multiindex"- 一个pandas.DataFrame,具有行多重索引(实例,时间),列 = 变量"numpy3D"- 一个三维的np.ndarray,其中轴 0 = 实例,轴 1 = 变量,轴 2 = 时间点"df-list"- 一个pandas.DataFrame的列表,列表索引 = 实例,数据框行 = 时间点,数据框列 = 变量
这些表示形式在 sktime 中被视为主要表示形式,并且是内部计算的核心。
在 sktime 中还有其他一些次要的时间序列面板表示:
"nested_univ"- 一个pandas.DataFrame,单元格中包含pandas.Series。数据框行 = 实例,数据框列 = 变量,序列轴 = 时间点"numpyflat"- 一个二维np.ndarray,行表示实例,列由 (变量, 时间点) 对索引。此格式仅用于转换,不能从其他格式转换(因为变量和时间点的数量可能不明确)。"pd-wide"- 一个pandas.DataFrame的宽格式:具有列多重索引(变量,时间点),行 = 实例;对于单变量时间序列,可以省略“变量”索引"pd-long"- 一个pandas.DataFrame的长格式:包含列instances、timepoints、variable、value;value中的条目由 (instances、timepoints、variable) 中的值的元组索引。
次要表示形式目前在代码中尚未完全整合,并且下面不再进一步讨论。欢迎贡献。
第1.2.1节:时间序列面板 - "pd-multiindex" mtype#
在 "pd-multiindex" mtype 中,时间序列面板由内存中的容器 obj: pandas.DataFrame 表示,如下所示。
结构约定:
obj.index必须是一个类型为(Index, t)的成对多索引,其中t是Int64Index、RangeIndex、DatetimeIndex、PeriodIndex之一且为单调的。obj.index必须有两个层级(可以命名或不命名)。实例索引:
obj.index中对的第一个元素(第0级值)被解释为实例索引,我们在下面称之为“实例索引”。实例:具有相同“实例索引”值的行对应于同一个实例;具有不同“实例索引”值的行对应于不同的实例。
时间索引:
obj.index中对的第二个元素(第一级值)被解释为时间索引,我们在下面称之为“时间索引”。时间点:具有相同“时间索引”值的
obj行对应于同一时间点;具有不同“时间索引”值的obj行对应于不同的时间点。变量:
obj的列对应于不同的变量变量名:列名
obj.columns功能:可以表示多变量序列的面板;可以表示不等间隔的序列;可以表示不等支持的序列面板;不能表示具有不同变量集的序列面板。
"pd-multiindex" mtype 表示中多元时间序列面板的示例。该面板包含三个多元时间序列,实例索引为 0、1、2。所有序列都有两个名为 "var_0"、"var_1" 的变量。所有序列都在三个时间点 0、1、2 被观测。
[8]:
get_examples(mtype="pd-multiindex", as_scitype="Panel")[0]
[8]:
| var_0 | var_1 | ||
|---|---|---|---|
| instances | timepoints | ||
| 0 | 0 | 1 | 4 |
| 1 | 2 | 5 | |
| 2 | 3 | 6 | |
| 1 | 0 | 1 | 4 |
| 1 | 2 | 55 | |
| 2 | 3 | 6 | |
| 2 | 0 | 1 | 42 |
| 1 | 2 | 5 | |
| 2 | 3 | 6 |
第1.2.2节:时间序列面板 - "numpy3D" mtype#
在 "numpy3D" mtype 中,时间序列面板由内存中的容器 obj: np.ndarray 表示,如下所示。
结构约定:
obj必须是三维的,即obj.shape的长度必须为 3。实例: 实例对应于
obj的轴 0 元素。实例索引:实例索引是隐含的,并且是约定俗成的。轴 0 的第
i个元素(对于整数i)被解释为表示观察实例i。变量:变量对应于
obj的轴 1 元素。变量名称:
"numpy3D"mtype 不能表示变量名称。时间点:时间点对应于
obj的轴 2 元素。时间索引:时间索引是隐含的,并且是约定俗成的。轴 2 的第
i个元素(对于整数i)被解释为在时间点i的观测值。功能:可以表示多变量序列的面板;不能表示不等间隔的序列;不能表示支持度不等的面板序列;不能表示具有不同变量集的面板序列。
"numpy3D" mtype 表示中多元时间序列面板的示例。该面板包含三个多元时间序列,实例索引为 0、1、2。所有序列都有两个变量(未命名)。所有序列在时间点 0、1、2 处被观测。
[9]:
get_examples(mtype="numpy3D", as_scitype="Panel")[0]
[9]:
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 1, 2, 3],
[ 4, 55, 6]],
[[ 1, 2, 3],
[42, 5, 6]]])
第1.2.3节:时间序列面板 - "df-list" mtype#
在 "df-list" mtype 中,时间序列面板由内存中的容器 obj: List[pandas.DataFrame] 表示,如下所示。
结构约定:
obj必须是一个pandas.DataFrames的列表。obj的单个列表元素必须遵循"pd.DataFrame"的 mtype 约定,用于"Series"的 scitype。实例: 实例对应于
obj的不同列表元素。实例索引:实例的实例索引是它在
obj中的列表索引位置。也就是说,obj[i]处的数据对应于索引为i的实例的观测值。时间点:
obj[i]的行对应于不同的、独特的时间点,在这些时间点上观察到实例i。时间索引:
obj[i].index被解释为实例i的时间索引。变量:
obj[i]的列对应于实例i可用的不同变量。变量名称: 列名
obj[i].columns是实例i可用的变量名称。功能:可以表示多变量序列的面板;可以表示不等间隔的序列;可以表示不等支持的序列面板;可以表示具有不同变量集的序列面板。
"df-list" mtype 表示中多元时间序列面板的示例。该面板包含三个多元时间序列,实例索引为 0、1、2。所有序列都有两个名为 "var_0"、"var_1" 的变量。所有序列在时间点 0、1、2 被观测。
[10]:
get_examples(mtype="df-list", as_scitype="Panel")[0]
[10]:
[ var_0 var_1
0 1 4
1 2 5
2 3 6,
var_0 var_1
0 1 4
1 2 55
2 3 6,
var_0 var_1
0 1 42
1 2 5
2 3 6]
第1.3节:层次时间序列 - "Hierarchical" 科学类型#
目前,sktime 中只有一种层次时间序列的表示方法:
"pd_multiindex_hier"- 一个pandas.DataFrame,具有行多重索引,最后一级解释为时间,其他级为层次结构,列 = 变量
分层时间序列 - "pd_multiindex_hier" mtype#
结构约定:
obj.index必须是一个类型为(Index, ..., Index, t)的 3 级或更多级的多重索引,其中t是Int64Index、RangeIndex、DatetimeIndex、PeriodIndex之一,并且是单调的。我们称最后一个索引为“类时间”索引。层次级别:具有相同非时间索引值的行对应于相同的层次单元;具有不同非时间索引组合的行对应于不同的层次单元。
层次结构:在
obj.index中,非时间类索引被解释为标识层次结构的索引。时间索引:
obj.index中元组的最后一个元素被解释为时间索引。时间点:具有相同
"timepoints"索引的obj行对应于同一时间点;具有不同"timepoints"索引的obj行对应于不同的时间点。变量:
obj的列对应于不同的变量变量名:列名
obj.columns功能:可以表示层次序列;可以表示不等间距序列;可以表示不等支持的层次序列;不能表示具有不同变量集的层次序列。
[11]:
get_examples(mtype="pd_multiindex_hier", as_scitype="Hierarchical")[0]
[11]:
| var_0 | var_1 | |||
|---|---|---|---|---|
| foo | bar | timepoints | ||
| a | 0 | 0 | 1 | 4 |
| 1 | 2 | 5 | ||
| 2 | 3 | 6 | ||
| 1 | 0 | 1 | 4 | |
| 1 | 2 | 55 | ||
| 2 | 3 | 6 | ||
| 2 | 0 | 1 | 42 | |
| 1 | 2 | 5 | ||
| 2 | 3 | 6 | ||
| b | 0 | 0 | 1 | 4 |
| 1 | 2 | 5 | ||
| 2 | 3 | 6 | ||
| 1 | 0 | 1 | 4 | |
| 1 | 2 | 55 | ||
| 2 | 3 | 6 | ||
| 2 | 0 | 1 | 42 | |
| 1 | 2 | 5 | ||
| 2 | 3 | 6 |
第2节:有效性检查和mtype转换#
sktime 的 datatypes 模块为用户提供了以下通用功能:
检查内存容器是否符合 mtype 约定,并提供信息丰富的错误消息,帮助将数据移动到正确的格式
将不同的 mtypes 相互转换,针对给定的 scitype
在本节中,将介绍此功能及其预期使用的工作流程。
第2.1节:准备数据,检查内存容器的有效性#
sktime 的 datatypes 模块为用户提供了便捷的功能,用于使用 check_is_mtype 和 check_raise 函数检查其内存数据容器的有效性。这两个函数都提供了通用的有效性检查功能,check_is_mtype 返回元数据和潜在问题作为返回参数,而 check_raise 则在容器不符合给定 mtype 时直接生成信息丰富的错误消息。
推荐的笔记本工作流程,以确保给定的数据容器符合 sktime mtype 规范,如下所示:
将数据加载到内存数据容器中
识别
scitype,例如,这应该是一个时间序列 (Series) 还是一个时间序列面板 (Panel)选择目标
mtype``(参见第1节列表),并尝试手动重新格式化数据以符合 ``mtype规范(如果数据尚未符合)。在数据容器上运行
check_raise,以检查其是否符合mtype和scitype如果引发错误,重复步骤3和4,直到不再引发错误。
第2.1.1节:有效性检查,示例1(简单错误)#
假设我们有一个表示单变量时间序列的 numpy.ndarray:
[12]:
import numpy as np
y = np.array([1, 6, 3, 7, 2])
检查与 sktime 的兼容性:
(指令: 取消注释并运行代码以查看信息性错误消息)
[13]:
from sktime.datatypes import check_raise
# check_raise(y, mtype="np.ndarray")
这告诉我们,如果使用 np.ndarray mtype,sktime 使用2D numpy数组来表示时间序列。虽然大多数方法提供了自动进行这种强制转换的便利功能,但“正确”的格式应如下所示为2D:
[14]:
check_raise(y.reshape(-1, 1), mtype="np.ndarray")
[14]:
True
要在自己的代码或附加元数据中使用,可以通过 check_is_mtype 函数获取错误消息:
[15]:
from sktime.datatypes import check_is_mtype
check_is_mtype(y, mtype="np.ndarray", return_metadata=True)
[15]:
(True,
None,
{'is_empty': False,
'is_univariate': True,
'n_features': 1,
'feature_names': [0],
'dtypekind_dfip': [<DtypeKind.FLOAT: 2>],
'feature_kind': [<DtypeKind.FLOAT: 2>],
'is_equally_spaced': True,
'has_nans': False,
'mtype': 'np.ndarray',
'scitype': 'Series'})
如果参数通过有效性检查,则会生成元数据:
[16]:
check_is_mtype(y.reshape(-1, 1), mtype="np.ndarray", return_metadata=True)
[16]:
(True,
None,
{'is_empty': False,
'is_univariate': True,
'n_features': 1,
'feature_names': [0],
'dtypekind_dfip': [<DtypeKind.FLOAT: 2>],
'feature_kind': [<DtypeKind.FLOAT: 2>],
'is_equally_spaced': True,
'has_nans': False,
'mtype': 'np.ndarray',
'scitype': 'Series'})
注意:如果 mtype 的名称是模糊的,并且可以指代多种 scitypes,则必须提供额外的参数 scitype。对于任何常见的内存容器,这种情况不应发生,我们提到这一点是为了完整性。
[17]:
check_is_mtype(y, mtype="np.ndarray", scitype="Series")
[17]:
True
第2.1.2节:有效性检查,示例2(不明显的错误)#
假设我们已经将数据转换为多索引面板,即我们希望有一个 Panel 类型为 pd-multiindex。
[18]:
import pandas as pd
cols = ["instances", "time points"] + [f"var_{i}" for i in range(2)]
X = pd.concat(
[
pd.DataFrame([[0, 0, 1, 4], [0, 1, 2, 5], [0, 2, 3, 6]], columns=cols),
pd.DataFrame([[1, 0, 1, 4], [1, 1, 2, 55], [1, 2, 3, 6]], columns=cols),
pd.DataFrame([[2, 0, 1, 42], [2, 1, 2, 5], [2, 2, 3, 6]], columns=cols),
]
).set_index(["instances", "time points"])
是否 X 满足 pd-multiindex 规范并不明显,所以让我们检查一下:
(指令: 取消注释并运行代码以查看信息性错误消息)
[19]:
from sktime.datatypes import check_raise
# check_raise(X, mtype="pd-multiindex")
信息性错误消息指出了多索引列中的一个拼写错误,因此我们这样做:
[20]:
X.index.names = ["instances", "timepoints"]
现在有效性检查通过了:
[21]:
check_raise(X, mtype="pd-multiindex")
[21]:
True
第2.1.3节:推断mtype#
sktime 还提供了推断内存数据容器mtype的功能,这在以下情况下非常有用:一是确定容器符合规范但忘记了确切的字符串,二是在想知道内存容器是否已经是某种支持的、符合规范的格式时。为此,只需指定scitype即可:
[22]:
from sktime.datatypes import mtype
mtype(X, as_scitype="Panel")
[22]:
'pd-multiindex'
第2.2节:mtypes之间的转换#
sktime 的 datatypes 模块还提供了不同 mtypes 之间的非统一转换功能。这对用户和方法开发者都很有用。
convert 函数需要指定转换的源mtype和目标mtype。convert_to 函数只需要指定目标mtype,如果可以推断,它会自动推断输入的mtype。如果输入可能有多种mtype,应使用``convert_to``。
第2.2.1节:简单转换#
示例:将 numpy3D 时间序列面板转换为 pd-multiindex mtype:
[23]:
from sktime.datatypes import get_examples
X = get_examples(mtype="numpy3D", as_scitype="Panel")[0]
X
[23]:
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 1, 2, 3],
[ 4, 55, 6]],
[[ 1, 2, 3],
[42, 5, 6]]])
[24]:
from sktime.datatypes import convert
convert(X, from_type="numpy3D", to_type="pd-multiindex")
[24]:
| var_0 | var_1 | ||
|---|---|---|---|
| instances | timepoints | ||
| 0 | 0 | 1 | 4 |
| 1 | 2 | 5 | |
| 2 | 3 | 6 | |
| 1 | 0 | 1 | 4 |
| 1 | 2 | 55 | |
| 2 | 3 | 6 | |
| 2 | 0 | 1 | 42 |
| 1 | 2 | 5 | |
| 2 | 3 | 6 |
[25]:
from sktime.datatypes import convert_to
convert_to(X, to_type="pd-multiindex")
[25]:
| var_0 | var_1 | ||
|---|---|---|---|
| instances | timepoints | ||
| 0 | 0 | 1 | 4 |
| 1 | 2 | 5 | |
| 2 | 3 | 6 | |
| 1 | 0 | 1 | 4 |
| 1 | 2 | 55 | |
| 2 | 3 | 6 | |
| 2 | 0 | 1 | 42 |
| 1 | 2 | 5 | |
| 2 | 3 | 6 |
第2.2.2节:高级转换功能#
convert_to 也允许指定多种输出类型。to_type 参数可以是一个 mtype 列表。在这种情况下,如果输入的 mtype 在列表中,则输入保持不变;如果输入的 mtype 不在列表中,则它将被转换为列表中的第一个 mtype。
示例:将时间序列面板转换为 "pd-multiindex" 或 "numpy3D"。如果输入是 "numpy3D",则保持不变。如果输入是 "df-list",则转换为 "pd-multiindex"。
[26]:
from sktime.datatypes import get_examples
X = get_examples(mtype="numpy3D", as_scitype="Panel")[0]
X
[26]:
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 1, 2, 3],
[ 4, 55, 6]],
[[ 1, 2, 3],
[42, 5, 6]]])
[27]:
from sktime.datatypes import convert_to
convert_to(X, to_type=["pd-multiindex", "numpy3D"])
[27]:
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 1, 2, 3],
[ 4, 55, 6]],
[[ 1, 2, 3],
[42, 5, 6]]])
[28]:
X = get_examples(mtype="df-list", as_scitype="Panel")[0]
X
[28]:
[ var_0 var_1
0 1 4
1 2 5
2 3 6,
var_0 var_1
0 1 4
1 2 55
2 3 6,
var_0 var_1
0 1 42
1 2 5
2 3 6]
[29]:
convert_to(X, to_type=["pd-multiindex", "numpy3D"])
[29]:
| var_0 | var_1 | ||
|---|---|---|---|
| instances | timepoints | ||
| 0 | 0 | 1 | 4 |
| 1 | 2 | 5 | |
| 2 | 3 | 6 | |
| 1 | 0 | 1 | 4 |
| 1 | 2 | 55 | |
| 2 | 3 | 6 | |
| 2 | 0 | 1 | 42 |
| 1 | 2 | 5 | |
| 2 | 3 | 6 |
第2.2.3节:检查已实现的转换#
目前,转换工作正在进行中,并非所有可能的转换都可用 - 欢迎贡献。要查看为某种科学类型当前实现了哪些转换,请使用 datatypes._convert 模块中的 _conversions_defined 开发者方法。这将生成一个表格,如果从行中的 mtype 转换到列中的 mtypw 已实现,则表格中显示为 “1”。
[30]:
from sktime.datatypes._convert import _conversions_defined
_conversions_defined(scitype="Panel")
[30]:
| df-list | gluonts_ListDataset_panel | gluonts_PandasDataset_panel | nested_univ | numpy3D | numpyflat | pd-long | pd-multiindex | pd-wide | |
|---|---|---|---|---|---|---|---|---|---|
| df-list | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
| gluonts_ListDataset_panel | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
| gluonts_PandasDataset_panel | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
| nested_univ | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| numpy3D | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
| numpyflat | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
| pd-long | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
| pd-multiindex | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
| pd-wide | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
第3节:加载预定义数据集#
sktime 的 datasets 模块允许加载用于测试和基准测试的数据集。这包括:
直接随
sktime提供的示例数据集从常见存储库下载数据集的下载器
通过这种方式检索的所有数据都与 sktime 兼容,可以是内存中或文件格式。
目前,尚未实现对可用数据集的系统化标记和注册表检索 - 对此的贡献将非常受欢迎。
第3.1节:预测数据集#
sktime 的 datasets 模块目前允许加载以下预测示例数据集:
数据集名称 |
加载函数 |
属性 |
|---|---|---|
Box/Jenkins 航空公司数据 |
|
单变量 |
Lynx 销售数据 |
|
单变量 |
洗发水销售数据 |
|
单变量 |
药品福利计划数据 |
|
单变量 |
Longley 美国宏观经济数据 |
|
多变量 |
MTS 消费/收入数据 |
|
多变量 |
sktime 目前没有连接到预测数据仓库的连接器 - 非常欢迎贡献。
预测数据集都是 Series 科学类型,它们可以是单变量或多变量。
单变量数据的加载器没有参数,并且总是以 "pd.Series" mtype 返回数据:
[31]:
from sktime.datasets import load_airline
load_airline()
[31]:
Period
1949-01 112.0
1949-02 118.0
1949-03 132.0
1949-04 129.0
1949-05 121.0
...
1960-08 606.0
1960-09 508.0
1960-10 461.0
1960-11 390.0
1960-12 432.0
Freq: M, Name: Number of airline passengers, Length: 144, dtype: float64
多变量数据的加载器可以通过两种方式调用:
如果没有参数,在这种情况下会返回一个多变量系列的
"pd.DataFrame"mtype:
[32]:
from sktime.datasets import load_longley
load_longley()
[32]:
(Period
1947 60323.0
1948 61122.0
1949 60171.0
1950 61187.0
1951 63221.0
1952 63639.0
1953 64989.0
1954 63761.0
1955 66019.0
1956 67857.0
1957 68169.0
1958 66513.0
1959 68655.0
1960 69564.0
1961 69331.0
1962 70551.0
Freq: A-DEC, Name: TOTEMP, dtype: float64,
GNPDEFL GNP UNEMP ARMED POP
Period
1947 83.0 234289.0 2356.0 1590.0 107608.0
1948 88.5 259426.0 2325.0 1456.0 108632.0
1949 88.2 258054.0 3682.0 1616.0 109773.0
1950 89.5 284599.0 3351.0 1650.0 110929.0
1951 96.2 328975.0 2099.0 3099.0 112075.0
1952 98.1 346999.0 1932.0 3594.0 113270.0
1953 99.0 365385.0 1870.0 3547.0 115094.0
1954 100.0 363112.0 3578.0 3350.0 116219.0
1955 101.2 397469.0 2904.0 3048.0 117388.0
1956 104.6 419180.0 2822.0 2857.0 118734.0
1957 108.4 442769.0 2936.0 2798.0 120445.0
1958 110.8 444546.0 4681.0 2637.0 121950.0
1959 112.6 482704.0 3813.0 2552.0 123366.0
1960 114.2 502601.0 3931.0 2514.0 125368.0
1961 115.7 518173.0 4806.0 2572.0 127852.0
1962 116.9 554894.0 4007.0 2827.0 130081.0)
使用参数
y_name,该参数必须与某一列/变量名称一致,返回一对序列y,X,其中y的mtype为"pd.Series",X的mtype为"pd.DataFrame"- 这对于使用外生变量的单变量预测非常方便。
[33]:
y, X = load_longley(y_name="TOTEMP")
[34]:
y
[34]:
Period
1947 60323.0
1948 61122.0
1949 60171.0
1950 61187.0
1951 63221.0
1952 63639.0
1953 64989.0
1954 63761.0
1955 66019.0
1956 67857.0
1957 68169.0
1958 66513.0
1959 68655.0
1960 69564.0
1961 69331.0
1962 70551.0
Freq: A-DEC, Name: TOTEMP, dtype: float64
[35]:
X
[35]:
| GNPDEFL | GNP | UNEMP | ARMED | POP | |
|---|---|---|---|---|---|
| Period | |||||
| 1947 | 83.0 | 234289.0 | 2356.0 | 1590.0 | 107608.0 |
| 1948 | 88.5 | 259426.0 | 2325.0 | 1456.0 | 108632.0 |
| 1949 | 88.2 | 258054.0 | 3682.0 | 1616.0 | 109773.0 |
| 1950 | 89.5 | 284599.0 | 3351.0 | 1650.0 | 110929.0 |
| 1951 | 96.2 | 328975.0 | 2099.0 | 3099.0 | 112075.0 |
| 1952 | 98.1 | 346999.0 | 1932.0 | 3594.0 | 113270.0 |
| 1953 | 99.0 | 365385.0 | 1870.0 | 3547.0 | 115094.0 |
| 1954 | 100.0 | 363112.0 | 3578.0 | 3350.0 | 116219.0 |
| 1955 | 101.2 | 397469.0 | 2904.0 | 3048.0 | 117388.0 |
| 1956 | 104.6 | 419180.0 | 2822.0 | 2857.0 | 118734.0 |
| 1957 | 108.4 | 442769.0 | 2936.0 | 2798.0 | 120445.0 |
| 1958 | 110.8 | 444546.0 | 4681.0 | 2637.0 | 121950.0 |
| 1959 | 112.6 | 482704.0 | 3813.0 | 2552.0 | 123366.0 |
| 1960 | 114.2 | 502601.0 | 3931.0 | 2514.0 | 125368.0 |
| 1961 | 115.7 | 518173.0 | 4806.0 | 2572.0 | 127852.0 |
| 1962 | 116.9 | 554894.0 | 4007.0 | 2827.0 | 130081.0 |
第3.2节:时间序列分类数据集#
sktime 的 datasets 模块目前允许加载以下时间序列分类示例数据集:
数据集名称 |
加载函数 |
属性 |
|---|---|---|
设备功耗数据 |
|
单变量, 等长/等索引 |
箭头形状数据 |
|
单变量, 等长/等索引 |
枪口运动数据 |
|
单变量, 等长/等索引 |
意大利电力需求数据 |
|
单变量, 等长/等索引 |
日语音节数据 |
|
单变量, 等长/等索引 |
OSUleaf 叶片形状数据 |
|
单变量, 等长/等索引 |
基本运动数据 |
|
多变量, 等长/等索引 |
目前,sktime 中没有直接提供不等长或不等索引时间序列分类示例数据。
sktime 还通过 load_UCR_UEA_dataset 函数提供了对 UCR/UEA 时间序列数据集存档的完整接口。UCR/UEA 存档还包含多变量或不等长/索引(或两者组合)的时间序列分类数据集。
第3.2.2节:sktime 中的时间序列分类数据集#
时间序列分类数据集由一系列 Panel 科学类型的面板时间序列组成,每个时间序列都有一个分类标签。
如果加载器以最少的参数调用,数据将以 "nested_univ" mtype 返回,标签和系列分类在同一个 pd.DataFrame 中。使用 return_X_y=True 参数,数据将分别返回为特征 X 和标签 y,其中 X 是一个 nested_univ mtype 的 Panel,而 y 是一个与 sklearn 兼容的标签 numpy 向量:
[36]:
from sktime.datasets import load_arrow_head
X, y = load_arrow_head(return_X_y=True)
[37]:
X
[37]:
| dim_0 | |
|---|---|
| 0 | 0 -1.963009 1 -1.957825 2 -1.95614... |
| 1 | 0 -1.774571 1 -1.774036 2 -1.77658... |
| 2 | 0 -1.866021 1 -1.841991 2 -1.83502... |
| 3 | 0 -2.073758 1 -2.073301 2 -2.04460... |
| 4 | 0 -1.746255 1 -1.741263 2 -1.72274... |
| ... | ... |
| 206 | 0 -1.625142 1 -1.622988 2 -1.62606... |
| 207 | 0 -1.657757 1 -1.664673 2 -1.63264... |
| 208 | 0 -1.603279 1 -1.587365 2 -1.57740... |
| 209 | 0 -1.739020 1 -1.741534 2 -1.73286... |
| 210 | 0 -1.630727 1 -1.629918 2 -1.62055... |
211 rows × 1 columns
[38]:
y
[38]:
array(['0', '1', '2', '0', '1', '2', '0', '1', '2', '0', '1', '2', '0',
'1', '2', '0', '1', '2', '0', '1', '2', '0', '1', '2', '0', '1',
'2', '0', '1', '2', '0', '1', '2', '0', '1', '2', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
'1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
'2', '2', '2'], dtype='<U1')
面板可以通过 datatypes.convert 或 convert_to``(见上文)从 ``"nested_univ" mtype 转换为其他 mtype 格式:
[39]:
from sktime.datatypes import convert_to
convert_to(X, to_type="pd-multiindex")
[39]:
| dim_0 | ||
|---|---|---|
| 0 | 0 | -1.963009 |
| 1 | -1.957825 | |
| 2 | -1.956145 | |
| 3 | -1.938289 | |
| 4 | -1.896657 | |
| ... | ... | ... |
| 210 | 246 | -1.513637 |
| 247 | -1.550431 | |
| 248 | -1.581576 | |
| 249 | -1.595273 | |
| 250 | -1.620783 |
52961 rows × 1 columns
数据集加载器可以通过 split 参数调用,以获取可重复的训练集和测试集,用于跨研究比较。如果 split="train" ,则检索预定义的训练集;如果 split="test" ,则检索预定义的测试集。
[40]:
X_train, y_train = load_arrow_head(return_X_y=True, split="train")
X_test, y_test = load_arrow_head(return_X_y=True, split="test")
# this retrieves training and test X/y for reproducible use in studies
第3.2.3节:来自UCR/UEA时间序列分类库的时间序列分类数据集#
load_UCR_UEA_dataset 实用程序将从 UCR/UEA 时间序列分类存储库下载数据集,并使其作为内存中的数据集可用,语法与 sktime 原生数据集加载器相同。
数据集通过唯一的字符串标识符进行索引,这些标识符可以在 仓库本身 上查看,或者通过 datasets.tsc_dataset_names 模块中的注册表,按属性查看:
[41]:
from sktime.datasets.tsc_dataset_names import univariate
导入的变量都是包含具有特定属性的数据集的唯一字符串标识符的列表,如下所示:
注册名称 |
单变量/多变量 |
等长/不等长 |
包含/不包含缺失值 |
|---|---|---|---|
|
仅单变量 |
两者都包含 |
两者都包含 |
|
仅多元 |
两者都包含 |
两者都包含 |
|
仅单变量 |
仅等长 |
两者都包含 |
|
仅单变量 |
仅不等长 |
两者都包含 |
|
仅单变量 |
两者都包含 |
仅包含缺失值 |
|
仅多元 |
仅等长 |
两者都包含 |
|
仅多元 |
仅不等长 |
两者都包含 |
使用这些列表进行查找和检索,坦白说,有点不方便 - 我们非常欢迎对 sktime 的贡献,编写诸如 all_estimators 或 all_tags 这样的查找函数,这些函数基于附加到数据集的能力或属性标签。
下面显示了一个示例列表:
[42]:
univariate
[42]:
['ACSF1',
'Adiac',
'AllGestureWiimoteX',
'AllGestureWiimoteY',
'AllGestureWiimoteZ',
'ArrowHead',
'AsphaltObstacles',
'Beef',
'BeetleFly',
'BirdChicken',
'BME',
'Car',
'CBF',
'Chinatown',
'ChlorineConcentration',
'CinCECGTorso',
'Coffee',
'Computers',
'CricketX',
'CricketY',
'CricketZ',
'Crop',
'DiatomSizeReduction',
'DistalPhalanxOutlineCorrect',
'DistalPhalanxOutlineAgeGroup',
'DistalPhalanxTW',
'DodgerLoopDay',
'DodgerLoopGame',
'DodgerLoopWeekend',
'Earthquakes',
'ECG200',
'ECG5000',
'ECGFiveDays',
'ElectricDevices',
'EOGHorizontalSignal',
'EOGVerticalSignal',
'EthanolLevel',
'FaceAll',
'FaceFour',
'FacesUCR',
'FiftyWords',
'Fish',
'FordA',
'FordB',
'FreezerRegularTrain',
'FreezerSmallTrain',
'Fungi',
'GestureMidAirD1',
'GestureMidAirD2',
'GestureMidAirD3',
'GesturePebbleZ1',
'GesturePebbleZ2',
'GunPoint',
'GunPointAgeSpan',
'GunPointMaleVersusFemale',
'GunPointOldVersusYoung',
'Ham',
'HandOutlines',
'Haptics',
'Herring',
'HouseTwenty',
'InlineSkate',
'InsectEPGRegularTrain',
'InsectEPGSmallTrain',
'InsectWingbeatSound',
'ItalyPowerDemand',
'LargeKitchenAppliances',
'Lightning2',
'Lightning7',
'Mallat',
'Meat',
'MedicalImages',
'MelbournePedestrian',
'MiddlePhalanxOutlineCorrect',
'MiddlePhalanxOutlineAgeGroup',
'MiddlePhalanxTW',
'MixedShapesRegularTrain',
'MixedShapesSmallTrain',
'MoteStrain',
'NonInvasiveFetalECGThorax1',
'NonInvasiveFetalECGThorax2',
'OliveOil',
'OSULeaf',
'PhalangesOutlinesCorrect',
'Phoneme',
'PickupGestureWiimoteZ',
'PigAirwayPressure',
'PigArtPressure',
'PigCVP',
'PLAID',
'Plane',
'PowerCons',
'ProximalPhalanxOutlineCorrect',
'ProximalPhalanxOutlineAgeGroup',
'ProximalPhalanxTW',
'RefrigerationDevices',
'Rock',
'ScreenType',
'SemgHandGenderCh2',
'SemgHandMovementCh2',
'SemgHandSubjectCh2',
'ShakeGestureWiimoteZ',
'ShapeletSim',
'ShapesAll',
'SmallKitchenAppliances',
'SmoothSubspace',
'SonyAIBORobotSurface1',
'SonyAIBORobotSurface2',
'StarLightCurves',
'Strawberry',
'SwedishLeaf',
'Symbols',
'SyntheticControl',
'ToeSegmentation1',
'ToeSegmentation2',
'Trace',
'TwoLeadECG',
'TwoPatterns',
'UMD',
'UWaveGestureLibraryAll',
'UWaveGestureLibraryX',
'UWaveGestureLibraryY',
'UWaveGestureLibraryZ',
'Wafer',
'Wine',
'WordSynonyms',
'Worms',
'WormsTwoClass']
加载函数 load_UCR_UEA_dataset 的行为与 sktime 数据加载器完全相同,只是增加了一个参数 name,该参数应设置为 UCR/UEA 数据集的唯一标识字符串之一,例如:
[43]:
from sktime.datasets import load_UCR_UEA_dataset
X, y = load_UCR_UEA_dataset(name="ArrowHead", return_X_y=True)
这将会把数据集下载到一个本地目录(默认情况下:对于本地克隆,是本地仓库中的 datasets/data 目录;对于发布安装,是在本地python环境文件夹中)。要更改该目录,请使用 load_UCR_UEA_dataset 函数的 extract_path 参数指定它。
第4节:从 csv 文件加载数据#
本节展示了如何将一些常见的表格 csv 格式加载到与 sktime 兼容的容器中。
我们将涵盖:
将系列数据集转换为
sktime兼容的容器将面板数据集转换为
sktime兼容的容器
我们假设 所有csv文件都具有某种表格格式。
这意味着 csv 文件包含时间索引的列,或者面板数据实例索引的列,或者处于宽表格格式。
注意:在每一步,我们都可以使用 check_is_mtype 来检查目标格式。读者可能会想要这样做。
第4.1节:简单的时间序列示例#
[44]:
import pandas as pd
df_series = pd.read_csv("../sktime/datasets/data/Airline/Airline.csv")
df_series.head()
[44]:
| Date | Passengers | |
|---|---|---|
| 0 | 1949-01 | 112 |
| 1 | 1949-02 | 118 |
| 2 | 1949-03 | 132 |
| 3 | 1949-04 | 129 |
| 4 | 1949-05 | 121 |
[45]:
df_series = df_series.set_index(
"Date"
).squeeze() # replace "Period" with the column name of the time index
df_series.index = pd.DatetimeIndex(df_series.index)
[46]:
mtype(df_series, as_scitype="Series")
[46]:
'pd.Series'
第4.2节:简单的面板数据示例#
[47]:
# mimicking a scenario where we already have a csv file in the right format
from sktime.datasets import load_arrow_head
df_panel = load_arrow_head(split="TRAIN", return_type="pd-multiindex")[0].reset_index()
# imagine this is the result of df_panel = pd.read_csv
[48]:
df_panel.head()
[48]:
| level_0 | level_1 | dim_0 | |
|---|---|---|---|
| 0 | 0 | 0 | -1.963009 |
| 1 | 0 | 1 | -1.957825 |
| 2 | 0 | 2 | -1.956145 |
| 3 | 0 | 3 | -1.938289 |
| 4 | 0 | 4 | -1.896657 |
这与 pd-multiindex 格式类似,因此我们尝试将其移动到该格式。
我们需要改变的一件事是将实例/时间设置为索引:
[49]:
df_panel = df_panel.set_index(["level_0", "level_1"])
type(df_panel.index)
[49]:
pandas.core.indexes.multi.MultiIndex
这现在被 sktime 识别为 pd-multiindex 格式
[50]:
mtype(df_panel, as_scitype="Panel")
# in general:
# replace "timepoints" with the time index column name
# replace "level_0" with the higher level column name of your file
[50]:
'pd-multiindex'
第4.3节:困难的面板数据示例#
我们现在尝试从文件中加载面板数据,其中格式有些挑战性。
在下面的文件中:
分隔符不是默认的(逗号)而是制表符。为此,我们设置 ``sep= ``
文件中没有标题
没有实例索引,所以我们需要添加它
索引与
sktime不同 - 第一列具有可变索引,而列是时间索引
这些或类似的挑战在面板数据的 csv 文件中很常见,因此我们在下面展示了如何解决这些问题。
建议将数据转换为纯 pandas 或 numpy 格式。在下面的例子中,我们将数据转换为 pd-multiindex 格式。
[51]:
# 1, 2 - dealing with the separator and header
import pandas as pd
df_panel = pd.read_csv(
"../sktime/datasets/data/ArrowHead/ArrowHead_TRAIN.tsv",
sep="\t",
header=None,
)
df_panel.head()
[51]:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | -1.963009 | -1.957825 | -1.956145 | -1.938289 | -1.896657 | -1.869857 | -1.838705 | -1.812289 | -1.736433 | ... | -1.583857 | -1.655329 | -1.719153 | -1.750881 | -1.796273 | -1.841345 | -1.884289 | -1.905393 | -1.923905 | -1.909153 |
| 1 | 1 | -1.774571 | -1.774036 | -1.776586 | -1.730749 | -1.696268 | -1.657377 | -1.636227 | -1.609807 | -1.543439 | ... | -1.471688 | -1.484666 | -1.539972 | -1.590150 | -1.635663 | -1.639989 | -1.678683 | -1.729227 | -1.775670 | -1.789324 |
| 2 | 2 | -1.866021 | -1.841991 | -1.835025 | -1.811902 | -1.764390 | -1.707687 | -1.648280 | -1.582643 | -1.531502 | ... | -1.584132 | -1.652337 | -1.684565 | -1.743972 | -1.799117 | -1.829069 | -1.875828 | -1.862512 | -1.863368 | -1.846493 |
| 3 | 0 | -2.073758 | -2.073301 | -2.044607 | -2.038346 | -1.959043 | -1.874494 | -1.805619 | -1.731043 | -1.712653 | ... | -1.678942 | -1.743732 | -1.819801 | -1.858136 | -1.886146 | -1.951247 | -2.012927 | -2.026963 | -2.073405 | -2.075292 |
| 4 | 1 | -1.746255 | -1.741263 | -1.722741 | -1.698640 | -1.677223 | -1.630356 | -1.579440 | -1.551225 | -1.473980 | ... | -1.547111 | -1.607101 | -1.635137 | -1.686346 | -1.691274 | -1.716886 | -1.740726 | -1.743442 | -1.762729 | -1.763428 |
5 rows × 252 columns
[52]:
# 2 - adding an instance index manually
import numpy as np
df_panel["instance"] = np.repeat(range(len(df_panel) // 3), 3)
现在我们通过解透视将数据转换为长格式:
[53]:
# 3 - add instance index
df_panel.columns = ["var"] + [f"value{i}" for i in range(251)] + ["instance"]
# 4 - move to long format
df_panel = pd.wide_to_long(df_panel, "value", i=["var", "instance"], j="time")
[54]:
df_panel.head()
[54]:
| value | |||
|---|---|---|---|
| var | instance | time | |
| 0 | 0 | 0 | -1.963009 |
| 1 | -1.957825 | ||
| 2 | -1.956145 | ||
| 3 | -1.938289 | ||
| 4 | -1.896657 |
现在“var”索引在行中,但它应该在列中:
[55]:
# 3 - move variable index to columns
df_panel = df_panel.reset_index("var")
df_panel = df_panel.pivot(columns="var", values="value")
[56]:
df_panel.head()
[56]:
| var | 0 | 1 | 2 | |
|---|---|---|---|---|
| instance | time | |||
| 0 | 0 | -1.963009 | -1.774571 | -1.866021 |
| 1 | -1.957825 | -1.774036 | -1.841991 | |
| 2 | -1.956145 | -1.776586 | -1.835025 | |
| 3 | -1.938289 | -1.730749 | -1.811902 | |
| 4 | -1.896657 | -1.696268 | -1.764390 |
现在这是 pd-multiindex 格式:
[57]:
mtype(df_panel, as_scitype="Panel")
[57]:
'pd-multiindex'
另一种方法是删除索引,并在 numpy 中使用重塑。
[57]:
使用 nbsphinx 生成。Jupyter 笔记本可以在这里找到。