-
Notifications
You must be signed in to change notification settings - Fork 42
/
Copy pathtime_series.py
76 lines (69 loc) · 3.66 KB
/
time_series.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""A time series cross validation class.
It implements a block cross validation. We can't simply shuffle the
observations z_t since we would lose both causality and the correlation
structure that follows natural order. To formalize the issue, let us first
define formally the predictor that we will produce in the RAMP. Let the time
series be z_1, ..., z_T and the let target to predict at time t be
y_t. The target is usually a function of the future
z_{t+1}, ..., but it can be anything else. We want to learn a function
that predicts y from the past, that is y_hat_t = f(z_1, ..., z_t) = f(Z_t),
where Z_t = (z_1, ..., z_t) is the past. Now, the sample (Z_t, y_t) is a
regular (although none iid) sample from the point of view of shuffling, so we
can train on {Z_t, y_t}_{t in I_train} and test on
(Z_t, y_t)_{t in I_test}, where I_train
and I_test are arbitrary but disjunct train and test index
sets, respectively (typically produced by sklearn's `ShuffleSplit`). Using
shuffling would nevertheless allow a second order leakage from training
points to test points that precede them, by, e.g., aggregating the training
set and adding the aggregate back as a feature. To avoid this, we use
block-CV: on each fold, all t in I_test are larger than all
t in I_train. We also make sure that all training and test
sets contain consecutive observations, so recurrent nets and similar
predictors, which rely on this, may be trained.
The block cv can be parameterized by `cv_block_size`, `n_cv`, and `period`.
`cv_block_size` is the relative size of the validation block. If it is, e.g.,
0.3, it means that all folds have a common block which is (approximately) 0.7
times the length of the sequence. `n_cv` is the number of the folds. `period`
can be used when we want that the length of each training block is a multiple
of an integer (e.g., the number of months in a year), assuring that each block
starts at the same phase (e.g., the beginning of the year).
A classical simple validation (a single training block followed by a test
block) can be done by setting `n_cv` to 1.
"""
# Author: Balazs Kegl <[email protected]>
# License: BSD 3 clause
import numpy as np
class TimeSeries(object):
def __init__(self, n_cv=8, cv_block_size=0.5, period=12, unit='',
unit_2=None):
self.n_cv = n_cv
self.cv_block_size = cv_block_size
self.period = period
self.unit = unit
self.unit_2 = unit_2
def get_cv(self, X, y):
n = len(y)
block_size = int(
n * self.cv_block_size / self.n_cv / self.period) * self.period
n_common_block = n - block_size * self.n_cv
n_validation = n - n_common_block
if self.unit_2 is None:
print('length of common block: {} {}s'.format(
n_common_block, self.unit))
print('length of validation block: {} {}s'.format(
n_validation, self.unit))
print('length of each cv block: {} {}s'.format(
block_size, self.unit))
else:
print('length of common block: {} {}s = {} {}s'.format(
n_common_block, self.unit, n_common_block / self.period,
self.unit_2))
print('length of validation block: {} {}s = {} {}s'.format(
n_validation, self.unit, n_validation / self.period,
self. unit_2))
print('length of each cv block: {} {}s = {} {}s'.format(
block_size, self.unit, block_size / self.period, self.unit_2))
for i in range(self.n_cv):
train_is = np.arange(0, n_common_block + i * block_size)
test_is = np.arange(n_common_block + i * block_size, n)
yield (train_is, test_is)