Files
pytorch-ts/pts/dataset/process.py
T
2019-10-11 13:59:36 +02:00

113 lines
3.9 KiB
Python

from functools import lru_cache
from typing import Callable, List, cast
import numpy as np
import pandas as pd
from pandas.tseries.offsets import Tick
from .common import DataEntry
class ProcessStartField:
def __init__(self, name: str, freq: str) -> None:
self.name = name
self.freq = freq
def __call__(self, data: DataEntry) -> DataEntry:
try:
value = ProcessStartField.process(data[self.name], self.freq)
except (TypeError, ValueError) as e:
raise Exception(
f'Error "{e}" occurred when reading field "{self.name}"')
data[self.name] = value
return data
@staticmethod
@lru_cache(maxsize=10000)
def process(string: str, freq: str) -> pd.Timestamp:
timestamp = pd.Timestamp(string, freq=freq)
# operate on time information (days, hours, minute, second)
if isinstance(timestamp.freq, Tick):
return pd.Timestamp(timestamp.floor(timestamp.freq),
timestamp.freq)
# since we are only interested in the data piece, we normalize the
# time information
timestamp = timestamp.replace(hour=0,
minute=0,
second=0,
microsecond=0,
nanosecond=0)
return timestamp.freq.rollforward(timestamp)
class ProcessTimeSeriesField:
def __init__(self, name, is_required: bool, is_static: bool,
is_cat: bool) -> None:
self.name = name
self.is_required = is_required
self.req_ndim = 1 if is_static else 2
self.dtype = np.int32 if is_cat else np.float32
def __call__(self, data: DataEntry) -> DataEntry:
value = data.get(self.name, None)
if value is not None:
value = np.asarray(value, dtype=self.dtype)
dim_diff = self.req_ndim - value.ndim
if dim_diff == 1:
value = np.expand_dims(a=value, axis=0)
elif dim_diff != 0:
raise Exception(
f"JSON array has bad shape - expected {self.req_ndim} dimensions got {dim_diff}"
)
data[self.name] = value
return data
elif not self.is_required:
return data
else:
raise Exception(
f"JSON object is missing a required field `{self.name}`")
class ProcessDataEntry:
def __init__(self, freq: str, one_dim_target: bool = True) -> None:
self.trans = cast(
List[Callable[[DataEntry], DataEntry]],
[
ProcessStartField("start", freq=freq),
ProcessTimeSeriesField("target",
is_required=True,
is_cat=False,
is_static=one_dim_target),
ProcessTimeSeriesField("feat_dynamic_cat",
is_required=False,
is_cat=True,
is_static=False),
ProcessTimeSeriesField(
"feat_dynamic_real",
is_required=False,
is_cat=False,
is_static=False,
),
ProcessTimeSeriesField("feat_static_cat",
is_required=False,
is_cat=True,
is_static=True),
ProcessTimeSeriesField("feat_static_real",
is_required=False,
is_cat=False,
is_static=True),
],
)
def __call__(self, data: DataEntry) -> DataEntry:
for t in self.trans:
data = t(data)
return data