pynumerics 2.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pynumerics/__init__.py +73 -0
- pynumerics/_bin/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- pynumerics/_bin/Microsoft.CodeAnalysis.LICENSE.txt +27 -0
- pynumerics/_bin/Microsoft.CodeAnalysis.dll +0 -0
- pynumerics/_bin/Numerics.dll +0 -0
- pynumerics/_bin/pynumerics.runtimeconfig.json +15 -0
- pynumerics/_coverage.py +3825 -0
- pynumerics/_util.py +237 -0
- pynumerics/_version.py +40 -0
- pynumerics/bootstrap.py +593 -0
- pynumerics/callbacks.py +791 -0
- pynumerics/copulas.py +910 -0
- pynumerics/data.py +2233 -0
- pynumerics/differentiation.py +348 -0
- pynumerics/distributions.py +4043 -0
- pynumerics/download.py +623 -0
- pynumerics/enums.py +119 -0
- pynumerics/errors.py +245 -0
- pynumerics/functions.py +1142 -0
- pynumerics/goodness_of_fit.py +699 -0
- pynumerics/graphs.py +556 -0
- pynumerics/hypothesis_tests.py +445 -0
- pynumerics/identity.py +144 -0
- pynumerics/integration.py +677 -0
- pynumerics/linalg.py +1408 -0
- pynumerics/machine_learning.py +1335 -0
- pynumerics/marshal.py +733 -0
- pynumerics/mathematics.py +1053 -0
- pynumerics/mcmc.py +1406 -0
- pynumerics/multivariate.py +1539 -0
- pynumerics/ode.py +156 -0
- pynumerics/optimization.py +1736 -0
- pynumerics/probability.py +966 -0
- pynumerics/py.typed +0 -0
- pynumerics/regional.py +522 -0
- pynumerics/roots.py +274 -0
- pynumerics/runtime.py +384 -0
- pynumerics/sampling.py +1758 -0
- pynumerics/special.py +1079 -0
- pynumerics/statistics.py +1958 -0
- pynumerics/timeseries.py +1326 -0
- pynumerics-2.3.0.dist-info/METADATA +142 -0
- pynumerics-2.3.0.dist-info/RECORD +46 -0
- pynumerics-2.3.0.dist-info/WHEEL +5 -0
- pynumerics-2.3.0.dist-info/licenses/LICENSE +10 -0
- pynumerics-2.3.0.dist-info/top_level.txt +1 -0
pynumerics/timeseries.py
ADDED
|
@@ -0,0 +1,1326 @@
|
|
|
1
|
+
"""Time series from ``Numerics.Data``.
|
|
2
|
+
|
|
3
|
+
:class:`TimeSeries` wraps ``Numerics.Data.TimeSeries``: arithmetic and transforms in place,
|
|
4
|
+
missing-data handling, smoothing, block series (annual, water-year, monthly and quarterly maxima
|
|
5
|
+
and other block functions), peaks over threshold, interval conversion and aggregation,
|
|
6
|
+
bootstrap and k-NN resampling, seasonal decomposition and summary statistics.
|
|
7
|
+
|
|
8
|
+
Dates are NumPy ``datetime64[us]`` values on the Python side and ``System.DateTime`` (kind
|
|
9
|
+
unspecified) on the .NET side; :attr:`TimeSeries.ticks` gives the exact .NET ticks (100 ns).
|
|
10
|
+
The dates and values of a series cross in two bulk copies (``IndexTicksToArray`` and
|
|
11
|
+
``ValuesToArray``), and a regular series is built from one array (the ``(TimeInterval, DateTime,
|
|
12
|
+
IList<double>)`` constructor). A series with explicit dates is built from its ticks and values in
|
|
13
|
+
one call (the ``(TimeInterval, IList<long>, IList<double>)`` constructor), so every value keeps its
|
|
14
|
+
bits, NaN payloads included.
|
|
15
|
+
|
|
16
|
+
Regular series step from the previous date (``AddTimeInterval``), so month, quarter and year steps
|
|
17
|
+
from a day after the 28th settle on the shortest day met: a monthly series from 31 January 2020
|
|
18
|
+
runs 31 January, 29 February, 29 March, as pandas ``DateOffset(months=1)`` applied to the previous
|
|
19
|
+
date. :meth:`TimeSeries.anchored_dates` counts each step from the start instead (31 January,
|
|
20
|
+
29 February, 31 March, as pandas ``DateOffset(months=k)`` applied to the start).
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from collections.abc import Iterator
|
|
26
|
+
from typing import Any
|
|
27
|
+
|
|
28
|
+
import numpy as np
|
|
29
|
+
|
|
30
|
+
from pynumerics._util import _data, net_enum
|
|
31
|
+
from pynumerics.data import parse_xml
|
|
32
|
+
from pynumerics.enums import NetEnum
|
|
33
|
+
from pynumerics.errors import translate_public
|
|
34
|
+
from pynumerics.marshal import (
|
|
35
|
+
to_net_doubles,
|
|
36
|
+
to_net_ints,
|
|
37
|
+
to_net_longs,
|
|
38
|
+
to_numpy_doubles,
|
|
39
|
+
to_numpy_longs,
|
|
40
|
+
to_numpy_matrix,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
_EPOCH_TICKS = 621355968000000000
|
|
44
|
+
"""``DateTime(1970, 1, 1).Ticks``: the .NET tick count of the NumPy epoch."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TimeInterval(NetEnum):
|
|
48
|
+
"""Time steps of a series (``Numerics.Data.TimeInterval``)."""
|
|
49
|
+
|
|
50
|
+
ONE_MINUTE = "OneMinute"
|
|
51
|
+
FIVE_MINUTE = "FiveMinute"
|
|
52
|
+
FIFTEEN_MINUTE = "FifteenMinute"
|
|
53
|
+
THIRTY_MINUTE = "ThirtyMinute"
|
|
54
|
+
ONE_HOUR = "OneHour"
|
|
55
|
+
SIX_HOUR = "SixHour"
|
|
56
|
+
TWELVE_HOUR = "TwelveHour"
|
|
57
|
+
ONE_DAY = "OneDay"
|
|
58
|
+
SEVEN_DAY = "SevenDay"
|
|
59
|
+
ONE_MONTH = "OneMonth"
|
|
60
|
+
ONE_QUARTER = "OneQuarter"
|
|
61
|
+
ONE_YEAR = "OneYear"
|
|
62
|
+
IRREGULAR = "Irregular"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class BlockFunction(NetEnum):
|
|
66
|
+
"""Reductions of a block of values (``Numerics.Data.BlockFunctionType``)."""
|
|
67
|
+
|
|
68
|
+
MINIMUM = "Minimum"
|
|
69
|
+
MAXIMUM = "Maximum"
|
|
70
|
+
AVERAGE = "Average"
|
|
71
|
+
SUM = "Sum"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class SmoothingFunction(NetEnum):
|
|
75
|
+
"""Smoothing applied before blocking (``Numerics.Data.SmoothingFunctionType``)."""
|
|
76
|
+
|
|
77
|
+
DIFFERENCE = "Difference"
|
|
78
|
+
MOVING_AVERAGE = "MovingAverage"
|
|
79
|
+
MOVING_SUM = "MovingSum"
|
|
80
|
+
NONE = "None"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class MathFunction(NetEnum):
|
|
84
|
+
"""Element-wise operations on a series (``Numerics.Data.MathFunctionType``), for user interfaces."""
|
|
85
|
+
|
|
86
|
+
ADD = "Add"
|
|
87
|
+
SUBTRACT = "Subtract"
|
|
88
|
+
MULTIPLY = "Multiply"
|
|
89
|
+
DIVIDE = "Divide"
|
|
90
|
+
LOGARITHM = "Logarithm"
|
|
91
|
+
EXPONENTIATE = "Exponentiate"
|
|
92
|
+
INVERSE = "Inverse"
|
|
93
|
+
REPLACE = "Replace"
|
|
94
|
+
INTERPOLATE = "Interpolate"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class TimeBlockWindow(NetEnum):
|
|
98
|
+
"""Block windows of a series (``Numerics.Data.TimeBlockWindow``), for user interfaces."""
|
|
99
|
+
|
|
100
|
+
CALENDAR_YEAR = "CalendarYear"
|
|
101
|
+
WATER_YEAR = "WaterYear"
|
|
102
|
+
CUSTOM_YEAR = "CustomYear"
|
|
103
|
+
QUARTER = "Quarter"
|
|
104
|
+
MONTH = "Month"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def to_ticks(dates: Any) -> np.ndarray:
|
|
108
|
+
"""Return dates as .NET ``DateTime.Ticks`` (100 ns since 0001-01-01).
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
dates : array_like
|
|
113
|
+
``datetime64`` values, ``datetime.datetime`` objects or ISO 8601 strings (a pandas
|
|
114
|
+
``DatetimeIndex`` included, without a time zone).
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
numpy.ndarray
|
|
119
|
+
The int64 ticks; nanosecond input keeps its 100 ns ticks, coarser input is exact.
|
|
120
|
+
"""
|
|
121
|
+
array = np.asarray(dates)
|
|
122
|
+
if array.dtype.kind != "M":
|
|
123
|
+
array = np.asarray(dates, dtype="datetime64[us]")
|
|
124
|
+
if np.datetime_data(array.dtype)[0] in ("ns", "ps", "fs", "as"):
|
|
125
|
+
return array.astype("datetime64[ns]").astype(np.int64) // 100 + _EPOCH_TICKS
|
|
126
|
+
return array.astype("datetime64[us]").astype(np.int64) * 10 + _EPOCH_TICKS
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def from_ticks(ticks: Any) -> np.ndarray:
|
|
130
|
+
"""Return .NET ``DateTime.Ticks`` as ``datetime64[us]`` dates.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
ticks : array_like
|
|
135
|
+
The int64 ticks (100 ns since 0001-01-01).
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
numpy.ndarray
|
|
140
|
+
The dates; sub-microsecond ticks are floored to the microsecond.
|
|
141
|
+
"""
|
|
142
|
+
return ((np.asarray(ticks, dtype=np.int64) - _EPOCH_TICKS) // 10).astype("datetime64[us]")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _net_date(value: Any) -> Any:
|
|
146
|
+
"""Return one date as a ``System.DateTime`` of unspecified kind."""
|
|
147
|
+
import System # noqa: PLC0415 - the caller started the runtime
|
|
148
|
+
|
|
149
|
+
return System.DateTime(int(to_ticks([value])[0]))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _py_date(net: Any) -> np.datetime64:
|
|
153
|
+
"""Return a ``System.DateTime`` as a ``datetime64[us]`` scalar."""
|
|
154
|
+
return from_ticks([int(net.Ticks)])[0]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class TimeSeries:
|
|
158
|
+
"""A series of dated values (``Numerics.Data.TimeSeries``).
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
values : array_like, optional
|
|
163
|
+
The values; NaN marks a missing value.
|
|
164
|
+
interval : TimeInterval or str, optional
|
|
165
|
+
The time step; default one day.
|
|
166
|
+
start : datetime-like, optional
|
|
167
|
+
The first date of a regular series; the later dates step by ``interval``.
|
|
168
|
+
end : datetime-like, optional
|
|
169
|
+
With ``start`` and no ``values``: the last date of a regular series filled with ``fill``.
|
|
170
|
+
dates : array_like, optional
|
|
171
|
+
Explicit dates, one per value, instead of ``start``.
|
|
172
|
+
fill : float, optional
|
|
173
|
+
The value of a series built from ``start`` and ``end``; default NaN.
|
|
174
|
+
|
|
175
|
+
Attributes
|
|
176
|
+
----------
|
|
177
|
+
net : Numerics.Data.TimeSeries
|
|
178
|
+
The wrapped .NET object.
|
|
179
|
+
|
|
180
|
+
Notes
|
|
181
|
+
-----
|
|
182
|
+
With neither ``start`` nor ``dates`` the series is empty and :meth:`append` adds ordinates.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
def __init__(self, values: Any = None, interval: TimeInterval | str = TimeInterval.ONE_DAY, start: Any = None,
|
|
186
|
+
end: Any = None, dates: Any = None, fill: float = float("nan")) -> None:
|
|
187
|
+
data = _data()
|
|
188
|
+
kind = net_enum(data.TimeInterval, interval)
|
|
189
|
+
if dates is not None:
|
|
190
|
+
self.net = self._explicit(kind, dates, values)
|
|
191
|
+
elif start is not None and values is not None:
|
|
192
|
+
self.net = data.TimeSeries(kind, _net_date(start), to_net_doubles(values))
|
|
193
|
+
elif start is not None and end is not None:
|
|
194
|
+
self.net = data.TimeSeries(kind, _net_date(start), _net_date(end), float(fill))
|
|
195
|
+
elif start is None and values is None:
|
|
196
|
+
self.net = data.TimeSeries(kind)
|
|
197
|
+
else:
|
|
198
|
+
raise ValueError("Give values with start or dates, start with end, or nothing for an empty series.")
|
|
199
|
+
|
|
200
|
+
@staticmethod
|
|
201
|
+
def _explicit(kind: Any, dates: Any, values: Any) -> Any:
|
|
202
|
+
"""Build a .NET series from explicit dates and values: two arrays in one constructor call."""
|
|
203
|
+
ticks = np.ravel(to_ticks(dates))
|
|
204
|
+
numbers = np.asarray(values, dtype=np.float64).ravel()
|
|
205
|
+
if ticks.shape != numbers.shape:
|
|
206
|
+
raise ValueError(f"Expected one value per date; got {numbers.size} values for {ticks.size} dates.")
|
|
207
|
+
return _data().TimeSeries(kind, to_net_longs(ticks), to_net_doubles(numbers))
|
|
208
|
+
|
|
209
|
+
@classmethod
|
|
210
|
+
def from_net(cls, net: Any) -> TimeSeries:
|
|
211
|
+
"""Wrap an existing .NET time series (for example one from ``TimeSeriesDownload``).
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
net : Numerics.Data.TimeSeries
|
|
216
|
+
The .NET series; it is shared, not copied.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
TimeSeries
|
|
221
|
+
The wrapper.
|
|
222
|
+
"""
|
|
223
|
+
instance = cls.__new__(cls)
|
|
224
|
+
instance.net = net
|
|
225
|
+
return instance
|
|
226
|
+
|
|
227
|
+
@classmethod
|
|
228
|
+
def from_xml(cls, xml: str) -> TimeSeries:
|
|
229
|
+
"""Restore a series from its XML (the ``XElement`` constructor).
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
xml : str
|
|
234
|
+
The text :meth:`to_xml` returns.
|
|
235
|
+
|
|
236
|
+
Returns
|
|
237
|
+
-------
|
|
238
|
+
TimeSeries
|
|
239
|
+
The series.
|
|
240
|
+
"""
|
|
241
|
+
return cls.from_net(_data().TimeSeries(parse_xml(xml)))
|
|
242
|
+
|
|
243
|
+
@classmethod
|
|
244
|
+
def from_pandas(cls, series: Any, interval: TimeInterval | str = TimeInterval.IRREGULAR) -> TimeSeries:
|
|
245
|
+
"""Build a series from a pandas ``Series`` with a ``DatetimeIndex``.
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
series : pandas.Series
|
|
250
|
+
The values, indexed by time zone naive dates.
|
|
251
|
+
interval : TimeInterval or str, optional
|
|
252
|
+
The time step to record; default irregular.
|
|
253
|
+
|
|
254
|
+
Returns
|
|
255
|
+
-------
|
|
256
|
+
TimeSeries
|
|
257
|
+
The series, with the same dates and values.
|
|
258
|
+
"""
|
|
259
|
+
return cls(series.to_numpy(dtype=np.float64), interval=interval, dates=series.index.to_numpy())
|
|
260
|
+
|
|
261
|
+
def to_pandas(self) -> Any:
|
|
262
|
+
"""Return the series as a pandas ``Series`` indexed by its dates.
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
pandas.Series
|
|
267
|
+
The values, with a ``DatetimeIndex``.
|
|
268
|
+
"""
|
|
269
|
+
import pandas as pd # noqa: PLC0415 - pandas is optional
|
|
270
|
+
|
|
271
|
+
return pd.Series(self.values, index=pd.DatetimeIndex(self.dates))
|
|
272
|
+
|
|
273
|
+
def to_xml(self) -> str:
|
|
274
|
+
"""Return the series' ``ToXElement()`` as XML text.
|
|
275
|
+
|
|
276
|
+
Returns
|
|
277
|
+
-------
|
|
278
|
+
str
|
|
279
|
+
The element as .NET writes it.
|
|
280
|
+
"""
|
|
281
|
+
return str(self.net.ToXElement().ToString())
|
|
282
|
+
|
|
283
|
+
def copy(self) -> TimeSeries:
|
|
284
|
+
"""Return an independent copy (``Clone``).
|
|
285
|
+
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
TimeSeries
|
|
289
|
+
The copy.
|
|
290
|
+
"""
|
|
291
|
+
return TimeSeries.from_net(self.net.Clone())
|
|
292
|
+
|
|
293
|
+
def __copy__(self) -> TimeSeries:
|
|
294
|
+
return self.copy()
|
|
295
|
+
|
|
296
|
+
def __deepcopy__(self, memo: dict) -> TimeSeries:
|
|
297
|
+
return self.copy()
|
|
298
|
+
|
|
299
|
+
# Collection --------------------------------------------------------------------------------
|
|
300
|
+
|
|
301
|
+
def __len__(self) -> int:
|
|
302
|
+
return int(self.net.Count)
|
|
303
|
+
|
|
304
|
+
def __iter__(self) -> Iterator[tuple[np.datetime64, float]]:
|
|
305
|
+
return iter(zip(list(self.dates), self.values.tolist(), strict=True))
|
|
306
|
+
|
|
307
|
+
def __getitem__(self, index: int) -> tuple[np.datetime64, float]:
|
|
308
|
+
ordinate = self.net[int(index)]
|
|
309
|
+
return _py_date(ordinate.Index), float(ordinate.Value)
|
|
310
|
+
|
|
311
|
+
def __setitem__(self, index: int, item: tuple[Any, float]) -> None:
|
|
312
|
+
self.net[int(index)] = self._ordinate(*item)
|
|
313
|
+
|
|
314
|
+
def _ordinate(self, date: Any, value: float) -> Any:
|
|
315
|
+
"""Return a ``SeriesOrdinate<DateTime, double>``."""
|
|
316
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
317
|
+
|
|
318
|
+
return _data().SeriesOrdinate[System.DateTime, System.Double](_net_date(date), float(value))
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def count(self) -> int:
|
|
322
|
+
"""The number of ordinates."""
|
|
323
|
+
return int(self.net.Count)
|
|
324
|
+
|
|
325
|
+
@property
|
|
326
|
+
def values(self) -> np.ndarray:
|
|
327
|
+
"""The values in collection order, copied in one transfer (``ValuesToArray``)."""
|
|
328
|
+
return to_numpy_doubles(self.net.ValuesToArray())
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def ticks(self) -> np.ndarray:
|
|
332
|
+
"""The dates as int64 .NET ticks, copied in one transfer (``IndexTicksToArray``)."""
|
|
333
|
+
return to_numpy_longs(self.net.IndexTicksToArray())
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def dates(self) -> np.ndarray:
|
|
337
|
+
"""The dates in collection order as ``datetime64[us]``."""
|
|
338
|
+
return from_ticks(self.ticks)
|
|
339
|
+
|
|
340
|
+
def append(self, date: Any, value: float) -> None:
|
|
341
|
+
"""Append an ordinate (``Add(SeriesOrdinate)``).
|
|
342
|
+
|
|
343
|
+
Parameters
|
|
344
|
+
----------
|
|
345
|
+
date : datetime-like
|
|
346
|
+
The date.
|
|
347
|
+
value : float
|
|
348
|
+
The value (NaN for missing).
|
|
349
|
+
"""
|
|
350
|
+
ordinate = self._ordinate(date, value)
|
|
351
|
+
# Bind Add(SeriesOrdinate) explicitly: the group also has Add(double) and IList.Add(object).
|
|
352
|
+
self.net.Add.Overloads[type(ordinate)](ordinate)
|
|
353
|
+
|
|
354
|
+
def insert(self, index: int, date: Any, value: float) -> None:
|
|
355
|
+
"""Insert an ordinate before a position (``Insert``).
|
|
356
|
+
|
|
357
|
+
Parameters
|
|
358
|
+
----------
|
|
359
|
+
index : int
|
|
360
|
+
The position.
|
|
361
|
+
date : datetime-like
|
|
362
|
+
The date.
|
|
363
|
+
value : float
|
|
364
|
+
The value.
|
|
365
|
+
"""
|
|
366
|
+
self.net.Insert(int(index), self._ordinate(date, value))
|
|
367
|
+
|
|
368
|
+
def contains(self, date: Any, value: float) -> bool:
|
|
369
|
+
"""Return whether an ordinate with this date and value is in the series (``Contains``).
|
|
370
|
+
|
|
371
|
+
Parameters
|
|
372
|
+
----------
|
|
373
|
+
date : datetime-like
|
|
374
|
+
The date.
|
|
375
|
+
value : float
|
|
376
|
+
The value.
|
|
377
|
+
|
|
378
|
+
Returns
|
|
379
|
+
-------
|
|
380
|
+
bool
|
|
381
|
+
Whether it is present.
|
|
382
|
+
"""
|
|
383
|
+
return bool(self.net.Contains(self._ordinate(date, value)))
|
|
384
|
+
|
|
385
|
+
def index_of(self, date: Any, value: float) -> int:
|
|
386
|
+
"""Return the position of an ordinate, or -1 (``IndexOf``).
|
|
387
|
+
|
|
388
|
+
Parameters
|
|
389
|
+
----------
|
|
390
|
+
date : datetime-like
|
|
391
|
+
The date.
|
|
392
|
+
value : float
|
|
393
|
+
The value.
|
|
394
|
+
|
|
395
|
+
Returns
|
|
396
|
+
-------
|
|
397
|
+
int
|
|
398
|
+
The first position, or -1.
|
|
399
|
+
"""
|
|
400
|
+
return int(self.net.IndexOf(self._ordinate(date, value)))
|
|
401
|
+
|
|
402
|
+
def index_of_date(self, date: Any) -> int:
|
|
403
|
+
"""Return the position of the first ordinate on a date, or -1 when no ordinate has it.
|
|
404
|
+
|
|
405
|
+
Unlike :meth:`index_of`, which matches the date and the value (``IndexOf``), this matches
|
|
406
|
+
the date alone, to the .NET tick (100 ns). The dates cross in one transfer
|
|
407
|
+
(``IndexTicksToArray``) and are compared in NumPy, so the series may be unsorted or
|
|
408
|
+
irregular.
|
|
409
|
+
|
|
410
|
+
Parameters
|
|
411
|
+
----------
|
|
412
|
+
date : datetime-like
|
|
413
|
+
The date, as for :func:`to_ticks`.
|
|
414
|
+
|
|
415
|
+
Returns
|
|
416
|
+
-------
|
|
417
|
+
int
|
|
418
|
+
The first position whose date equals ``date``, or -1.
|
|
419
|
+
|
|
420
|
+
Examples
|
|
421
|
+
--------
|
|
422
|
+
>>> from pynumerics.timeseries import TimeSeries
|
|
423
|
+
>>> series = TimeSeries([1.0, 2.0, 3.0], "one_day", "2020-01-01") # doctest: +SKIP
|
|
424
|
+
>>> series.index_of_date("2020-01-02"), series.index_of_date("2021-01-01") # doctest: +SKIP
|
|
425
|
+
(1, -1)
|
|
426
|
+
"""
|
|
427
|
+
target = to_ticks([date])[0]
|
|
428
|
+
matches = np.flatnonzero(self.ticks == target)
|
|
429
|
+
return int(matches[0]) if matches.size else -1
|
|
430
|
+
|
|
431
|
+
def remove(self, date: Any, value: float) -> bool:
|
|
432
|
+
"""Remove the first ordinate with this date and value (``Remove``).
|
|
433
|
+
|
|
434
|
+
Parameters
|
|
435
|
+
----------
|
|
436
|
+
date : datetime-like
|
|
437
|
+
The date.
|
|
438
|
+
value : float
|
|
439
|
+
The value.
|
|
440
|
+
|
|
441
|
+
Returns
|
|
442
|
+
-------
|
|
443
|
+
bool
|
|
444
|
+
Whether one was removed.
|
|
445
|
+
"""
|
|
446
|
+
return bool(self.net.Remove(self._ordinate(date, value)))
|
|
447
|
+
|
|
448
|
+
def remove_at(self, index: int) -> None:
|
|
449
|
+
"""Remove the ordinate at a position (``RemoveAt``).
|
|
450
|
+
|
|
451
|
+
Parameters
|
|
452
|
+
----------
|
|
453
|
+
index : int
|
|
454
|
+
The position.
|
|
455
|
+
"""
|
|
456
|
+
self.net.RemoveAt(int(index))
|
|
457
|
+
|
|
458
|
+
def clear(self) -> None:
|
|
459
|
+
"""Remove every ordinate (``Clear``)."""
|
|
460
|
+
self.net.Clear()
|
|
461
|
+
|
|
462
|
+
# Properties --------------------------------------------------------------------------------
|
|
463
|
+
|
|
464
|
+
@property
|
|
465
|
+
def interval(self) -> TimeInterval:
|
|
466
|
+
"""The time step."""
|
|
467
|
+
return TimeInterval(str(self.net.TimeInterval))
|
|
468
|
+
|
|
469
|
+
@property
|
|
470
|
+
def start_date(self) -> np.datetime64:
|
|
471
|
+
"""The earliest date."""
|
|
472
|
+
return _py_date(self.net.StartDate)
|
|
473
|
+
|
|
474
|
+
@property
|
|
475
|
+
def end_date(self) -> np.datetime64:
|
|
476
|
+
"""The latest date."""
|
|
477
|
+
return _py_date(self.net.EndDate)
|
|
478
|
+
|
|
479
|
+
@property
|
|
480
|
+
def has_missing_values(self) -> bool:
|
|
481
|
+
"""Whether any value is NaN."""
|
|
482
|
+
return bool(self.net.HasMissingValues)
|
|
483
|
+
|
|
484
|
+
def number_of_missing_values(self) -> int:
|
|
485
|
+
"""Return the number of NaN values (``NumberOfMissingValues``).
|
|
486
|
+
|
|
487
|
+
Returns
|
|
488
|
+
-------
|
|
489
|
+
int
|
|
490
|
+
The count.
|
|
491
|
+
"""
|
|
492
|
+
return int(self.net.NumberOfMissingValues())
|
|
493
|
+
|
|
494
|
+
# In-place operations -----------------------------------------------------------------------
|
|
495
|
+
|
|
496
|
+
def _apply(self, name: str, indexes: Any, *arguments: Any) -> None:
|
|
497
|
+
"""Call an in-place member on every value, or on the values at ``indexes``."""
|
|
498
|
+
member = getattr(self.net, name)
|
|
499
|
+
if indexes is None:
|
|
500
|
+
member(*arguments)
|
|
501
|
+
else:
|
|
502
|
+
member(*arguments, to_net_ints(indexes))
|
|
503
|
+
|
|
504
|
+
def add(self, constant: float, indexes: Any = None) -> None:
|
|
505
|
+
"""Add a constant to the values in place (``Add(double)``).
|
|
506
|
+
|
|
507
|
+
Parameters
|
|
508
|
+
----------
|
|
509
|
+
constant : float
|
|
510
|
+
The addend.
|
|
511
|
+
indexes : array_like of int, optional
|
|
512
|
+
The positions to change; default all.
|
|
513
|
+
"""
|
|
514
|
+
self._apply("Add", indexes, float(constant))
|
|
515
|
+
|
|
516
|
+
def subtract(self, constant: float, indexes: Any = None) -> None:
|
|
517
|
+
"""Subtract a constant from the values in place (``Subtract``).
|
|
518
|
+
|
|
519
|
+
Parameters
|
|
520
|
+
----------
|
|
521
|
+
constant : float
|
|
522
|
+
The subtrahend.
|
|
523
|
+
indexes : array_like of int, optional
|
|
524
|
+
The positions to change; default all.
|
|
525
|
+
"""
|
|
526
|
+
self._apply("Subtract", indexes, float(constant))
|
|
527
|
+
|
|
528
|
+
def multiply(self, constant: float, indexes: Any = None) -> None:
|
|
529
|
+
"""Multiply the values by a constant in place (``Multiply``).
|
|
530
|
+
|
|
531
|
+
Parameters
|
|
532
|
+
----------
|
|
533
|
+
constant : float
|
|
534
|
+
The factor.
|
|
535
|
+
indexes : array_like of int, optional
|
|
536
|
+
The positions to change; default all.
|
|
537
|
+
"""
|
|
538
|
+
self._apply("Multiply", indexes, float(constant))
|
|
539
|
+
|
|
540
|
+
def divide(self, constant: float, indexes: Any = None) -> None:
|
|
541
|
+
"""Divide the values by a constant in place (``Divide``).
|
|
542
|
+
|
|
543
|
+
Parameters
|
|
544
|
+
----------
|
|
545
|
+
constant : float
|
|
546
|
+
The divisor (not zero).
|
|
547
|
+
indexes : array_like of int, optional
|
|
548
|
+
The positions to change; default all.
|
|
549
|
+
"""
|
|
550
|
+
self._apply("Divide", indexes, float(constant))
|
|
551
|
+
|
|
552
|
+
def absolute_value(self, indexes: Any = None) -> None:
|
|
553
|
+
"""Replace the values by their absolute values in place (``AbsoluteValue``).
|
|
554
|
+
|
|
555
|
+
Parameters
|
|
556
|
+
----------
|
|
557
|
+
indexes : array_like of int, optional
|
|
558
|
+
The positions to change; default all.
|
|
559
|
+
"""
|
|
560
|
+
self._apply("AbsoluteValue", indexes)
|
|
561
|
+
|
|
562
|
+
def exponentiate(self, power: float, indexes: Any = None) -> None:
|
|
563
|
+
"""Raise the values to a power in place (``Exponentiate``).
|
|
564
|
+
|
|
565
|
+
Parameters
|
|
566
|
+
----------
|
|
567
|
+
power : float
|
|
568
|
+
The exponent.
|
|
569
|
+
indexes : array_like of int, optional
|
|
570
|
+
The positions to change; default all.
|
|
571
|
+
"""
|
|
572
|
+
self._apply("Exponentiate", indexes, float(power))
|
|
573
|
+
|
|
574
|
+
def log_transform(self, base: float = 10.0, indexes: Any = None) -> None:
|
|
575
|
+
"""Replace the values by their logarithms in place (``LogTransform``).
|
|
576
|
+
|
|
577
|
+
Parameters
|
|
578
|
+
----------
|
|
579
|
+
base : float, optional
|
|
580
|
+
The logarithm base; default 10.
|
|
581
|
+
indexes : array_like of int, optional
|
|
582
|
+
The positions to change; default all.
|
|
583
|
+
"""
|
|
584
|
+
if indexes is None:
|
|
585
|
+
self.net.LogTransform(float(base))
|
|
586
|
+
else:
|
|
587
|
+
self.net.LogTransform(to_net_ints(indexes), float(base))
|
|
588
|
+
|
|
589
|
+
def inverse(self, indexes: Any = None) -> None:
|
|
590
|
+
"""Replace the values by their reciprocals in place (``Inverse``).
|
|
591
|
+
|
|
592
|
+
Parameters
|
|
593
|
+
----------
|
|
594
|
+
indexes : array_like of int, optional
|
|
595
|
+
The positions to change; default all.
|
|
596
|
+
"""
|
|
597
|
+
self._apply("Inverse", indexes)
|
|
598
|
+
|
|
599
|
+
def standardize(self) -> None:
|
|
600
|
+
"""Replace the values by their z-scores ``(x - mean) / sd`` in place (``Standardize``)."""
|
|
601
|
+
self.net.Standardize()
|
|
602
|
+
|
|
603
|
+
def replace_missing_data(self, value: float, indexes: Any = None) -> None:
|
|
604
|
+
"""Replace NaN values by a constant in place (``ReplaceMissingData``).
|
|
605
|
+
|
|
606
|
+
Parameters
|
|
607
|
+
----------
|
|
608
|
+
value : float
|
|
609
|
+
The replacement.
|
|
610
|
+
indexes : array_like of int, optional
|
|
611
|
+
The positions to change; default all.
|
|
612
|
+
"""
|
|
613
|
+
if indexes is None:
|
|
614
|
+
self.net.ReplaceMissingData(float(value))
|
|
615
|
+
else:
|
|
616
|
+
self.net.ReplaceMissingData(to_net_ints(indexes), float(value))
|
|
617
|
+
|
|
618
|
+
def interpolate_missing_data(self, max_number_of_missing: int, indexes: Any = None) -> None:
|
|
619
|
+
"""Fill gaps of NaN values by linear interpolation in place (``InterpolateMissingData``).
|
|
620
|
+
|
|
621
|
+
Parameters
|
|
622
|
+
----------
|
|
623
|
+
max_number_of_missing : int
|
|
624
|
+
The longest gap filled; longer gaps stay missing.
|
|
625
|
+
indexes : array_like of int, optional
|
|
626
|
+
The positions to consider; default all.
|
|
627
|
+
"""
|
|
628
|
+
if indexes is None:
|
|
629
|
+
self.net.InterpolateMissingData(int(max_number_of_missing))
|
|
630
|
+
else:
|
|
631
|
+
self.net.InterpolateMissingData(int(max_number_of_missing), to_net_ints(indexes))
|
|
632
|
+
|
|
633
|
+
def sort_by_time(self, descending: bool = False) -> None:
|
|
634
|
+
"""Sort the ordinates by date in place (``SortByTime``).
|
|
635
|
+
|
|
636
|
+
Parameters
|
|
637
|
+
----------
|
|
638
|
+
descending : bool, optional
|
|
639
|
+
Whether the latest date comes first; default False.
|
|
640
|
+
"""
|
|
641
|
+
self._sort("SortByTime", descending)
|
|
642
|
+
|
|
643
|
+
def sort_by_value(self, descending: bool = False) -> None:
|
|
644
|
+
"""Sort the ordinates by value in place (``SortByValue``).
|
|
645
|
+
|
|
646
|
+
Parameters
|
|
647
|
+
----------
|
|
648
|
+
descending : bool, optional
|
|
649
|
+
Whether the largest value comes first; default False.
|
|
650
|
+
"""
|
|
651
|
+
self._sort("SortByValue", descending)
|
|
652
|
+
|
|
653
|
+
def _sort(self, name: str, descending: bool) -> None:
|
|
654
|
+
"""Call a sort member with a ``ListSortDirection``."""
|
|
655
|
+
import clr # noqa: PLC0415 - the runtime is running
|
|
656
|
+
import System # noqa: PLC0415
|
|
657
|
+
|
|
658
|
+
direction = clr.GetClrType(_data().TimeSeries).GetMethod(name).GetParameters()[0].ParameterType
|
|
659
|
+
getattr(self.net, name)(System.Enum.ToObject(direction, 1 if descending else 0))
|
|
660
|
+
|
|
661
|
+
def shift_all_dates(self, new_start_date: Any) -> None:
|
|
662
|
+
"""Move the first date and restep the others by the interval in place (``ShiftAllDates``).
|
|
663
|
+
|
|
664
|
+
Parameters
|
|
665
|
+
----------
|
|
666
|
+
new_start_date : datetime-like
|
|
667
|
+
The new first date; an irregular series moves only its first date.
|
|
668
|
+
"""
|
|
669
|
+
self.net.ShiftAllDates(_net_date(new_start_date))
|
|
670
|
+
|
|
671
|
+
# Derived series ----------------------------------------------------------------------------
|
|
672
|
+
|
|
673
|
+
def cumulative_sum(self) -> TimeSeries:
|
|
674
|
+
"""Return the running sum (``CumulativeSum``).
|
|
675
|
+
|
|
676
|
+
Returns
|
|
677
|
+
-------
|
|
678
|
+
TimeSeries
|
|
679
|
+
The cumulative series.
|
|
680
|
+
"""
|
|
681
|
+
return TimeSeries.from_net(self.net.CumulativeSum())
|
|
682
|
+
|
|
683
|
+
def difference(self, lag: int = 1, differences: int = 1, label_at_later_date: bool = False) -> TimeSeries:
|
|
684
|
+
"""Return the lagged differences ``x[t + lag] - x[t]``, applied ``differences`` times (``Difference``).
|
|
685
|
+
|
|
686
|
+
Parameters
|
|
687
|
+
----------
|
|
688
|
+
lag : int, optional
|
|
689
|
+
The lag; default 1.
|
|
690
|
+
differences : int, optional
|
|
691
|
+
The order of differencing; default 1.
|
|
692
|
+
label_at_later_date : bool, optional
|
|
693
|
+
If True, date each difference at its later value, as pandas ``Series.diff`` does
|
|
694
|
+
(``Difference(int, int, bool)``); default False, the earlier value.
|
|
695
|
+
|
|
696
|
+
Returns
|
|
697
|
+
-------
|
|
698
|
+
TimeSeries
|
|
699
|
+
The differenced series, shorter by ``lag * differences``. By default each difference
|
|
700
|
+
carries the date of its earlier value, so the result starts at the start date; with
|
|
701
|
+
``label_at_later_date`` it starts ``lag * differences`` ordinates later, as
|
|
702
|
+
``s.diff(lag).dropna()``. The dates are the series' own, so a series with gaps or an
|
|
703
|
+
irregular interval keeps its real dates.
|
|
704
|
+
"""
|
|
705
|
+
if not label_at_later_date:
|
|
706
|
+
return TimeSeries.from_net(self.net.Difference(int(lag), int(differences)))
|
|
707
|
+
import System # noqa: PLC0415 - the runtime is started
|
|
708
|
+
|
|
709
|
+
member = self.net.Difference.Overloads[System.Int32, System.Int32, System.Boolean]
|
|
710
|
+
return TimeSeries.from_net(member(int(lag), int(differences), True))
|
|
711
|
+
|
|
712
|
+
def moving_average(self, period: int, min_valid_count: int | None = None) -> TimeSeries:
|
|
713
|
+
"""Return the trailing moving average over ``period`` steps (``MovingAverage``).
|
|
714
|
+
|
|
715
|
+
Parameters
|
|
716
|
+
----------
|
|
717
|
+
period : int
|
|
718
|
+
The window length, less than the series length.
|
|
719
|
+
min_valid_count : int, optional
|
|
720
|
+
The fewest non-missing values a window needs, else NaN; default ``period``.
|
|
721
|
+
|
|
722
|
+
Returns
|
|
723
|
+
-------
|
|
724
|
+
TimeSeries
|
|
725
|
+
One value per full window, dated at the window's last date.
|
|
726
|
+
"""
|
|
727
|
+
return TimeSeries.from_net(self.net.MovingAverage(int(period), self._optional(min_valid_count)))
|
|
728
|
+
|
|
729
|
+
def moving_sum(self, period: int, min_valid_count: int | None = None) -> TimeSeries:
|
|
730
|
+
"""Return the trailing moving sum over ``period`` steps (``MovingSum``).
|
|
731
|
+
|
|
732
|
+
Parameters
|
|
733
|
+
----------
|
|
734
|
+
period : int
|
|
735
|
+
The window length, less than the series length.
|
|
736
|
+
min_valid_count : int, optional
|
|
737
|
+
The fewest non-missing values a window needs, else NaN; default ``period``.
|
|
738
|
+
|
|
739
|
+
Returns
|
|
740
|
+
-------
|
|
741
|
+
TimeSeries
|
|
742
|
+
One value per full window, dated at the window's last date.
|
|
743
|
+
"""
|
|
744
|
+
return TimeSeries.from_net(self.net.MovingSum(int(period), self._optional(min_valid_count)))
|
|
745
|
+
|
|
746
|
+
@staticmethod
|
|
747
|
+
def _optional(value: int | None) -> Any:
|
|
748
|
+
"""Return a ``Nullable<int>`` argument."""
|
|
749
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
750
|
+
|
|
751
|
+
return None if value is None else System.Nullable[System.Int32](int(value))
|
|
752
|
+
|
|
753
|
+
def smoothed_series(self, smoothing_function: SmoothingFunction | str, period: int = 1) -> TimeSeries:
|
|
754
|
+
"""Return the series smoothed by a moving average, moving sum or difference (``SmoothedSeries``).
|
|
755
|
+
|
|
756
|
+
Parameters
|
|
757
|
+
----------
|
|
758
|
+
smoothing_function : SmoothingFunction or str
|
|
759
|
+
The smoothing.
|
|
760
|
+
period : int, optional
|
|
761
|
+
The window length or lag; default 1 (a copy for the moving functions).
|
|
762
|
+
|
|
763
|
+
Returns
|
|
764
|
+
-------
|
|
765
|
+
TimeSeries
|
|
766
|
+
The smoothed series.
|
|
767
|
+
"""
|
|
768
|
+
return TimeSeries.from_net(self.net.SmoothedSeries(net_enum(_data().SmoothingFunctionType,
|
|
769
|
+
smoothing_function), int(period)))
|
|
770
|
+
|
|
771
|
+
def shift_dates_by_day(self, number_of_days: int) -> TimeSeries:
|
|
772
|
+
"""Return the series with every date moved by whole days (``ShiftDatesByDay``).
|
|
773
|
+
|
|
774
|
+
Parameters
|
|
775
|
+
----------
|
|
776
|
+
number_of_days : int
|
|
777
|
+
The shift; negative moves earlier.
|
|
778
|
+
|
|
779
|
+
Returns
|
|
780
|
+
-------
|
|
781
|
+
TimeSeries
|
|
782
|
+
The shifted series.
|
|
783
|
+
"""
|
|
784
|
+
return TimeSeries.from_net(self.net.ShiftDatesByDay(int(number_of_days)))
|
|
785
|
+
|
|
786
|
+
def shift_dates_by_month(self, number_of_months: int) -> TimeSeries:
|
|
787
|
+
"""Return the series with every date moved by whole months (``ShiftDatesByMonth``).
|
|
788
|
+
|
|
789
|
+
Parameters
|
|
790
|
+
----------
|
|
791
|
+
number_of_months : int
|
|
792
|
+
The shift; negative moves earlier.
|
|
793
|
+
|
|
794
|
+
Returns
|
|
795
|
+
-------
|
|
796
|
+
TimeSeries
|
|
797
|
+
The shifted series.
|
|
798
|
+
"""
|
|
799
|
+
return TimeSeries.from_net(self.net.ShiftDatesByMonth(int(number_of_months)))
|
|
800
|
+
|
|
801
|
+
def shift_dates_by_year(self, number_of_years: int) -> TimeSeries:
|
|
802
|
+
"""Return the series with every date moved by whole years (``ShiftDatesByYear``).
|
|
803
|
+
|
|
804
|
+
Parameters
|
|
805
|
+
----------
|
|
806
|
+
number_of_years : int
|
|
807
|
+
The shift; negative moves earlier.
|
|
808
|
+
|
|
809
|
+
Returns
|
|
810
|
+
-------
|
|
811
|
+
TimeSeries
|
|
812
|
+
The shifted series.
|
|
813
|
+
"""
|
|
814
|
+
return TimeSeries.from_net(self.net.ShiftDatesByYear(int(number_of_years)))
|
|
815
|
+
|
|
816
|
+
def clip(self, start_date: Any, end_date: Any) -> TimeSeries:
|
|
817
|
+
"""Return the ordinates dated within ``[start_date, end_date]`` (``ClipTimeSeries``).
|
|
818
|
+
|
|
819
|
+
Parameters
|
|
820
|
+
----------
|
|
821
|
+
start_date, end_date : datetime-like
|
|
822
|
+
The window, inside the series' own dates.
|
|
823
|
+
|
|
824
|
+
Returns
|
|
825
|
+
-------
|
|
826
|
+
TimeSeries
|
|
827
|
+
The clipped series.
|
|
828
|
+
"""
|
|
829
|
+
return TimeSeries.from_net(self.net.ClipTimeSeries(_net_date(start_date), _net_date(end_date)))
|
|
830
|
+
|
|
831
|
+
def fill_missing_dates(self, start_date: Any, end_date: Any, value: float = 0.0) -> TimeSeries:
|
|
832
|
+
"""Return the series on every step from start to end, missing steps given a value (``FillMissingDates``).
|
|
833
|
+
|
|
834
|
+
Parameters
|
|
835
|
+
----------
|
|
836
|
+
start_date, end_date : datetime-like
|
|
837
|
+
The range.
|
|
838
|
+
value : float, optional
|
|
839
|
+
The value of the added steps; default 0.
|
|
840
|
+
|
|
841
|
+
Returns
|
|
842
|
+
-------
|
|
843
|
+
TimeSeries
|
|
844
|
+
The completed series.
|
|
845
|
+
"""
|
|
846
|
+
return TimeSeries.from_net(_data().TimeSeries.FillMissingDates(self.net, _net_date(start_date),
|
|
847
|
+
_net_date(end_date), float(value)))
|
|
848
|
+
|
|
849
|
+
def convert_time_interval(self, interval: TimeInterval | str, average: bool = True) -> TimeSeries | None:
|
|
850
|
+
"""Return the series at another time step (``ConvertTimeInterval``).
|
|
851
|
+
|
|
852
|
+
A finer step interpolates linearly (``average``) or splits each value evenly; a coarser
|
|
853
|
+
step averages (``average``) or sums consecutive blocks, ending with the last block that
|
|
854
|
+
holds data, so 72 hourly values give 3 daily values. Both steps must have a fixed length (see
|
|
855
|
+
:meth:`time_interval_in_hours`).
|
|
856
|
+
|
|
857
|
+
Parameters
|
|
858
|
+
----------
|
|
859
|
+
interval : TimeInterval or str
|
|
860
|
+
The new step.
|
|
861
|
+
average : bool, optional
|
|
862
|
+
Whether values are rates (averaged) rather than volumes (summed); default True.
|
|
863
|
+
|
|
864
|
+
Returns
|
|
865
|
+
-------
|
|
866
|
+
TimeSeries or None
|
|
867
|
+
The converted series (a copy at the same step), or None when Numerics cannot convert.
|
|
868
|
+
"""
|
|
869
|
+
net = self.net.ConvertTimeInterval(net_enum(_data().TimeInterval, interval), bool(average))
|
|
870
|
+
return None if net is None else TimeSeries.from_net(net)
|
|
871
|
+
|
|
872
|
+
def aggregate_by_interval(self, interval: TimeInterval | str,
|
|
873
|
+
block_function: BlockFunction | str = BlockFunction.AVERAGE,
|
|
874
|
+
min_valid_count: int = 1) -> TimeSeries:
|
|
875
|
+
"""Return one value per calendar-aligned block of a coarser step (``AggregateByInterval``).
|
|
876
|
+
|
|
877
|
+
Blocks start at midnight, the first of a month, quarter or year (seven-day blocks at the
|
|
878
|
+
first date); missing values are skipped and empty blocks are NaN.
|
|
879
|
+
|
|
880
|
+
Parameters
|
|
881
|
+
----------
|
|
882
|
+
interval : TimeInterval or str
|
|
883
|
+
The block length (not irregular).
|
|
884
|
+
block_function : BlockFunction or str, optional
|
|
885
|
+
The reduction; default average.
|
|
886
|
+
min_valid_count : int, optional
|
|
887
|
+
The fewest non-missing values a block needs, else NaN; default 1.
|
|
888
|
+
|
|
889
|
+
Returns
|
|
890
|
+
-------
|
|
891
|
+
TimeSeries
|
|
892
|
+
One ordinate per block from the first to the last, dated at the block start.
|
|
893
|
+
"""
|
|
894
|
+
data = _data()
|
|
895
|
+
return TimeSeries.from_net(self.net.AggregateByInterval(
|
|
896
|
+
net_enum(data.TimeInterval, interval), net_enum(data.BlockFunctionType, block_function),
|
|
897
|
+
int(min_valid_count)))
|
|
898
|
+
|
|
899
|
+
# Block series ------------------------------------------------------------------------------
|
|
900
|
+
|
|
901
|
+
def _block(self, name: str, block_function: Any, smoothing_function: Any, period: int, *leading: int) -> Any:
|
|
902
|
+
"""Call a block-series member and wrap the result."""
|
|
903
|
+
data = _data()
|
|
904
|
+
return TimeSeries.from_net(getattr(self.net, name)(
|
|
905
|
+
*leading, net_enum(data.BlockFunctionType, block_function),
|
|
906
|
+
net_enum(data.SmoothingFunctionType, smoothing_function), int(period)))
|
|
907
|
+
|
|
908
|
+
def calendar_year_series(self, block_function: BlockFunction | str = BlockFunction.MAXIMUM,
|
|
909
|
+
smoothing_function: SmoothingFunction | str = SmoothingFunction.NONE,
|
|
910
|
+
period: int = 1) -> TimeSeries:
|
|
911
|
+
"""Return one value per calendar year (``CalendarYearSeries``), for example the annual maxima.
|
|
912
|
+
|
|
913
|
+
Parameters
|
|
914
|
+
----------
|
|
915
|
+
block_function : BlockFunction or str, optional
|
|
916
|
+
The reduction; default maximum (dated at the maximum).
|
|
917
|
+
smoothing_function : SmoothingFunction or str, optional
|
|
918
|
+
Smoothing applied first; default none.
|
|
919
|
+
period : int, optional
|
|
920
|
+
The smoothing window; default 1.
|
|
921
|
+
|
|
922
|
+
Returns
|
|
923
|
+
-------
|
|
924
|
+
TimeSeries
|
|
925
|
+
An irregular series, one ordinate per year with data.
|
|
926
|
+
"""
|
|
927
|
+
return self._block("CalendarYearSeries", block_function, smoothing_function, period)
|
|
928
|
+
|
|
929
|
+
def custom_year_series(self, start_month: int = 10, end_month: int | None = None,
|
|
930
|
+
block_function: BlockFunction | str = BlockFunction.MAXIMUM,
|
|
931
|
+
smoothing_function: SmoothingFunction | str = SmoothingFunction.NONE,
|
|
932
|
+
period: int = 1) -> TimeSeries:
|
|
933
|
+
"""Return one value per year starting in a given month, or per seasonal window (``CustomYearSeries``).
|
|
934
|
+
|
|
935
|
+
With only ``start_month`` the year runs from that month (10, the default, is the US water
|
|
936
|
+
year, labelled by the year it ends in); with ``end_month`` too, only the months from
|
|
937
|
+
``start_month`` to ``end_month`` (wrapping over the new year) are blocked.
|
|
938
|
+
|
|
939
|
+
Parameters
|
|
940
|
+
----------
|
|
941
|
+
start_month : int, optional
|
|
942
|
+
The first month, 1 to 12; default 10.
|
|
943
|
+
end_month : int, optional
|
|
944
|
+
The last month of a seasonal window, 1 to 12.
|
|
945
|
+
block_function : BlockFunction or str, optional
|
|
946
|
+
The reduction; default maximum.
|
|
947
|
+
smoothing_function : SmoothingFunction or str, optional
|
|
948
|
+
Smoothing applied first; default none.
|
|
949
|
+
period : int, optional
|
|
950
|
+
The smoothing window; default 1.
|
|
951
|
+
|
|
952
|
+
Returns
|
|
953
|
+
-------
|
|
954
|
+
TimeSeries
|
|
955
|
+
An irregular series, one ordinate per year with data.
|
|
956
|
+
"""
|
|
957
|
+
months = (int(start_month),) if end_month is None else (int(start_month), int(end_month))
|
|
958
|
+
return self._block("CustomYearSeries", block_function, smoothing_function, period, *months)
|
|
959
|
+
|
|
960
|
+
def monthly_series(self, block_function: BlockFunction | str = BlockFunction.MAXIMUM,
|
|
961
|
+
smoothing_function: SmoothingFunction | str = SmoothingFunction.NONE,
|
|
962
|
+
period: int = 1) -> TimeSeries:
|
|
963
|
+
"""Return one value per calendar month (``MonthlySeries``).
|
|
964
|
+
|
|
965
|
+
Parameters
|
|
966
|
+
----------
|
|
967
|
+
block_function : BlockFunction or str, optional
|
|
968
|
+
The reduction; default maximum.
|
|
969
|
+
smoothing_function : SmoothingFunction or str, optional
|
|
970
|
+
Smoothing applied first; default none.
|
|
971
|
+
period : int, optional
|
|
972
|
+
The smoothing window; default 1.
|
|
973
|
+
|
|
974
|
+
Returns
|
|
975
|
+
-------
|
|
976
|
+
TimeSeries
|
|
977
|
+
An irregular series, one ordinate per month with data.
|
|
978
|
+
"""
|
|
979
|
+
return self._block("MonthlySeries", block_function, smoothing_function, period)
|
|
980
|
+
|
|
981
|
+
def quarterly_series(self, block_function: BlockFunction | str = BlockFunction.MAXIMUM,
|
|
982
|
+
smoothing_function: SmoothingFunction | str = SmoothingFunction.NONE,
|
|
983
|
+
period: int = 1) -> TimeSeries:
|
|
984
|
+
"""Return one value per calendar quarter (``QuarterlySeries``).
|
|
985
|
+
|
|
986
|
+
Parameters
|
|
987
|
+
----------
|
|
988
|
+
block_function : BlockFunction or str, optional
|
|
989
|
+
The reduction; default maximum.
|
|
990
|
+
smoothing_function : SmoothingFunction or str, optional
|
|
991
|
+
Smoothing applied first; default none.
|
|
992
|
+
period : int, optional
|
|
993
|
+
The smoothing window; default 1.
|
|
994
|
+
|
|
995
|
+
Returns
|
|
996
|
+
-------
|
|
997
|
+
TimeSeries
|
|
998
|
+
An irregular series, one ordinate per quarter with data.
|
|
999
|
+
"""
|
|
1000
|
+
return self._block("QuarterlySeries", block_function, smoothing_function, period)
|
|
1001
|
+
|
|
1002
|
+
def peaks_over_threshold(self, threshold: float, min_steps_between_events: int = 1,
|
|
1003
|
+
smoothing_function: SmoothingFunction | str = SmoothingFunction.NONE,
|
|
1004
|
+
period: int = 1) -> TimeSeries:
|
|
1005
|
+
"""Return the peak of each cluster of values above a threshold (``PeaksOverThresholdSeries``).
|
|
1006
|
+
|
|
1007
|
+
A cluster ends once the values have stayed at or below the threshold for more than
|
|
1008
|
+
``min_steps_between_events`` steps after its peak.
|
|
1009
|
+
|
|
1010
|
+
Parameters
|
|
1011
|
+
----------
|
|
1012
|
+
threshold : float
|
|
1013
|
+
The threshold.
|
|
1014
|
+
min_steps_between_events : int, optional
|
|
1015
|
+
The independence separation in steps; default 1.
|
|
1016
|
+
smoothing_function : SmoothingFunction or str, optional
|
|
1017
|
+
Smoothing applied first; default none.
|
|
1018
|
+
period : int, optional
|
|
1019
|
+
The smoothing window; default 1.
|
|
1020
|
+
|
|
1021
|
+
Returns
|
|
1022
|
+
-------
|
|
1023
|
+
TimeSeries
|
|
1024
|
+
An irregular series of the cluster peaks.
|
|
1025
|
+
"""
|
|
1026
|
+
return TimeSeries.from_net(self.net.PeaksOverThresholdSeries(
|
|
1027
|
+
float(threshold), int(min_steps_between_events),
|
|
1028
|
+
net_enum(_data().SmoothingFunctionType, smoothing_function), int(period)))
|
|
1029
|
+
|
|
1030
|
+
# Resampling and decomposition --------------------------------------------------------------
|
|
1031
|
+
|
|
1032
|
+
def resample_with_knn(self, time_steps: int, k: int, seed: int = 12345) -> TimeSeries:
|
|
1033
|
+
"""Return a synthetic series by k-nearest-neighbor conditional bootstrap (``ResampleWithKNN``).
|
|
1034
|
+
|
|
1035
|
+
Parameters
|
|
1036
|
+
----------
|
|
1037
|
+
time_steps : int
|
|
1038
|
+
The length of the synthetic series.
|
|
1039
|
+
k : int
|
|
1040
|
+
The number of neighbors.
|
|
1041
|
+
seed : int, optional
|
|
1042
|
+
The Mersenne Twister seed; default 12345.
|
|
1043
|
+
|
|
1044
|
+
Returns
|
|
1045
|
+
-------
|
|
1046
|
+
TimeSeries
|
|
1047
|
+
The synthetic series, from the start date at this interval.
|
|
1048
|
+
"""
|
|
1049
|
+
return TimeSeries.from_net(self.net.ResampleWithKNN(int(time_steps), int(k), int(seed)))
|
|
1050
|
+
|
|
1051
|
+
def resample_with_block_bootstrap(self, time_steps: int, block_size: int, seed: int = 12345) -> TimeSeries:
|
|
1052
|
+
"""Return a synthetic series by moving-block bootstrap (``ResampleWithBlockBootstrap``).
|
|
1053
|
+
|
|
1054
|
+
Parameters
|
|
1055
|
+
----------
|
|
1056
|
+
time_steps : int
|
|
1057
|
+
The length of the synthetic series.
|
|
1058
|
+
block_size : int
|
|
1059
|
+
The block length.
|
|
1060
|
+
seed : int, optional
|
|
1061
|
+
The Mersenne Twister seed; default 12345.
|
|
1062
|
+
|
|
1063
|
+
Returns
|
|
1064
|
+
-------
|
|
1065
|
+
TimeSeries
|
|
1066
|
+
The synthetic series, from the start date at this interval.
|
|
1067
|
+
"""
|
|
1068
|
+
return TimeSeries.from_net(self.net.ResampleWithBlockBootstrap(int(time_steps), int(block_size), int(seed)))
|
|
1069
|
+
|
|
1070
|
+
def seasonal_decompose(self, period: int) -> tuple[TimeSeries, np.ndarray, TimeSeries]:
|
|
1071
|
+
"""Split the series into trend, seasonal and residual parts (``SeasonalDecompose``).
|
|
1072
|
+
|
|
1073
|
+
The trend is the trailing moving average over ``period``; the seasonal part keeps the
|
|
1074
|
+
harmonics of ``period`` in the FFT of the detrended series.
|
|
1075
|
+
|
|
1076
|
+
Parameters
|
|
1077
|
+
----------
|
|
1078
|
+
period : int
|
|
1079
|
+
The season length in steps (at least 2, at most half the series).
|
|
1080
|
+
|
|
1081
|
+
Returns
|
|
1082
|
+
-------
|
|
1083
|
+
tuple of (TimeSeries, numpy.ndarray, TimeSeries)
|
|
1084
|
+
The trend, the seasonal component (one value per ordinate) and the residual (where
|
|
1085
|
+
the trend is defined).
|
|
1086
|
+
"""
|
|
1087
|
+
result = self.net.SeasonalDecompose(int(period))
|
|
1088
|
+
return (TimeSeries.from_net(result.Item1), to_numpy_doubles(result.Item2),
|
|
1089
|
+
TimeSeries.from_net(result.Item3))
|
|
1090
|
+
|
|
1091
|
+
# Statistics --------------------------------------------------------------------------------
|
|
1092
|
+
|
|
1093
|
+
def min_value(self) -> float:
|
|
1094
|
+
"""Return the smallest non-missing value (``MinValue``).
|
|
1095
|
+
|
|
1096
|
+
Returns
|
|
1097
|
+
-------
|
|
1098
|
+
float
|
|
1099
|
+
The minimum.
|
|
1100
|
+
"""
|
|
1101
|
+
return float(self.net.MinValue())
|
|
1102
|
+
|
|
1103
|
+
def max_value(self) -> float:
|
|
1104
|
+
"""Return the largest non-missing value (``MaxValue``).
|
|
1105
|
+
|
|
1106
|
+
Returns
|
|
1107
|
+
-------
|
|
1108
|
+
float
|
|
1109
|
+
The maximum.
|
|
1110
|
+
"""
|
|
1111
|
+
return float(self.net.MaxValue())
|
|
1112
|
+
|
|
1113
|
+
def mean_value(self) -> float:
|
|
1114
|
+
"""Return the mean of the non-missing values (``MeanValue``).
|
|
1115
|
+
|
|
1116
|
+
Returns
|
|
1117
|
+
-------
|
|
1118
|
+
float
|
|
1119
|
+
The mean.
|
|
1120
|
+
"""
|
|
1121
|
+
return float(self.net.MeanValue())
|
|
1122
|
+
|
|
1123
|
+
def standard_deviation(self) -> float:
|
|
1124
|
+
"""Return the sample standard deviation of the non-missing values (``StandardDeviation``).
|
|
1125
|
+
|
|
1126
|
+
Returns
|
|
1127
|
+
-------
|
|
1128
|
+
float
|
|
1129
|
+
The standard deviation (n - 1 denominator).
|
|
1130
|
+
"""
|
|
1131
|
+
return float(self.net.StandardDeviation())
|
|
1132
|
+
|
|
1133
|
+
def percentiles(self, k: Any) -> np.ndarray:
|
|
1134
|
+
"""Return percentiles of the non-missing values (``Percentiles``).
|
|
1135
|
+
|
|
1136
|
+
Parameters
|
|
1137
|
+
----------
|
|
1138
|
+
k : array_like
|
|
1139
|
+
Probabilities in [0, 1].
|
|
1140
|
+
|
|
1141
|
+
Returns
|
|
1142
|
+
-------
|
|
1143
|
+
numpy.ndarray
|
|
1144
|
+
One percentile per probability (``Statistics.Percentile`` on the sorted values).
|
|
1145
|
+
"""
|
|
1146
|
+
return to_numpy_doubles(self.net.Percentiles(to_net_doubles(k)))
|
|
1147
|
+
|
|
1148
|
+
def summary_percentiles(self) -> np.ndarray:
|
|
1149
|
+
"""Return the 5, 25, 50, 75 and 95 percent percentiles (``SummaryPercentiles``).
|
|
1150
|
+
|
|
1151
|
+
Returns
|
|
1152
|
+
-------
|
|
1153
|
+
numpy.ndarray
|
|
1154
|
+
The five percentiles.
|
|
1155
|
+
"""
|
|
1156
|
+
return to_numpy_doubles(self.net.SummaryPercentiles())
|
|
1157
|
+
|
|
1158
|
+
def duration(self) -> np.ndarray:
|
|
1159
|
+
"""Return the flow-duration curve (``Duration``).
|
|
1160
|
+
|
|
1161
|
+
Returns
|
|
1162
|
+
-------
|
|
1163
|
+
numpy.ndarray
|
|
1164
|
+
Shape ``(n, 2)``: the Weibull exceedance percent and the value, largest value first.
|
|
1165
|
+
"""
|
|
1166
|
+
return to_numpy_matrix(self.net.Duration())
|
|
1167
|
+
|
|
1168
|
+
def monthly_percentiles(self, k: Any) -> np.ndarray:
|
|
1169
|
+
"""Return percentiles of the values of each calendar month (``MonthlyPercentiles``).
|
|
1170
|
+
|
|
1171
|
+
Parameters
|
|
1172
|
+
----------
|
|
1173
|
+
k : array_like
|
|
1174
|
+
Probabilities in [0, 1].
|
|
1175
|
+
|
|
1176
|
+
Returns
|
|
1177
|
+
-------
|
|
1178
|
+
numpy.ndarray
|
|
1179
|
+
Shape ``(12, len(k))``, January first; a month without data is zeros.
|
|
1180
|
+
"""
|
|
1181
|
+
return to_numpy_matrix(self.net.MonthlyPercentiles(to_net_doubles(k)))
|
|
1182
|
+
|
|
1183
|
+
def monthly_summary_statistics(self) -> np.ndarray:
|
|
1184
|
+
"""Return summary statistics of each calendar month (``MonthlySummaryStatistics``).
|
|
1185
|
+
|
|
1186
|
+
Returns
|
|
1187
|
+
-------
|
|
1188
|
+
numpy.ndarray
|
|
1189
|
+
Shape ``(12, 8)``, January first: minimum, 5, 25, 50, 75 and 95 percent percentiles,
|
|
1190
|
+
maximum and mean of the non-missing values.
|
|
1191
|
+
"""
|
|
1192
|
+
return to_numpy_matrix(self.net.MonthlySummaryStatistics())
|
|
1193
|
+
|
|
1194
|
+
def monthly_frequency(self) -> np.ndarray:
|
|
1195
|
+
"""Return the number of ordinates in each calendar month (``MonthlyFrequency``).
|
|
1196
|
+
|
|
1197
|
+
Returns
|
|
1198
|
+
-------
|
|
1199
|
+
numpy.ndarray
|
|
1200
|
+
Twelve counts, January first.
|
|
1201
|
+
"""
|
|
1202
|
+
return to_numpy_doubles(self.net.MonthlyFrequency())
|
|
1203
|
+
|
|
1204
|
+
def summary_statistics(self) -> dict[str, float]:
|
|
1205
|
+
"""Return the record length, missing count, moments and percentiles (``SummaryStatistics``).
|
|
1206
|
+
|
|
1207
|
+
Returns
|
|
1208
|
+
-------
|
|
1209
|
+
dict of str to float
|
|
1210
|
+
Keys ``"Record Length"``, ``"Missing Values"``, ``"Minimum"``, ``"Maximum"``,
|
|
1211
|
+
``"Mean"``, ``"Std Dev"``, ``"Skewness"``, ``"Kurtosis"`` and ``"1%"`` to ``"99%"``.
|
|
1212
|
+
"""
|
|
1213
|
+
return {str(pair.Key): float(pair.Value) for pair in self.net.SummaryStatistics()}
|
|
1214
|
+
|
|
1215
|
+
def summary_hypothesis_test(self, split_location: int = -1) -> dict[str, float]:
|
|
1216
|
+
"""Return the p-values of normality, independence, trend and homogeneity tests (``SummaryHypothesisTest``).
|
|
1217
|
+
|
|
1218
|
+
Parameters
|
|
1219
|
+
----------
|
|
1220
|
+
split_location : int, optional
|
|
1221
|
+
The index splitting the two samples of the two-sample tests; default the middle.
|
|
1222
|
+
|
|
1223
|
+
Returns
|
|
1224
|
+
-------
|
|
1225
|
+
dict of str to float
|
|
1226
|
+
Test name to p-value (Jarque-Bera, Ljung-Box, Wald-Wolfowitz, Mann-Whitney,
|
|
1227
|
+
Mann-Kendall, Welch t and F tests).
|
|
1228
|
+
"""
|
|
1229
|
+
return {str(pair.Key): float(pair.Value) for pair in self.net.SummaryHypothesisTest(int(split_location))}
|
|
1230
|
+
|
|
1231
|
+
# Interval arithmetic -----------------------------------------------------------------------
|
|
1232
|
+
|
|
1233
|
+
@staticmethod
|
|
1234
|
+
def add_time_interval(date: Any, interval: TimeInterval | str) -> np.datetime64:
|
|
1235
|
+
"""Return a date one step later (``TimeSeries.AddTimeInterval``).
|
|
1236
|
+
|
|
1237
|
+
Parameters
|
|
1238
|
+
----------
|
|
1239
|
+
date : datetime-like
|
|
1240
|
+
The date.
|
|
1241
|
+
interval : TimeInterval or str
|
|
1242
|
+
The step (months, quarters and years are calendar steps).
|
|
1243
|
+
|
|
1244
|
+
Returns
|
|
1245
|
+
-------
|
|
1246
|
+
numpy.datetime64
|
|
1247
|
+
The later date.
|
|
1248
|
+
"""
|
|
1249
|
+
data = _data()
|
|
1250
|
+
return _py_date(data.TimeSeries.AddTimeInterval(_net_date(date), net_enum(data.TimeInterval, interval)))
|
|
1251
|
+
|
|
1252
|
+
@staticmethod
|
|
1253
|
+
def anchored_dates(start: Any, count: int, interval: TimeInterval | str) -> np.ndarray:
|
|
1254
|
+
"""Return ``count`` dates that count each step from the start (``TimeSeries.AnchoredDates``).
|
|
1255
|
+
|
|
1256
|
+
Parameters
|
|
1257
|
+
----------
|
|
1258
|
+
start : datetime-like
|
|
1259
|
+
The first date.
|
|
1260
|
+
count : int
|
|
1261
|
+
The number of dates, the start included.
|
|
1262
|
+
interval : TimeInterval or str
|
|
1263
|
+
The step (not irregular).
|
|
1264
|
+
|
|
1265
|
+
Returns
|
|
1266
|
+
-------
|
|
1267
|
+
numpy.ndarray
|
|
1268
|
+
The ``datetime64[us]`` dates. Month, quarter and year dates keep the start's day of
|
|
1269
|
+
month where the month has it, as pandas ``Timestamp(start) + DateOffset(months=k)``:
|
|
1270
|
+
from 31 January 2020 the monthly dates are 31 January, 29 February, 31 March, 30 April.
|
|
1271
|
+
Fixed steps, and calendar steps from day 28 or earlier, give the dates of a regular
|
|
1272
|
+
series. Pass the dates as ``dates`` to :class:`TimeSeries` to build a series on them.
|
|
1273
|
+
|
|
1274
|
+
Raises
|
|
1275
|
+
------
|
|
1276
|
+
ValueError
|
|
1277
|
+
If ``interval`` is irregular or ``count`` is negative (.NET ``ArgumentException``).
|
|
1278
|
+
"""
|
|
1279
|
+
data = _data()
|
|
1280
|
+
dates = data.TimeSeries.AnchoredDates(_net_date(start), int(count), net_enum(data.TimeInterval, interval))
|
|
1281
|
+
return from_ticks(np.fromiter((d.Ticks for d in dates), dtype=np.int64, count=len(dates)))
|
|
1282
|
+
|
|
1283
|
+
@staticmethod
|
|
1284
|
+
def subtract_time_interval(date: Any, interval: TimeInterval | str) -> np.datetime64:
|
|
1285
|
+
"""Return a date one step earlier (``TimeSeries.SubtractTimeInterval``).
|
|
1286
|
+
|
|
1287
|
+
Parameters
|
|
1288
|
+
----------
|
|
1289
|
+
date : datetime-like
|
|
1290
|
+
The date.
|
|
1291
|
+
interval : TimeInterval or str
|
|
1292
|
+
The step.
|
|
1293
|
+
|
|
1294
|
+
Returns
|
|
1295
|
+
-------
|
|
1296
|
+
numpy.datetime64
|
|
1297
|
+
The earlier date.
|
|
1298
|
+
"""
|
|
1299
|
+
data = _data()
|
|
1300
|
+
return _py_date(data.TimeSeries.SubtractTimeInterval(_net_date(date), net_enum(data.TimeInterval, interval)))
|
|
1301
|
+
|
|
1302
|
+
@staticmethod
|
|
1303
|
+
def time_interval_in_hours(interval: TimeInterval | str) -> float:
|
|
1304
|
+
"""Return the nominal length of a step in hours (``TimeSeries.TimeIntervalInHours``).
|
|
1305
|
+
|
|
1306
|
+
Parameters
|
|
1307
|
+
----------
|
|
1308
|
+
interval : TimeInterval or str
|
|
1309
|
+
The step.
|
|
1310
|
+
|
|
1311
|
+
Returns
|
|
1312
|
+
-------
|
|
1313
|
+
float
|
|
1314
|
+
The hours of a fixed step up to seven days; NaN for months, quarters, years and
|
|
1315
|
+
irregular steps, which have no fixed length.
|
|
1316
|
+
"""
|
|
1317
|
+
data = _data()
|
|
1318
|
+
return float(data.TimeSeries.TimeIntervalInHours(net_enum(data.TimeInterval, interval)))
|
|
1319
|
+
|
|
1320
|
+
|
|
1321
|
+
__all__ = [
|
|
1322
|
+
"BlockFunction", "MathFunction", "SmoothingFunction", "TimeBlockWindow", "TimeInterval", "TimeSeries",
|
|
1323
|
+
"from_ticks", "to_ticks",
|
|
1324
|
+
]
|
|
1325
|
+
|
|
1326
|
+
translate_public(globals())
|