Skip to content

Commit 14fb2ab

Browse files
authored
Merge pull request #266 from unit8co/develop
Develop
2 parents df72876 + 14461dc commit 14fb2ab

File tree

83 files changed

+20282
-5293
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+20282
-5293
lines changed

.github/workflows/develop.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
runs-on: ubuntu-latest
7272
strategy:
7373
matrix:
74-
example-name: [FFT-examples.ipynb, RNN-examples.ipynb, darts-intro.ipynb, multivariate-examples.ipynb, data-processing.ipynb]
74+
example-name: [04-FFT-examples.ipynb, 05-RNN-examples.ipynb, 01-darts-intro.ipynb, 03-data-processing.ipynb, 02-multi-time-series-and-covariates.ipynb]
7575
steps:
7676
- name: "1. Clone repository"
7777
uses: actions/checkout@v2

.github/workflows/merge.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
runs-on: ubuntu-latest
4444
strategy:
4545
matrix:
46-
example-name: [FFT-examples.ipynb, TCN-examples.ipynb, RNN-examples.ipynb, darts-intro.ipynb, multivariate-examples.ipynb, Transformer-examples.ipynb, data-processing.ipynb]
46+
example-name: [01-darts-intro.ipynb, 02-multi-time-series-and-covariates.ipynb, 03-data-processing.ipynb, 04-FFT-examples.ipynb, 05-RNN-examples.ipynb]
4747
steps:
4848
- name: "1. Clone repository"
4949
uses: actions/checkout@v2

CHANGELOG.md

+37-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,43 @@ Darts is still in an early development phase and we cannot always guarantee back
44

55
## [Unreleased](https://github.com/unit8co/darts/tree/develop)
66

