binder

使用aeon预处理时间序列

在应用机器学习算法之前,通常需要对时间序列数据进行预处理。这样算法可以处理这些特性,或者可以使用aeon转换器将时间序列集合预处理为标准格式。本笔记本演示了三种常见的使用场景

  1. 重新缩放时间序列

  2. 调整时间序列大小

  3. 处理缺失值

重新缩放时间序列

不同级别的尺度和方差可能会掩盖时间序列中的判别模式。这对于基于距离的方法尤其如此。通常会将时间序列重新缩放为零均值和单位方差。例如,UnitTest数据集中的数据是[Chinatown数据集] (https://timeseriesclassification.com/description.php?Dataset=Chinatown)的一个子集。这些是墨尔本唐人街的行人计数。时间序列具有不同的均值。

[3]:
import numpy as np

from aeon.datasets import load_unit_test

X, y = load_unit_test(split="Train")
np.mean(X, axis=-1)[0:5]
[3]:
array([[561.875     ],
       [604.95833333],
       [629.16666667],
       [801.45833333],
       [540.75      ]])
[4]:
np.std(X, axis=-1)[0:5]
[4]:
array([[428.95224215],
       [483.35481095],
       [514.90052977],
       [629.00847763],
       [389.10059218]])

我们可以通过三种方式重新调整时间序列:

  1. 归一化:减去均值并除以标准差,使所有序列具有零均值和单位方差。

[5]:
from aeon.transformations.collection import Normalizer

normalizer = Normalizer()
X2 = normalizer.fit_transform(X)
np.round(np.mean(X2, axis=-1)[0:5], 6)
[5]:
array([[ 0.],
       [-0.],
       [ 0.],
       [-0.],
       [-0.]])
[6]:
np.round(np.std(X2, axis=-1)[0:5], 6)
[6]:
array([[1.],
       [1.],
       [1.],
       [1.],
       [1.]])
  1. 重新居中:重新居中涉及减去每个序列的平均值

[7]:
from aeon.transformations.collection import Centerer

c = Centerer()
X3 = c.fit_transform(X)
np.round(np.mean(X3, axis=-1)[0:5], 6)
[7]:
array([[ 0.],
       [-0.],
       [ 0.],
       [-0.],
       [ 0.]])
  1. 最小-最大:将数据缩放到0到1之间

[8]:
from aeon.transformations.collection import MinMaxScaler

minmax = MinMaxScaler()
X4 = minmax.fit_transform(X)
np.round(np.min(X4, axis=-1)[0:5], 6)
[8]:
array([[0.],
       [0.],
       [0.],
       [0.],
       [0.]])
[9]:
np.round(np.max(X4, axis=-1)[0:5], 6)
[9]:
array([[1.],
       [1.],
       [1.],
       [1.],
       [1.]])

虽然没有最佳的方法来做到这一点,但对于像这样的计数,更常见的是使用MinMax缩放,这样数据仍然可以解释为比例。

调整时间序列大小

假设我们有一组长度不同的时间序列,即时间点的数量不同。目前,aeon的大多数集合估计器(分类、聚类或回归)要求时间序列长度相等。那些能够处理不等长时间序列的估计器被标记为“capability:unequal”。

[10]:
from aeon.classification.convolution_based import RocketClassifier
from aeon.datasets import load_basic_motions, load_japanese_vowels, load_plaid
from aeon.utils.validation import has_missing, is_equal_length, is_univariate

如果你想使用一个无法内部处理缺失值的估计器,一个选择是将不等长的序列转换为等长的。这可以通过填充、截断或通过拟合函数和重采样来调整大小来实现。

不等长或等长集合时间序列

如果一个集合包含所有等长的序列,它将数据存储在一个形状为(n_cases, n_channels, n_timepoints)的3D numpy数组中。如果长度不等,则存储在2D numpy数组的列表中:

[11]:
# Equal length multivariate data
bm_X, bm_y = load_basic_motions()
X = bm_X
print(f"{type(X)}, {X.shape}")
print(
    f"univariate = {is_univariate(X)}, has missing ={has_missing(X)}, equal "
    f"length = {is_equal_length(X)}"
)
<class 'numpy.ndarray'>, (80, 6, 100)
univariate = False, has missing =False, equal length = True
[12]:
# Unequal length univariate data
plaid_X, plaid_y = load_plaid()
X = plaid_X
print(type(plaid_X), "\n", plaid_X[0].shape, "\n", plaid_X[10].shape)
print(
    f"univariate = {is_univariate(X)}, has missing ={has_missing(X)}, equal "
    f"length = {is_equal_length(X)}"
)
<class 'list'>
 (1, 500)
 (1, 300)
univariate = True, has missing =False, equal length = False
[13]:
vowels_X, vowels_y = load_japanese_vowels(split="train")
X = vowels_X
print(
    f"univariate = {is_univariate(X)}, has missing ={has_missing(X)}, equal "
    f"length = {is_equal_length(X)}"
)
univariate = False, has missing =False, equal length = False
[14]:
series_lengths = [array.shape[1] for array in plaid_X]

# Find the minimum and maximum of the second dimensions
min_length = min(series_lengths)
max_length = max(series_lengths)
print(" Min length = ", min_length, " max length = ", max_length)
 Min length =  100  max length =  1344

对于不等长问题,有两种基本策略

  1. 使用一个可以在内部处理缺失值的估计器

  2. 将数据转换为等长,例如通过截断或填充序列

带有标签 "capability:unequal_length": True 的估计器具有处理不等长序列的能力。对于分类、回归和聚类,当前的列表是

[15]:
from aeon.utils.discovery import all_estimators

all_estimators(
    type_filter=["classifier", "regressor", "clusterer"],
    tag_filter={"capability:unequal_length": True},
)
[15]:
[('Catch22Classifier',
  aeon.classification.feature_based._catch22.Catch22Classifier),
 ('Catch22Clusterer', aeon.clustering.feature_based._catch22.Catch22Clusterer),
 ('Catch22Regressor', aeon.regression.feature_based._catch22.Catch22Regressor),
 ('DummyClassifier', aeon.classification.dummy.DummyClassifier),
 ('DummyRegressor', aeon.regression._dummy.DummyRegressor),
 ('ElasticEnsemble',
  aeon.classification.distance_based._elastic_ensemble.ElasticEnsemble),
 ('KNeighborsTimeSeriesClassifier',
  aeon.classification.distance_based._time_series_neighbors.KNeighborsTimeSeriesClassifier),
 ('KNeighborsTimeSeriesRegressor',
  aeon.regression.distance_based._time_series_neighbors.KNeighborsTimeSeriesRegressor),
 ('RDSTClassifier', aeon.classification.shapelet_based._rdst.RDSTClassifier),
 ('RDSTRegressor', aeon.regression.shapelet_based._rdst.RDSTRegressor)]

你可以传递这些估计器不等长的序列,它们将按预期工作。

[16]:
from aeon.classification.distance_based import KNeighborsTimeSeriesClassifier

knn = KNeighborsTimeSeriesClassifier()
model = knn.fit(plaid_X, plaid_y)

如果时间序列长度不等,集合估计器如果没有处理这一特性的能力,将会引发错误。如果你想使用它们,你需要预处理数据使其长度相等。

[17]:
rc = RocketClassifier()
try:
    rc.fit(plaid_X, plaid_y)
except ValueError as e:
    print(f"ValueError: {e}")
ValueError: Data seen by instance of RocketClassifier has unequal length series, but RocketClassifier cannot handle unequal length series.

填充、截断或调整大小。

我们可以进行填充、截断或调整大小。默认情况下,填充会添加零以使所有序列达到最长序列的长度,截断会移除超出最短序列长度的所有值,而调整大小则会拉伸或缩小序列。

[18]:
from aeon.transformations.collection import Padder, Resizer, Truncator

pad = Padder()
truncate = Truncator()
resize = Resizer(length=600)
X2 = pad.fit_transform(plaid_X)
X3 = truncate.fit_transform(plaid_X)
X4 = resize.fit_transform(plaid_X)
print(X2.shape, "\n", X3.shape, "\n", X4.shape)
(1074, 1, 1344)
 (1074, 1, 100)
 (1074, 1, 600)
[19]:
import matplotlib.pyplot as plt

plt.title("Before and after padding: PLAID first case (shifted up for unpadded)")
plt.plot(plaid_X[0][0] + 10)
plt.plot(X2[0][0])
[19]:
[<matplotlib.lines.Line2D at 0x20069acd130>]
../../_images/examples_transformations_preprocessing_30_1.png

你可以将这些转换器放入管道中以应用于训练/测试分割

[20]:
from sklearn.metrics import accuracy_score

# Unequal length univariate data
from aeon.pipeline import make_pipeline

train_X, train_y = load_plaid(split="Train")
test_X, test_y = load_plaid(split="Test")
steps = [truncate, rc]
pipe = make_pipeline(steps)
pipe.fit(train_X, train_y)
preds = pipe.predict(test_X)
accuracy_score(train_y, preds)
[20]:
0.813780260707635

缺失值

缺失值在numpy数组中由NaN表示。您可以使用实用函数测试任何aeon数据结构是否包含缺失值。

[21]:
X = np.random.random(size=(10, 2, 200))
has_missing(X)
[21]:
False
[22]:
X[5][0][55] = np.NAN
has_missing(X)
[22]:
True

处理缺失值有多种策略。这些包括:

  1. 使用一个内部处理缺失值的估计器。对于一些算法(如决策树)来说,内部处理缺失值相当容易,通常是在离散化后将其作为一个独特的序列值使用。我们目前还没有很多具备这种能力的估计器。能够内部处理缺失值的估计器会被标记为"capability:missing_values": True

[23]:
from aeon.utils.discovery import all_estimators

all_estimators(
    tag_filter={"capability:missing_values": True},
)
[23]:
[('BORF', aeon.transformations.collection.dictionary_based._borf.BORF),
 ('CollectionId',
  aeon.transformations.collection.compose._identity.CollectionId),
 ('DummyClassifier', aeon.classification.dummy.DummyClassifier),
 ('DummyRegressor', aeon.regression._dummy.DummyRegressor),
 ('RandomSegmenter', aeon.segmentation._random.RandomSegmenter),
 ('STRAY', aeon.anomaly_detection._stray.STRAY),
 ('SimpleImputer', aeon.transformations.collection._impute.SimpleImputer)]
  1. 移除缺失数据的系列:如果训练集规模较大,缺失数据的系列数量较少,且这些系列的缺失值比例较高,这通常是可取的。

我们还没有为此提供一个转换器,但自己实现起来很容易。

  1. 从序列中插补缺失值:通常通过时间序列中的其他值来估计缺失值。如果训练集的大小较小且缺失值的比例较低,这通常是可取的。你可以使用转换器SimpleImputer来实现这一点。这会独立地插补每个序列和每个通道。例如,对于具有两个通道的序列[[NaN,1.0,2.0,3.0],[-1.0,-2.0,-3.0,-4.0]]的均值插补将是 [[2.0,1.0,2.0,3.0],[-1.0,-2.0,-3.0,-4.0]]

[26]:
from aeon.transformations.collection import SimpleImputer

imput = SimpleImputer(strategy="mean")
X2 = imput.fit_transform(X)
has_missing(X2)
[26]:
False
[27]:
imp2 = SimpleImputer(strategy="median")
X3 = imp2.fit_transform(X)
has_missing(X3)
[27]:
False
[28]:
imp3 = SimpleImputer(strategy="constant", fill_value=0)
X4 = imp3.fit_transform(X)
has_missing(X4)
[28]:
False
[ ]:


使用nbsphinx生成。Jupyter笔记本可以在这里找到。