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.
Files changed (46) hide show
  1. pynumerics/__init__.py +73 -0
  2. pynumerics/_bin/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  3. pynumerics/_bin/Microsoft.CodeAnalysis.LICENSE.txt +27 -0
  4. pynumerics/_bin/Microsoft.CodeAnalysis.dll +0 -0
  5. pynumerics/_bin/Numerics.dll +0 -0
  6. pynumerics/_bin/pynumerics.runtimeconfig.json +15 -0
  7. pynumerics/_coverage.py +3825 -0
  8. pynumerics/_util.py +237 -0
  9. pynumerics/_version.py +40 -0
  10. pynumerics/bootstrap.py +593 -0
  11. pynumerics/callbacks.py +791 -0
  12. pynumerics/copulas.py +910 -0
  13. pynumerics/data.py +2233 -0
  14. pynumerics/differentiation.py +348 -0
  15. pynumerics/distributions.py +4043 -0
  16. pynumerics/download.py +623 -0
  17. pynumerics/enums.py +119 -0
  18. pynumerics/errors.py +245 -0
  19. pynumerics/functions.py +1142 -0
  20. pynumerics/goodness_of_fit.py +699 -0
  21. pynumerics/graphs.py +556 -0
  22. pynumerics/hypothesis_tests.py +445 -0
  23. pynumerics/identity.py +144 -0
  24. pynumerics/integration.py +677 -0
  25. pynumerics/linalg.py +1408 -0
  26. pynumerics/machine_learning.py +1335 -0
  27. pynumerics/marshal.py +733 -0
  28. pynumerics/mathematics.py +1053 -0
  29. pynumerics/mcmc.py +1406 -0
  30. pynumerics/multivariate.py +1539 -0
  31. pynumerics/ode.py +156 -0
  32. pynumerics/optimization.py +1736 -0
  33. pynumerics/probability.py +966 -0
  34. pynumerics/py.typed +0 -0
  35. pynumerics/regional.py +522 -0
  36. pynumerics/roots.py +274 -0
  37. pynumerics/runtime.py +384 -0
  38. pynumerics/sampling.py +1758 -0
  39. pynumerics/special.py +1079 -0
  40. pynumerics/statistics.py +1958 -0
  41. pynumerics/timeseries.py +1326 -0
  42. pynumerics-2.3.0.dist-info/METADATA +142 -0
  43. pynumerics-2.3.0.dist-info/RECORD +46 -0
  44. pynumerics-2.3.0.dist-info/WHEEL +5 -0
  45. pynumerics-2.3.0.dist-info/licenses/LICENSE +10 -0
  46. pynumerics-2.3.0.dist-info/top_level.txt +1 -0
@@ -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())