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
pynumerics/data.py ADDED
@@ -0,0 +1,2233 @@
1
+ """Paired data, interpolation, line simplification and regression from ``Numerics.Data``.
2
+
3
+ :class:`Ordinate`, :class:`OrderedPairedData`, :class:`UncertainOrdinate` and
4
+ :class:`UncertainOrderedPairedData` wrap the paired-data types; :class:`Linear`,
5
+ :class:`CubicSpline` and :class:`Polynomial` (all :class:`Interpolater`) and :class:`Bilinear`
6
+ wrap the interpolators; :func:`search` wraps ``Search``; :func:`ramer_douglas_peucker` and
7
+ :func:`perpendicular_distance` wrap ``LineSimplification``; :class:`ProbabilityOrdinates` and
8
+ :class:`LinearRegression` wrap their namesakes; :func:`time_series_index_ticks` and
9
+ :func:`time_series_values` read a ``Numerics.Data.TimeSeries`` in bulk (the full wrapper is
10
+ :class:`pynumerics.timeseries.TimeSeries`). Lookups on arrays cross once into the
11
+ ``IList<double>`` overloads (``GetYFromX(IList, Transform, Transform, ExtrapolationSides)``,
12
+ ``GetXFromY(...)``, ``Interpolater.Interpolate(IList)``), so the interpolation arithmetic is
13
+ Numerics' own.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from collections.abc import Iterator, Sequence
19
+ from typing import Any
20
+
21
+ import numpy as np
22
+
23
+ from pynumerics import distributions
24
+ from pynumerics._util import (
25
+ _data,
26
+ bind,
27
+ bind_list,
28
+ evaluate,
29
+ namespace,
30
+ net_enum,
31
+ )
32
+ from pynumerics.enums import Extrapolation, NetEnum, SortOrder, Transform
33
+ from pynumerics.errors import translate_public
34
+ from pynumerics.marshal import (
35
+ _numerics_matrix,
36
+ to_net_doubles,
37
+ to_net_ints,
38
+ to_net_matrix,
39
+ to_numpy_doubles,
40
+ to_numpy_longs,
41
+ to_numpy_matrix,
42
+ )
43
+
44
+ _XELEMENT: list[Any] = []
45
+
46
+
47
+ def parse_xml(xml: str) -> Any:
48
+ """Parse XML text into a ``System.Xml.Linq.XElement`` (``XElement.Parse``).
49
+
50
+ The element is what the ``XElement`` constructors and ``FromXElement`` members of Numerics
51
+ take; the text is what the wrappers' ``to_xml`` methods return.
52
+
53
+ Parameters
54
+ ----------
55
+ xml : str
56
+ One XML element.
57
+
58
+ Returns
59
+ -------
60
+ System.Xml.Linq.XElement
61
+ The parsed element.
62
+ """
63
+ if not _XELEMENT:
64
+ _data()
65
+ import clr # noqa: PLC0415 - the runtime is running
66
+ import System # noqa: PLC0415
67
+
68
+ # On .NET Core and later, System.Xml.Linq only forwards XElement to System.Private.Xml.Linq;
69
+ # the .NET Framework defines it in System.Xml.Linq itself.
70
+ name = "System.Private.Xml.Linq" if System.Type.GetType("System.Xml.Linq.XElement, System.Private.Xml.Linq") \
71
+ else "System.Xml.Linq"
72
+ clr.AddReference(name)
73
+ from System.Xml.Linq import XElement # noqa: PLC0415
74
+
75
+ _XELEMENT.append(XElement.Parse)
76
+ return _XELEMENT[0](str(xml))
77
+
78
+
79
+ def _sort_order(net: Any) -> SortOrder:
80
+ """Return the Python member of a .NET ``SortOrder``."""
81
+ return SortOrder(str(net))
82
+
83
+
84
+ class Ordinate:
85
+ """One (x, y) point (``Numerics.Data.Ordinate``).
86
+
87
+ Parameters
88
+ ----------
89
+ x, y : float
90
+ The coordinates.
91
+
92
+ Attributes
93
+ ----------
94
+ net : Numerics.Data.Ordinate
95
+ The wrapped .NET value.
96
+ """
97
+
98
+ def __init__(self, x: float, y: float) -> None:
99
+ self.net = _data().Ordinate(float(x), float(y))
100
+
101
+ @classmethod
102
+ def _from_net(cls, net: Any) -> Ordinate:
103
+ """Wrap an existing .NET ordinate."""
104
+ instance = cls.__new__(cls)
105
+ instance.net = net
106
+ return instance
107
+
108
+ @classmethod
109
+ def from_xml(cls, xml: str) -> Ordinate:
110
+ """Restore an ordinate from its XML (the ``XElement`` constructor).
111
+
112
+ Parameters
113
+ ----------
114
+ xml : str
115
+ The text :meth:`to_xml` returns.
116
+
117
+ Returns
118
+ -------
119
+ Ordinate
120
+ The ordinate.
121
+ """
122
+ return cls._from_net(_data().Ordinate(parse_xml(xml)))
123
+
124
+ def __repr__(self) -> str:
125
+ return f"Ordinate({self.x!r}, {self.y!r})"
126
+
127
+ @property
128
+ def x(self) -> float:
129
+ """The x value."""
130
+ return float(self.net.X)
131
+
132
+ @property
133
+ def y(self) -> float:
134
+ """The y value."""
135
+ return float(self.net.Y)
136
+
137
+ @property
138
+ def is_valid(self) -> bool:
139
+ """Whether both values are finite numbers."""
140
+ return bool(self.net.IsValid)
141
+
142
+ def transform(self, x_transform: Transform | str = Transform.NONE,
143
+ y_transform: Transform | str = Transform.NONE) -> Ordinate:
144
+ """Return the ordinate in transformed spaces (``Transform``).
145
+
146
+ Parameters
147
+ ----------
148
+ x_transform, y_transform : Transform or str, optional
149
+ ``"logarithmic"`` is log10, ``"normal_z"`` the standard Normal quantile; default none.
150
+
151
+ Returns
152
+ -------
153
+ Ordinate
154
+ The transformed ordinate.
155
+ """
156
+ data = _data()
157
+ return Ordinate._from_net(self.net.Transform(net_enum(data.Transform, x_transform),
158
+ net_enum(data.Transform, y_transform)))
159
+
160
+ def ordinate_valid(self, other: Ordinate, strict_x: bool = True, strict_y: bool = True,
161
+ x_order: SortOrder | str = SortOrder.ASCENDING, y_order: SortOrder | str = SortOrder.ASCENDING,
162
+ other_is_next: bool = True) -> bool:
163
+ """Return whether this ordinate and a neighbor satisfy an order (``OrdinateValid``).
164
+
165
+ Parameters
166
+ ----------
167
+ other : Ordinate
168
+ The neighbor.
169
+ strict_x, strict_y : bool, optional
170
+ Whether equal values break the order; default True.
171
+ x_order, y_order : SortOrder or str, optional
172
+ The required orders; default ascending.
173
+ other_is_next : bool, optional
174
+ Whether ``other`` follows this ordinate; default True.
175
+
176
+ Returns
177
+ -------
178
+ bool
179
+ Whether the pair is valid.
180
+ """
181
+ data = _data()
182
+ return bool(self.net.OrdinateValid(other.net, bool(strict_x), bool(strict_y),
183
+ net_enum(data.SortOrder, x_order), net_enum(data.SortOrder, y_order),
184
+ bool(other_is_next)))
185
+
186
+ def ordinate_errors(self, other: Ordinate | None = None, strict_x: bool = True, strict_y: bool = True,
187
+ x_order: SortOrder | str = SortOrder.ASCENDING,
188
+ y_order: SortOrder | str = SortOrder.ASCENDING, other_is_next: bool = True) -> list[str]:
189
+ """Return the validation messages of this ordinate, or of it and a neighbor (``OrdinateErrors``).
190
+
191
+ Parameters
192
+ ----------
193
+ other : Ordinate, optional
194
+ The neighbor; without it only this ordinate's own values are checked.
195
+ strict_x, strict_y : bool, optional
196
+ Whether equal values break the order; default True.
197
+ x_order, y_order : SortOrder or str, optional
198
+ The required orders; default ascending.
199
+ other_is_next : bool, optional
200
+ Whether ``other`` follows this ordinate; default True.
201
+
202
+ Returns
203
+ -------
204
+ list of str
205
+ The messages (empty when valid).
206
+ """
207
+ if other is None:
208
+ return [str(m) for m in self.net.OrdinateErrors()]
209
+ data = _data()
210
+ return [str(m) for m in self.net.OrdinateErrors(other.net, bool(strict_x), bool(strict_y),
211
+ net_enum(data.SortOrder, x_order),
212
+ net_enum(data.SortOrder, y_order), bool(other_is_next))]
213
+
214
+ def to_xml(self) -> str:
215
+ """Return the ordinate's ``ToXElement()`` as XML text.
216
+
217
+ Returns
218
+ -------
219
+ str
220
+ The element as .NET writes it.
221
+ """
222
+ return str(self.net.ToXElement().ToString())
223
+
224
+
225
+ class OrderedPairedData:
226
+ """Monotone (x, y) pairs with transformed linear lookups in both directions.
227
+
228
+ Parameters
229
+ ----------
230
+ x, y : array_like
231
+ Paired values of equal length.
232
+ strict_x : bool, optional
233
+ Whether x must be strictly monotone; default True.
234
+ x_order : SortOrder or str, optional
235
+ Order of x; default ascending.
236
+ strict_y : bool, optional
237
+ Whether y must be strictly monotone; default True.
238
+ y_order : SortOrder or str, optional
239
+ Order of y; default ascending.
240
+
241
+ Attributes
242
+ ----------
243
+ net : Numerics.Data.OrderedPairedData
244
+ The wrapped .NET object.
245
+ """
246
+
247
+ def __init__(self, x: Any, y: Any, strict_x: bool = True, x_order: SortOrder | str = SortOrder.ASCENDING,
248
+ strict_y: bool = True, y_order: SortOrder | str = SortOrder.ASCENDING) -> None:
249
+ data = _data()
250
+ self.net = data.OrderedPairedData(to_net_doubles(x), to_net_doubles(y), bool(strict_x),
251
+ net_enum(data.SortOrder, x_order), bool(strict_y),
252
+ net_enum(data.SortOrder, y_order))
253
+
254
+ def _lookup(self, name: str) -> tuple[Any, Any]:
255
+ """Return the scalar and list overloads of ``GetYFromX`` or ``GetXFromY`` with extrapolation sides.
256
+
257
+ Both are bound once per wrapped object: pythonnet resolves the four-overload group on every
258
+ call otherwise, which costs more than a short lookup. The bound overloads are the members the
259
+ group resolves to for these arguments.
260
+ """
261
+ cache = self.__dict__.setdefault("_bound", {})
262
+ entry = cache.get(name)
263
+ if entry is None or entry[0] is not self.net:
264
+ data = _data()
265
+ import System # noqa: PLC0415 - the runtime is running (self.net exists)
266
+ from System.Collections.Generic import IList # noqa: PLC0415
267
+
268
+ group = getattr(self.net, name)
269
+ tail = (data.Transform, data.Transform, data.ExtrapolationSides)
270
+ entry = cache[name] = (self.net, group.Overloads[(System.Double, *tail)],
271
+ group.Overloads[(IList[System.Double], *tail)])
272
+ return entry[1], entry[2]
273
+
274
+ def __len__(self) -> int:
275
+ return int(self.net.Count)
276
+
277
+ @property
278
+ def x(self) -> np.ndarray:
279
+ """The x values."""
280
+ return np.array([float(o.X) for o in self.net], dtype=np.float64)
281
+
282
+ @property
283
+ def y(self) -> np.ndarray:
284
+ """The y values."""
285
+ return np.array([float(o.Y) for o in self.net], dtype=np.float64)
286
+
287
+ @property
288
+ def is_valid(self) -> bool:
289
+ """Whether the pairs satisfy the configured order and strictness."""
290
+ return bool(self.net.IsValid)
291
+
292
+ @property
293
+ def errors(self) -> list[str]:
294
+ """The validation errors (``GetErrors``)."""
295
+ return [str(message) for message in self.net.GetErrors()]
296
+
297
+ def get_y_from_x(self, x: Any, x_transform: Transform | str = Transform.NONE,
298
+ y_transform: Transform | str = Transform.NONE,
299
+ extrapolation: Extrapolation | str = Extrapolation.NONE) -> Any:
300
+ """Interpolate y at x (``GetYFromX``).
301
+
302
+ Parameters
303
+ ----------
304
+ x : float or array_like
305
+ Lookup values.
306
+ x_transform, y_transform : Transform or str, optional
307
+ Interpolation spaces; default none.
308
+ extrapolation : Extrapolation or str, optional
309
+ Sides on which to extend linearly; default none (endpoint hold).
310
+
311
+ Returns
312
+ -------
313
+ float or numpy.ndarray
314
+ Interpolated y, shaped like ``x``.
315
+ """
316
+ data = _data()
317
+ xt, yt = net_enum(data.Transform, x_transform), net_enum(data.Transform, y_transform)
318
+ ext = net_enum(data.ExtrapolationSides, extrapolation)
319
+ scalar, vector = self._lookup("GetYFromX")
320
+ return evaluate(x, lambda v: scalar(v, xt, yt, ext), lambda arr: vector(arr, xt, yt, ext))
321
+
322
+ def get_x_from_y(self, y: Any, x_transform: Transform | str = Transform.NONE,
323
+ y_transform: Transform | str = Transform.NONE,
324
+ extrapolation: Extrapolation | str = Extrapolation.NONE) -> Any:
325
+ """Interpolate x at y (``GetXFromY``).
326
+
327
+ Parameters
328
+ ----------
329
+ y : float or array_like
330
+ Lookup values.
331
+ x_transform, y_transform : Transform or str, optional
332
+ Interpolation spaces; default none.
333
+ extrapolation : Extrapolation or str, optional
334
+ Sides on which to extend linearly; default none (endpoint hold).
335
+
336
+ Returns
337
+ -------
338
+ float or numpy.ndarray
339
+ Interpolated x, shaped like ``y``.
340
+ """
341
+ data = _data()
342
+ xt, yt = net_enum(data.Transform, x_transform), net_enum(data.Transform, y_transform)
343
+ ext = net_enum(data.ExtrapolationSides, extrapolation)
344
+ scalar, vector = self._lookup("GetXFromY")
345
+ return evaluate(y, lambda v: scalar(v, xt, yt, ext), lambda arr: vector(arr, xt, yt, ext))
346
+
347
+ @classmethod
348
+ def _from_net(cls, net: Any) -> OrderedPairedData:
349
+ """Wrap an existing .NET paired data object."""
350
+ instance = cls.__new__(cls)
351
+ instance.net = net
352
+ return instance
353
+
354
+ @classmethod
355
+ def from_ordinates(cls, ordinates: Sequence[Ordinate], strict_x: bool = True,
356
+ x_order: SortOrder | str = SortOrder.ASCENDING, strict_y: bool = True,
357
+ y_order: SortOrder | str = SortOrder.ASCENDING) -> OrderedPairedData:
358
+ """Build paired data from ordinates (the ``IList<Ordinate>`` constructor).
359
+
360
+ Parameters
361
+ ----------
362
+ ordinates : sequence of Ordinate
363
+ The points, in order.
364
+ strict_x : bool, optional
365
+ Whether x must be strictly monotone; default True.
366
+ x_order : SortOrder or str, optional
367
+ Order of x; default ascending.
368
+ strict_y : bool, optional
369
+ Whether y must be strictly monotone; default True.
370
+ y_order : SortOrder or str, optional
371
+ Order of y; default ascending.
372
+
373
+ Returns
374
+ -------
375
+ OrderedPairedData
376
+ The paired data.
377
+ """
378
+ data = _data()
379
+ import System # noqa: PLC0415 - the runtime is running
380
+
381
+ array = System.Array[data.Ordinate]([o.net for o in ordinates])
382
+ return cls._from_net(data.OrderedPairedData(array, bool(strict_x), net_enum(data.SortOrder, x_order),
383
+ bool(strict_y), net_enum(data.SortOrder, y_order)))
384
+
385
+ @classmethod
386
+ def from_xml(cls, xml: str) -> OrderedPairedData:
387
+ """Restore paired data from its XML (the ``XElement`` constructor).
388
+
389
+ Parameters
390
+ ----------
391
+ xml : str
392
+ The text :meth:`to_xml` returns.
393
+
394
+ Returns
395
+ -------
396
+ OrderedPairedData
397
+ The paired data.
398
+ """
399
+ return cls._from_net(_data().OrderedPairedData(parse_xml(xml)))
400
+
401
+ def to_xml(self) -> str:
402
+ """Return the paired data's ``SaveToXElement()`` as XML text.
403
+
404
+ Returns
405
+ -------
406
+ str
407
+ The element as .NET writes it.
408
+ """
409
+ return str(self.net.SaveToXElement().ToString())
410
+
411
+ def __iter__(self) -> Iterator[Ordinate]:
412
+ for net in self.net:
413
+ yield Ordinate._from_net(net)
414
+
415
+ def __getitem__(self, index: int) -> Ordinate:
416
+ return Ordinate._from_net(self.net[int(index)])
417
+
418
+ def __setitem__(self, index: int, ordinate: Ordinate) -> None:
419
+ self.net[int(index)] = ordinate.net
420
+
421
+ def __contains__(self, ordinate: Ordinate) -> bool:
422
+ return bool(self.net.Contains(ordinate.net))
423
+
424
+ @property
425
+ def count(self) -> int:
426
+ """The number of ordinates."""
427
+ return int(self.net.Count)
428
+
429
+ @property
430
+ def strict_x(self) -> bool:
431
+ """Whether x must be strictly monotone."""
432
+ return bool(self.net.StrictX)
433
+
434
+ @strict_x.setter
435
+ def strict_x(self, value: bool) -> None:
436
+ self.net.StrictX = bool(value)
437
+
438
+ @property
439
+ def strict_y(self) -> bool:
440
+ """Whether y must be strictly monotone."""
441
+ return bool(self.net.StrictY)
442
+
443
+ @strict_y.setter
444
+ def strict_y(self, value: bool) -> None:
445
+ self.net.StrictY = bool(value)
446
+
447
+ @property
448
+ def order_x(self) -> SortOrder:
449
+ """The required order of x."""
450
+ return _sort_order(self.net.OrderX)
451
+
452
+ @order_x.setter
453
+ def order_x(self, value: SortOrder | str) -> None:
454
+ self.net.OrderX = net_enum(_data().SortOrder, value)
455
+
456
+ @property
457
+ def order_y(self) -> SortOrder:
458
+ """The required order of y."""
459
+ return _sort_order(self.net.OrderY)
460
+
461
+ @order_y.setter
462
+ def order_y(self, value: SortOrder | str) -> None:
463
+ self.net.OrderY = net_enum(_data().SortOrder, value)
464
+
465
+ @property
466
+ def use_smart_search(self) -> bool:
467
+ """Whether lookups use bisection and hunting (True) or a sequential scan."""
468
+ return bool(self.net.UseSmartSearch)
469
+
470
+ @use_smart_search.setter
471
+ def use_smart_search(self, value: bool) -> None:
472
+ self.net.UseSmartSearch = bool(value)
473
+
474
+ @property
475
+ def x_search_start(self) -> int:
476
+ """The cursor the next x search starts from."""
477
+ return int(self.net.XSearchStart)
478
+
479
+ @x_search_start.setter
480
+ def x_search_start(self, value: int) -> None:
481
+ self.net.XSearchStart = int(value)
482
+
483
+ @property
484
+ def y_search_start(self) -> int:
485
+ """The cursor the next y search starts from."""
486
+ return int(self.net.YSearchStart)
487
+
488
+ @y_search_start.setter
489
+ def y_search_start(self, value: int) -> None:
490
+ self.net.YSearchStart = int(value)
491
+
492
+ def append(self, x: float, y: float) -> None:
493
+ """Append an ordinate (``Add``); the validity is updated.
494
+
495
+ Parameters
496
+ ----------
497
+ x, y : float
498
+ The point.
499
+ """
500
+ self.net.Add(_data().Ordinate(float(x), float(y)))
501
+
502
+ def insert(self, index: int, x: float, y: float) -> None:
503
+ """Insert an ordinate before a position (``Insert``).
504
+
505
+ Parameters
506
+ ----------
507
+ index : int
508
+ The position.
509
+ x, y : float
510
+ The point.
511
+ """
512
+ self.net.Insert(int(index), _data().Ordinate(float(x), float(y)))
513
+
514
+ def index_of(self, x: float, y: float) -> int:
515
+ """Return the position of an ordinate, or -1 (``IndexOf``).
516
+
517
+ Parameters
518
+ ----------
519
+ x, y : float
520
+ The point.
521
+
522
+ Returns
523
+ -------
524
+ int
525
+ The first position holding the point, or -1.
526
+ """
527
+ return int(self.net.IndexOf(float(x), float(y)))
528
+
529
+ def remove(self, x: float, y: float) -> bool:
530
+ """Remove the first ordinate equal to a point (``Remove``).
531
+
532
+ Parameters
533
+ ----------
534
+ x, y : float
535
+ The point.
536
+
537
+ Returns
538
+ -------
539
+ bool
540
+ Whether an ordinate was removed.
541
+ """
542
+ return bool(self.net.Remove(_data().Ordinate(float(x), float(y))))
543
+
544
+ def remove_at(self, index: int) -> None:
545
+ """Remove the ordinate at a position (``RemoveAt``).
546
+
547
+ Parameters
548
+ ----------
549
+ index : int
550
+ The position.
551
+ """
552
+ self.net.RemoveAt(int(index))
553
+
554
+ def remove_range(self, index: int, count: int) -> None:
555
+ """Remove a run of ordinates (``RemoveRange``).
556
+
557
+ Parameters
558
+ ----------
559
+ index : int
560
+ The first position.
561
+ count : int
562
+ How many to remove.
563
+ """
564
+ self.net.RemoveRange(int(index), int(count))
565
+
566
+ def clear(self) -> None:
567
+ """Remove every ordinate (``Clear``)."""
568
+ self.net.Clear()
569
+
570
+ def copy(self) -> OrderedPairedData:
571
+ """Return an independent copy (``Clone``).
572
+
573
+ Returns
574
+ -------
575
+ OrderedPairedData
576
+ The copy, validated.
577
+ """
578
+ return OrderedPairedData._from_net(self.net.Clone())
579
+
580
+ def copy_without_validation(self) -> OrderedPairedData:
581
+ """Return an independent copy that skips revalidation (``CloneWithoutValidation``).
582
+
583
+ Returns
584
+ -------
585
+ OrderedPairedData
586
+ The copy.
587
+ """
588
+ return OrderedPairedData._from_net(self.net.CloneWithoutValidation())
589
+
590
+ def invert(self) -> OrderedPairedData:
591
+ """Return the paired data with x and y swapped (``Invert``).
592
+
593
+ Returns
594
+ -------
595
+ OrderedPairedData
596
+ The inverted paired data.
597
+ """
598
+ return OrderedPairedData._from_net(self.net.Invert())
599
+
600
+ def trapezoidal_area_under_y(self) -> float:
601
+ """Return the trapezoidal area between the curve and the x axis (``TrapezoidalAreaUnderY``).
602
+
603
+ Returns
604
+ -------
605
+ float
606
+ ``sum (x[i+1] - x[i]) (y[i] + y[i+1]) / 2``.
607
+ """
608
+ return float(self.net.TrapezoidalAreaUnderY())
609
+
610
+ def trapezoidal_area_under_x(self) -> float:
611
+ """Return the trapezoidal area between the curve and the y axis (``TrapezoidalAreaUnderX``).
612
+
613
+ Returns
614
+ -------
615
+ float
616
+ ``sum (y[i+1] - y[i]) (x[i] + x[i+1]) / 2``.
617
+ """
618
+ return float(self.net.TrapezoidalAreaUnderX())
619
+
620
+ def search_x(self, x: float) -> int:
621
+ """Return the lower bracketing index of x with the configured search (``SearchX``).
622
+
623
+ Parameters
624
+ ----------
625
+ x : float
626
+ The value.
627
+
628
+ Returns
629
+ -------
630
+ int
631
+ The index ``i`` with x between ``x[i]`` and ``x[i + 1]``.
632
+ """
633
+ return int(self.net.SearchX(float(x)))
634
+
635
+ def search_y(self, y: float) -> int:
636
+ """Return the lower bracketing index of y with the configured search (``SearchY``).
637
+
638
+ Parameters
639
+ ----------
640
+ y : float
641
+ The value.
642
+
643
+ Returns
644
+ -------
645
+ int
646
+ The index ``i`` with y between ``y[i]`` and ``y[i + 1]``.
647
+ """
648
+ return int(self.net.SearchY(float(y)))
649
+
650
+ def binary_search_x(self, x: float) -> int:
651
+ """Return the index of x by binary search (``BinarySearchX``).
652
+
653
+ Parameters
654
+ ----------
655
+ x : float
656
+ The value.
657
+
658
+ Returns
659
+ -------
660
+ int
661
+ The index of an equal x, or the bitwise complement of the insertion point (as
662
+ ``List.BinarySearch``).
663
+ """
664
+ return int(self.net.BinarySearchX(float(x)))
665
+
666
+ def binary_search_y(self, y: float) -> int:
667
+ """Return the index of y by binary search (``BinarySearchY``).
668
+
669
+ Parameters
670
+ ----------
671
+ y : float
672
+ The value.
673
+
674
+ Returns
675
+ -------
676
+ int
677
+ The index of an equal y, or the bitwise complement of the insertion point.
678
+ """
679
+ return int(self.net.BinarySearchY(float(y)))
680
+
681
+ def sequential_search_x(self, x: float) -> int:
682
+ """Return the lower bracketing index of x by a sequential scan (``SequentialSearchX``).
683
+
684
+ Parameters
685
+ ----------
686
+ x : float
687
+ The value.
688
+
689
+ Returns
690
+ -------
691
+ int
692
+ The lower bracketing index.
693
+ """
694
+ return int(self.net.SequentialSearchX(float(x)))
695
+
696
+ def sequential_search_y(self, y: float) -> int:
697
+ """Return the lower bracketing index of y by a sequential scan (``SequentialSearchY``).
698
+
699
+ Parameters
700
+ ----------
701
+ y : float
702
+ The value.
703
+
704
+ Returns
705
+ -------
706
+ int
707
+ The lower bracketing index.
708
+ """
709
+ return int(self.net.SequentialSearchY(float(y)))
710
+
711
+ def bisection_search_x(self, x: float) -> int:
712
+ """Return the lower bracketing index of x by bisection (``BisectionSearchX``).
713
+
714
+ Parameters
715
+ ----------
716
+ x : float
717
+ The value.
718
+
719
+ Returns
720
+ -------
721
+ int
722
+ The lower bracketing index.
723
+ """
724
+ return int(self.net.BisectionSearchX(float(x)))
725
+
726
+ def bisection_search_y(self, y: float) -> int:
727
+ """Return the lower bracketing index of y by bisection (``BisectionSearchY``).
728
+
729
+ Parameters
730
+ ----------
731
+ y : float
732
+ The value.
733
+
734
+ Returns
735
+ -------
736
+ int
737
+ The lower bracketing index.
738
+ """
739
+ return int(self.net.BisectionSearchY(float(y)))
740
+
741
+ def hunt_search_x(self, x: float) -> int:
742
+ """Return the lower bracketing index of x by hunting from the cursor (``HuntSearchX``).
743
+
744
+ Parameters
745
+ ----------
746
+ x : float
747
+ The value.
748
+
749
+ Returns
750
+ -------
751
+ int
752
+ The lower bracketing index.
753
+ """
754
+ return int(self.net.HuntSearchX(float(x)))
755
+
756
+ def hunt_search_y(self, y: float) -> int:
757
+ """Return the lower bracketing index of y by hunting from the cursor (``HuntSearchY``).
758
+
759
+ Parameters
760
+ ----------
761
+ y : float
762
+ The value.
763
+
764
+ Returns
765
+ -------
766
+ int
767
+ The lower bracketing index.
768
+ """
769
+ return int(self.net.HuntSearchY(float(y)))
770
+
771
+ def douglas_peucker_simplify(self, tolerance: float) -> OrderedPairedData:
772
+ """Return the curve simplified by the Douglas-Peucker algorithm (``DouglasPeuckerSimplify``).
773
+
774
+ Parameters
775
+ ----------
776
+ tolerance : float
777
+ The largest perpendicular distance a removed point may have from the simplified curve.
778
+
779
+ Returns
780
+ -------
781
+ OrderedPairedData
782
+ The kept ordinates, end points included.
783
+ """
784
+ return OrderedPairedData._from_net(self.net.DouglasPeuckerSimplify(float(tolerance)))
785
+
786
+ def visvalingam_whyatt_simplify(self, number_to_keep: int) -> OrderedPairedData:
787
+ """Return the curve simplified by the Visvalingam-Whyatt algorithm (``VisvaligamWhyattSimplify``).
788
+
789
+ Points with the smallest effective triangle area are removed one at a time.
790
+
791
+ Parameters
792
+ ----------
793
+ number_to_keep : int
794
+ How many ordinates remain.
795
+
796
+ Returns
797
+ -------
798
+ OrderedPairedData
799
+ The kept ordinates.
800
+ """
801
+ return OrderedPairedData._from_net(self.net.VisvaligamWhyattSimplify(int(number_to_keep)))
802
+
803
+ def lang_simplify(self, tolerance: float, look_ahead: int) -> OrderedPairedData:
804
+ """Return the curve simplified by the Lang algorithm (``LangSimplify``).
805
+
806
+ Parameters
807
+ ----------
808
+ tolerance : float
809
+ The largest perpendicular distance a removed point may have from its chord.
810
+ look_ahead : int
811
+ The number of points each search region spans.
812
+
813
+ Returns
814
+ -------
815
+ OrderedPairedData
816
+ The kept ordinates (a copy when ``look_ahead <= 1`` or ``tolerance <= 0``).
817
+ """
818
+ return OrderedPairedData._from_net(self.net.LangSimplify(float(tolerance), int(look_ahead)))
819
+
820
+
821
+ class Interpolater:
822
+ """Shared members of the one-dimensional interpolators (``Numerics.Data.Interpolater``).
823
+
824
+ Construct :class:`Linear`, :class:`CubicSpline` or :class:`Polynomial`. Build once and
825
+ reuse: the interpolator keeps a search cursor between calls.
826
+
827
+ Attributes
828
+ ----------
829
+ net : Numerics.Data.Interpolater
830
+ The wrapped .NET object.
831
+ """
832
+
833
+ net: Any
834
+
835
+ def _bind(self) -> None:
836
+ """Bind the overloads of ``Interpolate`` once per wrapped object."""
837
+ self._interpolate = bind(self.net.Interpolate)
838
+ self._interpolate_list = bind_list(self.net.Interpolate)
839
+
840
+ def __len__(self) -> int:
841
+ return int(self.net.Count)
842
+
843
+ @property
844
+ def count(self) -> int:
845
+ """The number of knots."""
846
+ return int(self.net.Count)
847
+
848
+ @property
849
+ def x_values(self) -> np.ndarray:
850
+ """The knot abscissas."""
851
+ return to_numpy_doubles(self.net.XValues)
852
+
853
+ @property
854
+ def y_values(self) -> np.ndarray:
855
+ """The knot ordinates."""
856
+ return to_numpy_doubles(self.net.YValues)
857
+
858
+ @property
859
+ def sort_order(self) -> SortOrder:
860
+ """The order of the abscissas."""
861
+ return _sort_order(self.net.SortOrder)
862
+
863
+ @property
864
+ def use_smart_search(self) -> bool:
865
+ """Whether :meth:`search` hunts or bisects (True, default) or scans sequentially."""
866
+ return bool(self.net.UseSmartSearch)
867
+
868
+ @use_smart_search.setter
869
+ def use_smart_search(self, value: bool) -> None:
870
+ self.net.UseSmartSearch = bool(value)
871
+
872
+ @property
873
+ def search_start(self) -> int:
874
+ """The cursor the next search starts from."""
875
+ return int(self.net.SearchStart)
876
+
877
+ @search_start.setter
878
+ def search_start(self, value: int) -> None:
879
+ self.net.SearchStart = int(value)
880
+
881
+ def interpolate(self, x: Any) -> Any:
882
+ """Interpolate at x (``Interpolate``).
883
+
884
+ Parameters
885
+ ----------
886
+ x : float or array_like
887
+ Lookup values.
888
+
889
+ Returns
890
+ -------
891
+ float or numpy.ndarray
892
+ Interpolated values, shaped like ``x``.
893
+ """
894
+ return evaluate(x, self._interpolate, self._interpolate_list)
895
+
896
+ def __call__(self, x: Any) -> Any:
897
+ return self.interpolate(x)
898
+
899
+ def base_interpolate(self, x: float, index: int) -> float:
900
+ """Evaluate the interpolant of the knot interval starting at an index (``BaseInterpolate``).
901
+
902
+ Parameters
903
+ ----------
904
+ x : float
905
+ The lookup value.
906
+ index : int
907
+ The lower bracketing knot, as :meth:`search` returns it.
908
+
909
+ Returns
910
+ -------
911
+ float
912
+ The interpolated value.
913
+ """
914
+ return float(self.net.BaseInterpolate(float(x), int(index)))
915
+
916
+ def search(self, x: float) -> int:
917
+ """Return the lower bracketing knot of x and move the cursor (``Search``).
918
+
919
+ Parameters
920
+ ----------
921
+ x : float
922
+ The value.
923
+
924
+ Returns
925
+ -------
926
+ int
927
+ The index ``i`` with x between ``x_values[i]`` and ``x_values[i + 1]``.
928
+ """
929
+ return int(self.net.Search(float(x)))
930
+
931
+ def sequential_search(self, x: float) -> int:
932
+ """Return the lower bracketing knot of x by a sequential scan from the cursor (``SequentialSearch``).
933
+
934
+ Parameters
935
+ ----------
936
+ x : float
937
+ The value.
938
+
939
+ Returns
940
+ -------
941
+ int
942
+ The lower bracketing knot.
943
+ """
944
+ return int(self.net.SequentialSearch(float(x)))
945
+
946
+ def bisection_search(self, x: float) -> int:
947
+ """Return the lower bracketing knot of x by bisection (``BisectionSearch``).
948
+
949
+ Parameters
950
+ ----------
951
+ x : float
952
+ The value.
953
+
954
+ Returns
955
+ -------
956
+ int
957
+ The lower bracketing knot.
958
+ """
959
+ return int(self.net.BisectionSearch(float(x)))
960
+
961
+ def hunt_search(self, x: float) -> int:
962
+ """Return the lower bracketing knot of x by hunting from the cursor (``HuntSearch``).
963
+
964
+ Parameters
965
+ ----------
966
+ x : float
967
+ The value.
968
+
969
+ Returns
970
+ -------
971
+ int
972
+ The lower bracketing knot.
973
+ """
974
+ return int(self.net.HuntSearch(float(x)))
975
+
976
+
977
+ class Linear(Interpolater):
978
+ """Linear interpolation in optionally transformed spaces (``Numerics.Data.Linear``).
979
+
980
+ :meth:`interpolate` holds the end values outside the knots; :meth:`extrapolate` extends the
981
+ end segments.
982
+
983
+ Parameters
984
+ ----------
985
+ x, y : array_like
986
+ Knots; x sorted in ``sort_order``.
987
+ sort_order : SortOrder or str, optional
988
+ Order of x; default ascending.
989
+ x_transform, y_transform : Transform or str, optional
990
+ Interpolation spaces; default none.
991
+
992
+ Attributes
993
+ ----------
994
+ net : Numerics.Data.Linear
995
+ The wrapped .NET object.
996
+ """
997
+
998
+ def __init__(self, x: Any, y: Any, sort_order: SortOrder | str = SortOrder.ASCENDING,
999
+ x_transform: Transform | str = Transform.NONE, y_transform: Transform | str = Transform.NONE) -> None:
1000
+ data = _data()
1001
+ self.net = data.Linear(to_net_doubles(x), to_net_doubles(y), net_enum(data.SortOrder, sort_order))
1002
+ self.net.XTransform = net_enum(data.Transform, x_transform)
1003
+ self.net.YTransform = net_enum(data.Transform, y_transform)
1004
+ self._bind()
1005
+
1006
+ def _bind(self) -> None:
1007
+ """Bind the overloads of ``Interpolate`` and ``Extrapolate`` once per wrapped object."""
1008
+ super()._bind()
1009
+ self._extrapolate = bind(self.net.Extrapolate)
1010
+ self._extrapolate_list = bind_list(self.net.Extrapolate)
1011
+
1012
+ @property
1013
+ def x_transform(self) -> Transform:
1014
+ """The interpolation space of x."""
1015
+ return Transform(str(self.net.XTransform))
1016
+
1017
+ @x_transform.setter
1018
+ def x_transform(self, value: Transform | str) -> None:
1019
+ self.net.XTransform = net_enum(_data().Transform, value)
1020
+
1021
+ @property
1022
+ def y_transform(self) -> Transform:
1023
+ """The interpolation space of y."""
1024
+ return Transform(str(self.net.YTransform))
1025
+
1026
+ @y_transform.setter
1027
+ def y_transform(self, value: Transform | str) -> None:
1028
+ self.net.YTransform = net_enum(_data().Transform, value)
1029
+
1030
+ def extrapolate(self, x: Any) -> Any:
1031
+ """Evaluate the end segments' straight lines beyond the knots (``Extrapolate``).
1032
+
1033
+ Inside the knots this equals :meth:`interpolate`; outside it extends the first or
1034
+ last segment in the transformed spaces instead of holding the end values. An array
1035
+ crosses once through ``Extrapolate(IList<double>)``, with the scalar call's bits.
1036
+
1037
+ Parameters
1038
+ ----------
1039
+ x : float or array_like
1040
+ Lookup values.
1041
+
1042
+ Returns
1043
+ -------
1044
+ float or numpy.ndarray
1045
+ Values, shaped like ``x``.
1046
+ """
1047
+ return evaluate(x, self._extrapolate, self._extrapolate_list)
1048
+
1049
+ def shallow_clone(self) -> Linear:
1050
+ """Return an interpolator over the same knot lists with its own search cursor (``ShallowClone``).
1051
+
1052
+ Returns
1053
+ -------
1054
+ Linear
1055
+ The clone; it shares the knots, so it is cheap for one interpolator per thread.
1056
+ """
1057
+ clone = Linear.__new__(Linear)
1058
+ clone.net = self.net.ShallowClone()
1059
+ clone._bind()
1060
+ return clone
1061
+
1062
+ def __deepcopy__(self, memo: dict) -> Linear:
1063
+ """Return an independent interpolator over the same knots (its own .NET object and search cursor)."""
1064
+ return Linear(self.x_values, self.y_values, self.sort_order, self.x_transform, self.y_transform)
1065
+
1066
+
1067
+ class CubicSpline(Interpolater):
1068
+ """Natural cubic spline interpolation (``Numerics.Data.CubicSpline``).
1069
+
1070
+ The second derivatives vanish at both end knots (Press et al., Numerical Recipes, 3rd ed.,
1071
+ section 3.3); outside the knots the end cubic pieces are extended.
1072
+
1073
+ Parameters
1074
+ ----------
1075
+ x, y : array_like
1076
+ Knots; x sorted in ``sort_order``.
1077
+ sort_order : SortOrder or str, optional
1078
+ Order of x; default ascending.
1079
+
1080
+ Attributes
1081
+ ----------
1082
+ net : Numerics.Data.CubicSpline
1083
+ The wrapped .NET object.
1084
+ """
1085
+
1086
+ def __init__(self, x: Any, y: Any, sort_order: SortOrder | str = SortOrder.ASCENDING) -> None:
1087
+ data = _data()
1088
+ self.net = data.CubicSpline(to_net_doubles(x), to_net_doubles(y), net_enum(data.SortOrder, sort_order))
1089
+ self._bind()
1090
+
1091
+ def set_second_derivatives(self) -> None:
1092
+ """Recompute the knot second derivatives of the natural spline (``SetSecondDerivatives``)."""
1093
+ self.net.SetSecondDerivatives()
1094
+
1095
+
1096
+ class Polynomial(Interpolater):
1097
+ """Polynomial interpolation through ``order + 1`` neighboring knots (``Numerics.Data.Polynomial``).
1098
+
1099
+ Neville's algorithm on the knots starting at the bracketing index (Press et al., Numerical
1100
+ Recipes, 3rd ed., section 3.2); :attr:`error` is the estimate of the last evaluation.
1101
+
1102
+ Parameters
1103
+ ----------
1104
+ order : int
1105
+ The polynomial degree; less than the number of knots.
1106
+ x, y : array_like
1107
+ Knots; x sorted in ``sort_order``.
1108
+ sort_order : SortOrder or str, optional
1109
+ Order of x; default ascending.
1110
+
1111
+ Attributes
1112
+ ----------
1113
+ net : Numerics.Data.Polynomial
1114
+ The wrapped .NET object.
1115
+ """
1116
+
1117
+ def __init__(self, order: int, x: Any, y: Any, sort_order: SortOrder | str = SortOrder.ASCENDING) -> None:
1118
+ data = _data()
1119
+ self.net = data.Polynomial(int(order), to_net_doubles(x), to_net_doubles(y),
1120
+ net_enum(data.SortOrder, sort_order))
1121
+ self._bind()
1122
+
1123
+ @property
1124
+ def order(self) -> int:
1125
+ """The polynomial degree."""
1126
+ return int(self.net.Order)
1127
+
1128
+ @order.setter
1129
+ def order(self, value: int) -> None:
1130
+ self.net.Order = int(value)
1131
+
1132
+ @property
1133
+ def error(self) -> float:
1134
+ """The error estimate of the last interpolation: its last Neville correction."""
1135
+ return float(self.net.Error)
1136
+
1137
+
1138
+ def _pair_overload(member: Any) -> Any:
1139
+ """Return the ``(IList<double>, IList<double>)`` overload of a .NET method group."""
1140
+ import System # noqa: PLC0415 - the runtime is running
1141
+ from System.Collections.Generic import IList # noqa: PLC0415
1142
+
1143
+ return member.Overloads[IList[System.Double], IList[System.Double]]
1144
+
1145
+
1146
+ class Bilinear:
1147
+ """Bilinear interpolation on a rectangular grid (``Numerics.Data.Bilinear``).
1148
+
1149
+ Outside the grid the nearest edge or corner value is held. Arrays of points cross once
1150
+ through ``Interpolate(IList<double>, IList<double>)``, with the scalar call's bits.
1151
+
1152
+ Parameters
1153
+ ----------
1154
+ x1, x2 : array_like
1155
+ Grid coordinates, sorted in ``sort_order``.
1156
+ y : array_like
1157
+ Shape ``(len(x1), len(x2))``: ``y[i, j]`` is the value at ``(x1[i], x2[j])``.
1158
+ sort_order : SortOrder or str, optional
1159
+ Order of both coordinates; default ascending.
1160
+ x1_transform, x2_transform, y_transform : Transform or str, optional
1161
+ Interpolation spaces; default none.
1162
+
1163
+ Attributes
1164
+ ----------
1165
+ net : Numerics.Data.Bilinear
1166
+ The wrapped .NET object.
1167
+ """
1168
+
1169
+ def __init__(self, x1: Any, x2: Any, y: Any, sort_order: SortOrder | str = SortOrder.ASCENDING,
1170
+ x1_transform: Transform | str = Transform.NONE, x2_transform: Transform | str = Transform.NONE,
1171
+ y_transform: Transform | str = Transform.NONE) -> None:
1172
+ data = _data()
1173
+ self.net = data.Bilinear(to_net_doubles(x1), to_net_doubles(x2), to_net_matrix(y),
1174
+ net_enum(data.SortOrder, sort_order))
1175
+ self.net.X1Transform = net_enum(data.Transform, x1_transform)
1176
+ self.net.X2Transform = net_enum(data.Transform, x2_transform)
1177
+ self.net.YTransform = net_enum(data.Transform, y_transform)
1178
+ self._interpolate = bind(self.net.Interpolate, 2)
1179
+ self._interpolate_list = _pair_overload(self.net.Interpolate)
1180
+
1181
+ @property
1182
+ def x1_values(self) -> np.ndarray:
1183
+ """The first grid coordinates."""
1184
+ return to_numpy_doubles(self.net.X1Values)
1185
+
1186
+ @property
1187
+ def x2_values(self) -> np.ndarray:
1188
+ """The second grid coordinates."""
1189
+ return to_numpy_doubles(self.net.X2Values)
1190
+
1191
+ @property
1192
+ def y_values(self) -> np.ndarray:
1193
+ """The grid values, shape ``(len(x1), len(x2))``."""
1194
+ return to_numpy_matrix(self.net.YValues)
1195
+
1196
+ @property
1197
+ def sort_order(self) -> SortOrder:
1198
+ """The order of the coordinates."""
1199
+ return _sort_order(self.net.SortOrder)
1200
+
1201
+ @property
1202
+ def use_smart_search(self) -> bool:
1203
+ """Whether the searches hunt or bisect (True, default) or scan sequentially."""
1204
+ return bool(self.net.UseSmartSearch)
1205
+
1206
+ @use_smart_search.setter
1207
+ def use_smart_search(self, value: bool) -> None:
1208
+ self.net.UseSmartSearch = bool(value)
1209
+
1210
+ @property
1211
+ def x1_transform(self) -> Transform:
1212
+ """The interpolation space of x1."""
1213
+ return Transform(str(self.net.X1Transform))
1214
+
1215
+ @x1_transform.setter
1216
+ def x1_transform(self, value: Transform | str) -> None:
1217
+ self.net.X1Transform = net_enum(_data().Transform, value)
1218
+
1219
+ @property
1220
+ def x2_transform(self) -> Transform:
1221
+ """The interpolation space of x2."""
1222
+ return Transform(str(self.net.X2Transform))
1223
+
1224
+ @x2_transform.setter
1225
+ def x2_transform(self, value: Transform | str) -> None:
1226
+ self.net.X2Transform = net_enum(_data().Transform, value)
1227
+
1228
+ @property
1229
+ def y_transform(self) -> Transform:
1230
+ """The interpolation space of y."""
1231
+ return Transform(str(self.net.YTransform))
1232
+
1233
+ @y_transform.setter
1234
+ def y_transform(self, value: Transform | str) -> None:
1235
+ self.net.YTransform = net_enum(_data().Transform, value)
1236
+
1237
+ def interpolate(self, x1: Any, x2: Any) -> Any:
1238
+ """Interpolate at points (``Interpolate``).
1239
+
1240
+ Parameters
1241
+ ----------
1242
+ x1, x2 : float or array_like
1243
+ Coordinates, broadcast against each other.
1244
+
1245
+ Returns
1246
+ -------
1247
+ float or numpy.ndarray
1248
+ Interpolated values, shaped like the broadcast coordinates.
1249
+ """
1250
+ a, b = np.broadcast_arrays(np.asarray(x1, dtype=np.float64), np.asarray(x2, dtype=np.float64))
1251
+ member = self._interpolate
1252
+ if a.ndim == 0:
1253
+ return float(member(float(a), float(b)))
1254
+ values = self._interpolate_list(to_net_doubles(a.ravel()), to_net_doubles(b.ravel()))
1255
+ return to_numpy_doubles(values).reshape(a.shape)
1256
+
1257
+ def __call__(self, x1: Any, x2: Any) -> Any:
1258
+ return self.interpolate(x1, x2)
1259
+
1260
+
1261
+ class SearchMethod(NetEnum):
1262
+ """Bracketing search methods (``Numerics.Data.Search.Method``)."""
1263
+
1264
+ SEQUENTIAL = "Sequential"
1265
+ BISECTION = "Bisection"
1266
+ HUNT = "Hunt"
1267
+
1268
+
1269
+ def search(x: float, values: Any, method: SearchMethod | str = SearchMethod.BISECTION, start: int = 0,
1270
+ order: SortOrder | str = SortOrder.ASCENDING) -> int:
1271
+ """Return the lower bracketing index of x in ordered values (``Search.Sequential``, ``Bisection`` or ``Hunt``).
1272
+
1273
+ Parameters
1274
+ ----------
1275
+ x : float
1276
+ The value.
1277
+ values : array_like or OrderedPairedData
1278
+ Ordered values, or paired data searched on x (in its own order).
1279
+ method : SearchMethod or str, optional
1280
+ The search; default bisection.
1281
+ start : int, optional
1282
+ The index the sequential and hunt searches start from; default 0.
1283
+ order : SortOrder or str, optional
1284
+ Order of ``values``; default ascending (ignored for paired data).
1285
+
1286
+ Returns
1287
+ -------
1288
+ int
1289
+ The index ``i`` with x between ``values[i]`` and ``values[i + 1]``: -1 below the first
1290
+ value and ``len(values)`` above the last.
1291
+ """
1292
+ data = _data()
1293
+ member = getattr(data.Search, SearchMethod(method).value)
1294
+ if isinstance(values, OrderedPairedData):
1295
+ return int(member(float(x), values.net, int(start)))
1296
+ return int(member(float(x), to_net_doubles(values), int(start), net_enum(data.SortOrder, order)))
1297
+
1298
+
1299
+ def _predictors(x: Any) -> Any:
1300
+ """Return a predictor ``Matrix``: one row per observation, a 1-D input as a single column."""
1301
+ array = np.asarray(x, dtype=np.float64)
1302
+ return _numerics_matrix(array.reshape(-1, 1) if array.ndim == 1 else array)
1303
+
1304
+
1305
+ class LinearRegression:
1306
+ """Ordinary least-squares regression ``y = X b + e`` (``Numerics.Data.LinearRegression``).
1307
+
1308
+ Numerics fits the model by singular value decomposition at construction; the estimates,
1309
+ standard errors, t statistics and goodness of fit match R's ``lm``.
1310
+
1311
+ Parameters
1312
+ ----------
1313
+ x : array_like
1314
+ Predictors: one row per observation and one column per predictor; a 1-D array is a single
1315
+ predictor.
1316
+ y : array_like
1317
+ Responses, one per row of ``x`` (at least three).
1318
+ has_intercept : bool, optional
1319
+ Whether to estimate an intercept, the first parameter; default True.
1320
+
1321
+ Attributes
1322
+ ----------
1323
+ net : Numerics.Data.LinearRegression
1324
+ The wrapped .NET object.
1325
+ """
1326
+
1327
+ def __init__(self, x: Any, y: Any, has_intercept: bool = True) -> None:
1328
+ linear_algebra = namespace("Numerics.Mathematics.LinearAlgebra")
1329
+ self.net = _data().LinearRegression(_predictors(x), linear_algebra.Vector(to_net_doubles(y)),
1330
+ bool(has_intercept))
1331
+
1332
+ @property
1333
+ def has_intercept(self) -> bool:
1334
+ """Whether the model has an intercept."""
1335
+ return bool(self.net.HasIntercept)
1336
+
1337
+ @property
1338
+ def parameters(self) -> np.ndarray:
1339
+ """The estimates, the intercept first when the model has one."""
1340
+ return to_numpy_doubles(self.net.Parameters)
1341
+
1342
+ @property
1343
+ def parameter_names(self) -> list[str]:
1344
+ """The parameter names (``"Intercept"``, then the predictor names)."""
1345
+ return [str(name) for name in self.net.ParameterNames]
1346
+
1347
+ @property
1348
+ def parameter_standard_errors(self) -> np.ndarray:
1349
+ """The standard errors of the estimates."""
1350
+ return to_numpy_doubles(self.net.ParameterStandardErrors)
1351
+
1352
+ @property
1353
+ def parameter_t_stats(self) -> np.ndarray:
1354
+ """The t statistics of the estimates."""
1355
+ return to_numpy_doubles(self.net.ParameterTStats)
1356
+
1357
+ @property
1358
+ def covariance(self) -> np.ndarray:
1359
+ """The unscaled covariance ``(X^T X)^-1``; times ``standard_error**2`` it is the estimates' covariance."""
1360
+ return to_numpy_matrix(self.net.Covariance.ToArray())
1361
+
1362
+ @property
1363
+ def residuals(self) -> np.ndarray:
1364
+ """The residuals ``y - X b``."""
1365
+ return to_numpy_doubles(self.net.Residuals)
1366
+
1367
+ @property
1368
+ def standard_error(self) -> float:
1369
+ """The residual standard error ``sqrt(SSE / (n - p))``."""
1370
+ return float(self.net.StandardError)
1371
+
1372
+ @property
1373
+ def sample_size(self) -> int:
1374
+ """The number of observations."""
1375
+ return int(self.net.SampleSize)
1376
+
1377
+ @property
1378
+ def degrees_of_freedom(self) -> int:
1379
+ """The residual degrees of freedom ``n - p``."""
1380
+ return int(self.net.DegreesOfFreedom)
1381
+
1382
+ @property
1383
+ def r_squared(self) -> float:
1384
+ """The coefficient of determination."""
1385
+ return float(self.net.RSquared)
1386
+
1387
+ @property
1388
+ def adjusted_r_squared(self) -> float:
1389
+ """The adjusted coefficient of determination."""
1390
+ return float(self.net.AdjRSquared)
1391
+
1392
+ @property
1393
+ def x(self) -> np.ndarray:
1394
+ """The design matrix, with the leading column of ones when the model has an intercept."""
1395
+ return to_numpy_matrix(self.net.X.ToArray())
1396
+
1397
+ @property
1398
+ def y(self) -> np.ndarray:
1399
+ """The responses."""
1400
+ return to_numpy_doubles(self.net.Y.ToArray())
1401
+
1402
+ def predict(self, x: Any) -> np.ndarray:
1403
+ """Return the fitted mean at new predictor rows (``Predict``).
1404
+
1405
+ Parameters
1406
+ ----------
1407
+ x : array_like
1408
+ Predictor rows, without the intercept column (or with it, as a leading column of ones).
1409
+
1410
+ Returns
1411
+ -------
1412
+ numpy.ndarray
1413
+ One prediction per row.
1414
+ """
1415
+ return to_numpy_doubles(self.net.Predict(_predictors(x)))
1416
+
1417
+ def prediction_intervals(self, x: Any, alpha: float = 0.1) -> np.ndarray:
1418
+ """Return two-sided prediction intervals at new predictor rows (``PredictionIntervals``).
1419
+
1420
+ The Student-t interval of R's ``predict(..., interval = "prediction")``, with level
1421
+ ``1 - alpha``.
1422
+
1423
+ Parameters
1424
+ ----------
1425
+ x : array_like
1426
+ Predictor rows, as for :meth:`predict`.
1427
+ alpha : float, optional
1428
+ One minus the interval level; default 0.1 (90 percent intervals).
1429
+
1430
+ Returns
1431
+ -------
1432
+ numpy.ndarray
1433
+ Shape ``(rows, 3)``: the lower bound, the upper bound and the prediction.
1434
+ """
1435
+ return to_numpy_matrix(self.net.PredictionIntervals(_predictors(x), float(alpha)))
1436
+
1437
+ def summary(self) -> list[str]:
1438
+ """Return the R-style text summary (``Summary``).
1439
+
1440
+ Returns
1441
+ -------
1442
+ list of str
1443
+ The summary lines.
1444
+ """
1445
+ return [str(line) for line in self.net.Summary()]
1446
+
1447
+
1448
+ def _net_distribution(distribution: Any) -> Any:
1449
+ """Return the .NET distribution behind a pynumerics wrapper, or a .NET distribution as it is."""
1450
+ return getattr(distribution, "net", distribution)
1451
+
1452
+
1453
+ def _wrap_distribution(net: Any) -> Any:
1454
+ """Wrap a .NET distribution, keeping None as None."""
1455
+ return None if net is None else distributions.wrap(net)
1456
+
1457
+
1458
+ class UncertainOrdinate:
1459
+ """An x value with a distribution of y (``Numerics.Data.UncertainOrdinate``).
1460
+
1461
+ Parameters
1462
+ ----------
1463
+ x : float
1464
+ The x value.
1465
+ distribution : UnivariateDistribution
1466
+ The distribution of y at x.
1467
+
1468
+ Attributes
1469
+ ----------
1470
+ net : Numerics.Data.UncertainOrdinate
1471
+ The wrapped .NET value.
1472
+ """
1473
+
1474
+ def __init__(self, x: float, distribution: Any) -> None:
1475
+ self.net = _data().UncertainOrdinate(float(x), _net_distribution(distribution))
1476
+
1477
+ @classmethod
1478
+ def _from_net(cls, net: Any) -> UncertainOrdinate:
1479
+ """Wrap an existing .NET uncertain ordinate."""
1480
+ instance = cls.__new__(cls)
1481
+ instance.net = net
1482
+ return instance
1483
+
1484
+ @classmethod
1485
+ def from_xml(cls, xml: str, distribution_type: str | None = None) -> UncertainOrdinate:
1486
+ """Restore an uncertain ordinate from its XML (the ``XElement`` constructors).
1487
+
1488
+ Parameters
1489
+ ----------
1490
+ xml : str
1491
+ The text :meth:`to_xml` returns.
1492
+ distribution_type : str, optional
1493
+ A ``UnivariateDistributionType`` name: read the legacy layout, whose distribution
1494
+ parameters are attributes of the element (``X="1" Mu="10" Sigma="2"``); default the
1495
+ current layout, with a child ``Distribution`` element.
1496
+
1497
+ Returns
1498
+ -------
1499
+ UncertainOrdinate
1500
+ The uncertain ordinate.
1501
+ """
1502
+ data = _data()
1503
+ element = parse_xml(xml)
1504
+ if distribution_type is None:
1505
+ return cls._from_net(data.UncertainOrdinate(element))
1506
+ kind = net_enum(namespace("Numerics.Distributions").UnivariateDistributionType, distribution_type)
1507
+ return cls._from_net(data.UncertainOrdinate(element, kind))
1508
+
1509
+ @property
1510
+ def x(self) -> float:
1511
+ """The x value."""
1512
+ return float(self.net.X)
1513
+
1514
+ @property
1515
+ def y(self) -> Any:
1516
+ """The distribution of y (None when unset)."""
1517
+ return _wrap_distribution(self.net.Y)
1518
+
1519
+ @property
1520
+ def is_valid(self) -> bool:
1521
+ """Whether x is finite and the distribution's parameters are valid."""
1522
+ return bool(self.net.IsValid)
1523
+
1524
+ def get_ordinate(self, probability: float | None = None) -> Ordinate:
1525
+ """Return the point at a quantile of y, or at its mean (``GetOrdinate``).
1526
+
1527
+ Parameters
1528
+ ----------
1529
+ probability : float, optional
1530
+ The non-exceedance probability of y; default the mean.
1531
+
1532
+ Returns
1533
+ -------
1534
+ Ordinate
1535
+ ``(x, y.inverse_cdf(probability))`` or ``(x, y.mean)``.
1536
+ """
1537
+ net = self.net.GetOrdinate() if probability is None else self.net.GetOrdinate(float(probability))
1538
+ return Ordinate._from_net(net)
1539
+
1540
+ def _comparison(self, other: UncertainOrdinate, strict_x: bool, strict_y: bool, x_order: Any, y_order: Any,
1541
+ other_is_next: bool, allow_different_types: bool, tail_probability: float | None) -> tuple:
1542
+ """Return the .NET arguments of ``OrdinateValid`` and ``OrdinateErrors``."""
1543
+ data = _data()
1544
+ arguments = (other.net, bool(strict_x), bool(strict_y), net_enum(data.SortOrder, x_order),
1545
+ net_enum(data.SortOrder, y_order), bool(other_is_next), bool(allow_different_types))
1546
+ return arguments if tail_probability is None else (*arguments, float(tail_probability))
1547
+
1548
+ def ordinate_valid(self, other: UncertainOrdinate, strict_x: bool = True, strict_y: bool = True,
1549
+ x_order: SortOrder | str = SortOrder.ASCENDING, y_order: SortOrder | str = SortOrder.ASCENDING,
1550
+ other_is_next: bool = True, allow_different_types: bool = False,
1551
+ tail_probability: float | None = None) -> bool:
1552
+ """Return whether this ordinate and a neighbor satisfy an order (``OrdinateValid``).
1553
+
1554
+ The y order is checked on the distributions' quantiles (at the tail probability when given).
1555
+
1556
+ Parameters
1557
+ ----------
1558
+ other : UncertainOrdinate
1559
+ The neighbor.
1560
+ strict_x, strict_y : bool, optional
1561
+ Whether equal values break the order; default True.
1562
+ x_order, y_order : SortOrder or str, optional
1563
+ The required orders; default ascending.
1564
+ other_is_next : bool, optional
1565
+ Whether ``other`` follows this ordinate; default True.
1566
+ allow_different_types : bool, optional
1567
+ Whether the two distributions may be of different families; default False.
1568
+ tail_probability : float, optional
1569
+ The tail probability the y order is checked at; default the Numerics default.
1570
+
1571
+ Returns
1572
+ -------
1573
+ bool
1574
+ Whether the pair is valid.
1575
+ """
1576
+ return bool(self.net.OrdinateValid(*self._comparison(other, strict_x, strict_y, x_order, y_order,
1577
+ other_is_next, allow_different_types, tail_probability)))
1578
+
1579
+ def ordinate_errors(self, other: UncertainOrdinate | None = None, strict_x: bool = True, strict_y: bool = True,
1580
+ x_order: SortOrder | str = SortOrder.ASCENDING,
1581
+ y_order: SortOrder | str = SortOrder.ASCENDING, other_is_next: bool = True,
1582
+ allow_different_types: bool = False, tail_probability: float | None = None) -> list[str]:
1583
+ """Return the validation messages of this ordinate, or of it and a neighbor (``OrdinateErrors``).
1584
+
1585
+ Parameters
1586
+ ----------
1587
+ other : UncertainOrdinate, optional
1588
+ The neighbor; without it only this ordinate is checked.
1589
+ strict_x, strict_y : bool, optional
1590
+ Whether equal values break the order; default True.
1591
+ x_order, y_order : SortOrder or str, optional
1592
+ The required orders; default ascending.
1593
+ other_is_next : bool, optional
1594
+ Whether ``other`` follows this ordinate; default True.
1595
+ allow_different_types : bool, optional
1596
+ Whether the two distributions may be of different families; default False.
1597
+ tail_probability : float, optional
1598
+ The tail probability the y order is checked at; default the Numerics default.
1599
+
1600
+ Returns
1601
+ -------
1602
+ list of str
1603
+ The messages (empty when valid).
1604
+ """
1605
+ if other is None:
1606
+ return [str(m) for m in self.net.OrdinateErrors()]
1607
+ return [str(m) for m in self.net.OrdinateErrors(*self._comparison(
1608
+ other, strict_x, strict_y, x_order, y_order, other_is_next, allow_different_types, tail_probability))]
1609
+
1610
+ def to_xml(self) -> str:
1611
+ """Return the uncertain ordinate's ``ToXElement()`` as XML text.
1612
+
1613
+ Returns
1614
+ -------
1615
+ str
1616
+ The element as .NET writes it.
1617
+ """
1618
+ return str(self.net.ToXElement().ToString())
1619
+
1620
+
1621
+ class UncertainOrderedPairedData:
1622
+ """Ordered x values with a distribution of y at each (``Numerics.Data.UncertainOrderedPairedData``).
1623
+
1624
+ Parameters
1625
+ ----------
1626
+ x : array_like
1627
+ The x values.
1628
+ distributions : sequence of UnivariateDistribution
1629
+ One distribution of y per x value.
1630
+ strict_x : bool, optional
1631
+ Whether x must be strictly monotone; default True.
1632
+ x_order : SortOrder or str, optional
1633
+ Order of x; default ascending.
1634
+ strict_y : bool, optional
1635
+ Whether the y quantiles must be strictly monotone; default True.
1636
+ y_order : SortOrder or str, optional
1637
+ Order of y; default ascending.
1638
+ distribution_type : str, optional
1639
+ The ``UnivariateDistributionType`` of the y distributions; default the type of the first.
1640
+
1641
+ Attributes
1642
+ ----------
1643
+ net : Numerics.Data.UncertainOrderedPairedData
1644
+ The wrapped .NET object.
1645
+ """
1646
+
1647
+ def __init__(self, x: Any, distributions: Sequence[Any], strict_x: bool = True,
1648
+ x_order: SortOrder | str = SortOrder.ASCENDING, strict_y: bool = True,
1649
+ y_order: SortOrder | str = SortOrder.ASCENDING, distribution_type: str | None = None) -> None:
1650
+ data = _data()
1651
+ nd = namespace("Numerics.Distributions")
1652
+ import System # noqa: PLC0415 - the runtime is running
1653
+
1654
+ nets = [_net_distribution(d) for d in distributions]
1655
+ kind = nets[0].Type if distribution_type is None else net_enum(nd.UnivariateDistributionType,
1656
+ distribution_type)
1657
+ self.net = data.UncertainOrderedPairedData(
1658
+ to_net_doubles(x), System.Array[nd.UnivariateDistributionBase](nets), bool(strict_x),
1659
+ net_enum(data.SortOrder, x_order), bool(strict_y), net_enum(data.SortOrder, y_order), kind)
1660
+
1661
+ @classmethod
1662
+ def _from_net(cls, net: Any) -> UncertainOrderedPairedData:
1663
+ """Wrap an existing .NET object."""
1664
+ instance = cls.__new__(cls)
1665
+ instance.net = net
1666
+ return instance
1667
+
1668
+ @classmethod
1669
+ def from_xml(cls, xml: str) -> UncertainOrderedPairedData:
1670
+ """Restore the paired data from its XML (the ``XElement`` constructor).
1671
+
1672
+ Parameters
1673
+ ----------
1674
+ xml : str
1675
+ The text :meth:`to_xml` returns.
1676
+
1677
+ Returns
1678
+ -------
1679
+ UncertainOrderedPairedData
1680
+ The paired data.
1681
+ """
1682
+ return cls._from_net(_data().UncertainOrderedPairedData(parse_xml(xml)))
1683
+
1684
+ def to_xml(self) -> str:
1685
+ """Return the paired data's ``SaveToXElement()`` as XML text.
1686
+
1687
+ Returns
1688
+ -------
1689
+ str
1690
+ The element as .NET writes it.
1691
+ """
1692
+ return str(self.net.SaveToXElement().ToString())
1693
+
1694
+ def __len__(self) -> int:
1695
+ return int(self.net.Count)
1696
+
1697
+ def __iter__(self) -> Iterator[UncertainOrdinate]:
1698
+ for net in self.net:
1699
+ yield UncertainOrdinate._from_net(net)
1700
+
1701
+ def __getitem__(self, index: int) -> UncertainOrdinate:
1702
+ return UncertainOrdinate._from_net(self.net[int(index)])
1703
+
1704
+ def __setitem__(self, index: int, ordinate: UncertainOrdinate) -> None:
1705
+ self.net[int(index)] = ordinate.net
1706
+
1707
+ def __contains__(self, ordinate: UncertainOrdinate) -> bool:
1708
+ return bool(self.net.Contains(ordinate.net))
1709
+
1710
+ @property
1711
+ def count(self) -> int:
1712
+ """The number of ordinates."""
1713
+ return int(self.net.Count)
1714
+
1715
+ @property
1716
+ def x(self) -> np.ndarray:
1717
+ """The x values."""
1718
+ return np.array([float(o.X) for o in self.net], dtype=np.float64)
1719
+
1720
+ @property
1721
+ def distributions(self) -> list[Any]:
1722
+ """The distribution of y at each x value."""
1723
+ return [_wrap_distribution(o.Y) for o in self.net]
1724
+
1725
+ @property
1726
+ def distribution(self) -> str:
1727
+ """The ``UnivariateDistributionType`` name of the y distributions."""
1728
+ return str(self.net.Distribution)
1729
+
1730
+ @property
1731
+ def is_valid(self) -> bool:
1732
+ """Whether the ordinates satisfy the configured orders."""
1733
+ return bool(self.net.IsValid)
1734
+
1735
+ @property
1736
+ def errors(self) -> list[str]:
1737
+ """The validation errors (``GetErrors``)."""
1738
+ return [str(message) for message in self.net.GetErrors()]
1739
+
1740
+ @property
1741
+ def strict_x(self) -> bool:
1742
+ """Whether x must be strictly monotone."""
1743
+ return bool(self.net.StrictX)
1744
+
1745
+ @strict_x.setter
1746
+ def strict_x(self, value: bool) -> None:
1747
+ self.net.StrictX = bool(value)
1748
+
1749
+ @property
1750
+ def strict_y(self) -> bool:
1751
+ """Whether the y quantiles must be strictly monotone."""
1752
+ return bool(self.net.StrictY)
1753
+
1754
+ @strict_y.setter
1755
+ def strict_y(self, value: bool) -> None:
1756
+ self.net.StrictY = bool(value)
1757
+
1758
+ @property
1759
+ def order_x(self) -> SortOrder:
1760
+ """The required order of x."""
1761
+ return _sort_order(self.net.OrderX)
1762
+
1763
+ @order_x.setter
1764
+ def order_x(self, value: SortOrder | str) -> None:
1765
+ self.net.OrderX = net_enum(_data().SortOrder, value)
1766
+
1767
+ @property
1768
+ def order_y(self) -> SortOrder:
1769
+ """The required order of y."""
1770
+ return _sort_order(self.net.OrderY)
1771
+
1772
+ @order_y.setter
1773
+ def order_y(self, value: SortOrder | str) -> None:
1774
+ self.net.OrderY = net_enum(_data().SortOrder, value)
1775
+
1776
+ @property
1777
+ def allow_different_distribution_types(self) -> bool:
1778
+ """Whether the y distributions may be of different families."""
1779
+ return bool(self.net.AllowDifferentDistributionTypes)
1780
+
1781
+ @allow_different_distribution_types.setter
1782
+ def allow_different_distribution_types(self, value: bool) -> None:
1783
+ self.net.AllowDifferentDistributionTypes = bool(value)
1784
+
1785
+ @property
1786
+ def validation_tail_probability(self) -> float:
1787
+ """The tail probability the y order is validated at (NaN: the Numerics default)."""
1788
+ return float(self.net.ValidationTailProbability)
1789
+
1790
+ @validation_tail_probability.setter
1791
+ def validation_tail_probability(self, value: float) -> None:
1792
+ self.net.ValidationTailProbability = float(value)
1793
+
1794
+ def validate(self) -> None:
1795
+ """Revalidate every ordinate (``Validate``)."""
1796
+ self.net.Validate()
1797
+
1798
+ def curve_sample(self, probability: float | None = None) -> OrderedPairedData:
1799
+ """Return the curve at a quantile of every distribution, or at their means (``CurveSample``).
1800
+
1801
+ Parameters
1802
+ ----------
1803
+ probability : float, optional
1804
+ The non-exceedance probability, clipped to [0, 1]; default the means.
1805
+
1806
+ Returns
1807
+ -------
1808
+ OrderedPairedData
1809
+ The sampled curve with this object's orders.
1810
+ """
1811
+ net = self.net.CurveSample() if probability is None else self.net.CurveSample(float(probability))
1812
+ return OrderedPairedData._from_net(net)
1813
+
1814
+ def copy(self) -> UncertainOrderedPairedData:
1815
+ """Return an independent copy (``Clone``).
1816
+
1817
+ Returns
1818
+ -------
1819
+ UncertainOrderedPairedData
1820
+ The copy.
1821
+ """
1822
+ return UncertainOrderedPairedData._from_net(self.net.Clone())
1823
+
1824
+ def append(self, x: float, distribution: Any) -> None:
1825
+ """Append an uncertain ordinate (``Add``).
1826
+
1827
+ Parameters
1828
+ ----------
1829
+ x : float
1830
+ The x value.
1831
+ distribution : UnivariateDistribution
1832
+ The distribution of y.
1833
+ """
1834
+ self.net.Add(_data().UncertainOrdinate(float(x), _net_distribution(distribution)))
1835
+
1836
+ def _ordinates(self, x: Any, distributions: Sequence[Any]) -> Any:
1837
+ """Return a ``UncertainOrdinate[]`` of paired values and distributions."""
1838
+ data = _data()
1839
+ import System # noqa: PLC0415 - the runtime is running
1840
+
1841
+ values = np.asarray(x, dtype=np.float64).ravel().tolist()
1842
+ return System.Array[data.UncertainOrdinate]([
1843
+ data.UncertainOrdinate(v, _net_distribution(d)) for v, d in zip(values, distributions, strict=True)])
1844
+
1845
+ def extend(self, x: Any, distributions: Sequence[Any]) -> None:
1846
+ """Append several uncertain ordinates (``AddRange``).
1847
+
1848
+ Parameters
1849
+ ----------
1850
+ x : array_like
1851
+ The x values.
1852
+ distributions : sequence of UnivariateDistribution
1853
+ One distribution of y per x value.
1854
+ """
1855
+ self.net.AddRange(self._ordinates(x, distributions))
1856
+
1857
+ def insert(self, index: int, x: float, distribution: Any) -> None:
1858
+ """Insert an uncertain ordinate before a position (``Insert``).
1859
+
1860
+ Parameters
1861
+ ----------
1862
+ index : int
1863
+ The position.
1864
+ x : float
1865
+ The x value.
1866
+ distribution : UnivariateDistribution
1867
+ The distribution of y.
1868
+ """
1869
+ self.net.Insert(int(index), _data().UncertainOrdinate(float(x), _net_distribution(distribution)))
1870
+
1871
+ def insert_range(self, index: int, x: Any, distributions: Sequence[Any]) -> None:
1872
+ """Insert several uncertain ordinates before a position (``InsertRange``).
1873
+
1874
+ Parameters
1875
+ ----------
1876
+ index : int
1877
+ The position.
1878
+ x : array_like
1879
+ The x values.
1880
+ distributions : sequence of UnivariateDistribution
1881
+ One distribution of y per x value.
1882
+ """
1883
+ self.net.InsertRange(int(index), self._ordinates(x, distributions))
1884
+
1885
+ def index_of(self, ordinate: UncertainOrdinate) -> int:
1886
+ """Return the position of an uncertain ordinate, or -1 (``IndexOf``).
1887
+
1888
+ Parameters
1889
+ ----------
1890
+ ordinate : UncertainOrdinate
1891
+ The ordinate (equal x and an equal distribution).
1892
+
1893
+ Returns
1894
+ -------
1895
+ int
1896
+ The first position holding it, or -1.
1897
+ """
1898
+ return int(self.net.IndexOf(ordinate.net))
1899
+
1900
+ def remove(self, ordinate: UncertainOrdinate) -> bool:
1901
+ """Remove the first equal uncertain ordinate (``Remove``).
1902
+
1903
+ Parameters
1904
+ ----------
1905
+ ordinate : UncertainOrdinate
1906
+ The ordinate.
1907
+
1908
+ Returns
1909
+ -------
1910
+ bool
1911
+ Whether one was removed.
1912
+ """
1913
+ return bool(self.net.Remove(ordinate.net))
1914
+
1915
+ def remove_at(self, index: int) -> None:
1916
+ """Remove the uncertain ordinate at a position (``RemoveAt``).
1917
+
1918
+ Parameters
1919
+ ----------
1920
+ index : int
1921
+ The position.
1922
+ """
1923
+ self.net.RemoveAt(int(index))
1924
+
1925
+ def remove_range(self, index: Any, count: int | None = None) -> None:
1926
+ """Remove a run of uncertain ordinates, or the ordinates at several rows (``RemoveRange``).
1927
+
1928
+ Parameters
1929
+ ----------
1930
+ index : int or sequence of int
1931
+ The first position of the run, or the rows to remove (any order).
1932
+ count : int, optional
1933
+ How many to remove from ``index``; omit when ``index`` lists rows.
1934
+ """
1935
+ if count is None:
1936
+ self.net.RemoveRange(to_net_ints(index))
1937
+ else:
1938
+ self.net.RemoveRange(int(index), int(count))
1939
+
1940
+ def clear(self) -> None:
1941
+ """Remove every uncertain ordinate (``Clear``)."""
1942
+ self.net.Clear()
1943
+
1944
+
1945
+ class ProbabilityOrdinates:
1946
+ """A list of exceedance probabilities for frequency curves (``Numerics.Data.ProbabilityOrdinates``).
1947
+
1948
+ Parameters
1949
+ ----------
1950
+ probabilities : array_like, optional
1951
+ The probabilities; default the 25 standard ordinates from 1e-6 to 0.99.
1952
+
1953
+ Attributes
1954
+ ----------
1955
+ net : Numerics.Data.ProbabilityOrdinates
1956
+ The wrapped .NET object.
1957
+ """
1958
+
1959
+ DEFAULT_DELIMITER = "|"
1960
+ """The delimiter of :meth:`to_delimited_string` and :meth:`parse` (``DefaultDelimiter``)."""
1961
+
1962
+ def __init__(self, probabilities: Any = None) -> None:
1963
+ data = _data()
1964
+ self.net = data.ProbabilityOrdinates() if probabilities is None else \
1965
+ data.ProbabilityOrdinates(to_net_doubles(probabilities))
1966
+
1967
+ @classmethod
1968
+ def parse(cls, value: str, delimiter: str = DEFAULT_DELIMITER) -> ProbabilityOrdinates:
1969
+ """Read probabilities from delimited text (``Parse``); unreadable tokens are skipped.
1970
+
1971
+ Parameters
1972
+ ----------
1973
+ value : str
1974
+ The text, for example ``"0.01|0.1|0.5"``.
1975
+ delimiter : str, optional
1976
+ The separator; default ``"|"``.
1977
+
1978
+ Returns
1979
+ -------
1980
+ ProbabilityOrdinates
1981
+ The probabilities.
1982
+ """
1983
+ instance = cls.__new__(cls)
1984
+ instance.net = _data().ProbabilityOrdinates.Parse(str(value), str(delimiter))
1985
+ return instance
1986
+
1987
+ def __len__(self) -> int:
1988
+ return int(self.net.Count)
1989
+
1990
+ def __getitem__(self, index: int) -> float:
1991
+ return float(self.net[int(index)])
1992
+
1993
+ def __setitem__(self, index: int, value: float) -> None:
1994
+ self.net[int(index)] = float(value)
1995
+
1996
+ @property
1997
+ def values(self) -> np.ndarray:
1998
+ """The probabilities, copied in one transfer."""
1999
+ return to_numpy_doubles(self.net)
2000
+
2001
+ def to_delimited_string(self, delimiter: str = DEFAULT_DELIMITER) -> str:
2002
+ """Return the probabilities as delimited text in round-trip precision (``ToDelimitedString``).
2003
+
2004
+ Parameters
2005
+ ----------
2006
+ delimiter : str, optional
2007
+ The separator; default ``"|"``.
2008
+
2009
+ Returns
2010
+ -------
2011
+ str
2012
+ The text.
2013
+ """
2014
+ return str(self.net.ToDelimitedString(str(delimiter)))
2015
+
2016
+ def from_delimited_string(self, value: str, delimiter: str = DEFAULT_DELIMITER) -> None:
2017
+ """Replace the probabilities with those read from delimited text (``FromDelimitedString``).
2018
+
2019
+ Parameters
2020
+ ----------
2021
+ value : str
2022
+ The text.
2023
+ delimiter : str, optional
2024
+ The separator; default ``"|"``.
2025
+ """
2026
+ self.net.FromDelimitedString(str(value), str(delimiter))
2027
+
2028
+ def reset_to_defaults(self) -> None:
2029
+ """Replace the probabilities with the 25 standard ordinates (``ResetToDefaults``)."""
2030
+ self.net.ResetToDefaults()
2031
+
2032
+ def validate(self) -> tuple[bool, list[str]]:
2033
+ """Check that the list is non-empty, in (0, 1) and strictly increasing (``Validate``).
2034
+
2035
+ Returns
2036
+ -------
2037
+ tuple of (bool, list of str)
2038
+ Whether the list is valid, and the messages.
2039
+ """
2040
+ result = self.net.Validate()
2041
+ return bool(result.Item1), [str(m) for m in result.Item2]
2042
+
2043
+ def append(self, value: float) -> None:
2044
+ """Append a probability (``Add``).
2045
+
2046
+ Parameters
2047
+ ----------
2048
+ value : float
2049
+ The probability.
2050
+ """
2051
+ self.net.Add(float(value))
2052
+
2053
+ def extend(self, values: Any) -> None:
2054
+ """Append several probabilities (``AddRange``).
2055
+
2056
+ Parameters
2057
+ ----------
2058
+ values : array_like
2059
+ The probabilities.
2060
+ """
2061
+ self.net.AddRange(to_net_doubles(values))
2062
+
2063
+ def insert(self, index: int, value: float) -> None:
2064
+ """Insert a probability before a position (``Insert``).
2065
+
2066
+ Parameters
2067
+ ----------
2068
+ index : int
2069
+ The position.
2070
+ value : float
2071
+ The probability.
2072
+ """
2073
+ self.net.Insert(int(index), float(value))
2074
+
2075
+ def insert_range(self, index: int, values: Any) -> None:
2076
+ """Insert several probabilities before a position (``InsertRange``).
2077
+
2078
+ Parameters
2079
+ ----------
2080
+ index : int
2081
+ The position.
2082
+ values : array_like
2083
+ The probabilities.
2084
+ """
2085
+ self.net.InsertRange(int(index), to_net_doubles(values))
2086
+
2087
+ def remove(self, value: float) -> bool:
2088
+ """Remove the first equal probability (``Remove``).
2089
+
2090
+ Parameters
2091
+ ----------
2092
+ value : float
2093
+ The probability.
2094
+
2095
+ Returns
2096
+ -------
2097
+ bool
2098
+ Whether one was removed.
2099
+ """
2100
+ return bool(self.net.Remove(float(value)))
2101
+
2102
+ def remove_at(self, index: int) -> None:
2103
+ """Remove the probability at a position (``RemoveAt``).
2104
+
2105
+ Parameters
2106
+ ----------
2107
+ index : int
2108
+ The position.
2109
+ """
2110
+ self.net.RemoveAt(int(index))
2111
+
2112
+ def remove_range(self, index: int, count: int) -> None:
2113
+ """Remove a run of probabilities (``RemoveRange``).
2114
+
2115
+ Parameters
2116
+ ----------
2117
+ index : int
2118
+ The first position.
2119
+ count : int
2120
+ How many to remove.
2121
+ """
2122
+ self.net.RemoveRange(int(index), int(count))
2123
+
2124
+ def clear(self) -> None:
2125
+ """Remove every probability (``Clear``)."""
2126
+ self.net.Clear()
2127
+
2128
+
2129
+ def _ordinate_list(x: Any, y: Any) -> Any:
2130
+ """Return a ``List<Ordinate>`` of paired arrays, built in .NET from one unvalidated paired data object."""
2131
+ data = _data()
2132
+ from System.Collections.Generic import List # noqa: PLC0415 - the runtime is running
2133
+
2134
+ unordered = getattr(data.SortOrder, "None")
2135
+ pairs = data.OrderedPairedData(to_net_doubles(x), to_net_doubles(y), False, unordered, False, unordered)
2136
+ return List[data.Ordinate](pairs)
2137
+
2138
+
2139
+ def ramer_douglas_peucker(x: Any, y: Any, epsilon: float) -> tuple[np.ndarray, np.ndarray]:
2140
+ """Simplify a polyline by the Ramer-Douglas-Peucker algorithm (``LineSimplification.RamerDouglasPeucker``).
2141
+
2142
+ The points cross once, as two arrays; the kept points come back one ordinate at a time.
2143
+
2144
+ Parameters
2145
+ ----------
2146
+ x, y : array_like
2147
+ The polyline vertices, in order (at least two).
2148
+ epsilon : float
2149
+ The largest perpendicular distance a removed vertex may have from the simplified line.
2150
+
2151
+ Returns
2152
+ -------
2153
+ tuple of numpy.ndarray
2154
+ The x and y of the kept vertices, end points included.
2155
+ """
2156
+ data = _data()
2157
+ from System.Collections.Generic import List # noqa: PLC0415 - the runtime is running
2158
+
2159
+ output = List[data.Ordinate]()
2160
+ result = data.LineSimplification.RamerDouglasPeucker(_ordinate_list(x, y), float(epsilon), output)
2161
+ kept = result[1] if isinstance(result, tuple) else output
2162
+ return (np.array([float(o.X) for o in kept], dtype=np.float64),
2163
+ np.array([float(o.Y) for o in kept], dtype=np.float64))
2164
+
2165
+
2166
+ def perpendicular_distance(point: Any, line_start: Any, line_end: Any) -> float:
2167
+ """Return the distance of a point from the line through two points (``LineSimplification.PerpendicularDistance``).
2168
+
2169
+ Parameters
2170
+ ----------
2171
+ point, line_start, line_end : Ordinate or tuple of float
2172
+ The point and two points on the line, as ordinates or ``(x, y)`` pairs.
2173
+
2174
+ Returns
2175
+ -------
2176
+ float
2177
+ The perpendicular distance (the distance to ``line_start`` when the two line points coincide).
2178
+ """
2179
+ data = _data()
2180
+
2181
+ def net(p: Any) -> Any:
2182
+ return p.net if isinstance(p, Ordinate) else data.Ordinate(float(p[0]), float(p[1]))
2183
+
2184
+ return float(data.LineSimplification.PerpendicularDistance(net(point), net(line_start), net(line_end)))
2185
+
2186
+
2187
+ def time_series_index_ticks(series: Any) -> np.ndarray:
2188
+ """Return the dates of a time series as ``DateTime.Ticks`` (``TimeSeries.IndexTicksToArray``).
2189
+
2190
+ One ``long[]`` crosses instead of one ``DateTime`` per ordinate. A tick is 100 nanoseconds
2191
+ since 0001-01-01 00:00; the ``DateTime.Kind`` is not included. For example,
2192
+ ``(ticks - 621355968000000000) // 10`` are microseconds since 1970-01-01, which
2193
+ ``.astype("datetime64[us]")`` turns into NumPy dates.
2194
+
2195
+ Parameters
2196
+ ----------
2197
+ series : Numerics.Data.TimeSeries
2198
+ The .NET time series (or an object holding it as ``.net``).
2199
+
2200
+ Returns
2201
+ -------
2202
+ numpy.ndarray
2203
+ The int64 ticks of each ordinate date, in collection order.
2204
+ """
2205
+ net = getattr(series, "net", series)
2206
+ return to_numpy_longs(net.IndexTicksToArray())
2207
+
2208
+
2209
+ def time_series_values(series: Any) -> np.ndarray:
2210
+ """Return the values of a time series (``Series.ValuesToArray``).
2211
+
2212
+ Parameters
2213
+ ----------
2214
+ series : Numerics.Data.TimeSeries
2215
+ The .NET time series (or an object holding it as ``.net``).
2216
+
2217
+ Returns
2218
+ -------
2219
+ numpy.ndarray
2220
+ The float64 value of each ordinate, in collection order, aligned with
2221
+ :func:`time_series_index_ticks`; missing values are NaN.
2222
+ """
2223
+ return to_numpy_doubles(getattr(series, "net", series).ValuesToArray())
2224
+
2225
+
2226
+ __all__ = [
2227
+ "Bilinear", "CubicSpline", "Interpolater", "Linear", "LinearRegression", "Ordinate", "OrderedPairedData",
2228
+ "Polynomial", "ProbabilityOrdinates", "SearchMethod", "UncertainOrderedPairedData", "UncertainOrdinate",
2229
+ "parse_xml", "perpendicular_distance", "ramer_douglas_peucker", "search", "time_series_index_ticks",
2230
+ "time_series_values",
2231
+ ]
2232
+
2233
+ translate_public(globals())