Files
pytorch-ts/test/dataset/test_stat.py
T
2020-01-14 20:14:37 +01:00

341 lines
11 KiB
Python

# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# A copy of the License is located at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# or in the "license" file accompanying this file. This file is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.
# Standard library imports
import unittest
from typing import cast
# Third-party imports
import numpy as np
import pandas as pd
# First-party imports
from pts.dataset import DataEntry, Dataset
from pts.dataset.stat import (
DatasetStatistics,
ScaleHistogram,
calculate_dataset_statistics,
)
def make_dummy_dynamic_feat(target, num_features) -> np.ndarray:
# gives dummy dynamic_feat constructed from the target
return np.vstack([target * (i + 1) for i in range(num_features)])
# default values for TimeSeries field
start = pd.Timestamp("1985-01-02", freq="1D")
target = np.random.randint(0, 10, 20)
fsc = [0, 1]
fsr = [0.1, 0.2]
def make_time_series(
start=start,
target=target,
feat_static_cat=fsc,
feat_static_real=fsr,
num_feat_dynamic_cat=1,
num_feat_dynamic_real=1,
) -> DataEntry:
feat_dynamic_cat = (
make_dummy_dynamic_feat(target, num_feat_dynamic_cat).astype("int64")
if num_feat_dynamic_cat > 0
else None
)
feat_dynamic_real = (
make_dummy_dynamic_feat(target, num_feat_dynamic_real).astype("float")
if num_feat_dynamic_real > 0
else None
)
data = {
"start": start,
"target": target,
"feat_static_cat": feat_static_cat,
"feat_static_real": feat_static_real,
"feat_dynamic_cat": feat_dynamic_cat,
"feat_dynamic_real": feat_dynamic_real,
}
return data
def ts(
start,
target,
feat_static_cat=None,
feat_static_real=None,
feat_dynamic_cat=None,
feat_dynamic_real=None,
) -> DataEntry:
d = {"start": start, "target": target}
if feat_static_cat is not None:
d["feat_static_cat"] = feat_static_cat
if feat_static_real is not None:
d["feat_static_real"] = feat_static_real
if feat_dynamic_cat is not None:
d["feat_dynamic_cat"] = feat_dynamic_cat
if feat_dynamic_real is not None:
d["feat_dynamic_real"] = feat_dynamic_real
return d
class DatasetStatisticsTest(unittest.TestCase):
def test_dataset_statistics(self) -> None:
n = 2
T = 10
# use integers to avoid float conversion that can fail comparison
np.random.seed(0)
targets = np.random.randint(0, 10, (n, T))
scale_histogram = ScaleHistogram()
for i in range(n):
scale_histogram.add(targets[i, :])
scale_histogram.add([])
expected = DatasetStatistics(
integer_dataset=True,
num_time_series=n + 1,
num_time_observations=targets.size,
mean_target_length=T * 2 / 3,
min_target=targets.min(),
mean_target=targets.mean(),
mean_abs_target=targets.mean(),
max_target=targets.max(),
feat_static_real=[{0.1}, {0.2, 0.3}],
feat_static_cat=[{1}, {2, 3}],
num_feat_dynamic_real=2,
num_feat_dynamic_cat=2,
num_missing_values=0,
scale_histogram=scale_histogram,
)
# FIXME: the cast below is a hack to make mypy happy
timeseries = cast(
Dataset,
[
make_time_series(
target=targets[0, :],
feat_static_cat=[1, 2],
feat_static_real=[0.1, 0.2],
num_feat_dynamic_cat=2,
num_feat_dynamic_real=2,
),
make_time_series(
target=targets[1, :],
feat_static_cat=[1, 3],
feat_static_real=[0.1, 0.3],
num_feat_dynamic_cat=2,
num_feat_dynamic_real=2,
),
make_time_series(
target=np.array([]),
feat_static_cat=[1, 3],
feat_static_real=[0.1, 0.3],
num_feat_dynamic_cat=2,
num_feat_dynamic_real=2,
),
],
)
found = calculate_dataset_statistics(timeseries)
assert expected == found
def test_dataset_histogram(self) -> None:
# generates 2 ** N - 1 timeseries with constant increasing values
N = 6
n = 2 ** N - 1
T = 5
targets = np.ones((n, T))
for i in range(0, n):
targets[i, :] = targets[i, :] * i
# FIXME: the cast below is a hack to make mypy happy
timeseries = cast(
Dataset, [make_time_series(target=targets[i, :]) for i in range(n)]
)
found = calculate_dataset_statistics(timeseries)
hist = found.scale_histogram.bin_counts
for i in range(0, N):
assert i in hist
assert hist[i] == 2 ** i
class DatasetStatisticsExceptions(unittest.TestCase):
def test_dataset_statistics_exceptions(self) -> None:
def check_error_message(expected_regex, dataset) -> None:
with self.assertRaisesRegex(Exception, expected_regex):
calculate_dataset_statistics(dataset)
check_error_message("Time series dataset is empty!", [])
check_error_message(
"Only empty time series found in the dataset!",
[make_time_series(target=np.random.randint(0, 10, 0))],
)
# infinite target
# check_error_message(
# "Target values have to be finite (e.g., not inf, -inf, "
# "or None) and cannot exceed single precision floating "
# "point range.",
# [make_time_series(target=np.full(20, np.inf))]
# )
# different number of feat_dynamic_{cat, real}
check_error_message(
"Found instances with different number of features in "
"feat_dynamic_cat, found one with 2 and another with 1.",
[
make_time_series(num_feat_dynamic_cat=2),
make_time_series(num_feat_dynamic_cat=1),
],
)
check_error_message(
"Found instances with different number of features in "
"feat_dynamic_cat, found one with 0 and another with 1.",
[
make_time_series(num_feat_dynamic_cat=0),
make_time_series(num_feat_dynamic_cat=1),
],
)
check_error_message(
"feat_dynamic_cat was found for some instances but not others.",
[
make_time_series(num_feat_dynamic_cat=1),
make_time_series(num_feat_dynamic_cat=0),
],
)
check_error_message(
"Found instances with different number of features in "
"feat_dynamic_real, found one with 2 and another with 1.",
[
make_time_series(num_feat_dynamic_real=2),
make_time_series(num_feat_dynamic_real=1),
],
)
check_error_message(
"Found instances with different number of features in "
"feat_dynamic_real, found one with 0 and another with 1.",
[
make_time_series(num_feat_dynamic_real=0),
make_time_series(num_feat_dynamic_real=1),
],
)
check_error_message(
"feat_dynamic_real was found for some instances but not others.",
[
make_time_series(num_feat_dynamic_real=1),
make_time_series(num_feat_dynamic_real=0),
],
)
# infinite feat_dynamic_{cat,real}
inf_dynamic_feat = np.full((2, len(target)), np.inf)
check_error_message(
"Features values have to be finite and cannot exceed single "
"precision floating point range.",
[
ts(
start,
target,
feat_dynamic_cat=inf_dynamic_feat,
feat_static_cat=[0, 1],
)
],
)
check_error_message(
"Features values have to be finite and cannot exceed single "
"precision floating point range.",
[
ts(
start,
target,
feat_dynamic_real=inf_dynamic_feat,
feat_static_cat=[0, 1],
)
],
)
# feat_dynamic_{cat, real} different length from target
check_error_message(
"Each feature in feat_dynamic_cat has to have the same length as the "
"target. Found an instance with feat_dynamic_cat of length 1 and a "
"target of length 20.",
[
ts(
start=start,
target=target,
feat_static_cat=[0, 1],
feat_dynamic_cat=np.ones((1, 1)),
)
],
)
check_error_message(
"Each feature in feat_dynamic_real has to have the same length as the "
"target. Found an instance with feat_dynamic_real of length 1 and a "
"target of length 20.",
[
ts(
start=start,
target=target,
feat_static_cat=[0, 1],
feat_dynamic_real=np.ones((1, 1)),
)
],
)
# feat_static_{cat, real} different length
check_error_message(
"Not all feat_static_cat vectors have the same length 2 != 1.",
[
ts(start=start, target=target, feat_static_cat=[0, 1]),
ts(start=start, target=target, feat_static_cat=[1]),
],
)
check_error_message(
"Not all feat_static_real vectors have the same length 2 != 1.",
[
ts(start=start, target=target, feat_static_real=[0, 1]),
ts(start=start, target=target, feat_static_real=[1]),
],
)
calculate_dataset_statistics(
# FIXME: the cast below is a hack to make mypy happy
cast(
Dataset,
[
make_time_series(num_feat_dynamic_cat=2),
make_time_series(num_feat_dynamic_cat=2),
],
)
)
calculate_dataset_statistics(
# FIXME: the cast below is a hack to make mypy happy
cast(
Dataset,
[
make_time_series(num_feat_dynamic_cat=0),
make_time_series(num_feat_dynamic_cat=0),
],
)
)