-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsplit.py
More file actions
106 lines (91 loc) · 3.2 KB
/
split.py
File metadata and controls
106 lines (91 loc) · 3.2 KB
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"""
Test and train splits for seasonal data
"""
import numpy
from typing import Callable
from datetime import datetime, timedelta
from iris.time import PartialDateTime
from iris.coords import DimCoord, AuxCoord
from .numbers import _FP_TYPE
from .numbers import _TIME_UNIT, _TIME_UNIT_ONLY_STRING, _TIME_UNIT_START
from .numbers import _DAY, _YEAR, _YEAR_START, _YEAR_END
from .useful import rnd
_SEASON_SIZE = timedelta(days=45)
def tconvert(t: datetime | _FP_TYPE) -> datetime:
"""
Allow datetime objects through, but convert
floating point times to datetime objects using
the standard coordinate settings.
"""
if type(t) == _FP_TYPE:
return _TIME_UNIT_START + t * _TIME_UNIT
else:
return t
def in_partial_range(rng: list[PartialDateTime],
t: datetime | _FP_TYPE) -> bool:
"""
Check if time is on or between two partials.
"""
tt = tconvert(t)
return (rng[0] <= tt) and (tt <= rng[1])
def in_many_partial_ranges(rngs: list[list[PartialDateTime]],
t: datetime | _FP_TYPE) -> bool:
"""
Check if t is on or between any of the pairs of partials.
"""
tt = tconvert(t)
for r in rngs:
if in_partial_range(r, tt):
return True
return False
def same_season(t0: datetime | _FP_TYPE) -> Callable[[datetime | _FP_TYPE], bool]:
"""
Construct a lambda that checks if a datetime is in the
same season as t0
"""
tt0 = tconvert(t0)
start = tt0 - _SEASON_SIZE
end = tt0 + _SEASON_SIZE
if start.year == tt0.year - 1:
wrap = True
elif end.year == tt0.year + 1:
wrap = True
else:
wrap = False
intervals = []
if wrap:
partial_start = _YEAR_START
partial_end = PartialDateTime(month=end.month,
day=end.day)
intervals.append([partial_start, partial_end])
partial_start = PartialDateTime(month=start.month,
day=start.day)
partial_end = _YEAR_END
intervals.append([partial_start, partial_end])
else:
partial_start = PartialDateTime(month=start.month,
day=start.day)
partial_end = PartialDateTime(month=end.month,
day=end.day)
intervals.append([partial_start, partial_end])
return lambda t: in_many_partial_ranges(intervals, t)
def season_mask(t0: datetime | _FP_TYPE) -> Callable[[DimCoord], list[bool]]:
"""
Construct a mask compatible with the given DimCoord.
"""
tt0 = tconvert(t0)
fn = same_season(tt0)
return lambda crd: numpy.logical_not(numpy.array(list(map(fn, crd.points))))
def count_seasons(t0: datetime | _FP_TYPE) -> Callable[[_FP_TYPE], int]:
"""
Count the seasons between two times
"""
tt0 = tconvert(t0)
return lambda t: int(rnd(((_TIME_UNIT_START + t * _TIME_UNIT) - tt0) / _YEAR))
def season_group(t0: datetime | _FP_TYPE) -> Callable[[DimCoord], list[int]]:
"""
Assign season number as group to each day in in the time coordinate
"""
tt0 = tconvert(t0)
fn = count_seasons(tt0)
return lambda crd: numpy.array(list(map(fn, crd.points)))