Skip to content

Commit

Permalink
feat: added error in `TimeSeriesDataset._into_dataloader_with_window_…
Browse files Browse the repository at this point in the history
…predict`

test: added device tests to `TimeSeriesDataset`
  • Loading branch information
Marsmaennchen221 committed May 7, 2024
1 parent 09cf872 commit ca6b8c6
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 8 deletions.
24 changes: 21 additions & 3 deletions src/safeds/data/labeled/containers/_time_series_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from safeds._config import _init_default_device
from safeds._utils import _structural_hash
from safeds.data.tabular.containers import Column, Table
from safeds.exceptions import OutOfBoundsError, ClosedBound

if TYPE_CHECKING:
from collections.abc import Mapping, Sequence
Expand Down Expand Up @@ -194,8 +195,11 @@ def _into_dataloader_with_window(self, window_size: int, forecast_horizon: int,
The size of data batches that should be loaded at one time.
Raises
------
OutOfBoundsError:
If window_size or forecast_horizon is below 1
ValueError:
If the size is smaller or even than forecast_horizon+window_size
If the size is smaller or even than forecast_horizon + window_size
Returns
-------
Expand All @@ -214,9 +218,9 @@ def _into_dataloader_with_window(self, window_size: int, forecast_horizon: int,

size = target_tensor.size(0)
if window_size < 1:
raise ValueError("window_size must be greater than or equal to 1")
raise OutOfBoundsError(actual=window_size, name="window_size", lower_bound=ClosedBound(1))
if forecast_horizon < 1:
raise ValueError("forecast_horizon must be greater than or equal to 1")
raise OutOfBoundsError(actual=forecast_horizon, name="forecast_horizon", lower_bound=ClosedBound(1))
if size <= forecast_horizon + window_size:
raise ValueError("Can not create windows with window size less then forecast horizon + window_size")
# create feature windows and for that features targets lagged by forecast len
Expand Down Expand Up @@ -255,6 +259,13 @@ def _into_dataloader_with_window_predict(
batch_size:
The size of data batches that should be loaded at one time.
Raises
------
OutOfBoundsError:
If window_size or forecast_horizon is below 1
ValueError:
If the size is smaller or even than forecast_horizon + window_size
Returns
-------
result:
Expand All @@ -269,6 +280,13 @@ def _into_dataloader_with_window_predict(
x_s = []

size = target_tensor.size(0)
if window_size < 1:
raise OutOfBoundsError(actual=window_size, name="window_size", lower_bound=ClosedBound(1))
if forecast_horizon < 1:
raise OutOfBoundsError(actual=forecast_horizon, name="forecast_horizon", lower_bound=ClosedBound(1))
if size <= forecast_horizon + window_size:
raise ValueError("Can not create windows with window size less then forecast horizon + window_size")

feature_cols = self.features.to_columns()
for i in range(size - (forecast_horizon + window_size)):
window = target_tensor[i : i + window_size]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
from typing import Type

import pytest
from torch.types import Device

from safeds._config import _get_device
from safeds.data.tabular.containers import Table
from safeds.data.labeled.containers import TimeSeriesDataset
from torch.utils.data import DataLoader

from safeds.exceptions import OutOfBoundsError
from tests.helpers import get_devices, get_devices_ids, configure_test_with_device


@pytest.mark.parametrize(
("data", "target_name", "time_name", "extra_names"),
Expand All @@ -23,14 +31,55 @@
"test",
],
)
@pytest.mark.parametrize("device", get_devices(), ids=get_devices_ids())
def test_should_create_dataloader(
data: dict[str, list[int]],
target_name: str,
time_name: str,
extra_names: list[str] | None,
device: Device,
) -> None:
configure_test_with_device(device)
tabular_dataset = Table.from_dict(data).to_time_series_dataset(target_name, time_name, extra_names)
data_loader = tabular_dataset._into_dataloader_with_window(1, 1, 1)
batch = next(iter(data_loader))
assert batch[0].device == _get_device()
assert batch[1].device == _get_device()
assert isinstance(data_loader, DataLoader)


@pytest.mark.parametrize(
("data", "target_name", "time_name", "extra_names"),
[
(
{
"A": [1, 4, 3],
"B": [2, 5, 4],
"C": [3, 6, 5],
"T": [0, 1, 6],
},
"T",
"B",
[],
),
],
ids=[
"test",
],
)
@pytest.mark.parametrize("device", get_devices(), ids=get_devices_ids())
def test_should_create_dataloader_predict(
data: dict[str, list[int]],
target_name: str,
time_name: str,
extra_names: list[str] | None,
device: Device,
) -> None:
configure_test_with_device(device)
tabular_dataset = Table.from_dict(data).to_time_series_dataset(target_name, time_name, extra_names)
data_loader = tabular_dataset._into_dataloader_with_window_predict(1, 1, 1)
batch = next(iter(data_loader))
assert batch[0].device == _get_device()
assert isinstance(data_loader, DataLoader)


Expand Down Expand Up @@ -62,8 +111,8 @@ def test_should_create_dataloader(
).to_time_series_dataset("T", "B"),
1,
0,
ValueError,
r"forecast_horizon must be greater than or equal to 1",
OutOfBoundsError,
r"forecast_horizon \(=0\) is not inside \[1, \u221e\).",
),
(
Table(
Expand All @@ -76,8 +125,8 @@ def test_should_create_dataloader(
).to_time_series_dataset("T", "B"),
0,
1,
ValueError,
r"window_size must be greater than or equal to 1",
OutOfBoundsError,
r"window_size \(=0\) is not inside \[1, \u221e\).",
),
],
ids=[
Expand All @@ -86,12 +135,81 @@ def test_should_create_dataloader(
"window_size",
],
)
@pytest.mark.parametrize("device", get_devices(), ids=get_devices_ids())
def test_should_create_dataloader_invalid(
data: TimeSeriesDataset,
window_size: int,
forecast_horizon: int,
error_type: ValueError,
error_type: Type[ValueError],
error_msg: str,
device: Device,
) -> None:
configure_test_with_device(device)
with pytest.raises(error_type, match=error_msg):
data._into_dataloader_with_window(window_size=window_size, forecast_horizon=forecast_horizon, batch_size=1)


@pytest.mark.parametrize(
("data", "window_size", "forecast_horizon", "error_type", "error_msg"),
[
(
Table(
{
"A": [1, 4],
"B": [2, 5],
"C": [3, 6],
"T": [0, 1],
}
).to_time_series_dataset("T", "B"),
1,
2,
ValueError,
r"Can not create windows with window size less then forecast horizon \+ window_size",
),
(
Table(
{
"A": [1, 4],
"B": [2, 5],
"C": [3, 6],
"T": [0, 1],
}
).to_time_series_dataset("T", "B"),
1,
0,
OutOfBoundsError,
r"forecast_horizon \(=0\) is not inside \[1, \u221e\).",
),
(
Table(
{
"A": [1, 4],
"B": [2, 5],
"C": [3, 6],
"T": [0, 1],
}
).to_time_series_dataset("T", "B"),
0,
1,
OutOfBoundsError,
r"window_size \(=0\) is not inside \[1, \u221e\).",
),
],
ids=[
"forecast_and_window",
"forecast",
"window_size",
],
)
@pytest.mark.parametrize("device", get_devices(), ids=get_devices_ids())
def test_should_create_dataloader_predict_invalid(
data: TimeSeriesDataset,
window_size: int,
forecast_horizon: int,
error_type: Type[ValueError],
error_msg: str,
device: Device,
) -> None:
configure_test_with_device(device)
with pytest.raises(error_type, match=error_msg):
data._into_dataloader_with_window_predict(window_size=window_size, forecast_horizon=forecast_horizon, batch_size=1)

0 comments on commit ca6b8c6

Please sign in to comment.