7-
[Full Changelog](https://github.com/unit8co/darts/compare/0.5.0...develop)
7+
[Full Changelog](https://github.com/unit8co/darts/compare/0.6.0...develop)
8+
9+
10+
## [0.6.0](https://github.com/unit8co/darts/tree/0.6.0) (2021-02-02)
11+
12+
[Full Changelog](https://github.com/unit8co/darts/compare/0.5.0...0.6.0)
13+
### For users of the library:
14+
**Added:**
15+
- `Pipeline.invertible()` a getter which returns whether the pipeline is invertible or not.
16+
- `TimeSeries.to_json()` and `TimeSeries.from_json()` methods to convert `TimeSeries` to/from a `JSON` string.
17+
- New base class `GlobalForecastingModel` for all models supporting training on multiple time series, as well
18+
as covariates. All PyTorch models are now `GlobalForecastingModel`s.
19+
- As a consequence of the above, the `fit()` function of PyTorch models (all neural networks) can optionally be called
20+
with a sequence of time series (instead of a single time series).
21+
- Similarly, the `predict()` function of these models also accepts a specification of which series should be forecasted
22+
- A new `TrainingDataset` base class.
23+
- Some implementations of `TrainingDataset` containing some slicing logic for the training of neural networks on
24+
several time series.
25+
- A new `TimeSeriesInferenceDataset` base class.
26+
- An implementation `SimpleInferenceDataset` of `TimeSeriesInferenceDataset`.
27+
- All PyTorch models have a new `fit_from_dataset()` method which allows to directly fit the model from a specified
28+
`TrainingDataset` instance (instead of using a default instance when going via the `fit()` method).
29+
- A new explanatory notebooks for global models:
30+
https://github.com/unit8co/darts/blob/master/examples/02-multi-time-series-and-covariates.ipynb
31+
32+
**Changed:**
33+
🔴 removed the arguments `training_series` and `target_series` in `ForecastingModel`s. Please consult
34+
the API documentation of forecasting models to see the new signatures.
35+
🔴 removed `UnivariateForecastingModel` and `MultivariateForecastingModel` base classes. This distinction does
36+
not exist anymore. Instead, now some models are "global" (can be trained on multiple series) or "local" (they cannot).
37+
All implementations of `GlobalForecastingModel`s support multivariate time series out of the box, except N-BEATS.
38+
- Improved the documentation and README.
39+
- Re-ordered the example notebooks to improve the flow of examples.
40+
41+
**Fixed:**
42+
- Many small bug fixes.
43+
- Unit test speedup by about 15x.
844

945
## [0.5.0](https://github.com/unit8co/darts/tree/0.5.0) (2020-11-09)
1046

README.md

+13-10
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,25 @@ It contains a variety of models, from classics such as ARIMA to neural networks.
1515
The models can all be used in the same way, using `fit()` and `predict()` functions,
1616
similar to scikit-learn. The library also makes it easy to backtest models,
1717
and combine the predictions of several models and external regressors. Darts supports both
18-
univariate and multivariate time series and models.
18+
univariate and multivariate time series and models, and the neural networks can be trained
19+
multiple time series.
20+
21+
## Documentation
22+
* [Examples & Tutorials](https://unit8co.github.io/darts/examples.html)
23+
* [API Documentation](https://unit8co.github.io/darts/generated_api/darts.html)
24+
25+
##### High Level Introductions
26+
* [Introductory Blog Post](https://medium.com/unit8-machine-learning-publication/darts-time-series-made-easy-in-python-5ac2947a8878)
27+
* [Introductory Video](https://www.youtube.com/watch?v=Sx-uI-PypmU&t=8s&ab_channel=Unit8)
1928

2029
## Install
2130

2231
We recommend to first setup a clean python environment for your project with at least python 3.6 using your favorite tool ([conda](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html "conda-env"), [venv](https://docs.python.org/3/library/venv.html), [virtualenv](https://virtualenv.pypa.io/en/latest/) with or without [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/)).
2332

24-
### Quick Install
25-
26-
Once your environment is setup you can install darts using the pip package:
33+
Once your environment is setup you can install darts using pip:
2734

2835
pip install 'u8darts[all]'
2936

30-
### Step-by-step Install
31-
3237
For more detailed install instructions you can refer to our installation guide at the end of this page.
3338

3439
## Example Usage
@@ -70,11 +75,9 @@ plt.xlabel('Year')
7075
<img src="https://github.com/unit8co/darts/raw/develop/static/images/example.png" alt="darts forecast example" />
7176
</div>
7277

73-
We invite you to go over the example notebooks in the `examples` directory.
74-
75-
## Documentation
78+
We invite you to go over the example and tutorial notebooks in
79+
the [examples](https://github.com/unit8co/darts/tree/master/examples) directory.
7680

77-
The documentation of the API and models is available [here](https://unit8co.github.io/darts/).
7881

7982
## Features
8083

darts/__init__.py

-20
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,8 @@
55

66
from .timeseries import TimeSeries
77
import matplotlib as mpl
8-
import matplotlib.pyplot as plt
98
from matplotlib import cycler
109

11-
# Enums
12-
from enum import Enum
13-
14-
15-
class SeasonalityMode(Enum):
16-
MULTIPLICATIVE = 'multiplicative'
17-
ADDITIVE = 'additive'
18-
NONE = None
19-
20-
21-
class TrendMode(Enum):
22-
LINEAR = 'linear'
23-
EXPONENTIAL = 'exponential'
24-
25-
26-
class ModelMode(Enum):
27-
MULTIPLICATIVE = 'multiplicative'
28-
ADDITIVE = 'additive'
29-
3010

3111
__version__ = '0.5.0'
3212

darts/dataprocessing/pipeline.py

+12
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,18 @@ def inverse_transform(self, data: TimeSeries) -> TimeSeries:
125125
data = transformer.inverse_transform(data)
126126
return data
127127

128+
def invertible(self) -> bool:
129+
"""
130+
Returns whether the pipeline is invertible or not.
131+
A pipeline is invertible if all transformers in the pipeline are themselves invertible.
132+
133+
Returns
134+
-------
135+
bool
136+
True if the pipeline is invertible, False otherwise
137+
"""
138+
return self._invertible
139+
128140
def __getitem__(self, key: Union[int, slice]) -> 'Pipeline':
129141
"""
130142
Gets subset of Pipeline based either on index or slice with indexes.

darts/metrics/metrics.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ def _get_values_or_raise(series_a: TimeSeries,
6969
return series_a_common.univariate_values(), series_b_common.univariate_values()
7070

7171

72+
def _remove_nan_union(array_a: np.ndarray,
73+
array_b: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
74+
"""
75+
Returnes the two inputs arrays where all elements are deleted that have an index that corresponds to
76+
a NaN value in either of the two input arrays.
77+
"""
78+
79+
isnan_mask = np.logical_or(np.isnan(array_a), np.isnan(array_b))
80+
return np.delete(array_a, isnan_mask), np.delete(array_b, isnan_mask)
81+
82+
7283
@multivariate_support
7384
def mae(series1: TimeSeries,
7485
series2: TimeSeries,
@@ -100,6 +111,7 @@ def mae(series1: TimeSeries,
100111
"""
101112

102113
y1, y2 = _get_values_or_raise(series1, series2, intersect)
114+
y1, y2 = _remove_nan_union(y1, y2)
103115
return np.mean(np.abs(y1 - y2))
104116

105117

@@ -134,6 +146,7 @@ def mse(series1: TimeSeries,
134146
"""
135147

136148
y_true, y_pred = _get_values_or_raise(series1, series2, intersect)
149+
y_true, y_pred = _remove_nan_union(y_true, y_pred)
137150
return np.mean((y_true - y_pred)**2)
138151

139152

@@ -202,6 +215,7 @@ def rmsle(series1: TimeSeries,
202215
"""
203216

204217
y1, y2 = _get_values_or_raise(series1, series2, intersect)
218+
y1, y2 = _remove_nan_union(y1, y2)
205219
y1, y2 = np.log(y1 + 1), np.log(y2 + 1)
206220
return np.sqrt(np.mean((y1 - y2)**2))
207221

@@ -283,6 +297,7 @@ def mape(actual_series: TimeSeries,
283297
"""
284298

285299
y_true, y_hat = _get_values_or_raise(actual_series, pred_series, intersect)
300+
y_true, y_hat = _remove_nan_union(y_true, y_hat)
286301
raise_if_not((y_true != 0).all(), 'The actual series must be strictly positive to compute the MAPE.', logger)
287302
return 100. * np.mean(np.abs((y_true - y_hat) / y_true))
288303

@@ -329,9 +344,10 @@ def smape(actual_series: TimeSeries,
329344
"""
330345

331346
y_true, y_hat = _get_values_or_raise(actual_series, pred_series, intersect)
347+
y_true, y_hat = _remove_nan_union(y_true, y_hat)
332348
raise_if_not(np.logical_or(y_true != 0, y_hat != 0).all(),
333349
'The actual series must be strictly positive to compute the sMAPE.', logger)
334-
return 200. * np.mean(np.abs((y_true - y_hat) / (np.abs(y_true) + np.abs(y_hat))))
350+
return 200. * np.mean(np.abs(y_true - y_hat) / (np.abs(y_true) + np.abs(y_hat)))
335351

336352

337353
@multivariate_support
@@ -431,6 +447,7 @@ def ope(actual_series: TimeSeries,
431447
"""
432448

433449
y_true, y_pred = _get_values_or_raise(actual_series, pred_series, intersect)
450+
y_true, y_pred = _remove_nan_union(y_true, y_pred)
434451
y_true_sum, y_pred_sum = np.sum(y_true), np.sum(y_pred)
435452
raise_if_not(y_true_sum > 0, 'The series of actual value cannot sum to zero when computing OPE.', logger)
436453
return np.abs((y_true_sum - y_pred_sum) / y_true_sum) * 100.
@@ -474,6 +491,7 @@ def marre(actual_series: TimeSeries,
474491
"""
475492

476493
y_true, y_hat = _get_values_or_raise(actual_series, pred_series, intersect)
494+
y_true, y_hat = _remove_nan_union(y_true, y_hat)
477495
raise_if_not(y_true.max() > y_true.min(), 'The difference between the max and min values must be strictly'
478496
'positive to compute the MARRE.', logger)
479497
true_range = y_true.max() - y_true.min()
@@ -512,6 +530,7 @@ def r2_score(series1: TimeSeries,
512530
"""
513531

514532
y1, y2 = _get_values_or_raise(series1, series2, intersect)
533+
y1, y2 = _remove_nan_union(y1, y2)
515534
ss_errors = np.sum((y1 - y2) ** 2)
516535
y_hat = y1.mean()
517536
ss_tot = np.sum((y1 - y_hat) ** 2)

darts/models/arima.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
from statsmodels.tsa.arima_model import ARMA as staARMA
1414
from statsmodels.tsa.arima_model import ARIMA as staARIMA
1515

16-
from .forecasting_model import UnivariateForecastingModel
16+
from .forecasting_model import ForecastingModel
1717
from ..timeseries import TimeSeries
1818
from ..logging import get_logger
1919

2020
logger = get_logger(__name__)
2121

2222

23-
class ARIMA(UnivariateForecastingModel):
23+
class ARIMA(ForecastingModel):
2424
def __init__(self, p: int = 12, d: int = 1, q: int = 0):
2525
""" ARIMA
2626

darts/models/auto_arima.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55

66
from pmdarima import AutoARIMA as PmdAutoARIMA
77

8-
from .forecasting_model import UnivariateForecastingModel
8+
from .forecasting_model import ForecastingModel
99
from ..timeseries import TimeSeries
1010
from ..logging import get_logger
1111

1212
logger = get_logger(__name__)
1313

1414

15-
class AutoARIMA(UnivariateForecastingModel):
15+
class AutoARIMA(ForecastingModel):
1616
def __init__(self, *autoarima_args, **autoarima_kwargs):
1717
""" Auto-ARIMA
1818

darts/models/baselines.py

+7-10
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22
Baseline Models
33
---------------
44
5-
A collection of simple benchmark models.
5+
A collection of simple benchmark models for univariate series.
66
"""
77

88
from typing import List, Optional
99
import numpy as np
1010

11-
from .forecasting_model import ForecastingModel, UnivariateForecastingModel
11+
from .forecasting_model import ForecastingModel
1212
from .ensemble_model import EnsembleModel
1313
from ..timeseries import TimeSeries
1414
from ..logging import raise_if_not, get_logger
1515

1616
logger = get_logger(__name__)
1717

1818

19-
class NaiveMean(UnivariateForecastingModel):
19+
class NaiveMean(ForecastingModel):
2020
def __init__(self):
2121
""" Naive Mean Model
2222
@@ -39,7 +39,7 @@ def predict(self, n: int):
3939
return self._build_forecast_series(forecast)
4040

4141

42-
class NaiveSeasonal(UnivariateForecastingModel):
42+
class NaiveSeasonal(ForecastingModel):
4343
def __init__(self, K: int = 1):
4444
""" Naive Seasonal Model
4545
@@ -74,7 +74,7 @@ def predict(self, n: int):
7474
return self._build_forecast_series(forecast)
7575

7676

77-
class NaiveDrift(UnivariateForecastingModel):
77+
class NaiveDrift(ForecastingModel):
7878
def __init__(self):
7979
""" Naive Drift Model
8080
@@ -112,13 +112,10 @@ def __init__(self, models: List[ForecastingModel]):
112112
super().__init__(models)
113113

114114
def fit(self, training_series: TimeSeries, target_series: Optional[TimeSeries] = None) -> None:
115-
super().fit(training_series, target_series)
115+
super().fit(training_series)
116116

117117
for model in self.models:
118-
if isinstance(model, UnivariateForecastingModel):
119-
model.fit(self.training_series)
120-
else:
121-
model.fit(self.training_series, self.target_series)
118+
model.fit(self.training_series)
122119

123120
def ensemble(self, predictions: List[TimeSeries]):
124121
return sum(predictions) / len(self.models)

darts/models/ensemble_model.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
2-
Ensemble model
3-
--------------
2+
Ensemble Model Base Class
3+
-------------------------
44
"""
55

66
from abc import abstractmethod
@@ -34,13 +34,13 @@ def __init__(self, models: List[ForecastingModel]):
3434
super().__init__()
3535
self.models = models
3636

37-
def fit(self, training_series: TimeSeries, target_series: Optional[TimeSeries] = None) -> None:
37+
def fit(self, training_series: TimeSeries) -> None:
3838
"""
3939
Fits the model on the provided series.
4040
Note that `EnsembleModel.fit()` does NOT call `fit()` on each of its constituent forecasting models.
4141
It is left to classes inheriting from EnsembleModel to do so appropriately when overriding `fit()`
4242
"""
43-
super().fit(training_series, target_series)
43+
super().fit(training_series)
4444

4545
def predict(self, n: int) -> TimeSeries:
4646
super().predict(n)

darts/models/exponential_smoothing.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
from typing import Optional
77
import statsmodels.tsa.holtwinters as hw
88

9-
from .forecasting_model import UnivariateForecastingModel
9+
from .forecasting_model import ForecastingModel
1010
from ..logging import get_logger
1111
from ..timeseries import TimeSeries
12-
from .. import ModelMode
12+
from ..utils.utils import ModelMode
1313

1414
logger = get_logger(__name__)
1515

1616

17-
class ExponentialSmoothing(UnivariateForecastingModel):
17+
class ExponentialSmoothing(ForecastingModel):
1818
def __init__(self,
1919
trend: Optional[ModelMode] = ModelMode.ADDITIVE,
2020
damped: Optional[bool] = False,

0 commit comments

Comments
 (0)