使用aeon预处理时间序列¶
在应用机器学习算法之前,通常需要对时间序列数据进行预处理。这样算法可以处理这些特性,或者可以使用aeon转换器将时间序列集合预处理为标准格式。本笔记本演示了三种常见的使用场景
重新缩放时间序列¶
不同级别的尺度和方差可能会掩盖时间序列中的判别模式。这对于基于距离的方法尤其如此。通常会将时间序列重新缩放为零均值和单位方差。例如,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]])
我们可以通过三种方式重新调整时间序列:
归一化:减去均值并除以标准差,使所有序列具有零均值和单位方差。
[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.]])
重新居中:重新居中涉及减去每个序列的平均值
[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.]])
最小-最大:将数据缩放到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
对于不等长问题,有两种基本策略
使用一个可以在内部处理缺失值的估计器
将数据转换为等长,例如通过截断或填充序列
带有标签 "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>]
你可以将这些转换器放入管道中以应用于训练/测试分割
[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
处理缺失值有多种策略。这些包括:
使用一个内部处理缺失值的估计器。对于一些算法(如决策树)来说,内部处理缺失值相当容易,通常是在离散化后将其作为一个独特的序列值使用。我们目前还没有很多具备这种能力的估计器。能够内部处理缺失值的估计器会被标记为
"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)]
移除缺失数据的系列:如果训练集规模较大,缺失数据的系列数量较少,且这些系列的缺失值比例较高,这通常是可取的。
我们还没有为此提供一个转换器,但自己实现起来很容易。
从序列中插补缺失值:通常通过时间序列中的其他值来估计缺失值。如果训练集的大小较小且缺失值的比例较低,这通常是可取的。你可以使用转换器
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
[ ]: