pynumerics 2.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pynumerics/__init__.py +73 -0
- pynumerics/_bin/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- pynumerics/_bin/Microsoft.CodeAnalysis.LICENSE.txt +27 -0
- pynumerics/_bin/Microsoft.CodeAnalysis.dll +0 -0
- pynumerics/_bin/Numerics.dll +0 -0
- pynumerics/_bin/pynumerics.runtimeconfig.json +15 -0
- pynumerics/_coverage.py +3825 -0
- pynumerics/_util.py +237 -0
- pynumerics/_version.py +40 -0
- pynumerics/bootstrap.py +593 -0
- pynumerics/callbacks.py +791 -0
- pynumerics/copulas.py +910 -0
- pynumerics/data.py +2233 -0
- pynumerics/differentiation.py +348 -0
- pynumerics/distributions.py +4043 -0
- pynumerics/download.py +623 -0
- pynumerics/enums.py +119 -0
- pynumerics/errors.py +245 -0
- pynumerics/functions.py +1142 -0
- pynumerics/goodness_of_fit.py +699 -0
- pynumerics/graphs.py +556 -0
- pynumerics/hypothesis_tests.py +445 -0
- pynumerics/identity.py +144 -0
- pynumerics/integration.py +677 -0
- pynumerics/linalg.py +1408 -0
- pynumerics/machine_learning.py +1335 -0
- pynumerics/marshal.py +733 -0
- pynumerics/mathematics.py +1053 -0
- pynumerics/mcmc.py +1406 -0
- pynumerics/multivariate.py +1539 -0
- pynumerics/ode.py +156 -0
- pynumerics/optimization.py +1736 -0
- pynumerics/probability.py +966 -0
- pynumerics/py.typed +0 -0
- pynumerics/regional.py +522 -0
- pynumerics/roots.py +274 -0
- pynumerics/runtime.py +384 -0
- pynumerics/sampling.py +1758 -0
- pynumerics/special.py +1079 -0
- pynumerics/statistics.py +1958 -0
- pynumerics/timeseries.py +1326 -0
- pynumerics-2.3.0.dist-info/METADATA +142 -0
- pynumerics-2.3.0.dist-info/RECORD +46 -0
- pynumerics-2.3.0.dist-info/WHEEL +5 -0
- pynumerics-2.3.0.dist-info/licenses/LICENSE +10 -0
- pynumerics-2.3.0.dist-info/top_level.txt +1 -0
pynumerics/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())
|