mindspore/predict/utils/timefeatures.py

133 lines
4.6 KiB
Python

import numpy as np
import pandas as pd
from pandas.tseries import offsets
from pandas.tseries.frequencies import to_offset
import mindspore as ms
class TimeFeature:
def __init__(self):
pass
def __call__(self, index: pd.DatetimeIndex) -> ms.Tensor:
pass
def __repr__(self):
return self.__class__.__name__ + "()"
class SecondOfMinute(TimeFeature):
"""Second of minute encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> ms.Tensor:
return ms.Tensor(index.second / 59.0 - 0.5)
class MinuteOfHour(TimeFeature):
"""Minute of hour encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> ms.Tensor:
return ms.Tensor(index.minute / 59.0 - 0.5)
class HourOfDay(TimeFeature):
"""Hour of day encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> ms.Tensor:
return ms.Tensor(index.hour / 23.0 - 0.5)
class DayOfWeek(TimeFeature):
"""Day of week encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> ms.Tensor:
return ms.Tensor(index.dayofweek / 6.0 - 0.5)
class DayOfMonth(TimeFeature):
"""Day of month encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> ms.Tensor:
return ms.Tensor((index.day - 1) / 30.0 - 0.5)
class DayOfYear(TimeFeature):
"""Day of year encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> ms.Tensor:
return ms.Tensor((index.dayofyear - 1) / 365.0 - 0.5)
class MonthOfYear(TimeFeature):
"""Month of year encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> ms.Tensor:
return ms.Tensor((index.month - 1) / 11.0 - 0.5)
class WeekOfYear(TimeFeature):
"""Week of year encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> ms.Tensor:
return ms.Tensor((index.isocalendar().week - 1) / 52.0 - 0.5)
def time_features_from_frequency_str(freq_str: str) -> List[TimeFeature]:
"""
Returns a list of time features that will be appropriate for the given frequency string.
Parameters
----------
freq_str
Frequency string of the form [multiple][granularity] such as "12H", "5min", "1D" etc.
"""
features_by_offsets = {
offsets.YearEnd: [],
offsets.QuarterEnd: [MonthOfYear],
offsets.MonthEnd: [MonthOfYear],
offsets.Week: [DayOfMonth, WeekOfYear],
offsets.Day: [DayOfWeek, DayOfMonth, DayOfYear],
offsets.BusinessDay: [DayOfWeek, DayOfMonth, DayOfYear],
offsets.Hour: [HourOfDay, DayOfWeek, DayOfMonth, DayOfYear],
offsets.Minute: [
MinuteOfHour,
HourOfDay,
DayOfWeek,
DayOfMonth,
DayOfYear,
],
offsets.Second: [
SecondOfMinute,
MinuteOfHour,
HourOfDay,
DayOfWeek,
DayOfMonth,
DayOfYear,
],
}
offset = to_offset(freq_str)
for offset_type, feature_classes in features_by_offsets.items():
if isinstance(offset, offset_type):
return [cls() for cls in feature_classes]
supported_freq_msg = f"""
Unsupported frequency {freq_str}
The following frequencies are supported:
Y - yearly
alias: A
M - monthly
W - weekly
D - daily
B - business days
H - hourly
T - minutely
alias: min
S - secondly
"""
raise RuntimeError(supported_freq_msg)
def time_features(dates, timeenc=1, freq='h'):
"""
Extracts time features from a DataFrame based on the given frequency and encoding type.
"""
if timeenc == 0:
dates['month'] = dates.date.apply(lambda row: row.month, 1)
dates['day'] = dates.date.apply(lambda row: row.day, 1)
dates['weekday'] = dates.date.apply(lambda row: row.weekday(), 1)
dates['hour'] = dates.date.apply(lambda row: row.hour, 1)
dates['minute'] = dates.date.apply(lambda row: row.minute, 1)
dates['minute'] = dates.minute.map(lambda x: x // 15)
freq_map = {
'y': [], 'm': ['month'], 'w': ['month'], 'd': ['month', 'day', 'weekday'],
'b': ['month', 'day', 'weekday'], 'h': ['month', 'day', 'weekday', 'hour'],
't': ['month', 'day', 'weekday', 'hour', 'minute'],
}
return dates[freq_map[freq.lower()]].values
if timeenc == 1:
dates = pd.to_datetime(dates.date.values)
return np.vstack([feat(dates) for feat in time_features_from_frequency_str(freq)]).transpose(1, 0)