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
|
@@ -0,0 +1,4043 @@
|
|
|
1
|
+
"""Univariate distributions from ``Numerics.Distributions``.
|
|
2
|
+
|
|
3
|
+
Every class wraps one .NET distribution object (``.net``). Evaluation members accept a
|
|
4
|
+
scalar or any array: a scalar calls the scalar member, an array crosses once as a
|
|
5
|
+
``System.Double[]`` into the ``UnivariateDistributionBase`` array overloads
|
|
6
|
+
(``PDF(IList<double>)``, ``CDF(IList<double>)``, ``InverseCDF(IList<double>)``), and
|
|
7
|
+
random generation uses ``GenerateRandomValues(n, seed)``, so no numerical work happens
|
|
8
|
+
in Python. Parameters follow the Numerics conventions (for example the generalized
|
|
9
|
+
extreme value shape is Hosking's kappa, and ``LogNormal`` is base 10 while ``LnNormal``
|
|
10
|
+
is natural-log with real-space mean and standard deviation).
|
|
11
|
+
|
|
12
|
+
Distributions compare by value: two wrappers are equal when they wrap the same .NET family with
|
|
13
|
+
bitwise-equal parameters (and the same ``Base`` where the family has one). A mutable distribution
|
|
14
|
+
is not hashable, because its value can change; :meth:`UnivariateDistribution.frozen` returns a
|
|
15
|
+
read-only :class:`FrozenDistribution` that is hashable by the same value. :data:`FAMILIES` maps each
|
|
16
|
+
Numerics type name to its typed class.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import types
|
|
22
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
import numpy as np
|
|
26
|
+
|
|
27
|
+
from pynumerics import enums
|
|
28
|
+
from pynumerics._util import (
|
|
29
|
+
_data,
|
|
30
|
+
_nd,
|
|
31
|
+
bind,
|
|
32
|
+
bind_list,
|
|
33
|
+
cached_overload,
|
|
34
|
+
evaluate,
|
|
35
|
+
list_overload,
|
|
36
|
+
loop,
|
|
37
|
+
namespace,
|
|
38
|
+
net_enum,
|
|
39
|
+
unwrap,
|
|
40
|
+
)
|
|
41
|
+
from pynumerics.errors import net_errors, translate, translate_class, translate_public, translated
|
|
42
|
+
from pynumerics.marshal import (
|
|
43
|
+
to_net_doubles,
|
|
44
|
+
to_net_ints,
|
|
45
|
+
to_net_matrix,
|
|
46
|
+
to_numpy_bools,
|
|
47
|
+
to_numpy_doubles,
|
|
48
|
+
to_numpy_matrix,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
_PROTOTYPES: dict[str, Any] = {}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _prototype(type_name: str) -> Any:
|
|
55
|
+
"""Return a default-parameter .NET instance of a family, created once per process.
|
|
56
|
+
|
|
57
|
+
Used read-only, for the class-level ``parameter_names`` and ``validate_parameters``. A family
|
|
58
|
+
the factory cannot build without parameters (``Mixture`` needs its components) has none: the
|
|
59
|
+
factory's ``NotSupportedException`` is raised as ``NotImplementedError``.
|
|
60
|
+
"""
|
|
61
|
+
net = _PROTOTYPES.get(type_name)
|
|
62
|
+
if net is None:
|
|
63
|
+
nd = _nd()
|
|
64
|
+
with net_errors():
|
|
65
|
+
net = _PROTOTYPES[type_name] = unwrap(nd.UnivariateDistributionFactory.CreateDistribution(
|
|
66
|
+
net_enum(nd.UnivariateDistributionType, type_name)))
|
|
67
|
+
return net
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _fixed_arity(net: Any) -> bool:
|
|
71
|
+
"""Return whether the family's parameter vector describes it (``GetParameters`` has ``NumberOfParameters`` values).
|
|
72
|
+
|
|
73
|
+
False for the table families (``EmpiricalDistribution``, ``KernelDensity``), whose state is not
|
|
74
|
+
a parameter vector; their arity is not checked.
|
|
75
|
+
"""
|
|
76
|
+
return len(net.GetParameters) == int(net.NumberOfParameters)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _check_arity(net: Any, parameters: Any) -> np.ndarray:
|
|
80
|
+
"""Return ``parameters`` as float64, or raise ValueError when their count is not the family's."""
|
|
81
|
+
values = np.atleast_1d(np.asarray(parameters, dtype=np.float64))
|
|
82
|
+
if values.ndim != 1:
|
|
83
|
+
raise ValueError(f"Expected a one-dimensional parameter vector; got shape {values.shape}.")
|
|
84
|
+
expected = int(net.NumberOfParameters)
|
|
85
|
+
if _fixed_arity(net) and values.size != expected:
|
|
86
|
+
names = ", ".join(str(name) for name in net.GetParameterPropertyNames)
|
|
87
|
+
raise ValueError(f"{net.Type} takes {expected} parameters ({names}); got {values.size}.")
|
|
88
|
+
return values
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _net_of(owner: Any) -> Any:
|
|
92
|
+
"""Return the .NET object a family method runs on: the instance's, or the typed class's default instance.
|
|
93
|
+
|
|
94
|
+
Raises
|
|
95
|
+
------
|
|
96
|
+
TypeError
|
|
97
|
+
If ``owner`` is the generic :class:`UnivariateDistribution` class.
|
|
98
|
+
"""
|
|
99
|
+
if isinstance(owner, type):
|
|
100
|
+
type_name = _class_type_name(owner)
|
|
101
|
+
if type_name is None:
|
|
102
|
+
raise TypeError("This method needs a typed family class or an instance.")
|
|
103
|
+
return _prototype(type_name)
|
|
104
|
+
return owner.net
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _interface(net: Any, name: str) -> Any:
|
|
108
|
+
"""Return ``net`` viewed through the ``Numerics.Distributions`` interface ``name``.
|
|
109
|
+
|
|
110
|
+
Raises
|
|
111
|
+
------
|
|
112
|
+
NotImplementedError
|
|
113
|
+
If the family does not implement the interface.
|
|
114
|
+
"""
|
|
115
|
+
import clr # noqa: PLC0415 - the runtime is running (net exists)
|
|
116
|
+
|
|
117
|
+
interface = getattr(_nd(), name)
|
|
118
|
+
if not clr.GetClrType(interface).IsInstanceOfType(net):
|
|
119
|
+
raise NotImplementedError(f"{net.DisplayName} does not implement {name}.")
|
|
120
|
+
return interface(net)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _net_property(name: str, doc: str, kind: Callable[[Any], Any] = float, settable: bool = True) -> property:
|
|
124
|
+
"""Return a property that reads (and, when ``settable``, writes) the .NET property ``name`` of ``self.net``.
|
|
125
|
+
|
|
126
|
+
The setter calls the owner's ``_ensure_mutable`` first when it has one (distributions do) and
|
|
127
|
+
passes the value through ``kind``; Numerics validates it as its own setter does.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
def fget(self: Any) -> Any:
|
|
131
|
+
return kind(getattr(self.net, name))
|
|
132
|
+
|
|
133
|
+
if not settable:
|
|
134
|
+
return property(fget, doc=doc)
|
|
135
|
+
|
|
136
|
+
def fset(self: Any, value: Any) -> None:
|
|
137
|
+
ensure_mutable = getattr(self, "_ensure_mutable", None)
|
|
138
|
+
if ensure_mutable is not None:
|
|
139
|
+
ensure_mutable()
|
|
140
|
+
setattr(self.net, name, kind(value))
|
|
141
|
+
|
|
142
|
+
return property(fget, fset, doc=doc)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _enum_property(name: str, net_type: Callable[[], Any], doc: str) -> property:
|
|
146
|
+
"""Return a property that reads the .NET enum property ``name`` as its member name and sets it by name."""
|
|
147
|
+
|
|
148
|
+
def fget(self: Any) -> str:
|
|
149
|
+
return str(getattr(self.net, name))
|
|
150
|
+
|
|
151
|
+
def fset(self: Any, value: Any) -> None:
|
|
152
|
+
self._ensure_mutable()
|
|
153
|
+
setattr(self.net, name, net_enum(net_type(), value))
|
|
154
|
+
|
|
155
|
+
return property(fget, fset, doc=doc)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _bins_net(bins: Any) -> Any:
|
|
159
|
+
"""Return the .NET ``List<StratificationBin>`` of a :class:`pynumerics.sampling.StratificationBins`.
|
|
160
|
+
|
|
161
|
+
A sequence of them becomes a ``List<StratificationBin>[]`` (the many-range overloads).
|
|
162
|
+
"""
|
|
163
|
+
if hasattr(bins, "net"):
|
|
164
|
+
return bins.net
|
|
165
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
166
|
+
from System.Collections.Generic import List # noqa: PLC0415
|
|
167
|
+
|
|
168
|
+
sampling = namespace("Numerics.Sampling")
|
|
169
|
+
return System.Array[List[sampling.StratificationBin]]([b.net for b in bins])
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _members_array(distributions: Sequence[UnivariateDistribution]) -> Any:
|
|
173
|
+
"""Return a ``UnivariateDistributionBase[]`` of the wrapped .NET objects."""
|
|
174
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
175
|
+
|
|
176
|
+
return System.Array[_nd().UnivariateDistributionBase]([d.net for d in distributions])
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _parse_xml(xml: str) -> Any:
|
|
180
|
+
"""Parse XML text into a ``System.Xml.Linq.XElement``."""
|
|
181
|
+
namespace("Numerics")
|
|
182
|
+
import clr # noqa: PLC0415
|
|
183
|
+
|
|
184
|
+
clr.AddReference("System.Xml.Linq")
|
|
185
|
+
from System.Xml.Linq import XElement # noqa: PLC0415
|
|
186
|
+
|
|
187
|
+
return XElement.Parse(str(xml))
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
_STATE: dict[str, tuple[bool, bool]] = {}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _state_info(net: Any) -> tuple[bool, bool]:
|
|
194
|
+
"""Return (has ``Base``, needs the XML) for the .NET type of ``net``, cached per type.
|
|
195
|
+
|
|
196
|
+
The XML joins the value key where the parameter vector is not the whole state: families that
|
|
197
|
+
write their own ``ToXElement`` (mixtures, tables, competing risks) or whose ``GetParameters`` is
|
|
198
|
+
not a full parameter vector.
|
|
199
|
+
"""
|
|
200
|
+
net_type = net.GetType()
|
|
201
|
+
key = str(net_type.FullName)
|
|
202
|
+
info = _STATE.get(key)
|
|
203
|
+
if info is None:
|
|
204
|
+
declaring = net_type.GetMethod("ToXElement", _empty_types()).DeclaringType
|
|
205
|
+
own_xml = str(declaring.FullName) != "Numerics.Distributions.UnivariateDistributionBase"
|
|
206
|
+
info = _STATE[key] = (net_type.GetProperty("Base") is not None, own_xml or not _fixed_arity(net))
|
|
207
|
+
return info
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _empty_types() -> Any:
|
|
211
|
+
"""Return an empty ``System.Type[]`` (the signature of a parameterless method lookup)."""
|
|
212
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
213
|
+
|
|
214
|
+
return System.Type.EmptyTypes
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class _FamilyAttribute:
|
|
218
|
+
"""A read-only attribute with a class-level and an instance-level value.
|
|
219
|
+
|
|
220
|
+
On a typed class (``GeneralizedExtremeValue.type_name``) it gives the family's value without an
|
|
221
|
+
instance; on an instance it reads the wrapped .NET object, so the generic wrapper of any
|
|
222
|
+
family answers too.
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
def __init__(self, on_class: Callable[[type], Any], on_instance: Callable[[Any], Any], doc: str) -> None:
|
|
226
|
+
self._on_class = on_class
|
|
227
|
+
self._on_instance = on_instance
|
|
228
|
+
self.__doc__ = doc
|
|
229
|
+
|
|
230
|
+
def __get__(self, obj: Any, owner: type) -> Any:
|
|
231
|
+
if obj is None:
|
|
232
|
+
return self._on_class(owner)
|
|
233
|
+
return self._on_instance(obj)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class _FamilyMethod:
|
|
237
|
+
"""A method callable on a typed class (on the family's default instance) or on an instance."""
|
|
238
|
+
|
|
239
|
+
def __init__(self, func: Callable[..., Any]) -> None:
|
|
240
|
+
self._func = translated(func)
|
|
241
|
+
self.__doc__ = func.__doc__
|
|
242
|
+
self.__wrapped__ = func
|
|
243
|
+
|
|
244
|
+
def __get__(self, obj: Any, owner: type) -> Any:
|
|
245
|
+
return types.MethodType(self._func, owner if obj is None else obj)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _class_type_name(cls: type) -> str | None:
|
|
249
|
+
return getattr(cls, "_type_name", None)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _class_parameter_names(cls: type) -> tuple[str, ...] | None:
|
|
253
|
+
type_name = _class_type_name(cls)
|
|
254
|
+
if type_name is None:
|
|
255
|
+
return None
|
|
256
|
+
try:
|
|
257
|
+
prototype = _prototype(type_name)
|
|
258
|
+
except NotImplementedError:
|
|
259
|
+
return None # the family has no default instance, so its names come from an instance only
|
|
260
|
+
return tuple(str(name) for name in prototype.GetParameterPropertyNames)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class UnivariateDistribution:
|
|
264
|
+
"""A wrapped ``Numerics.Distributions.UnivariateDistributionBase``.
|
|
265
|
+
|
|
266
|
+
Parameters
|
|
267
|
+
----------
|
|
268
|
+
net : Numerics.Distributions.UnivariateDistributionBase
|
|
269
|
+
The .NET distribution object; interface-typed values are unwrapped to their
|
|
270
|
+
concrete type.
|
|
271
|
+
|
|
272
|
+
Attributes
|
|
273
|
+
----------
|
|
274
|
+
net : Numerics.Distributions.UnivariateDistributionBase
|
|
275
|
+
The wrapped .NET object, for passing back into Numerics members.
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
_type_name: str | None = None
|
|
279
|
+
|
|
280
|
+
def __init__(self, net: Any) -> None:
|
|
281
|
+
self.net = unwrap(net)
|
|
282
|
+
|
|
283
|
+
def __init_subclass__(cls, **kwargs: Any) -> None:
|
|
284
|
+
super().__init_subclass__(**kwargs)
|
|
285
|
+
translate_class(cls)
|
|
286
|
+
|
|
287
|
+
def __deepcopy__(self, memo: dict) -> UnivariateDistribution:
|
|
288
|
+
"""Return a wrapper of the distribution's ``Clone``, so a deep copy owns its own .NET object."""
|
|
289
|
+
return wrap(self.net.Clone())
|
|
290
|
+
|
|
291
|
+
def _ensure_mutable(self) -> None:
|
|
292
|
+
"""Do nothing; :class:`FrozenDistribution` raises here. Members that change the distribution call it first."""
|
|
293
|
+
|
|
294
|
+
def _value_key(self) -> tuple:
|
|
295
|
+
"""Return the value compared by ``==``: .NET type, parameter bits, ``Base`` bits and, where needed, the XML."""
|
|
296
|
+
net = self.net
|
|
297
|
+
has_base, needs_xml = _state_info(net)
|
|
298
|
+
key: tuple = (str(net.GetType().FullName), to_numpy_doubles(net.GetParameters).tobytes())
|
|
299
|
+
if has_base:
|
|
300
|
+
key += (np.float64(float(net.Base)).tobytes(),)
|
|
301
|
+
if needs_xml:
|
|
302
|
+
key += (str(net.ToXElement().ToString()),)
|
|
303
|
+
return key
|
|
304
|
+
|
|
305
|
+
def __eq__(self, other: object) -> bool:
|
|
306
|
+
"""Return whether ``other`` is a distribution of the same family with bitwise-equal parameters.
|
|
307
|
+
|
|
308
|
+
The family is the wrapped .NET type; parameters compare by their bits (so ``0.0`` and ``-0.0``
|
|
309
|
+
differ and a NaN equals the same NaN), as does ``Base`` where the family has one. Families whose
|
|
310
|
+
parameter vector is not their whole state (mixtures, empirical tables, kernel densities,
|
|
311
|
+
competing risks) also compare their ``ToXElement`` text. Frozen and mutable wrappers of equal
|
|
312
|
+
values are equal.
|
|
313
|
+
"""
|
|
314
|
+
if not isinstance(other, UnivariateDistribution):
|
|
315
|
+
return NotImplemented
|
|
316
|
+
return self is other or self._value_key() == other._value_key()
|
|
317
|
+
|
|
318
|
+
__hash__ = None # type: ignore[assignment] # mutable values are unhashable; see frozen()
|
|
319
|
+
|
|
320
|
+
def frozen(self) -> FrozenDistribution:
|
|
321
|
+
"""Return a read-only, hashable copy of this distribution.
|
|
322
|
+
|
|
323
|
+
The copy wraps its own ``Clone`` of the .NET object, so later changes to this distribution do
|
|
324
|
+
not reach it. It is an instance of :class:`FrozenDistribution` and of this distribution's
|
|
325
|
+
class, so the family's members stay available.
|
|
326
|
+
|
|
327
|
+
Returns
|
|
328
|
+
-------
|
|
329
|
+
FrozenDistribution
|
|
330
|
+
The frozen copy; ``set_parameters``, ``estimate`` and attribute assignment raise, and
|
|
331
|
+
``copy.deepcopy`` returns it unchanged.
|
|
332
|
+
"""
|
|
333
|
+
cls = _frozen_class(type(self))
|
|
334
|
+
instance = cls.__new__(cls)
|
|
335
|
+
instance.__dict__["net"] = unwrap(self.net.Clone())
|
|
336
|
+
return instance
|
|
337
|
+
|
|
338
|
+
def _scalar(self, name: str) -> Any:
|
|
339
|
+
"""Return the ``double`` overload of a scalar member, bound once per instance (see ``_util.bind``)."""
|
|
340
|
+
cache = self.__dict__.setdefault("_bound", {})
|
|
341
|
+
member = cache.get(name)
|
|
342
|
+
if member is None:
|
|
343
|
+
member = cache[name] = bind(getattr(self.net, name))
|
|
344
|
+
return member
|
|
345
|
+
|
|
346
|
+
def _vector(self, name: str) -> Any:
|
|
347
|
+
"""Return the ``IList<double>`` overload of an array member, bound once per instance (``_util.bind_list``)."""
|
|
348
|
+
cache = self.__dict__.setdefault("_bound_lists", {})
|
|
349
|
+
member = cache.get(name)
|
|
350
|
+
if member is None:
|
|
351
|
+
member = cache[name] = bind_list(getattr(self.net, name))
|
|
352
|
+
return member
|
|
353
|
+
|
|
354
|
+
def _evaluate(self, name: str, values: Any) -> Any:
|
|
355
|
+
"""Evaluate a scalar member on a scalar or an array, through its array overload when Numerics has one.
|
|
356
|
+
|
|
357
|
+
A member without an ``IList<double>`` overload (``HF``) evaluates an array element by element
|
|
358
|
+
with the bound scalar overload. Both give the distribution's own scalar values, bit for bit.
|
|
359
|
+
"""
|
|
360
|
+
cache = self.__dict__.setdefault("_optional_lists", {})
|
|
361
|
+
if name not in cache:
|
|
362
|
+
cache[name] = list_overload(getattr(self.net, name))
|
|
363
|
+
vector = cache[name]
|
|
364
|
+
if vector is None:
|
|
365
|
+
return loop(values, self._scalar(name))
|
|
366
|
+
return evaluate(values, self._scalar(name), vector)
|
|
367
|
+
|
|
368
|
+
def __repr__(self) -> str:
|
|
369
|
+
return f"{type(self).__name__}({', '.join(repr(float(p)) for p in self.parameters)})"
|
|
370
|
+
|
|
371
|
+
@property
|
|
372
|
+
def display_name(self) -> str:
|
|
373
|
+
"""The Numerics display name."""
|
|
374
|
+
return str(self.net.DisplayName)
|
|
375
|
+
|
|
376
|
+
@property
|
|
377
|
+
def parameters(self) -> np.ndarray:
|
|
378
|
+
"""The parameter values in Numerics order (``GetParameters``)."""
|
|
379
|
+
return to_numpy_doubles(self.net.GetParameters)
|
|
380
|
+
|
|
381
|
+
@property
|
|
382
|
+
def parameters_valid(self) -> bool:
|
|
383
|
+
"""Whether the current parameters are valid (``ParametersValid``)."""
|
|
384
|
+
return bool(self.net.ParametersValid)
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def mean(self) -> float:
|
|
388
|
+
"""The distribution mean."""
|
|
389
|
+
return float(self.net.Mean)
|
|
390
|
+
|
|
391
|
+
@property
|
|
392
|
+
def standard_deviation(self) -> float:
|
|
393
|
+
"""The distribution standard deviation."""
|
|
394
|
+
return float(self.net.StandardDeviation)
|
|
395
|
+
|
|
396
|
+
@property
|
|
397
|
+
def skewness(self) -> float:
|
|
398
|
+
"""The distribution skewness."""
|
|
399
|
+
return float(self.net.Skewness)
|
|
400
|
+
|
|
401
|
+
@property
|
|
402
|
+
def kurtosis(self) -> float:
|
|
403
|
+
"""The distribution kurtosis."""
|
|
404
|
+
return float(self.net.Kurtosis)
|
|
405
|
+
|
|
406
|
+
@property
|
|
407
|
+
def minimum(self) -> float:
|
|
408
|
+
"""The lower end of the support."""
|
|
409
|
+
return float(self.net.Minimum)
|
|
410
|
+
|
|
411
|
+
@property
|
|
412
|
+
def maximum(self) -> float:
|
|
413
|
+
"""The upper end of the support."""
|
|
414
|
+
return float(self.net.Maximum)
|
|
415
|
+
|
|
416
|
+
def pdf(self, x: Any) -> Any:
|
|
417
|
+
"""Evaluate the probability density (or mass) function.
|
|
418
|
+
|
|
419
|
+
Parameters
|
|
420
|
+
----------
|
|
421
|
+
x : float or array_like
|
|
422
|
+
Values at which to evaluate.
|
|
423
|
+
|
|
424
|
+
Returns
|
|
425
|
+
-------
|
|
426
|
+
float or numpy.ndarray
|
|
427
|
+
Densities, shaped like ``x``.
|
|
428
|
+
"""
|
|
429
|
+
return evaluate(x, self._scalar("PDF"), self._vector("PDF"))
|
|
430
|
+
|
|
431
|
+
def cdf(self, x: Any) -> Any:
|
|
432
|
+
"""Evaluate the cumulative distribution function (non-exceedance probability).
|
|
433
|
+
|
|
434
|
+
Parameters
|
|
435
|
+
----------
|
|
436
|
+
x : float or array_like
|
|
437
|
+
Values at which to evaluate.
|
|
438
|
+
|
|
439
|
+
Returns
|
|
440
|
+
-------
|
|
441
|
+
float or numpy.ndarray
|
|
442
|
+
Non-exceedance probabilities, shaped like ``x``.
|
|
443
|
+
"""
|
|
444
|
+
return evaluate(x, self._scalar("CDF"), self._vector("CDF"))
|
|
445
|
+
|
|
446
|
+
def ccdf(self, x: Any) -> Any:
|
|
447
|
+
"""Evaluate the complementary CDF (exceedance probability) through ``CCDF``.
|
|
448
|
+
|
|
449
|
+
An array crosses once into ``CCDF(IList<double>)``; every value is the distribution's own ``CCDF``.
|
|
450
|
+
|
|
451
|
+
Parameters
|
|
452
|
+
----------
|
|
453
|
+
x : float or array_like
|
|
454
|
+
Values at which to evaluate.
|
|
455
|
+
|
|
456
|
+
Returns
|
|
457
|
+
-------
|
|
458
|
+
float or numpy.ndarray
|
|
459
|
+
Exceedance probabilities, shaped like ``x``.
|
|
460
|
+
"""
|
|
461
|
+
return self._evaluate("CCDF", x)
|
|
462
|
+
|
|
463
|
+
def inverse_cdf(self, probability: Any) -> Any:
|
|
464
|
+
"""Evaluate the quantile function at non-exceedance probabilities.
|
|
465
|
+
|
|
466
|
+
Parameters
|
|
467
|
+
----------
|
|
468
|
+
probability : float or array_like
|
|
469
|
+
Non-exceedance probabilities in [0, 1].
|
|
470
|
+
|
|
471
|
+
Returns
|
|
472
|
+
-------
|
|
473
|
+
float or numpy.ndarray
|
|
474
|
+
Quantiles, shaped like ``probability``.
|
|
475
|
+
"""
|
|
476
|
+
return evaluate(probability, self._scalar("InverseCDF"), self._vector("InverseCDF"))
|
|
477
|
+
|
|
478
|
+
def log_pdf(self, x: Any) -> Any:
|
|
479
|
+
"""Evaluate the natural logarithm of the density through ``LogPDF``.
|
|
480
|
+
|
|
481
|
+
An array crosses once into ``LogPDF(IList<double>)``; every value is the distribution's own ``LogPDF``.
|
|
482
|
+
|
|
483
|
+
Parameters
|
|
484
|
+
----------
|
|
485
|
+
x : float or array_like
|
|
486
|
+
Values at which to evaluate.
|
|
487
|
+
|
|
488
|
+
Returns
|
|
489
|
+
-------
|
|
490
|
+
float or numpy.ndarray
|
|
491
|
+
Log densities, shaped like ``x``.
|
|
492
|
+
"""
|
|
493
|
+
return self._evaluate("LogPDF", x)
|
|
494
|
+
|
|
495
|
+
def log_cdf(self, x: Any) -> Any:
|
|
496
|
+
"""Evaluate the natural logarithm of the CDF through ``LogCDF``.
|
|
497
|
+
|
|
498
|
+
An array crosses once into ``LogCDF(IList<double>)``.
|
|
499
|
+
|
|
500
|
+
Parameters
|
|
501
|
+
----------
|
|
502
|
+
x : float or array_like
|
|
503
|
+
Values at which to evaluate.
|
|
504
|
+
|
|
505
|
+
Returns
|
|
506
|
+
-------
|
|
507
|
+
float or numpy.ndarray
|
|
508
|
+
Log non-exceedance probabilities, shaped like ``x``.
|
|
509
|
+
"""
|
|
510
|
+
return self._evaluate("LogCDF", x)
|
|
511
|
+
|
|
512
|
+
def log_ccdf(self, x: Any) -> Any:
|
|
513
|
+
"""Evaluate the natural logarithm of the complementary CDF through ``LogCCDF``.
|
|
514
|
+
|
|
515
|
+
An array crosses once into ``LogCCDF(IList<double>)``.
|
|
516
|
+
|
|
517
|
+
Parameters
|
|
518
|
+
----------
|
|
519
|
+
x : float or array_like
|
|
520
|
+
Values at which to evaluate.
|
|
521
|
+
|
|
522
|
+
Returns
|
|
523
|
+
-------
|
|
524
|
+
float or numpy.ndarray
|
|
525
|
+
Log exceedance probabilities, shaped like ``x``.
|
|
526
|
+
"""
|
|
527
|
+
return self._evaluate("LogCCDF", x)
|
|
528
|
+
|
|
529
|
+
type_name = _FamilyAttribute(
|
|
530
|
+
_class_type_name, lambda self: str(self.net.Type),
|
|
531
|
+
"""The ``UnivariateDistributionType`` member name, for example ``"GeneralizedExtremeValue"``.
|
|
532
|
+
|
|
533
|
+
Available on the typed classes without an instance (``Gumbel.type_name``); None on the
|
|
534
|
+
generic :class:`UnivariateDistribution` class.
|
|
535
|
+
""")
|
|
536
|
+
|
|
537
|
+
parameter_names = _FamilyAttribute(
|
|
538
|
+
_class_parameter_names, lambda self: tuple(str(name) for name in self.net.GetParameterPropertyNames),
|
|
539
|
+
"""The parameter property names in ``GetParameters`` order (``GetParameterPropertyNames``).
|
|
540
|
+
|
|
541
|
+
Available on the typed classes without an instance, from the family's default instance;
|
|
542
|
+
None on the generic :class:`UnivariateDistribution` class and on a family Numerics cannot
|
|
543
|
+
build without parameters (``Mixture``).
|
|
544
|
+
""")
|
|
545
|
+
|
|
546
|
+
@property
|
|
547
|
+
def number_of_parameters(self) -> int:
|
|
548
|
+
"""The number of parameters (``NumberOfParameters``)."""
|
|
549
|
+
return int(self.net.NumberOfParameters)
|
|
550
|
+
|
|
551
|
+
def set_parameters(self, parameters: Any) -> None:
|
|
552
|
+
"""Set the parameters in place through ``SetParameters(IList<double>)``.
|
|
553
|
+
|
|
554
|
+
Invalid values are accepted and recorded, as in Numerics: ``parameters_valid``
|
|
555
|
+
becomes False and the evaluation members raise.
|
|
556
|
+
|
|
557
|
+
Parameters
|
|
558
|
+
----------
|
|
559
|
+
parameters : array_like
|
|
560
|
+
The parameters in Numerics order.
|
|
561
|
+
|
|
562
|
+
Raises
|
|
563
|
+
------
|
|
564
|
+
TypeError
|
|
565
|
+
If the distribution is frozen.
|
|
566
|
+
"""
|
|
567
|
+
self._ensure_mutable()
|
|
568
|
+
self.net.SetParameters(to_net_doubles(parameters))
|
|
569
|
+
|
|
570
|
+
@_FamilyMethod
|
|
571
|
+
def validate_parameters(self, parameters: Any) -> str | None:
|
|
572
|
+
"""Return why a parameter vector is invalid for this family, or None when it is valid.
|
|
573
|
+
|
|
574
|
+
Callable on a typed class without an instance (``GeneralizedExtremeValue.validate_parameters(p)``,
|
|
575
|
+
checked on the family's default instance) or on an instance. The count of values is checked
|
|
576
|
+
first; then ``ValidateParameters(IList<double>, false)`` runs and the message of the exception
|
|
577
|
+
Numerics builds, without throwing it, is returned.
|
|
578
|
+
|
|
579
|
+
Parameters
|
|
580
|
+
----------
|
|
581
|
+
parameters : array_like
|
|
582
|
+
A parameter vector in Numerics order.
|
|
583
|
+
|
|
584
|
+
Returns
|
|
585
|
+
-------
|
|
586
|
+
str or None
|
|
587
|
+
The Numerics message, or None for a valid vector.
|
|
588
|
+
|
|
589
|
+
Raises
|
|
590
|
+
------
|
|
591
|
+
ValueError
|
|
592
|
+
If the vector does not have the family's number of parameters (families described by
|
|
593
|
+
a parameter vector only).
|
|
594
|
+
TypeError
|
|
595
|
+
If called on the generic :class:`UnivariateDistribution` class.
|
|
596
|
+
NotImplementedError
|
|
597
|
+
If called on the class of a family Numerics cannot build without parameters (``Mixture``).
|
|
598
|
+
"""
|
|
599
|
+
if isinstance(self, type):
|
|
600
|
+
type_name = _class_type_name(self)
|
|
601
|
+
if type_name is None:
|
|
602
|
+
raise TypeError("validate_parameters needs a typed family class or an instance.")
|
|
603
|
+
net = _prototype(type_name)
|
|
604
|
+
else:
|
|
605
|
+
net = self.net
|
|
606
|
+
values = _check_arity(net, parameters)
|
|
607
|
+
error = net.ValidateParameters(to_net_doubles(values), False)
|
|
608
|
+
return None if error is None else str(error.Message)
|
|
609
|
+
|
|
610
|
+
@property
|
|
611
|
+
def has_parameter_covariance(self) -> bool:
|
|
612
|
+
"""Whether the family implements ``IStandardError`` (an asymptotic parameter covariance)."""
|
|
613
|
+
nd = _nd()
|
|
614
|
+
import clr # noqa: PLC0415
|
|
615
|
+
|
|
616
|
+
return bool(clr.GetClrType(nd.IStandardError).IsInstanceOfType(self.net))
|
|
617
|
+
|
|
618
|
+
def parameter_covariance(self, sample_size: int,
|
|
619
|
+
method: enums.EstimationMethod | str = enums.EstimationMethod.MAXIMUM_LIKELIHOOD,
|
|
620
|
+
) -> np.ndarray:
|
|
621
|
+
"""Return the asymptotic parameter covariance through ``IStandardError.ParameterCovariance``.
|
|
622
|
+
|
|
623
|
+
Parameters
|
|
624
|
+
----------
|
|
625
|
+
sample_size : int
|
|
626
|
+
The sample size n; the covariance scales as 1/n.
|
|
627
|
+
method : EstimationMethod or str, optional
|
|
628
|
+
The estimation method whose covariance is returned; default maximum likelihood.
|
|
629
|
+
|
|
630
|
+
Returns
|
|
631
|
+
-------
|
|
632
|
+
numpy.ndarray
|
|
633
|
+
Array of shape ``(number_of_parameters, number_of_parameters)``.
|
|
634
|
+
|
|
635
|
+
Raises
|
|
636
|
+
------
|
|
637
|
+
NotImplementedError
|
|
638
|
+
If the distribution does not implement ``IStandardError``.
|
|
639
|
+
"""
|
|
640
|
+
nd = _nd()
|
|
641
|
+
if not self.has_parameter_covariance:
|
|
642
|
+
raise NotImplementedError(f"{self.display_name} exposes no parameter covariance.")
|
|
643
|
+
covariance = nd.IStandardError(self.net).ParameterCovariance(int(sample_size),
|
|
644
|
+
net_enum(nd.ParameterEstimationMethod, method))
|
|
645
|
+
return to_numpy_matrix(covariance)
|
|
646
|
+
|
|
647
|
+
def parameter_set_quantiles(self, parameter_sets: Any, probability: float, *, validate: bool = False) -> np.ndarray:
|
|
648
|
+
"""Return one quantile per parameter set, each from this family at that set.
|
|
649
|
+
|
|
650
|
+
A clone of this distribution receives every row through ``SetParameters`` and
|
|
651
|
+
returns ``InverseCDF(probability)``: the probe pattern a .NET caller uses for many
|
|
652
|
+
parameter sets, with the scalar overloads bound once. The rows cross one at a time
|
|
653
|
+
and are not validated; :meth:`parameter_set_quantile_table` passes the whole matrix
|
|
654
|
+
to ``ParameterSetQuantiles`` in one call.
|
|
655
|
+
|
|
656
|
+
Parameters
|
|
657
|
+
----------
|
|
658
|
+
parameter_sets : array_like
|
|
659
|
+
Array of shape ``(count, number_of_parameters)``.
|
|
660
|
+
probability : float
|
|
661
|
+
The non-exceedance probability.
|
|
662
|
+
validate : bool, optional
|
|
663
|
+
When True, each row is checked with ``ValidateParameters(row, false)`` before it is applied,
|
|
664
|
+
and the first invalid row raises its own Numerics message with its index. Default False:
|
|
665
|
+
rows are not validated and an invalid row raises from ``InverseCDF`` without an index.
|
|
666
|
+
|
|
667
|
+
Returns
|
|
668
|
+
-------
|
|
669
|
+
numpy.ndarray
|
|
670
|
+
``count`` quantiles.
|
|
671
|
+
|
|
672
|
+
Raises
|
|
673
|
+
------
|
|
674
|
+
ValueError
|
|
675
|
+
If the rows do not have ``number_of_parameters`` columns, or ``validate`` is True and a
|
|
676
|
+
row is invalid (``"Parameter set <index>: <Numerics message>"``).
|
|
677
|
+
"""
|
|
678
|
+
rows = np.atleast_2d(np.asarray(parameter_sets, dtype=np.float64))
|
|
679
|
+
arity = self.number_of_parameters
|
|
680
|
+
if rows.shape[1] != arity:
|
|
681
|
+
raise ValueError(f"Expected {arity} parameters per row; got {rows.shape[1]}.")
|
|
682
|
+
if validate:
|
|
683
|
+
self._raise_first_invalid(rows, self.parameter_set_validity(rows))
|
|
684
|
+
probe = self.net.Clone()
|
|
685
|
+
quantile = bind(probe.InverseCDF)
|
|
686
|
+
p = float(probability)
|
|
687
|
+
out = np.empty(rows.shape[0], dtype=np.float64)
|
|
688
|
+
setter = _typed_member(probe, "SetParameters", arity) if arity > 1 else None
|
|
689
|
+
for index, row in enumerate(rows.tolist()):
|
|
690
|
+
if setter is not None:
|
|
691
|
+
setter(probe, *row)
|
|
692
|
+
else:
|
|
693
|
+
probe.SetParameters(to_net_doubles(row))
|
|
694
|
+
out[index] = quantile(p)
|
|
695
|
+
return out
|
|
696
|
+
|
|
697
|
+
def parameter_set_quantile_table(self, parameter_sets: Any, probabilities: Any, *,
|
|
698
|
+
validate: bool = False) -> tuple[np.ndarray, np.ndarray]:
|
|
699
|
+
"""Return the quantiles of many probabilities under many parameter sets, in one call into Numerics.
|
|
700
|
+
|
|
701
|
+
Calls ``UnivariateDistributionBase.ParameterSetQuantiles(double[,], IList<double>, out bool[])``:
|
|
702
|
+
the parameter matrix crosses into .NET once, every row is validated with
|
|
703
|
+
``ValidateParameters(row, false)``, a valid row is applied to one private clone with
|
|
704
|
+
``SetParameters(row)`` and evaluated with ``InverseCDF`` at each probability, and the
|
|
705
|
+
quantile matrix and the validity flags cross back once. The values are bitwise those
|
|
706
|
+
of the per-row loop; the rows of invalid parameter sets are NaN.
|
|
707
|
+
|
|
708
|
+
Parameters
|
|
709
|
+
----------
|
|
710
|
+
parameter_sets : array_like
|
|
711
|
+
Array of shape ``(count, number_of_parameters)``.
|
|
712
|
+
probabilities : array_like
|
|
713
|
+
The non-exceedance probabilities, one-dimensional.
|
|
714
|
+
validate : bool, optional
|
|
715
|
+
When True, an invalid row raises its own Numerics message with its index instead of
|
|
716
|
+
returning a NaN row. Default False.
|
|
717
|
+
|
|
718
|
+
Returns
|
|
719
|
+
-------
|
|
720
|
+
quantiles : numpy.ndarray
|
|
721
|
+
Array of shape ``(count, len(probabilities))``; NaN rows where the set is invalid.
|
|
722
|
+
valid : numpy.ndarray
|
|
723
|
+
Boolean array of length ``count``, True where ``ValidateParameters(row, false)`` is null.
|
|
724
|
+
|
|
725
|
+
Raises
|
|
726
|
+
------
|
|
727
|
+
ValueError
|
|
728
|
+
If the rows do not have ``number_of_parameters`` columns, ``probabilities`` is not
|
|
729
|
+
one-dimensional, or ``validate`` is True and a row is invalid
|
|
730
|
+
(``"Parameter set <index>: <Numerics message>"``, for the first invalid row).
|
|
731
|
+
"""
|
|
732
|
+
rows = np.atleast_2d(np.asarray(parameter_sets, dtype=np.float64))
|
|
733
|
+
arity = self.number_of_parameters
|
|
734
|
+
if rows.shape[1] != arity:
|
|
735
|
+
raise ValueError(f"Expected {arity} parameters per row; got {rows.shape[1]}.")
|
|
736
|
+
p = np.asarray(probabilities, dtype=np.float64)
|
|
737
|
+
if p.ndim != 1:
|
|
738
|
+
raise ValueError(f"Expected one-dimensional probabilities; got shape {p.shape}.")
|
|
739
|
+
quantiles, valid = self.net.ParameterSetQuantiles(to_net_matrix(rows), to_net_doubles(p), None)
|
|
740
|
+
valid = to_numpy_bools(valid)
|
|
741
|
+
if validate:
|
|
742
|
+
self._raise_first_invalid(rows, valid)
|
|
743
|
+
return to_numpy_matrix(quantiles), valid
|
|
744
|
+
|
|
745
|
+
def _raise_first_invalid(self, rows: np.ndarray, valid: np.ndarray) -> None:
|
|
746
|
+
"""Raise ValueError for the first row marked invalid, with its index and its own Numerics message."""
|
|
747
|
+
invalid = np.flatnonzero(~valid)
|
|
748
|
+
if invalid.size == 0:
|
|
749
|
+
return
|
|
750
|
+
index = int(invalid[0])
|
|
751
|
+
error = self.net.ValidateParameters(to_net_doubles(rows[index]), False)
|
|
752
|
+
message = "invalid parameters" if error is None else str(error.Message)
|
|
753
|
+
raise ValueError(f"Parameter set {index}: {message}") from (None if error is None else translate(error))
|
|
754
|
+
|
|
755
|
+
def parameter_set_validity(self, parameter_sets: Any) -> np.ndarray:
|
|
756
|
+
"""Return which parameter sets this family accepts (``ValidateParameters(row, false)`` is null).
|
|
757
|
+
|
|
758
|
+
The matrix crosses once, into ``ParameterSetQuantiles`` with no probabilities, whose validity
|
|
759
|
+
flags are this test row by row.
|
|
760
|
+
|
|
761
|
+
Parameters
|
|
762
|
+
----------
|
|
763
|
+
parameter_sets : array_like
|
|
764
|
+
Array of shape ``(count, number_of_parameters)``.
|
|
765
|
+
|
|
766
|
+
Returns
|
|
767
|
+
-------
|
|
768
|
+
numpy.ndarray
|
|
769
|
+
Boolean array of length ``count``, True where the row is valid.
|
|
770
|
+
|
|
771
|
+
Raises
|
|
772
|
+
------
|
|
773
|
+
ValueError
|
|
774
|
+
If the rows do not have ``number_of_parameters`` columns.
|
|
775
|
+
"""
|
|
776
|
+
rows = np.atleast_2d(np.asarray(parameter_sets, dtype=np.float64))
|
|
777
|
+
arity = self.number_of_parameters
|
|
778
|
+
if rows.shape[1] != arity:
|
|
779
|
+
raise ValueError(f"Expected {arity} parameters per row; got {rows.shape[1]}.")
|
|
780
|
+
_, valid = self.net.ParameterSetQuantiles(to_net_matrix(rows), to_net_doubles(np.empty(0)), None)
|
|
781
|
+
return to_numpy_bools(valid)
|
|
782
|
+
|
|
783
|
+
def parameter_set_distributions(self, parameter_sets: Any, valid: Any = None) -> Any:
|
|
784
|
+
"""Build one distribution of this family per parameter set, as a .NET array.
|
|
785
|
+
|
|
786
|
+
Each member is a ``Clone`` of this distribution with ``SetParameters(row)``, so fixed
|
|
787
|
+
settings that are not parameters (a ``LogNormal`` base) carry over. The result stays
|
|
788
|
+
on the .NET side for the members that take ``UnivariateDistributionBase[]``
|
|
789
|
+
(``UncertaintyAnalysisResults``, ``BootstrapAnalysis.Quantiles``); rows marked False
|
|
790
|
+
in ``valid`` are null entries, which those members treat as missing.
|
|
791
|
+
|
|
792
|
+
Parameters
|
|
793
|
+
----------
|
|
794
|
+
parameter_sets : array_like
|
|
795
|
+
Array of shape ``(count, number_of_parameters)``.
|
|
796
|
+
valid : array_like of bool, optional
|
|
797
|
+
Which rows to build; None builds every row.
|
|
798
|
+
|
|
799
|
+
Returns
|
|
800
|
+
-------
|
|
801
|
+
System.Array[Numerics.Distributions.UnivariateDistributionBase]
|
|
802
|
+
``count`` members, null where ``valid`` is False.
|
|
803
|
+
|
|
804
|
+
Raises
|
|
805
|
+
------
|
|
806
|
+
ValueError
|
|
807
|
+
If the rows do not have ``number_of_parameters`` columns or ``valid`` has another length.
|
|
808
|
+
"""
|
|
809
|
+
rows = np.atleast_2d(np.asarray(parameter_sets, dtype=np.float64))
|
|
810
|
+
arity = self.number_of_parameters
|
|
811
|
+
if rows.shape[1] != arity:
|
|
812
|
+
raise ValueError(f"Expected {arity} parameters per row; got {rows.shape[1]}.")
|
|
813
|
+
keep = np.ones(rows.shape[0], dtype=bool) if valid is None else np.asarray(valid, dtype=bool)
|
|
814
|
+
if keep.shape != (rows.shape[0],):
|
|
815
|
+
raise ValueError("valid must hold one flag per row.")
|
|
816
|
+
nd = _nd()
|
|
817
|
+
import System # noqa: PLC0415 - the runtime is running (self.net exists)
|
|
818
|
+
|
|
819
|
+
members = System.Array.CreateInstance(nd.UnivariateDistributionBase, rows.shape[0])
|
|
820
|
+
clone = self.net.Clone
|
|
821
|
+
setter = _typed_member(self.net, "SetParameters", arity) if arity > 1 else None
|
|
822
|
+
for index, row in enumerate(rows.tolist()):
|
|
823
|
+
if not keep[index]:
|
|
824
|
+
continue
|
|
825
|
+
member = clone()
|
|
826
|
+
if setter is not None:
|
|
827
|
+
setter(member, *row)
|
|
828
|
+
else:
|
|
829
|
+
member.SetParameters(to_net_doubles(row))
|
|
830
|
+
members[index] = member
|
|
831
|
+
return members
|
|
832
|
+
|
|
833
|
+
def to_xml(self) -> str:
|
|
834
|
+
"""Return the distribution's ``ToXElement()`` as XML text.
|
|
835
|
+
|
|
836
|
+
Returns
|
|
837
|
+
-------
|
|
838
|
+
str
|
|
839
|
+
The element as .NET writes it (``XElement.ToString``).
|
|
840
|
+
"""
|
|
841
|
+
return str(self.net.ToXElement().ToString())
|
|
842
|
+
|
|
843
|
+
def generate_random_values(self, sample_size: int, seed: int = -1) -> np.ndarray:
|
|
844
|
+
"""Draw a Monte Carlo sample through ``GenerateRandomValues``.
|
|
845
|
+
|
|
846
|
+
Parameters
|
|
847
|
+
----------
|
|
848
|
+
sample_size : int
|
|
849
|
+
Number of values.
|
|
850
|
+
seed : int, optional
|
|
851
|
+
Mersenne Twister seed; zero or negative seeds from the clock (not
|
|
852
|
+
reproducible).
|
|
853
|
+
|
|
854
|
+
Returns
|
|
855
|
+
-------
|
|
856
|
+
numpy.ndarray
|
|
857
|
+
The sample.
|
|
858
|
+
"""
|
|
859
|
+
return to_numpy_doubles(self.net.GenerateRandomValues(int(sample_size), int(seed)))
|
|
860
|
+
|
|
861
|
+
def log_likelihood(self, sample: Any) -> float:
|
|
862
|
+
"""Return the log-likelihood of a sample (``LogLikelihood(IList<double>)``).
|
|
863
|
+
|
|
864
|
+
Parameters
|
|
865
|
+
----------
|
|
866
|
+
sample : array_like
|
|
867
|
+
Observations.
|
|
868
|
+
|
|
869
|
+
Returns
|
|
870
|
+
-------
|
|
871
|
+
float
|
|
872
|
+
The log-likelihood.
|
|
873
|
+
"""
|
|
874
|
+
return float(self.net.LogLikelihood(to_net_doubles(sample)))
|
|
875
|
+
|
|
876
|
+
def estimate(self, sample: Any, method: enums.EstimationMethod | str = enums.EstimationMethod.MAXIMUM_LIKELIHOOD,
|
|
877
|
+
fixed_parameters: Any = None) -> None:
|
|
878
|
+
"""Fit the parameters in place through ``IEstimation.Estimate``.
|
|
879
|
+
|
|
880
|
+
With ``fixed_parameters`` the fit holds those parameters at their values through
|
|
881
|
+
``IFixedParameterEstimation.Estimate(sample, method, fixed)``, for example the generalized
|
|
882
|
+
Pareto scale and shape by maximum likelihood at a known location.
|
|
883
|
+
|
|
884
|
+
Parameters
|
|
885
|
+
----------
|
|
886
|
+
sample : array_like
|
|
887
|
+
Observations.
|
|
888
|
+
method : EstimationMethod or str, optional
|
|
889
|
+
Estimation method; default maximum likelihood.
|
|
890
|
+
fixed_parameters : array_like, optional
|
|
891
|
+
One entry per parameter in Numerics order: a finite value holds that parameter fixed and
|
|
892
|
+
NaN marks a parameter to estimate. None (the default) estimates every parameter.
|
|
893
|
+
|
|
894
|
+
Raises
|
|
895
|
+
------
|
|
896
|
+
TypeError
|
|
897
|
+
If the distribution does not implement ``IEstimation``, or ``fixed_parameters`` is given and
|
|
898
|
+
it does not implement ``IFixedParameterEstimation``.
|
|
899
|
+
|
|
900
|
+
Notes
|
|
901
|
+
-----
|
|
902
|
+
A frozen distribution raises ``TypeError``.
|
|
903
|
+
"""
|
|
904
|
+
self._ensure_mutable()
|
|
905
|
+
nd = _nd()
|
|
906
|
+
import clr # noqa: PLC0415
|
|
907
|
+
|
|
908
|
+
if not clr.GetClrType(nd.IEstimation).IsInstanceOfType(self.net):
|
|
909
|
+
raise TypeError(f"{self.display_name} does not support parameter estimation.")
|
|
910
|
+
kind = net_enum(nd.ParameterEstimationMethod, method)
|
|
911
|
+
if fixed_parameters is None:
|
|
912
|
+
nd.IEstimation(self.net).Estimate(to_net_doubles(sample), kind)
|
|
913
|
+
return
|
|
914
|
+
fixed = nd.IFixedParameterEstimation
|
|
915
|
+
if not clr.GetClrType(fixed).IsInstanceOfType(self.net):
|
|
916
|
+
raise TypeError(f"{self.display_name} does not support estimation with fixed parameters.")
|
|
917
|
+
fixed(self.net).Estimate(to_net_doubles(sample), kind, to_net_doubles(fixed_parameters))
|
|
918
|
+
|
|
919
|
+
def clone(self) -> UnivariateDistribution:
|
|
920
|
+
"""Return a deep copy through ``Clone``.
|
|
921
|
+
|
|
922
|
+
Returns
|
|
923
|
+
-------
|
|
924
|
+
UnivariateDistribution
|
|
925
|
+
A wrapper of the cloned .NET object, of the same Python class when possible.
|
|
926
|
+
"""
|
|
927
|
+
return wrap(self.net.Clone())
|
|
928
|
+
|
|
929
|
+
@property
|
|
930
|
+
def median(self) -> float:
|
|
931
|
+
"""The distribution median."""
|
|
932
|
+
return float(self.net.Median)
|
|
933
|
+
|
|
934
|
+
@property
|
|
935
|
+
def mode(self) -> float:
|
|
936
|
+
"""The distribution mode."""
|
|
937
|
+
return float(self.net.Mode)
|
|
938
|
+
|
|
939
|
+
@property
|
|
940
|
+
def variance(self) -> float:
|
|
941
|
+
"""The distribution variance (``Variance``, the square of ``StandardDeviation``)."""
|
|
942
|
+
return float(self.net.Variance)
|
|
943
|
+
|
|
944
|
+
@property
|
|
945
|
+
def coefficient_of_variation(self) -> float:
|
|
946
|
+
"""The coefficient of variation, standard deviation over mean (``CoefficientOfVariation``)."""
|
|
947
|
+
return float(self.net.CoefficientOfVariation)
|
|
948
|
+
|
|
949
|
+
@property
|
|
950
|
+
def short_display_name(self) -> str:
|
|
951
|
+
"""The Numerics short display name, for example ``"GEV"``."""
|
|
952
|
+
return str(self.net.ShortDisplayName)
|
|
953
|
+
|
|
954
|
+
@property
|
|
955
|
+
def display_label(self) -> str:
|
|
956
|
+
"""The Numerics label: the short display name followed by the parameter values."""
|
|
957
|
+
return str(self.net.DisplayLabel)
|
|
958
|
+
|
|
959
|
+
@property
|
|
960
|
+
def parameter_display_names(self) -> tuple[str, ...]:
|
|
961
|
+
"""The parameter names for display, for example ``"Location (ξ)"`` (``ParameterNames``)."""
|
|
962
|
+
return tuple(str(name) for name in self.net.ParameterNames)
|
|
963
|
+
|
|
964
|
+
@property
|
|
965
|
+
def parameter_names_short_form(self) -> tuple[str, ...]:
|
|
966
|
+
"""The short parameter symbols, for example ``"ξ"`` (``ParameterNamesShortForm``)."""
|
|
967
|
+
return tuple(str(name) for name in self.net.ParameterNamesShortForm)
|
|
968
|
+
|
|
969
|
+
@property
|
|
970
|
+
def parameters_to_string(self) -> list[tuple[str, str]]:
|
|
971
|
+
"""The (name, value) text rows Numerics shows for the parameters (``ParametersToString``)."""
|
|
972
|
+
table = self.net.ParametersToString
|
|
973
|
+
return [(str(table[i, 0]), str(table[i, 1])) for i in range(int(table.GetLength(0)))]
|
|
974
|
+
|
|
975
|
+
@property
|
|
976
|
+
def minimum_of_parameters(self) -> np.ndarray:
|
|
977
|
+
"""The lower bound of each parameter, in Numerics order (``MinimumOfParameters``)."""
|
|
978
|
+
return to_numpy_doubles(self.net.MinimumOfParameters)
|
|
979
|
+
|
|
980
|
+
@property
|
|
981
|
+
def maximum_of_parameters(self) -> np.ndarray:
|
|
982
|
+
"""The upper bound of each parameter, in Numerics order (``MaximumOfParameters``)."""
|
|
983
|
+
return to_numpy_doubles(self.net.MaximumOfParameters)
|
|
984
|
+
|
|
985
|
+
def hf(self, x: Any) -> Any:
|
|
986
|
+
"""Evaluate the hazard function, the density over the survival probability (``HF``).
|
|
987
|
+
|
|
988
|
+
Parameters
|
|
989
|
+
----------
|
|
990
|
+
x : float or array_like
|
|
991
|
+
Values at which to evaluate.
|
|
992
|
+
|
|
993
|
+
Returns
|
|
994
|
+
-------
|
|
995
|
+
float or numpy.ndarray
|
|
996
|
+
Hazard rates, shaped like ``x``; Numerics has no array overload, so an array is evaluated
|
|
997
|
+
element by element with the bound scalar member.
|
|
998
|
+
"""
|
|
999
|
+
return self._evaluate("HF", x)
|
|
1000
|
+
|
|
1001
|
+
def inverse_ccdf(self, probability: Any) -> Any:
|
|
1002
|
+
"""Evaluate the survival quantile: x with ``ccdf(x) = probability`` (``InverseCCDF``).
|
|
1003
|
+
|
|
1004
|
+
Families with a closed form in the exceedance probability (``GeneralizedPareto``) never form
|
|
1005
|
+
``1 - probability``, which keeps the precision of small exceedance probabilities; the others
|
|
1006
|
+
return ``InverseCDF(1 - probability)``. An array crosses once into ``InverseCCDF(IList<double>)``.
|
|
1007
|
+
|
|
1008
|
+
Parameters
|
|
1009
|
+
----------
|
|
1010
|
+
probability : float or array_like
|
|
1011
|
+
Exceedance probabilities in [0, 1].
|
|
1012
|
+
|
|
1013
|
+
Returns
|
|
1014
|
+
-------
|
|
1015
|
+
float or numpy.ndarray
|
|
1016
|
+
Quantiles, shaped like ``probability``.
|
|
1017
|
+
"""
|
|
1018
|
+
return self._evaluate("InverseCCDF", probability)
|
|
1019
|
+
|
|
1020
|
+
def central_moments(self, tolerance: float = 1e-8, steps: int | None = None) -> np.ndarray:
|
|
1021
|
+
"""Return the mean, standard deviation, skewness and kurtosis by numerical integration (``CentralMoments``).
|
|
1022
|
+
|
|
1023
|
+
With ``steps`` None the adaptive Gauss-Kronrod overload ``CentralMoments(double tolerance)``
|
|
1024
|
+
runs over ``[InverseCDF(1e-16), InverseCDF(1 - 1e-16)]``; with ``steps`` the fixed-step
|
|
1025
|
+
bin-expectation overload ``CentralMoments(int steps)`` runs instead and ``tolerance`` is unused.
|
|
1026
|
+
|
|
1027
|
+
Parameters
|
|
1028
|
+
----------
|
|
1029
|
+
tolerance : float, optional
|
|
1030
|
+
Relative tolerance of the adaptive integration; default 1e-8.
|
|
1031
|
+
steps : int, optional
|
|
1032
|
+
Number of bins of the fixed-step overload (Numerics' default is 300).
|
|
1033
|
+
|
|
1034
|
+
Returns
|
|
1035
|
+
-------
|
|
1036
|
+
numpy.ndarray
|
|
1037
|
+
``[mean, standard deviation, skewness, kurtosis]``; a moment whose integration fails is NaN.
|
|
1038
|
+
"""
|
|
1039
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
1040
|
+
|
|
1041
|
+
member = self.net.CentralMoments
|
|
1042
|
+
if steps is None:
|
|
1043
|
+
return to_numpy_doubles(member.Overloads[System.Double](float(tolerance)))
|
|
1044
|
+
return to_numpy_doubles(member.Overloads[System.Int32](int(steps)))
|
|
1045
|
+
|
|
1046
|
+
def conditional_moments(self, a: float, b: float) -> np.ndarray:
|
|
1047
|
+
"""Return the first four moments of X restricted to [a, b] by 300-bin integration (``ConditionalMoments``).
|
|
1048
|
+
|
|
1049
|
+
Parameters
|
|
1050
|
+
----------
|
|
1051
|
+
a : float
|
|
1052
|
+
Lower limit.
|
|
1053
|
+
b : float
|
|
1054
|
+
Upper limit.
|
|
1055
|
+
|
|
1056
|
+
Returns
|
|
1057
|
+
-------
|
|
1058
|
+
numpy.ndarray
|
|
1059
|
+
``[E[X | a < X < b], m2, m3, m4]``, where ``mk`` is the conditional expectation of
|
|
1060
|
+
``(X - mean)**k`` about the unconditional distribution mean, as Numerics computes them;
|
|
1061
|
+
NaN when ``a >= b`` or the interval has no probability. They are not the central moments
|
|
1062
|
+
of the truncated distribution: its variance is ``m2 - (m1 - mean)**2``.
|
|
1063
|
+
"""
|
|
1064
|
+
return to_numpy_doubles(self.net.ConditionalMoments(float(a), float(b)))
|
|
1065
|
+
|
|
1066
|
+
def conditional_mean(self, a: float, b: float, tolerance: float = 1e-8) -> float:
|
|
1067
|
+
"""Return E[X | a < X < b] by adaptive Gauss-Kronrod integration (``ConditionalMean``).
|
|
1068
|
+
|
|
1069
|
+
Parameters
|
|
1070
|
+
----------
|
|
1071
|
+
a : float
|
|
1072
|
+
Lower limit.
|
|
1073
|
+
b : float
|
|
1074
|
+
Upper limit.
|
|
1075
|
+
tolerance : float, optional
|
|
1076
|
+
Relative integration tolerance; default 1e-8.
|
|
1077
|
+
|
|
1078
|
+
Returns
|
|
1079
|
+
-------
|
|
1080
|
+
float
|
|
1081
|
+
The integral of ``x f(x)`` over [a, b] divided by ``F(b) - F(a)``; NaN if the integration fails.
|
|
1082
|
+
"""
|
|
1083
|
+
return float(self.net.ConditionalMean(float(a), float(b), float(tolerance)))
|
|
1084
|
+
|
|
1085
|
+
def conditional_variance(self, a: float, b: float, mean: float = 0.0, tolerance: float = 1e-8) -> float:
|
|
1086
|
+
"""Return E[(X - mean)^2 | a < X < b] by adaptive integration (``ConditionalVariance``).
|
|
1087
|
+
|
|
1088
|
+
Parameters
|
|
1089
|
+
----------
|
|
1090
|
+
a : float
|
|
1091
|
+
Lower limit.
|
|
1092
|
+
b : float
|
|
1093
|
+
Upper limit.
|
|
1094
|
+
mean : float, optional
|
|
1095
|
+
The centre of the moment; default 0 (pass :meth:`conditional_mean` for the central moment).
|
|
1096
|
+
tolerance : float, optional
|
|
1097
|
+
Relative integration tolerance; default 1e-8.
|
|
1098
|
+
|
|
1099
|
+
Returns
|
|
1100
|
+
-------
|
|
1101
|
+
float
|
|
1102
|
+
The conditional second moment about ``mean``.
|
|
1103
|
+
"""
|
|
1104
|
+
return float(self.net.ConditionalVariance(float(a), float(b), float(mean), float(tolerance)))
|
|
1105
|
+
|
|
1106
|
+
def conditional_skewness(self, a: float, b: float, mean: float = 0.0, tolerance: float = 1e-8) -> float:
|
|
1107
|
+
"""Return E[(X - mean)^3 | a < X < b] by adaptive integration (``ConditionalSkewness``).
|
|
1108
|
+
|
|
1109
|
+
Parameters
|
|
1110
|
+
----------
|
|
1111
|
+
a : float
|
|
1112
|
+
Lower limit.
|
|
1113
|
+
b : float
|
|
1114
|
+
Upper limit.
|
|
1115
|
+
mean : float, optional
|
|
1116
|
+
The centre of the moment; default 0.
|
|
1117
|
+
tolerance : float, optional
|
|
1118
|
+
Relative integration tolerance; default 1e-8.
|
|
1119
|
+
|
|
1120
|
+
Returns
|
|
1121
|
+
-------
|
|
1122
|
+
float
|
|
1123
|
+
The conditional third moment about ``mean`` (not standardized).
|
|
1124
|
+
"""
|
|
1125
|
+
return float(self.net.ConditionalSkewness(float(a), float(b), float(mean), float(tolerance)))
|
|
1126
|
+
|
|
1127
|
+
def conditional_kurtosis(self, a: float, b: float, mean: float = 0.0, tolerance: float = 1e-8) -> float:
|
|
1128
|
+
"""Return E[(X - mean)^4 | a < X < b] by adaptive integration (``ConditionalKurtosis``).
|
|
1129
|
+
|
|
1130
|
+
Parameters
|
|
1131
|
+
----------
|
|
1132
|
+
a : float
|
|
1133
|
+
Lower limit.
|
|
1134
|
+
b : float
|
|
1135
|
+
Upper limit.
|
|
1136
|
+
mean : float, optional
|
|
1137
|
+
The centre of the moment; default 0.
|
|
1138
|
+
tolerance : float, optional
|
|
1139
|
+
Relative integration tolerance; default 1e-8.
|
|
1140
|
+
|
|
1141
|
+
Returns
|
|
1142
|
+
-------
|
|
1143
|
+
float
|
|
1144
|
+
The conditional fourth moment about ``mean`` (not standardized).
|
|
1145
|
+
"""
|
|
1146
|
+
return float(self.net.ConditionalKurtosis(float(a), float(b), float(mean), float(tolerance)))
|
|
1147
|
+
|
|
1148
|
+
def conditional_expected_value(self, alpha: float, tolerance: float = 1e-8) -> float:
|
|
1149
|
+
"""Return the mean of the upper tail beyond the ``alpha`` quantile (``ConditionalExpectedValue``).
|
|
1150
|
+
|
|
1151
|
+
Parameters
|
|
1152
|
+
----------
|
|
1153
|
+
alpha : float
|
|
1154
|
+
The non-exceedance level; the tail is ``[InverseCDF(alpha), InverseCDF(1 - 1e-16)]``.
|
|
1155
|
+
tolerance : float, optional
|
|
1156
|
+
Relative integration tolerance; default 1e-8.
|
|
1157
|
+
|
|
1158
|
+
Returns
|
|
1159
|
+
-------
|
|
1160
|
+
float
|
|
1161
|
+
The integral of ``x f(x)`` over the tail divided by ``1 - alpha`` (the conditional value at risk).
|
|
1162
|
+
"""
|
|
1163
|
+
return float(self.net.ConditionalExpectedValue(float(alpha), float(tolerance)))
|
|
1164
|
+
|
|
1165
|
+
def create_pdf_graph(self, bins: Any = None) -> np.ndarray:
|
|
1166
|
+
"""Return a (x, density) table for plotting (``CreatePDFGraph``).
|
|
1167
|
+
|
|
1168
|
+
Parameters
|
|
1169
|
+
----------
|
|
1170
|
+
bins : pynumerics.sampling.StratificationBins or sequence of them, optional
|
|
1171
|
+
X-value bins whose midpoints are the table points; None, the default, lets Numerics
|
|
1172
|
+
choose the bounds.
|
|
1173
|
+
|
|
1174
|
+
Returns
|
|
1175
|
+
-------
|
|
1176
|
+
numpy.ndarray
|
|
1177
|
+
Array of shape ``(n, 2)``: x and the density.
|
|
1178
|
+
"""
|
|
1179
|
+
member = self.net.CreatePDFGraph
|
|
1180
|
+
return to_numpy_matrix(member() if bins is None else member(_bins_net(bins)))
|
|
1181
|
+
|
|
1182
|
+
def create_cdf_graph(self, bins: Any = None) -> np.ndarray:
|
|
1183
|
+
"""Return a (x, non-exceedance probability) table for plotting (``CreateCDFGraph``).
|
|
1184
|
+
|
|
1185
|
+
Parameters
|
|
1186
|
+
----------
|
|
1187
|
+
bins : pynumerics.sampling.StratificationBins or sequence of them, optional
|
|
1188
|
+
X-value bins; None, the default, lets Numerics choose the bounds.
|
|
1189
|
+
|
|
1190
|
+
Returns
|
|
1191
|
+
-------
|
|
1192
|
+
numpy.ndarray
|
|
1193
|
+
Array of shape ``(n, 2)``: x and the CDF.
|
|
1194
|
+
"""
|
|
1195
|
+
member = self.net.CreateCDFGraph
|
|
1196
|
+
return to_numpy_matrix(member() if bins is None else member(_bins_net(bins)))
|
|
1197
|
+
|
|
1198
|
+
def create_inverse_cdf_graph(self, bins: Any = None) -> np.ndarray:
|
|
1199
|
+
"""Return a (probability, quantile) table for plotting (``CreateInverseCDFGraph``).
|
|
1200
|
+
|
|
1201
|
+
Parameters
|
|
1202
|
+
----------
|
|
1203
|
+
bins : pynumerics.sampling.StratificationBins or sequence of them, optional
|
|
1204
|
+
Probability bins; None, the default, lets Numerics choose the probabilities.
|
|
1205
|
+
|
|
1206
|
+
Returns
|
|
1207
|
+
-------
|
|
1208
|
+
numpy.ndarray
|
|
1209
|
+
Array of shape ``(n, 2)``: the probability and its quantile.
|
|
1210
|
+
"""
|
|
1211
|
+
member = self.net.CreateInverseCDFGraph
|
|
1212
|
+
return to_numpy_matrix(member() if bins is None else member(_bins_net(bins)))
|
|
1213
|
+
|
|
1214
|
+
def log_likelihood_left_censored(self, threshold: float, number_below: int) -> float:
|
|
1215
|
+
"""Return the log-likelihood of ``number_below`` values known only to lie below ``threshold``.
|
|
1216
|
+
|
|
1217
|
+
Parameters
|
|
1218
|
+
----------
|
|
1219
|
+
threshold : float
|
|
1220
|
+
The censoring threshold.
|
|
1221
|
+
number_below : int
|
|
1222
|
+
The count of censored values.
|
|
1223
|
+
|
|
1224
|
+
Returns
|
|
1225
|
+
-------
|
|
1226
|
+
float
|
|
1227
|
+
``number_below * log F(threshold)`` as Numerics computes it (``LogLikelihood_LeftCensored``).
|
|
1228
|
+
"""
|
|
1229
|
+
return float(self.net.LogLikelihood_LeftCensored(float(threshold), int(number_below)))
|
|
1230
|
+
|
|
1231
|
+
def log_likelihood_right_censored(self, threshold: float, number_above: int) -> float:
|
|
1232
|
+
"""Return the log-likelihood of ``number_above`` values known only to exceed ``threshold``.
|
|
1233
|
+
|
|
1234
|
+
Parameters
|
|
1235
|
+
----------
|
|
1236
|
+
threshold : float
|
|
1237
|
+
The censoring threshold.
|
|
1238
|
+
number_above : int
|
|
1239
|
+
The count of censored values.
|
|
1240
|
+
|
|
1241
|
+
Returns
|
|
1242
|
+
-------
|
|
1243
|
+
float
|
|
1244
|
+
``number_above * log(1 - F(threshold))`` as Numerics computes it (``LogLikelihood_RightCensored``).
|
|
1245
|
+
"""
|
|
1246
|
+
return float(self.net.LogLikelihood_RightCensored(float(threshold), int(number_above)))
|
|
1247
|
+
|
|
1248
|
+
def log_likelihood_intervals(self, lower: float, upper: float) -> float:
|
|
1249
|
+
"""Return the log-likelihood of one value known to lie in [lower, upper] (``LogLikelihood_Intervals``).
|
|
1250
|
+
|
|
1251
|
+
Parameters
|
|
1252
|
+
----------
|
|
1253
|
+
lower : float
|
|
1254
|
+
Lower limit of the interval.
|
|
1255
|
+
upper : float
|
|
1256
|
+
Upper limit of the interval.
|
|
1257
|
+
|
|
1258
|
+
Returns
|
|
1259
|
+
-------
|
|
1260
|
+
float
|
|
1261
|
+
``log(F(upper) - F(lower))`` as Numerics computes it.
|
|
1262
|
+
"""
|
|
1263
|
+
return float(self.net.LogLikelihood_Intervals(float(lower), float(upper)))
|
|
1264
|
+
|
|
1265
|
+
def compare_to(self, other: UnivariateDistribution | None) -> int:
|
|
1266
|
+
"""Order two distributions by their means (``CompareTo``).
|
|
1267
|
+
|
|
1268
|
+
Parameters
|
|
1269
|
+
----------
|
|
1270
|
+
other : UnivariateDistribution or None
|
|
1271
|
+
The distribution to compare with; None orders below every distribution.
|
|
1272
|
+
|
|
1273
|
+
Returns
|
|
1274
|
+
-------
|
|
1275
|
+
int
|
|
1276
|
+
Negative, zero or positive as this mean is below, equal to or above the other's.
|
|
1277
|
+
"""
|
|
1278
|
+
return int(self.net.CompareTo(None if other is None else other.net))
|
|
1279
|
+
|
|
1280
|
+
def bootstrap(self, method: enums.EstimationMethod | str, sample_size: int,
|
|
1281
|
+
seed: int = -1) -> UnivariateDistribution:
|
|
1282
|
+
"""Draw a sample from this distribution and refit the family to it (``IBootstrappable.Bootstrap``).
|
|
1283
|
+
|
|
1284
|
+
Parameters
|
|
1285
|
+
----------
|
|
1286
|
+
method : EstimationMethod or str
|
|
1287
|
+
The estimation method of the refit.
|
|
1288
|
+
sample_size : int
|
|
1289
|
+
The size of the drawn sample.
|
|
1290
|
+
seed : int, optional
|
|
1291
|
+
Mersenne Twister seed; zero or negative seeds from the clock (not reproducible).
|
|
1292
|
+
|
|
1293
|
+
Returns
|
|
1294
|
+
-------
|
|
1295
|
+
UnivariateDistribution
|
|
1296
|
+
The refitted distribution (a new object; this one is unchanged).
|
|
1297
|
+
|
|
1298
|
+
Raises
|
|
1299
|
+
------
|
|
1300
|
+
NotImplementedError
|
|
1301
|
+
If the family does not implement ``IBootstrappable``.
|
|
1302
|
+
"""
|
|
1303
|
+
nd = _nd()
|
|
1304
|
+
replicate = _interface(self.net, "IBootstrappable").Bootstrap(
|
|
1305
|
+
net_enum(nd.ParameterEstimationMethod, method), int(sample_size), int(seed))
|
|
1306
|
+
return wrap(replicate)
|
|
1307
|
+
|
|
1308
|
+
def mle(self, sample: Any) -> np.ndarray:
|
|
1309
|
+
"""Return the maximum likelihood parameters of a sample, without changing this distribution.
|
|
1310
|
+
|
|
1311
|
+
Parameters
|
|
1312
|
+
----------
|
|
1313
|
+
sample : array_like
|
|
1314
|
+
Observations.
|
|
1315
|
+
|
|
1316
|
+
Returns
|
|
1317
|
+
-------
|
|
1318
|
+
numpy.ndarray
|
|
1319
|
+
The parameters in Numerics order (``IMaximumLikelihoodEstimation.MLE``).
|
|
1320
|
+
|
|
1321
|
+
Raises
|
|
1322
|
+
------
|
|
1323
|
+
NotImplementedError
|
|
1324
|
+
If the family does not implement ``IMaximumLikelihoodEstimation``.
|
|
1325
|
+
"""
|
|
1326
|
+
return to_numpy_doubles(_interface(self.net, "IMaximumLikelihoodEstimation").MLE(to_net_doubles(sample)))
|
|
1327
|
+
|
|
1328
|
+
def parameter_constraints(self, sample: Any) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
1329
|
+
"""Return the starting values and bounds the maximum likelihood search uses for a sample.
|
|
1330
|
+
|
|
1331
|
+
Parameters
|
|
1332
|
+
----------
|
|
1333
|
+
sample : array_like
|
|
1334
|
+
Observations.
|
|
1335
|
+
|
|
1336
|
+
Returns
|
|
1337
|
+
-------
|
|
1338
|
+
initial : numpy.ndarray
|
|
1339
|
+
The starting parameters (``IMaximumLikelihoodEstimation.GetParameterConstraints``).
|
|
1340
|
+
lower : numpy.ndarray
|
|
1341
|
+
The lower bound of each parameter.
|
|
1342
|
+
upper : numpy.ndarray
|
|
1343
|
+
The upper bound of each parameter.
|
|
1344
|
+
|
|
1345
|
+
Raises
|
|
1346
|
+
------
|
|
1347
|
+
NotImplementedError
|
|
1348
|
+
If the family does not implement ``IMaximumLikelihoodEstimation``.
|
|
1349
|
+
"""
|
|
1350
|
+
result = _interface(self.net, "IMaximumLikelihoodEstimation").GetParameterConstraints(to_net_doubles(sample))
|
|
1351
|
+
return to_numpy_doubles(result.Item1), to_numpy_doubles(result.Item2), to_numpy_doubles(result.Item3)
|
|
1352
|
+
|
|
1353
|
+
@_FamilyMethod
|
|
1354
|
+
def parameters_from_moments(self, moments: Any) -> np.ndarray:
|
|
1355
|
+
"""Return the parameters matching product moments (``IMomentEstimation.ParametersFromMoments``).
|
|
1356
|
+
|
|
1357
|
+
Callable on a typed class without an instance (on the family's default instance) or on an
|
|
1358
|
+
instance; families with a setting that is not a parameter (a ``LogNormal`` base) use the
|
|
1359
|
+
instance's setting.
|
|
1360
|
+
|
|
1361
|
+
Parameters
|
|
1362
|
+
----------
|
|
1363
|
+
moments : array_like
|
|
1364
|
+
``[mean, standard deviation, skewness, kurtosis]`` (the leading entries the family needs).
|
|
1365
|
+
|
|
1366
|
+
Returns
|
|
1367
|
+
-------
|
|
1368
|
+
numpy.ndarray
|
|
1369
|
+
The parameters in Numerics order.
|
|
1370
|
+
|
|
1371
|
+
Raises
|
|
1372
|
+
------
|
|
1373
|
+
NotImplementedError
|
|
1374
|
+
If the family does not implement ``IMomentEstimation``.
|
|
1375
|
+
"""
|
|
1376
|
+
net = _interface(_net_of(self), "IMomentEstimation")
|
|
1377
|
+
return to_numpy_doubles(net.ParametersFromMoments(to_net_doubles(moments)))
|
|
1378
|
+
|
|
1379
|
+
@_FamilyMethod
|
|
1380
|
+
def moments_from_parameters(self, parameters: Any) -> np.ndarray:
|
|
1381
|
+
"""Return the product moments of a parameter vector (``IMomentEstimation.MomentsFromParameters``).
|
|
1382
|
+
|
|
1383
|
+
Callable on a typed class without an instance or on an instance.
|
|
1384
|
+
|
|
1385
|
+
Parameters
|
|
1386
|
+
----------
|
|
1387
|
+
parameters : array_like
|
|
1388
|
+
The parameters in Numerics order.
|
|
1389
|
+
|
|
1390
|
+
Returns
|
|
1391
|
+
-------
|
|
1392
|
+
numpy.ndarray
|
|
1393
|
+
``[mean, standard deviation, skewness, kurtosis]``.
|
|
1394
|
+
|
|
1395
|
+
Raises
|
|
1396
|
+
------
|
|
1397
|
+
NotImplementedError
|
|
1398
|
+
If the family does not implement ``IMomentEstimation``.
|
|
1399
|
+
"""
|
|
1400
|
+
net = _interface(_net_of(self), "IMomentEstimation")
|
|
1401
|
+
return to_numpy_doubles(net.MomentsFromParameters(to_net_doubles(parameters)))
|
|
1402
|
+
|
|
1403
|
+
@_FamilyMethod
|
|
1404
|
+
def parameters_from_linear_moments(self, moments: Any) -> np.ndarray:
|
|
1405
|
+
"""Return the parameters matching L-moments (``ILinearMomentEstimation.ParametersFromLinearMoments``).
|
|
1406
|
+
|
|
1407
|
+
Callable on a typed class without an instance
|
|
1408
|
+
(``GeneralizedExtremeValue.parameters_from_linear_moments(l)``) or on an instance.
|
|
1409
|
+
|
|
1410
|
+
Parameters
|
|
1411
|
+
----------
|
|
1412
|
+
moments : array_like
|
|
1413
|
+
``[λ1, λ2, τ3, τ4]``: the first two L-moments and the L-skewness and L-kurtosis ratios,
|
|
1414
|
+
as :func:`pynumerics.statistics.linear_moments` returns them.
|
|
1415
|
+
|
|
1416
|
+
Returns
|
|
1417
|
+
-------
|
|
1418
|
+
numpy.ndarray
|
|
1419
|
+
The parameters in Numerics order.
|
|
1420
|
+
|
|
1421
|
+
Raises
|
|
1422
|
+
------
|
|
1423
|
+
NotImplementedError
|
|
1424
|
+
If the family does not implement ``ILinearMomentEstimation``.
|
|
1425
|
+
"""
|
|
1426
|
+
net = _interface(_net_of(self), "ILinearMomentEstimation")
|
|
1427
|
+
return to_numpy_doubles(net.ParametersFromLinearMoments(to_net_doubles(moments)))
|
|
1428
|
+
|
|
1429
|
+
@_FamilyMethod
|
|
1430
|
+
def linear_moments_from_parameters(self, parameters: Any) -> np.ndarray:
|
|
1431
|
+
"""Return the L-moments of a parameter vector (``ILinearMomentEstimation.LinearMomentsFromParameters``).
|
|
1432
|
+
|
|
1433
|
+
Callable on a typed class without an instance or on an instance.
|
|
1434
|
+
|
|
1435
|
+
Parameters
|
|
1436
|
+
----------
|
|
1437
|
+
parameters : array_like
|
|
1438
|
+
The parameters in Numerics order.
|
|
1439
|
+
|
|
1440
|
+
Returns
|
|
1441
|
+
-------
|
|
1442
|
+
numpy.ndarray
|
|
1443
|
+
``[λ1, λ2, τ3, τ4]``.
|
|
1444
|
+
|
|
1445
|
+
Raises
|
|
1446
|
+
------
|
|
1447
|
+
NotImplementedError
|
|
1448
|
+
If the family does not implement ``ILinearMomentEstimation``.
|
|
1449
|
+
"""
|
|
1450
|
+
net = _interface(_net_of(self), "ILinearMomentEstimation")
|
|
1451
|
+
return to_numpy_doubles(net.LinearMomentsFromParameters(to_net_doubles(parameters)))
|
|
1452
|
+
|
|
1453
|
+
def quantile_variance(self, probability: float, sample_size: int,
|
|
1454
|
+
method: enums.EstimationMethod | str = enums.EstimationMethod.MAXIMUM_LIKELIHOOD) -> float:
|
|
1455
|
+
"""Return the asymptotic variance of the quantile estimate (``IStandardError.QuantileVariance``).
|
|
1456
|
+
|
|
1457
|
+
Parameters
|
|
1458
|
+
----------
|
|
1459
|
+
probability : float
|
|
1460
|
+
The non-exceedance probability.
|
|
1461
|
+
sample_size : int
|
|
1462
|
+
The sample size n.
|
|
1463
|
+
method : EstimationMethod or str, optional
|
|
1464
|
+
The estimation method; default maximum likelihood.
|
|
1465
|
+
|
|
1466
|
+
Returns
|
|
1467
|
+
-------
|
|
1468
|
+
float
|
|
1469
|
+
The variance of the estimated ``inverse_cdf(probability)``.
|
|
1470
|
+
|
|
1471
|
+
Raises
|
|
1472
|
+
------
|
|
1473
|
+
NotImplementedError
|
|
1474
|
+
If the family does not implement ``IStandardError``.
|
|
1475
|
+
"""
|
|
1476
|
+
net = _interface(self.net, "IStandardError")
|
|
1477
|
+
return float(net.QuantileVariance(float(probability), int(sample_size),
|
|
1478
|
+
net_enum(_nd().ParameterEstimationMethod, method)))
|
|
1479
|
+
|
|
1480
|
+
def quantile_gradient(self, probability: float) -> np.ndarray:
|
|
1481
|
+
"""Return the gradient of the quantile with respect to the parameters (``IStandardError.QuantileGradient``).
|
|
1482
|
+
|
|
1483
|
+
Parameters
|
|
1484
|
+
----------
|
|
1485
|
+
probability : float
|
|
1486
|
+
The non-exceedance probability.
|
|
1487
|
+
|
|
1488
|
+
Returns
|
|
1489
|
+
-------
|
|
1490
|
+
numpy.ndarray
|
|
1491
|
+
The partial derivatives of ``inverse_cdf(probability)``, in parameter order.
|
|
1492
|
+
|
|
1493
|
+
Raises
|
|
1494
|
+
------
|
|
1495
|
+
NotImplementedError
|
|
1496
|
+
If the family does not implement ``IStandardError``.
|
|
1497
|
+
"""
|
|
1498
|
+
return to_numpy_doubles(_interface(self.net, "IStandardError").QuantileGradient(float(probability)))
|
|
1499
|
+
|
|
1500
|
+
def quantile_jacobian(self, probabilities: Any) -> tuple[np.ndarray, float]:
|
|
1501
|
+
"""Return the Jacobian of quantiles with respect to the parameters (``IStandardError.QuantileJacobian``).
|
|
1502
|
+
|
|
1503
|
+
Parameters
|
|
1504
|
+
----------
|
|
1505
|
+
probabilities : array_like
|
|
1506
|
+
Non-exceedance probabilities, one per parameter.
|
|
1507
|
+
|
|
1508
|
+
Returns
|
|
1509
|
+
-------
|
|
1510
|
+
jacobian : numpy.ndarray
|
|
1511
|
+
Square matrix; row i is the gradient of the quantile at ``probabilities[i]``.
|
|
1512
|
+
determinant : float
|
|
1513
|
+
Its determinant.
|
|
1514
|
+
|
|
1515
|
+
Raises
|
|
1516
|
+
------
|
|
1517
|
+
NotImplementedError
|
|
1518
|
+
If the family does not implement ``IStandardError``.
|
|
1519
|
+
"""
|
|
1520
|
+
jacobian, determinant = _interface(self.net, "IStandardError").QuantileJacobian(
|
|
1521
|
+
to_net_doubles(probabilities), 0.0)
|
|
1522
|
+
return to_numpy_matrix(jacobian), float(determinant)
|
|
1523
|
+
|
|
1524
|
+
def log_abs_quantile_jacobian(self, probabilities: Any) -> float:
|
|
1525
|
+
"""Return log abs(det J) of the quantile Jacobian (``StandardErrorExtensions.LogAbsQuantileJacobian``).
|
|
1526
|
+
|
|
1527
|
+
Parameters
|
|
1528
|
+
----------
|
|
1529
|
+
probabilities : array_like
|
|
1530
|
+
Non-exceedance probabilities, one per parameter.
|
|
1531
|
+
|
|
1532
|
+
Returns
|
|
1533
|
+
-------
|
|
1534
|
+
float
|
|
1535
|
+
The natural logarithm of the absolute Jacobian determinant.
|
|
1536
|
+
|
|
1537
|
+
Raises
|
|
1538
|
+
------
|
|
1539
|
+
NotImplementedError
|
|
1540
|
+
If the family does not implement ``IStandardError``.
|
|
1541
|
+
"""
|
|
1542
|
+
net = _interface(self.net, "IStandardError")
|
|
1543
|
+
return float(_nd().StandardErrorExtensions.LogAbsQuantileJacobian(net, to_net_doubles(probabilities)))
|
|
1544
|
+
|
|
1545
|
+
|
|
1546
|
+
def _typed_member(net: Any, name: str, arity: int) -> Any:
|
|
1547
|
+
"""Return the unbound ``name(double x arity)`` overload of ``net``'s concrete type, or None.
|
|
1548
|
+
|
|
1549
|
+
Called as ``member(instance, *values)``. Binding the overload once on the type, rather than
|
|
1550
|
+
resolving the method group on every call, is what makes the per-row loops of
|
|
1551
|
+
``parameter_set_*`` cheap; families without such an overload use the ``IList<double>`` one.
|
|
1552
|
+
"""
|
|
1553
|
+
import System # noqa: PLC0415 - the runtime is running (net exists)
|
|
1554
|
+
|
|
1555
|
+
types = (System.Double,) * arity
|
|
1556
|
+
try:
|
|
1557
|
+
return getattr(type(net), name).Overloads[types if len(types) > 1 else types[0]]
|
|
1558
|
+
except (AttributeError, KeyError, TypeError):
|
|
1559
|
+
return None
|
|
1560
|
+
|
|
1561
|
+
|
|
1562
|
+
class FrozenDistribution(UnivariateDistribution):
|
|
1563
|
+
"""A read-only, hashable distribution, made by :meth:`UnivariateDistribution.frozen`.
|
|
1564
|
+
|
|
1565
|
+
Each frozen object is an instance of ``FrozenDistribution`` and of the family's class (for
|
|
1566
|
+
example ``FrozenGeneralizedExtremeValue`` derives from both), and wraps its own ``Clone`` of the
|
|
1567
|
+
.NET object. ``set_parameters``, ``estimate`` and every attribute assignment (including the
|
|
1568
|
+
settable properties) raise; ``copy.copy`` and ``copy.deepcopy`` return the object itself;
|
|
1569
|
+
:meth:`clone` returns a mutable copy. It hashes by the value ``==`` compares. Changing the
|
|
1570
|
+
``.net`` object through .NET directly bypasses the protection and must not be done.
|
|
1571
|
+
|
|
1572
|
+
Parameters
|
|
1573
|
+
----------
|
|
1574
|
+
net : Numerics.Distributions.UnivariateDistributionBase
|
|
1575
|
+
Unused; frozen distributions are made with :meth:`UnivariateDistribution.frozen`.
|
|
1576
|
+
"""
|
|
1577
|
+
|
|
1578
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
1579
|
+
raise AttributeError(f"{type(self).__name__} is frozen; use clone() for a mutable copy.")
|
|
1580
|
+
|
|
1581
|
+
def __delattr__(self, name: str) -> None:
|
|
1582
|
+
raise AttributeError(f"{type(self).__name__} is frozen; use clone() for a mutable copy.")
|
|
1583
|
+
|
|
1584
|
+
def _ensure_mutable(self) -> None:
|
|
1585
|
+
raise TypeError(f"{type(self).__name__} is frozen; use clone() for a mutable copy.")
|
|
1586
|
+
|
|
1587
|
+
def __hash__(self) -> int:
|
|
1588
|
+
key = self.__dict__.get("_hash")
|
|
1589
|
+
if key is None:
|
|
1590
|
+
key = self.__dict__["_hash"] = hash(self._value_key())
|
|
1591
|
+
return key
|
|
1592
|
+
|
|
1593
|
+
def __copy__(self) -> FrozenDistribution:
|
|
1594
|
+
return self
|
|
1595
|
+
|
|
1596
|
+
def __deepcopy__(self, memo: dict) -> FrozenDistribution:
|
|
1597
|
+
return self
|
|
1598
|
+
|
|
1599
|
+
def frozen(self) -> FrozenDistribution:
|
|
1600
|
+
"""Return this distribution, which is already frozen.
|
|
1601
|
+
|
|
1602
|
+
Returns
|
|
1603
|
+
-------
|
|
1604
|
+
FrozenDistribution
|
|
1605
|
+
``self``.
|
|
1606
|
+
"""
|
|
1607
|
+
return self
|
|
1608
|
+
|
|
1609
|
+
|
|
1610
|
+
_FROZEN_CLASSES: dict[type, type] = {}
|
|
1611
|
+
|
|
1612
|
+
|
|
1613
|
+
def _frozen_class(cls: type) -> type:
|
|
1614
|
+
"""Return the frozen class of a wrapper class, created once: ``Frozen<Name>(FrozenDistribution, cls)``."""
|
|
1615
|
+
if issubclass(cls, FrozenDistribution):
|
|
1616
|
+
return cls
|
|
1617
|
+
frozen = _FROZEN_CLASSES.get(cls)
|
|
1618
|
+
if frozen is None:
|
|
1619
|
+
if cls is UnivariateDistribution:
|
|
1620
|
+
frozen = FrozenDistribution
|
|
1621
|
+
else:
|
|
1622
|
+
frozen = types.new_class(f"Frozen{cls.__name__}", (FrozenDistribution, cls), exec_body=lambda ns: ns.update(
|
|
1623
|
+
{"__module__": cls.__module__, "__doc__": f"Frozen {cls.__name__}; see FrozenDistribution."}))
|
|
1624
|
+
_FROZEN_CLASSES[cls] = frozen
|
|
1625
|
+
return frozen
|
|
1626
|
+
|
|
1627
|
+
|
|
1628
|
+
_CLASS_BY_NET_NAME: dict[str, type[UnivariateDistribution]] = {}
|
|
1629
|
+
_FAMILIES: dict[str, type[UnivariateDistribution]] = {}
|
|
1630
|
+
|
|
1631
|
+
FAMILIES: Mapping[str, type[UnivariateDistribution]] = types.MappingProxyType(_FAMILIES)
|
|
1632
|
+
"""The typed distribution classes, keyed by their Numerics type name (``UnivariateDistributionType``).
|
|
1633
|
+
|
|
1634
|
+
Read-only. ``FAMILIES[name].type_name == name`` for every entry, so a persisted type name finds its
|
|
1635
|
+
class without an instance; families without a typed class are created by :func:`create_distribution`.
|
|
1636
|
+
"""
|
|
1637
|
+
|
|
1638
|
+
|
|
1639
|
+
def wrap(net: Any) -> UnivariateDistribution:
|
|
1640
|
+
"""Wrap a .NET univariate distribution in the matching Python class.
|
|
1641
|
+
|
|
1642
|
+
Parameters
|
|
1643
|
+
----------
|
|
1644
|
+
net : Numerics.Distributions.UnivariateDistributionBase or IUnivariateDistribution
|
|
1645
|
+
The .NET object.
|
|
1646
|
+
|
|
1647
|
+
Returns
|
|
1648
|
+
-------
|
|
1649
|
+
UnivariateDistribution
|
|
1650
|
+
The specific wrapper class when one exists, otherwise the generic wrapper.
|
|
1651
|
+
"""
|
|
1652
|
+
concrete = unwrap(net)
|
|
1653
|
+
cls = _CLASS_BY_NET_NAME.get(str(concrete.GetType().Name), UnivariateDistribution)
|
|
1654
|
+
instance = cls.__new__(cls)
|
|
1655
|
+
UnivariateDistribution.__init__(instance, concrete)
|
|
1656
|
+
return instance
|
|
1657
|
+
|
|
1658
|
+
|
|
1659
|
+
def _register(net_name: str, type_name: str | None = None):
|
|
1660
|
+
"""Register a wrapper class for a .NET class name (for :func:`wrap`) and its type name (for :data:`FAMILIES`).
|
|
1661
|
+
|
|
1662
|
+
``type_name`` is the ``UnivariateDistributionType`` member, which is the .NET class name unless
|
|
1663
|
+
given (``BetaDistribution`` is ``Beta``).
|
|
1664
|
+
"""
|
|
1665
|
+
|
|
1666
|
+
def decorator(cls: type[UnivariateDistribution]) -> type[UnivariateDistribution]:
|
|
1667
|
+
name = type_name or net_name
|
|
1668
|
+
cls._type_name = name
|
|
1669
|
+
_CLASS_BY_NET_NAME[net_name] = cls
|
|
1670
|
+
_FAMILIES[name] = cls
|
|
1671
|
+
return cls
|
|
1672
|
+
|
|
1673
|
+
return decorator
|
|
1674
|
+
|
|
1675
|
+
|
|
1676
|
+
_LOCATION = "The location parameter ξ (``Xi``); settable."
|
|
1677
|
+
_SCALE = "The scale parameter α > 0 (``Alpha``); settable."
|
|
1678
|
+
_SHAPE = "The shape parameter κ, Hosking sign (``Kappa``); settable."
|
|
1679
|
+
|
|
1680
|
+
|
|
1681
|
+
class _KappaMoments:
|
|
1682
|
+
"""Members of the three-parameter Hosking families with a skew-to-shape solution (GEV, GPA, GLO)."""
|
|
1683
|
+
|
|
1684
|
+
def solve_for_kappa(self, skew: float) -> float:
|
|
1685
|
+
"""Return the shape κ whose product-moment skewness is ``skew`` (``SolveForKappa``).
|
|
1686
|
+
|
|
1687
|
+
Parameters
|
|
1688
|
+
----------
|
|
1689
|
+
skew : float
|
|
1690
|
+
The skewness coefficient.
|
|
1691
|
+
|
|
1692
|
+
Returns
|
|
1693
|
+
-------
|
|
1694
|
+
float
|
|
1695
|
+
The shape κ, Hosking sign.
|
|
1696
|
+
"""
|
|
1697
|
+
return float(self.net.SolveForKappa(float(skew)))
|
|
1698
|
+
|
|
1699
|
+
def direct_method_of_moments(self, moments: Any) -> np.ndarray:
|
|
1700
|
+
"""Return the parameters of the direct method of moments (``DirectMethodOfMoments``).
|
|
1701
|
+
|
|
1702
|
+
Parameters
|
|
1703
|
+
----------
|
|
1704
|
+
moments : array_like
|
|
1705
|
+
``[mean, standard deviation, skewness]`` (further entries are ignored).
|
|
1706
|
+
|
|
1707
|
+
Returns
|
|
1708
|
+
-------
|
|
1709
|
+
numpy.ndarray
|
|
1710
|
+
``[location, scale, shape]``.
|
|
1711
|
+
"""
|
|
1712
|
+
return to_numpy_doubles(self.net.DirectMethodOfMoments(to_net_doubles(moments)))
|
|
1713
|
+
|
|
1714
|
+
|
|
1715
|
+
class _LogMoments:
|
|
1716
|
+
"""Indirect (log-space) moment fits of the log-transformed families (LnNormal, LogNormal, LogPearsonTypeIII)."""
|
|
1717
|
+
|
|
1718
|
+
def indirect_method_of_moments(self, sample: Any) -> np.ndarray:
|
|
1719
|
+
"""Return the product moments of the log-transformed sample (``IndirectMethodOfMoments``).
|
|
1720
|
+
|
|
1721
|
+
The indirect method of moments (U.S. Water Resources Council, 1967) fits the log-space
|
|
1722
|
+
parameters to these moments.
|
|
1723
|
+
|
|
1724
|
+
Parameters
|
|
1725
|
+
----------
|
|
1726
|
+
sample : array_like
|
|
1727
|
+
Positive observations.
|
|
1728
|
+
|
|
1729
|
+
Returns
|
|
1730
|
+
-------
|
|
1731
|
+
numpy.ndarray
|
|
1732
|
+
``[mean, standard deviation, skewness, kurtosis]`` of log_base(sample), in this
|
|
1733
|
+
distribution's base.
|
|
1734
|
+
"""
|
|
1735
|
+
return to_numpy_doubles(self.net.IndirectMethodOfMoments(to_net_doubles(sample)))
|
|
1736
|
+
|
|
1737
|
+
def indirect_method_of_linear_moments(self, sample: Any) -> np.ndarray:
|
|
1738
|
+
"""Return the L-moments of the log-transformed sample (``IndirectMethodOfLinearMoments``).
|
|
1739
|
+
|
|
1740
|
+
Parameters
|
|
1741
|
+
----------
|
|
1742
|
+
sample : array_like
|
|
1743
|
+
Positive observations.
|
|
1744
|
+
|
|
1745
|
+
Returns
|
|
1746
|
+
-------
|
|
1747
|
+
numpy.ndarray
|
|
1748
|
+
``[λ1, λ2, τ3, τ4]`` of log_base(sample), in this distribution's base.
|
|
1749
|
+
"""
|
|
1750
|
+
return to_numpy_doubles(self.net.IndirectMethodOfLinearMoments(to_net_doubles(sample)))
|
|
1751
|
+
|
|
1752
|
+
|
|
1753
|
+
class _WilsonHilferty:
|
|
1754
|
+
"""The Wilson-Hilferty quantile approximation of the gamma-type families."""
|
|
1755
|
+
|
|
1756
|
+
def wilson_hilferty_inverse_cdf(self, probability: float) -> float:
|
|
1757
|
+
"""Return the Wilson-Hilferty approximation of the quantile (``WilsonHilfertyInverseCDF``).
|
|
1758
|
+
|
|
1759
|
+
Parameters
|
|
1760
|
+
----------
|
|
1761
|
+
probability : float
|
|
1762
|
+
The non-exceedance probability.
|
|
1763
|
+
|
|
1764
|
+
Returns
|
|
1765
|
+
-------
|
|
1766
|
+
float
|
|
1767
|
+
The approximate quantile; :meth:`UnivariateDistribution.inverse_cdf` gives the exact one.
|
|
1768
|
+
"""
|
|
1769
|
+
return float(self.net.WilsonHilfertyInverseCDF(float(probability)))
|
|
1770
|
+
|
|
1771
|
+
|
|
1772
|
+
class _PearsonForm(_WilsonHilferty):
|
|
1773
|
+
"""The three-parameter gamma form (ξ, β, α) of the Pearson type III families."""
|
|
1774
|
+
|
|
1775
|
+
support_bound = _net_property(
|
|
1776
|
+
"Xi", "The location ξ of the gamma form: the lower support bound for positive skew, the upper for "
|
|
1777
|
+
"negative (``Xi``; in log space for LogPearsonTypeIII).", settable=False)
|
|
1778
|
+
scale = _net_property("Beta", "The scale β of the gamma form, negative for negative skew (``Beta``).",
|
|
1779
|
+
settable=False)
|
|
1780
|
+
shape = _net_property("Alpha", "The shape α of the gamma form, 4 / skew² (``Alpha``).", settable=False)
|
|
1781
|
+
|
|
1782
|
+
def quantile_gradient_for_moments(self, probability: float) -> np.ndarray:
|
|
1783
|
+
"""Return the quantile gradient in (mean, standard deviation, skew) (``QuantileGradientForMoments``).
|
|
1784
|
+
|
|
1785
|
+
Parameters
|
|
1786
|
+
----------
|
|
1787
|
+
probability : float
|
|
1788
|
+
The non-exceedance probability.
|
|
1789
|
+
|
|
1790
|
+
Returns
|
|
1791
|
+
-------
|
|
1792
|
+
numpy.ndarray
|
|
1793
|
+
The three partial derivatives.
|
|
1794
|
+
"""
|
|
1795
|
+
return to_numpy_doubles(self.net.QuantileGradientForMoments(float(probability)))
|
|
1796
|
+
|
|
1797
|
+
|
|
1798
|
+
class _MonteCarloIntervals:
|
|
1799
|
+
"""Monte Carlo quantile confidence intervals of the normal families."""
|
|
1800
|
+
|
|
1801
|
+
def monte_carlo_confidence_intervals(self, sample_size: int, realizations: int, probabilities: Any,
|
|
1802
|
+
percentiles: Any) -> np.ndarray:
|
|
1803
|
+
"""Return Monte Carlo confidence limits of quantiles (``MonteCarloConfidenceIntervals``).
|
|
1804
|
+
|
|
1805
|
+
Parameters
|
|
1806
|
+
----------
|
|
1807
|
+
sample_size : int
|
|
1808
|
+
The sample size of each realization.
|
|
1809
|
+
realizations : int
|
|
1810
|
+
The number of simulated samples.
|
|
1811
|
+
probabilities : array_like
|
|
1812
|
+
Non-exceedance probabilities of the quantiles.
|
|
1813
|
+
percentiles : array_like
|
|
1814
|
+
Confidence percentiles, for example ``[0.05, 0.95]``.
|
|
1815
|
+
|
|
1816
|
+
Returns
|
|
1817
|
+
-------
|
|
1818
|
+
numpy.ndarray
|
|
1819
|
+
Array of shape ``(len(probabilities), len(percentiles))``.
|
|
1820
|
+
"""
|
|
1821
|
+
return to_numpy_matrix(self.net.MonteCarloConfidenceIntervals(int(sample_size), int(realizations),
|
|
1822
|
+
to_net_doubles(probabilities),
|
|
1823
|
+
to_net_doubles(percentiles)))
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
for _mixin in (_KappaMoments, _LogMoments, _WilsonHilferty, _PearsonForm, _MonteCarloIntervals):
|
|
1827
|
+
translate_class(_mixin)
|
|
1828
|
+
|
|
1829
|
+
|
|
1830
|
+
@_register("GeneralizedExtremeValue")
|
|
1831
|
+
class GeneralizedExtremeValue(_KappaMoments, UnivariateDistribution):
|
|
1832
|
+
"""Generalized extreme value distribution (Hosking parameterization).
|
|
1833
|
+
|
|
1834
|
+
Parameters
|
|
1835
|
+
----------
|
|
1836
|
+
location : float, optional
|
|
1837
|
+
Location ξ; default 100.
|
|
1838
|
+
scale : float, optional
|
|
1839
|
+
Scale α > 0; default 10.
|
|
1840
|
+
shape : float, optional
|
|
1841
|
+
Shape κ (Hosking sign: κ < 0 is heavy-tailed); default 0.
|
|
1842
|
+
"""
|
|
1843
|
+
|
|
1844
|
+
def __init__(self, location: float = 100.0, scale: float = 10.0, shape: float = 0.0) -> None:
|
|
1845
|
+
super().__init__(_nd().GeneralizedExtremeValue(float(location), float(scale), float(shape)))
|
|
1846
|
+
|
|
1847
|
+
location = _net_property("Xi", _LOCATION)
|
|
1848
|
+
scale = _net_property("Alpha", _SCALE)
|
|
1849
|
+
shape = _net_property("Kappa", _SHAPE)
|
|
1850
|
+
|
|
1851
|
+
@classmethod
|
|
1852
|
+
def from_poisson_generalized_pareto(cls, rate: float, location: float, scale: float,
|
|
1853
|
+
shape: float) -> GeneralizedExtremeValue:
|
|
1854
|
+
"""Return the annual-maximum GEV of a Poisson count of generalized Pareto exceedances.
|
|
1855
|
+
|
|
1856
|
+
Calls ``GeneralizedExtremeValue.FromPoissonGeneralizedPareto``: with N ~ Poisson(``rate``)
|
|
1857
|
+
exceedances a year of GPD(``location``, ``scale``, ``shape``), the maximum is GEV with
|
|
1858
|
+
``scale * rate**(-shape)`` and the same shape (Hosking sign), the location formed through
|
|
1859
|
+
``expm1`` so it stays accurate near ``shape = 0``.
|
|
1860
|
+
|
|
1861
|
+
Parameters
|
|
1862
|
+
----------
|
|
1863
|
+
rate : float
|
|
1864
|
+
The mean number of exceedances per year (> 0, finite).
|
|
1865
|
+
location : float
|
|
1866
|
+
The GPD location (the threshold).
|
|
1867
|
+
scale : float
|
|
1868
|
+
The GPD scale (> 0).
|
|
1869
|
+
shape : float
|
|
1870
|
+
The GPD shape κ (Hosking sign).
|
|
1871
|
+
|
|
1872
|
+
Returns
|
|
1873
|
+
-------
|
|
1874
|
+
GeneralizedExtremeValue
|
|
1875
|
+
The annual-maximum distribution.
|
|
1876
|
+
|
|
1877
|
+
Raises
|
|
1878
|
+
------
|
|
1879
|
+
ValueError
|
|
1880
|
+
If ``rate`` or ``scale`` is not positive and finite.
|
|
1881
|
+
"""
|
|
1882
|
+
member = _nd().GeneralizedExtremeValue.FromPoissonGeneralizedPareto
|
|
1883
|
+
instance = cls.__new__(cls)
|
|
1884
|
+
UnivariateDistribution.__init__(instance, member(float(rate), float(location), float(scale), float(shape)))
|
|
1885
|
+
return instance
|
|
1886
|
+
|
|
1887
|
+
def expected_information_matrix(self, sample_size: int) -> np.ndarray:
|
|
1888
|
+
"""Return the expected (Fisher) information matrix of a sample (``ExpectedInformationMatrix``).
|
|
1889
|
+
|
|
1890
|
+
Parameters
|
|
1891
|
+
----------
|
|
1892
|
+
sample_size : int
|
|
1893
|
+
The sample size n.
|
|
1894
|
+
|
|
1895
|
+
Returns
|
|
1896
|
+
-------
|
|
1897
|
+
numpy.ndarray
|
|
1898
|
+
The 3 x 3 information matrix in parameter order.
|
|
1899
|
+
"""
|
|
1900
|
+
return to_numpy_matrix(self.net.ExpectedInformationMatrix(int(sample_size)).ToArray())
|
|
1901
|
+
|
|
1902
|
+
|
|
1903
|
+
@_register("GeneralizedPareto")
|
|
1904
|
+
class GeneralizedPareto(_KappaMoments, UnivariateDistribution):
|
|
1905
|
+
"""Generalized Pareto distribution (Hosking parameterization).
|
|
1906
|
+
|
|
1907
|
+
Parameters
|
|
1908
|
+
----------
|
|
1909
|
+
location : float, optional
|
|
1910
|
+
Location ξ; default 100.
|
|
1911
|
+
scale : float, optional
|
|
1912
|
+
Scale α > 0; default 10.
|
|
1913
|
+
shape : float, optional
|
|
1914
|
+
Shape κ (Hosking sign); default 0.
|
|
1915
|
+
"""
|
|
1916
|
+
|
|
1917
|
+
def __init__(self, location: float = 100.0, scale: float = 10.0, shape: float = 0.0) -> None:
|
|
1918
|
+
super().__init__(_nd().GeneralizedPareto(float(location), float(scale), float(shape)))
|
|
1919
|
+
|
|
1920
|
+
location = _net_property("Xi", _LOCATION)
|
|
1921
|
+
scale = _net_property("Alpha", _SCALE)
|
|
1922
|
+
shape = _net_property("Kappa", _SHAPE)
|
|
1923
|
+
peaks_per_block = _net_property("Lambda", "The average number of peaks per block (``Lambda``); settable.")
|
|
1924
|
+
|
|
1925
|
+
@classmethod
|
|
1926
|
+
def from_poisson_generalized_extreme_value(cls, rate: float, location: float, scale: float,
|
|
1927
|
+
shape: float) -> GeneralizedPareto:
|
|
1928
|
+
"""Return the exceedance GPD whose Poisson annual count gives a stated GEV annual maximum.
|
|
1929
|
+
|
|
1930
|
+
Calls ``GeneralizedPareto.FromPoissonGeneralizedExtremeValue``, the inverse of
|
|
1931
|
+
:meth:`GeneralizedExtremeValue.from_poisson_generalized_pareto`: for an annual-maximum
|
|
1932
|
+
GEV(``location``, ``scale``, ``shape``) and N ~ Poisson(``rate``) exceedances a year, the
|
|
1933
|
+
exceedances are GPD with scale ``scale * rate**shape`` and the same shape (Hosking sign), the
|
|
1934
|
+
location ``location - scale * expm1(shape * log(rate)) / shape`` formed through ``expm1`` so it
|
|
1935
|
+
stays accurate near ``shape = 0`` (``location - scale * log(rate)`` at ``shape = 0``).
|
|
1936
|
+
|
|
1937
|
+
Parameters
|
|
1938
|
+
----------
|
|
1939
|
+
rate : float
|
|
1940
|
+
The mean number of exceedances per year (> 0, finite).
|
|
1941
|
+
location : float
|
|
1942
|
+
The GEV location.
|
|
1943
|
+
scale : float
|
|
1944
|
+
The GEV scale (> 0).
|
|
1945
|
+
shape : float
|
|
1946
|
+
The GEV shape κ (Hosking sign).
|
|
1947
|
+
|
|
1948
|
+
Returns
|
|
1949
|
+
-------
|
|
1950
|
+
GeneralizedPareto
|
|
1951
|
+
The exceedance distribution; its location is the threshold.
|
|
1952
|
+
|
|
1953
|
+
Raises
|
|
1954
|
+
------
|
|
1955
|
+
ValueError
|
|
1956
|
+
If ``rate`` or ``scale`` is not positive and finite, ``location`` or ``shape`` is not finite,
|
|
1957
|
+
or the mapped parameters overflow.
|
|
1958
|
+
"""
|
|
1959
|
+
member = _nd().GeneralizedPareto.FromPoissonGeneralizedExtremeValue
|
|
1960
|
+
instance = cls.__new__(cls)
|
|
1961
|
+
UnivariateDistribution.__init__(instance, member(float(rate), float(location), float(scale), float(shape)))
|
|
1962
|
+
return instance
|
|
1963
|
+
|
|
1964
|
+
@classmethod
|
|
1965
|
+
def fit_known_location(cls, sample: Any, location: float) -> GeneralizedPareto:
|
|
1966
|
+
"""Fit the scale and shape by maximum likelihood at a known location (``MLE(sample, location)``).
|
|
1967
|
+
|
|
1968
|
+
Parameters
|
|
1969
|
+
----------
|
|
1970
|
+
sample : array_like
|
|
1971
|
+
Observations, each at or above ``location`` (for example the peaks over a threshold).
|
|
1972
|
+
location : float
|
|
1973
|
+
The known location ξ (for example the threshold).
|
|
1974
|
+
|
|
1975
|
+
Returns
|
|
1976
|
+
-------
|
|
1977
|
+
GeneralizedPareto
|
|
1978
|
+
The fitted distribution, with location ``location``.
|
|
1979
|
+
"""
|
|
1980
|
+
parameters = to_numpy_doubles(_nd().GeneralizedPareto().MLE(to_net_doubles(sample), float(location)))
|
|
1981
|
+
return cls(float(parameters[0]), float(parameters[1]), float(parameters[2]))
|
|
1982
|
+
|
|
1983
|
+
def mle(self, sample: Any, location: float | None = None) -> np.ndarray:
|
|
1984
|
+
"""Return the maximum likelihood parameters of a sample, without changing this distribution.
|
|
1985
|
+
|
|
1986
|
+
Parameters
|
|
1987
|
+
----------
|
|
1988
|
+
sample : array_like
|
|
1989
|
+
Observations.
|
|
1990
|
+
location : float, optional
|
|
1991
|
+
A known location; when given only the scale and shape are fitted (``MLE(sample, location)``).
|
|
1992
|
+
|
|
1993
|
+
Returns
|
|
1994
|
+
-------
|
|
1995
|
+
numpy.ndarray
|
|
1996
|
+
``[location, scale, shape]``.
|
|
1997
|
+
"""
|
|
1998
|
+
if location is None:
|
|
1999
|
+
return super().mle(sample)
|
|
2000
|
+
return to_numpy_doubles(self.net.MLE(to_net_doubles(sample), float(location)))
|
|
2001
|
+
|
|
2002
|
+
def modified_method_of_moments(self, sample: Any) -> np.ndarray:
|
|
2003
|
+
"""Return the parameters of the modified method of moments (``ModifiedMethodOfMoments``).
|
|
2004
|
+
|
|
2005
|
+
Parameters
|
|
2006
|
+
----------
|
|
2007
|
+
sample : array_like
|
|
2008
|
+
Observations.
|
|
2009
|
+
|
|
2010
|
+
Returns
|
|
2011
|
+
-------
|
|
2012
|
+
numpy.ndarray
|
|
2013
|
+
``[location, scale, shape]``.
|
|
2014
|
+
"""
|
|
2015
|
+
return to_numpy_doubles(self.net.ModifiedMethodOfMoments(to_net_doubles(sample)))
|
|
2016
|
+
|
|
2017
|
+
|
|
2018
|
+
@_register("Gumbel")
|
|
2019
|
+
class Gumbel(UnivariateDistribution):
|
|
2020
|
+
"""Gumbel (extreme value type I) distribution.
|
|
2021
|
+
|
|
2022
|
+
Parameters
|
|
2023
|
+
----------
|
|
2024
|
+
location : float, optional
|
|
2025
|
+
Location ξ; default 100.
|
|
2026
|
+
scale : float, optional
|
|
2027
|
+
Scale α > 0; default 10.
|
|
2028
|
+
"""
|
|
2029
|
+
|
|
2030
|
+
def __init__(self, location: float = 100.0, scale: float = 10.0) -> None:
|
|
2031
|
+
super().__init__(_nd().Gumbel(float(location), float(scale)))
|
|
2032
|
+
|
|
2033
|
+
location = _net_property("Xi", _LOCATION)
|
|
2034
|
+
scale = _net_property("Alpha", _SCALE)
|
|
2035
|
+
|
|
2036
|
+
def set_parameters_from_mle(self, sample: Any) -> None:
|
|
2037
|
+
"""Fit the parameters in place by maximum likelihood (``SetParametersFromMLE``).
|
|
2038
|
+
|
|
2039
|
+
Parameters
|
|
2040
|
+
----------
|
|
2041
|
+
sample : array_like
|
|
2042
|
+
Observations.
|
|
2043
|
+
|
|
2044
|
+
Raises
|
|
2045
|
+
------
|
|
2046
|
+
TypeError
|
|
2047
|
+
If the distribution is frozen.
|
|
2048
|
+
"""
|
|
2049
|
+
self._ensure_mutable()
|
|
2050
|
+
self.net.SetParametersFromMLE(to_net_doubles(sample))
|
|
2051
|
+
|
|
2052
|
+
|
|
2053
|
+
@_register("Normal")
|
|
2054
|
+
class Normal(_MonteCarloIntervals, UnivariateDistribution):
|
|
2055
|
+
"""Normal distribution.
|
|
2056
|
+
|
|
2057
|
+
Parameters
|
|
2058
|
+
----------
|
|
2059
|
+
mean : float, optional
|
|
2060
|
+
Mean μ; default 0.
|
|
2061
|
+
standard_deviation : float, optional
|
|
2062
|
+
Standard deviation σ > 0; default 1.
|
|
2063
|
+
"""
|
|
2064
|
+
|
|
2065
|
+
def __init__(self, mean: float = 0.0, standard_deviation: float = 1.0) -> None:
|
|
2066
|
+
super().__init__(_nd().Normal(float(mean), float(standard_deviation)))
|
|
2067
|
+
|
|
2068
|
+
mean = _net_property("Mu", "The mean μ (``Mu``); settable.")
|
|
2069
|
+
standard_deviation = _net_property("Sigma", "The standard deviation σ > 0 (``Sigma``); settable.")
|
|
2070
|
+
|
|
2071
|
+
def normal_confidence_intervals(self, sample_size: int, probabilities: Any, percentiles: Any) -> np.ndarray:
|
|
2072
|
+
"""Return normal-approximation confidence limits of quantiles (``NormalConfidenceIntervals``).
|
|
2073
|
+
|
|
2074
|
+
Parameters
|
|
2075
|
+
----------
|
|
2076
|
+
sample_size : int
|
|
2077
|
+
The sample size.
|
|
2078
|
+
probabilities : array_like
|
|
2079
|
+
Non-exceedance probabilities of the quantiles.
|
|
2080
|
+
percentiles : array_like
|
|
2081
|
+
Confidence percentiles, for example ``[0.05, 0.95]``.
|
|
2082
|
+
|
|
2083
|
+
Returns
|
|
2084
|
+
-------
|
|
2085
|
+
numpy.ndarray
|
|
2086
|
+
Array of shape ``(len(probabilities), len(percentiles))``.
|
|
2087
|
+
"""
|
|
2088
|
+
return to_numpy_matrix(self.net.NormalConfidenceIntervals(int(sample_size), to_net_doubles(probabilities),
|
|
2089
|
+
to_net_doubles(percentiles)))
|
|
2090
|
+
|
|
2091
|
+
def noncentral_t_confidence_intervals(self, sample_size: int, probabilities: Any,
|
|
2092
|
+
percentiles: Any) -> np.ndarray:
|
|
2093
|
+
"""Return exact noncentral-t confidence limits of quantiles (``NoncentralTConfidenceIntervals``).
|
|
2094
|
+
|
|
2095
|
+
Parameters
|
|
2096
|
+
----------
|
|
2097
|
+
sample_size : int
|
|
2098
|
+
The sample size.
|
|
2099
|
+
probabilities : array_like
|
|
2100
|
+
Non-exceedance probabilities of the quantiles.
|
|
2101
|
+
percentiles : array_like
|
|
2102
|
+
Confidence percentiles, for example ``[0.05, 0.95]``.
|
|
2103
|
+
|
|
2104
|
+
Returns
|
|
2105
|
+
-------
|
|
2106
|
+
numpy.ndarray
|
|
2107
|
+
Array of shape ``(len(probabilities), len(percentiles))``.
|
|
2108
|
+
"""
|
|
2109
|
+
return to_numpy_matrix(self.net.NoncentralTConfidenceIntervals(
|
|
2110
|
+
int(sample_size), to_net_doubles(probabilities), to_net_doubles(percentiles)))
|
|
2111
|
+
|
|
2112
|
+
def expected_probability(self, sample_size: int, probability: float) -> float:
|
|
2113
|
+
"""Return the expected non-exceedance probability of the estimated quantile (``ExpectedProbability``).
|
|
2114
|
+
|
|
2115
|
+
Parameters
|
|
2116
|
+
----------
|
|
2117
|
+
sample_size : int
|
|
2118
|
+
The sample size.
|
|
2119
|
+
probability : float
|
|
2120
|
+
The nominal non-exceedance probability.
|
|
2121
|
+
|
|
2122
|
+
Returns
|
|
2123
|
+
-------
|
|
2124
|
+
float
|
|
2125
|
+
The expected probability, which accounts for parameter uncertainty.
|
|
2126
|
+
"""
|
|
2127
|
+
return float(self.net.ExpectedProbability(int(sample_size), float(probability)))
|
|
2128
|
+
|
|
2129
|
+
|
|
2130
|
+
@_register("LogNormal")
|
|
2131
|
+
class LogNormal(_LogMoments, _MonteCarloIntervals, UnivariateDistribution):
|
|
2132
|
+
"""Log-normal distribution with log-space parameters (``Numerics.Distributions.LogNormal``).
|
|
2133
|
+
|
|
2134
|
+
Parameters
|
|
2135
|
+
----------
|
|
2136
|
+
mean_of_log : float, optional
|
|
2137
|
+
Mean of log_base(X); default 3.
|
|
2138
|
+
standard_deviation_of_log : float, optional
|
|
2139
|
+
Standard deviation of log_base(X); default 0.5.
|
|
2140
|
+
base : float, optional
|
|
2141
|
+
The logarithm base, finite and greater than one; default 10.
|
|
2142
|
+
"""
|
|
2143
|
+
|
|
2144
|
+
def __init__(self, mean_of_log: float = 3.0, standard_deviation_of_log: float = 0.5, base: float = 10.0) -> None:
|
|
2145
|
+
net = _nd().LogNormal(float(mean_of_log), float(standard_deviation_of_log))
|
|
2146
|
+
net.Base = float(base)
|
|
2147
|
+
super().__init__(net)
|
|
2148
|
+
|
|
2149
|
+
mean_of_log = _net_property("Mu", "The mean of log_base(X) (``Mu``); settable.")
|
|
2150
|
+
standard_deviation_of_log = _net_property("Sigma", "The standard deviation of log_base(X) (``Sigma``); settable.")
|
|
2151
|
+
base = _net_property("Base", "The logarithm base, finite and greater than one (``Base``); settable.")
|
|
2152
|
+
|
|
2153
|
+
def direct_method_of_moments(self, mean: float, standard_deviation: float) -> tuple[float, float]:
|
|
2154
|
+
"""Return (mean_of_log, standard_deviation_of_log) for real-space moments (``DirectMethodOfMoments``).
|
|
2155
|
+
|
|
2156
|
+
Parameters
|
|
2157
|
+
----------
|
|
2158
|
+
mean : float
|
|
2159
|
+
The mean of X.
|
|
2160
|
+
standard_deviation : float
|
|
2161
|
+
The standard deviation of X.
|
|
2162
|
+
|
|
2163
|
+
Returns
|
|
2164
|
+
-------
|
|
2165
|
+
tuple of float
|
|
2166
|
+
The log-space parameters in this distribution's base.
|
|
2167
|
+
"""
|
|
2168
|
+
values = self.net.DirectMethodOfMoments(float(mean), float(standard_deviation))
|
|
2169
|
+
return float(values[0]), float(values[1])
|
|
2170
|
+
|
|
2171
|
+
|
|
2172
|
+
@_register("LnNormal")
|
|
2173
|
+
class LnNormal(_LogMoments, UnivariateDistribution):
|
|
2174
|
+
"""Natural-log normal distribution with real-space moments (``LnNormal``).
|
|
2175
|
+
|
|
2176
|
+
Parameters
|
|
2177
|
+
----------
|
|
2178
|
+
mean : float, optional
|
|
2179
|
+
Real-space mean of X; default 10.
|
|
2180
|
+
standard_deviation : float, optional
|
|
2181
|
+
Real-space standard deviation of X; default 10.
|
|
2182
|
+
"""
|
|
2183
|
+
|
|
2184
|
+
def __init__(self, mean: float = 10.0, standard_deviation: float = 10.0) -> None:
|
|
2185
|
+
super().__init__(_nd().LnNormal(float(mean), float(standard_deviation)))
|
|
2186
|
+
|
|
2187
|
+
mean_of_log = _net_property("Mu", "The mean μ of ln X (``Mu``); settable.")
|
|
2188
|
+
standard_deviation_of_log = _net_property("Sigma", "The standard deviation σ of ln X (``Sigma``); settable.")
|
|
2189
|
+
|
|
2190
|
+
@staticmethod
|
|
2191
|
+
def direct_method_of_moments(mean: float, standard_deviation: float) -> tuple[float, float]:
|
|
2192
|
+
"""Return (mu, sigma) of ln X for real-space moments (``LnNormal.DirectMethodOfMoments``).
|
|
2193
|
+
|
|
2194
|
+
Parameters
|
|
2195
|
+
----------
|
|
2196
|
+
mean : float
|
|
2197
|
+
The mean of X.
|
|
2198
|
+
standard_deviation : float
|
|
2199
|
+
The standard deviation of X.
|
|
2200
|
+
|
|
2201
|
+
Returns
|
|
2202
|
+
-------
|
|
2203
|
+
tuple of float
|
|
2204
|
+
``(mu, sigma)`` as Numerics computes them, before the ``Sigma`` setter's clamp.
|
|
2205
|
+
"""
|
|
2206
|
+
values = _nd().LnNormal.DirectMethodOfMoments(float(mean), float(standard_deviation))
|
|
2207
|
+
return float(values[0]), float(values[1])
|
|
2208
|
+
|
|
2209
|
+
@classmethod
|
|
2210
|
+
def from_log_parameters(cls, mu: float, sigma: float) -> LnNormal:
|
|
2211
|
+
"""Create the distribution from mu and sigma of ln X (the ``Mu`` and ``Sigma`` setters).
|
|
2212
|
+
|
|
2213
|
+
Parameters
|
|
2214
|
+
----------
|
|
2215
|
+
mu : float
|
|
2216
|
+
The mean of ln X.
|
|
2217
|
+
sigma : float
|
|
2218
|
+
The standard deviation of ln X.
|
|
2219
|
+
|
|
2220
|
+
Returns
|
|
2221
|
+
-------
|
|
2222
|
+
LnNormal
|
|
2223
|
+
The distribution; Numerics derives the real-space moments from mu and sigma.
|
|
2224
|
+
"""
|
|
2225
|
+
net = _nd().LnNormal()
|
|
2226
|
+
net.Mu = float(mu)
|
|
2227
|
+
net.Sigma = float(sigma)
|
|
2228
|
+
instance = cls.__new__(cls)
|
|
2229
|
+
UnivariateDistribution.__init__(instance, net)
|
|
2230
|
+
return instance
|
|
2231
|
+
|
|
2232
|
+
def indirect_method_of_moments(self, sample: Any) -> np.ndarray:
|
|
2233
|
+
"""Return the product moments of ln(sample) (``LnNormal.IndirectMethodOfMoments``).
|
|
2234
|
+
|
|
2235
|
+
Parameters
|
|
2236
|
+
----------
|
|
2237
|
+
sample : array_like
|
|
2238
|
+
Positive observations.
|
|
2239
|
+
|
|
2240
|
+
Returns
|
|
2241
|
+
-------
|
|
2242
|
+
numpy.ndarray
|
|
2243
|
+
``[mean, standard deviation, skewness, kurtosis]`` of the natural logarithms.
|
|
2244
|
+
"""
|
|
2245
|
+
return to_numpy_doubles(_nd().LnNormal.IndirectMethodOfMoments(to_net_doubles(sample)))
|
|
2246
|
+
|
|
2247
|
+
|
|
2248
|
+
@_register("PearsonTypeIII")
|
|
2249
|
+
class PearsonTypeIII(_PearsonForm, UnivariateDistribution):
|
|
2250
|
+
"""Pearson type III distribution.
|
|
2251
|
+
|
|
2252
|
+
Parameters
|
|
2253
|
+
----------
|
|
2254
|
+
mean : float, optional
|
|
2255
|
+
Mean μ; default 100.
|
|
2256
|
+
standard_deviation : float, optional
|
|
2257
|
+
Standard deviation σ > 0; default 10.
|
|
2258
|
+
skew : float, optional
|
|
2259
|
+
Skewness γ; default 0.
|
|
2260
|
+
"""
|
|
2261
|
+
|
|
2262
|
+
def __init__(self, mean: float = 100.0, standard_deviation: float = 10.0, skew: float = 0.0) -> None:
|
|
2263
|
+
super().__init__(_nd().PearsonTypeIII(float(mean), float(standard_deviation), float(skew)))
|
|
2264
|
+
|
|
2265
|
+
mean = _net_property("Mu", "The mean μ (``Mu``); settable.")
|
|
2266
|
+
standard_deviation = _net_property("Sigma", "The standard deviation σ > 0 (``Sigma``); settable.")
|
|
2267
|
+
skew = _net_property("Gamma", "The skewness γ (``Gamma``); settable.")
|
|
2268
|
+
|
|
2269
|
+
|
|
2270
|
+
@_register("LogPearsonTypeIII")
|
|
2271
|
+
class LogPearsonTypeIII(_LogMoments, _PearsonForm, UnivariateDistribution):
|
|
2272
|
+
"""Log-Pearson type III distribution: Pearson type III in log_base(X).
|
|
2273
|
+
|
|
2274
|
+
Parameters
|
|
2275
|
+
----------
|
|
2276
|
+
mean_of_log : float, optional
|
|
2277
|
+
Mean of log_base(X); default 3.
|
|
2278
|
+
standard_deviation_of_log : float, optional
|
|
2279
|
+
Standard deviation of log_base(X); default 0.5.
|
|
2280
|
+
skew_of_log : float, optional
|
|
2281
|
+
Skewness of log_base(X); default 0.
|
|
2282
|
+
base : float, optional
|
|
2283
|
+
The logarithm base, finite and greater than one; default 10.
|
|
2284
|
+
"""
|
|
2285
|
+
|
|
2286
|
+
def __init__(self, mean_of_log: float = 3.0, standard_deviation_of_log: float = 0.5, skew_of_log: float = 0.0,
|
|
2287
|
+
base: float = 10.0) -> None:
|
|
2288
|
+
net = _nd().LogPearsonTypeIII(float(mean_of_log), float(standard_deviation_of_log), float(skew_of_log))
|
|
2289
|
+
net.Base = float(base)
|
|
2290
|
+
super().__init__(net)
|
|
2291
|
+
|
|
2292
|
+
mean_of_log = _net_property("Mu", "The mean of log_base(X) (``Mu``); settable.")
|
|
2293
|
+
standard_deviation_of_log = _net_property("Sigma", "The standard deviation of log_base(X) (``Sigma``); settable.")
|
|
2294
|
+
skew_of_log = _net_property("Gamma", "The skewness of log_base(X) (``Gamma``); settable.")
|
|
2295
|
+
base = _net_property("Base", "The logarithm base, finite and greater than one (``Base``); settable.")
|
|
2296
|
+
|
|
2297
|
+
def log_space(self) -> PearsonTypeIII:
|
|
2298
|
+
"""Return the Pearson type III distribution of log_base(X).
|
|
2299
|
+
|
|
2300
|
+
Returns
|
|
2301
|
+
-------
|
|
2302
|
+
PearsonTypeIII
|
|
2303
|
+
``PearsonTypeIII(mean_of_log, standard_deviation_of_log, skew_of_log)``.
|
|
2304
|
+
"""
|
|
2305
|
+
return PearsonTypeIII(self.mean_of_log, self.standard_deviation_of_log, self.skew_of_log)
|
|
2306
|
+
|
|
2307
|
+
|
|
2308
|
+
@_register("KappaFour")
|
|
2309
|
+
class KappaFour(UnivariateDistribution):
|
|
2310
|
+
"""Four-parameter kappa distribution (Hosking).
|
|
2311
|
+
|
|
2312
|
+
Parameters
|
|
2313
|
+
----------
|
|
2314
|
+
location : float, optional
|
|
2315
|
+
Location ξ; default 100.
|
|
2316
|
+
scale : float, optional
|
|
2317
|
+
Scale α > 0; default 10.
|
|
2318
|
+
shape : float, optional
|
|
2319
|
+
Shape κ; default 0.
|
|
2320
|
+
shape2 : float, optional
|
|
2321
|
+
Second shape h; default 0.
|
|
2322
|
+
"""
|
|
2323
|
+
|
|
2324
|
+
def __init__(self, location: float = 100.0, scale: float = 10.0, shape: float = 0.0, shape2: float = 0.0) -> None:
|
|
2325
|
+
super().__init__(_nd().KappaFour(float(location), float(scale), float(shape), float(shape2)))
|
|
2326
|
+
|
|
2327
|
+
location = _net_property("Xi", _LOCATION)
|
|
2328
|
+
scale = _net_property("Alpha", _SCALE)
|
|
2329
|
+
shape = _net_property("Kappa", _SHAPE)
|
|
2330
|
+
shape2 = _net_property("Hondo", "The second shape parameter h (``Hondo``); settable.")
|
|
2331
|
+
|
|
2332
|
+
|
|
2333
|
+
@_register("Uniform")
|
|
2334
|
+
class Uniform(UnivariateDistribution):
|
|
2335
|
+
"""Continuous uniform distribution on [minimum, maximum] (``Uniform``).
|
|
2336
|
+
|
|
2337
|
+
Parameters
|
|
2338
|
+
----------
|
|
2339
|
+
minimum : float, optional
|
|
2340
|
+
Lower end; default 0.
|
|
2341
|
+
maximum : float, optional
|
|
2342
|
+
Upper end; default 1.
|
|
2343
|
+
"""
|
|
2344
|
+
|
|
2345
|
+
def __init__(self, minimum: float = 0.0, maximum: float = 1.0) -> None:
|
|
2346
|
+
super().__init__(_nd().Uniform(float(minimum), float(maximum)))
|
|
2347
|
+
|
|
2348
|
+
minimum = _net_property("Min", "The lower end of the support (``Min``); settable.")
|
|
2349
|
+
maximum = _net_property("Max", "The upper end of the support (``Max``); settable.")
|
|
2350
|
+
|
|
2351
|
+
|
|
2352
|
+
@_register("BetaDistribution", "Beta")
|
|
2353
|
+
class Beta(UnivariateDistribution):
|
|
2354
|
+
"""Beta distribution on [0, 1] (``BetaDistribution``).
|
|
2355
|
+
|
|
2356
|
+
Parameters
|
|
2357
|
+
----------
|
|
2358
|
+
alpha : float, optional
|
|
2359
|
+
First shape α > 0; default 2.
|
|
2360
|
+
beta : float, optional
|
|
2361
|
+
Second shape β > 0; default 2.
|
|
2362
|
+
"""
|
|
2363
|
+
|
|
2364
|
+
def __init__(self, alpha: float = 2.0, beta: float = 2.0) -> None:
|
|
2365
|
+
super().__init__(_nd().BetaDistribution(float(alpha), float(beta)))
|
|
2366
|
+
|
|
2367
|
+
alpha = _net_property("Alpha", "The first shape α > 0 (``Alpha``); settable.")
|
|
2368
|
+
beta = _net_property("Beta", "The second shape β > 0 (``Beta``); settable.")
|
|
2369
|
+
|
|
2370
|
+
|
|
2371
|
+
@_register("Poisson")
|
|
2372
|
+
class Poisson(UnivariateDistribution):
|
|
2373
|
+
"""Poisson distribution (discrete; ``pdf`` is the probability mass).
|
|
2374
|
+
|
|
2375
|
+
Parameters
|
|
2376
|
+
----------
|
|
2377
|
+
rate : float, optional
|
|
2378
|
+
Rate λ > 0; default 1.
|
|
2379
|
+
"""
|
|
2380
|
+
|
|
2381
|
+
def __init__(self, rate: float = 1.0) -> None:
|
|
2382
|
+
super().__init__(_nd().Poisson(float(rate)))
|
|
2383
|
+
|
|
2384
|
+
rate = _net_property("Lambda", "The rate λ > 0 (``Lambda``); settable.")
|
|
2385
|
+
|
|
2386
|
+
|
|
2387
|
+
@_register("Bernoulli")
|
|
2388
|
+
class Bernoulli(UnivariateDistribution):
|
|
2389
|
+
"""Bernoulli distribution on {0, 1} (discrete; ``pdf`` is the probability mass).
|
|
2390
|
+
|
|
2391
|
+
Parameters
|
|
2392
|
+
----------
|
|
2393
|
+
probability : float, optional
|
|
2394
|
+
Probability of a one, in [0, 1]; default 0.5.
|
|
2395
|
+
"""
|
|
2396
|
+
|
|
2397
|
+
def __init__(self, probability: float = 0.5) -> None:
|
|
2398
|
+
super().__init__(_nd().Bernoulli(float(probability)))
|
|
2399
|
+
|
|
2400
|
+
probability = _net_property("Probability", "The probability p of a one (``Probability``); settable.")
|
|
2401
|
+
complement = _net_property("Complement", "The probability 1 - p of a zero (``Complement``).", settable=False)
|
|
2402
|
+
|
|
2403
|
+
|
|
2404
|
+
@_register("Binomial")
|
|
2405
|
+
class Binomial(UnivariateDistribution):
|
|
2406
|
+
"""Binomial distribution: successes in ``number_of_trials`` trials (discrete).
|
|
2407
|
+
|
|
2408
|
+
Parameters
|
|
2409
|
+
----------
|
|
2410
|
+
probability : float, optional
|
|
2411
|
+
Probability of success per trial, in [0, 1]; default 0.5.
|
|
2412
|
+
number_of_trials : int, optional
|
|
2413
|
+
The number of trials n >= 0; default 10.
|
|
2414
|
+
"""
|
|
2415
|
+
|
|
2416
|
+
def __init__(self, probability: float = 0.5, number_of_trials: int = 10) -> None:
|
|
2417
|
+
super().__init__(_nd().Binomial(float(probability), int(number_of_trials)))
|
|
2418
|
+
|
|
2419
|
+
probability = _net_property("ProbabilityOfSuccess", "The probability of success p (``ProbabilityOfSuccess``); "
|
|
2420
|
+
"settable.")
|
|
2421
|
+
number_of_trials = _net_property("NumberOfTrials", "The number of trials n (``NumberOfTrials``); settable.",
|
|
2422
|
+
kind=int)
|
|
2423
|
+
complement = _net_property("Complement", "The probability of failure 1 - p (``Complement``).", settable=False)
|
|
2424
|
+
|
|
2425
|
+
|
|
2426
|
+
@_register("Cauchy")
|
|
2427
|
+
class Cauchy(UnivariateDistribution):
|
|
2428
|
+
"""Cauchy distribution.
|
|
2429
|
+
|
|
2430
|
+
Parameters
|
|
2431
|
+
----------
|
|
2432
|
+
location : float, optional
|
|
2433
|
+
Location x0; default 0.
|
|
2434
|
+
scale : float, optional
|
|
2435
|
+
Scale γ > 0; default 1.
|
|
2436
|
+
"""
|
|
2437
|
+
|
|
2438
|
+
def __init__(self, location: float = 0.0, scale: float = 1.0) -> None:
|
|
2439
|
+
super().__init__(_nd().Cauchy(float(location), float(scale)))
|
|
2440
|
+
|
|
2441
|
+
location = _net_property("X0", "The location x0 (``X0``); settable.")
|
|
2442
|
+
scale = _net_property("Gamma", "The scale γ > 0 (``Gamma``); settable.")
|
|
2443
|
+
|
|
2444
|
+
|
|
2445
|
+
@_register("ChiSquared")
|
|
2446
|
+
class ChiSquared(UnivariateDistribution):
|
|
2447
|
+
"""Chi-squared distribution.
|
|
2448
|
+
|
|
2449
|
+
Parameters
|
|
2450
|
+
----------
|
|
2451
|
+
degrees_of_freedom : int, optional
|
|
2452
|
+
Degrees of freedom ν >= 1; default 10.
|
|
2453
|
+
"""
|
|
2454
|
+
|
|
2455
|
+
def __init__(self, degrees_of_freedom: int = 10) -> None:
|
|
2456
|
+
super().__init__(_nd().ChiSquared(int(degrees_of_freedom)))
|
|
2457
|
+
|
|
2458
|
+
degrees_of_freedom = _net_property("DegreesOfFreedom", "The degrees of freedom ν (``DegreesOfFreedom``); "
|
|
2459
|
+
"settable.", kind=int)
|
|
2460
|
+
|
|
2461
|
+
|
|
2462
|
+
@_register("Deterministic")
|
|
2463
|
+
class Deterministic(UnivariateDistribution):
|
|
2464
|
+
"""A point mass at one value (``Deterministic``).
|
|
2465
|
+
|
|
2466
|
+
Parameters
|
|
2467
|
+
----------
|
|
2468
|
+
value : float, optional
|
|
2469
|
+
The value; default 0.5.
|
|
2470
|
+
"""
|
|
2471
|
+
|
|
2472
|
+
def __init__(self, value: float = 0.5) -> None:
|
|
2473
|
+
super().__init__(_nd().Deterministic(float(value)))
|
|
2474
|
+
|
|
2475
|
+
value = _net_property("Value", "The value of the point mass (``Value``); settable.")
|
|
2476
|
+
|
|
2477
|
+
|
|
2478
|
+
@_register("Exponential")
|
|
2479
|
+
class Exponential(UnivariateDistribution):
|
|
2480
|
+
"""Shifted exponential distribution: ``F(x) = 1 - exp(-(x - ξ) / α)`` for x >= ξ.
|
|
2481
|
+
|
|
2482
|
+
Parameters
|
|
2483
|
+
----------
|
|
2484
|
+
location : float, optional
|
|
2485
|
+
Location ξ; default 100.
|
|
2486
|
+
scale : float, optional
|
|
2487
|
+
Scale α > 0 (the mean excess); default 10.
|
|
2488
|
+
"""
|
|
2489
|
+
|
|
2490
|
+
def __init__(self, location: float = 100.0, scale: float = 10.0) -> None:
|
|
2491
|
+
super().__init__(_nd().Exponential(float(location), float(scale)))
|
|
2492
|
+
|
|
2493
|
+
location = _net_property("Xi", _LOCATION)
|
|
2494
|
+
scale = _net_property("Alpha", _SCALE)
|
|
2495
|
+
|
|
2496
|
+
|
|
2497
|
+
@_register("GammaDistribution")
|
|
2498
|
+
class GammaDistribution(_WilsonHilferty, UnivariateDistribution):
|
|
2499
|
+
"""Gamma distribution with scale θ and shape κ (``GammaDistribution``).
|
|
2500
|
+
|
|
2501
|
+
Parameters
|
|
2502
|
+
----------
|
|
2503
|
+
scale : float, optional
|
|
2504
|
+
Scale θ > 0; default 10.
|
|
2505
|
+
shape : float, optional
|
|
2506
|
+
Shape κ > 0; default 2.
|
|
2507
|
+
"""
|
|
2508
|
+
|
|
2509
|
+
def __init__(self, scale: float = 10.0, shape: float = 2.0) -> None:
|
|
2510
|
+
super().__init__(_nd().GammaDistribution(float(scale), float(shape)))
|
|
2511
|
+
|
|
2512
|
+
scale = _net_property("Theta", "The scale θ > 0 (``Theta``); settable.")
|
|
2513
|
+
shape = _net_property("Kappa", "The shape κ > 0 (``Kappa``); settable.")
|
|
2514
|
+
rate = _net_property("Rate", "The rate 1 / θ (``Rate``).", settable=False)
|
|
2515
|
+
|
|
2516
|
+
def estimate_mle_newton_raphson(self, sample: Any) -> None:
|
|
2517
|
+
"""Fit the parameters in place by maximum likelihood with Newton-Raphson iterations (``MLE_NR``).
|
|
2518
|
+
|
|
2519
|
+
Parameters
|
|
2520
|
+
----------
|
|
2521
|
+
sample : array_like
|
|
2522
|
+
Positive observations.
|
|
2523
|
+
|
|
2524
|
+
Raises
|
|
2525
|
+
------
|
|
2526
|
+
TypeError
|
|
2527
|
+
If the distribution is frozen.
|
|
2528
|
+
"""
|
|
2529
|
+
self._ensure_mutable()
|
|
2530
|
+
self.net.MLE_NR(to_net_doubles(sample))
|
|
2531
|
+
|
|
2532
|
+
def estimate_mle_bobee(self, sample: Any) -> None:
|
|
2533
|
+
"""Fit the parameters in place by the maximum likelihood approximation of Bobee (``MLE_Bobee``).
|
|
2534
|
+
|
|
2535
|
+
Parameters
|
|
2536
|
+
----------
|
|
2537
|
+
sample : array_like
|
|
2538
|
+
Positive observations.
|
|
2539
|
+
|
|
2540
|
+
Raises
|
|
2541
|
+
------
|
|
2542
|
+
TypeError
|
|
2543
|
+
If the distribution is frozen.
|
|
2544
|
+
"""
|
|
2545
|
+
self._ensure_mutable()
|
|
2546
|
+
self.net.MLE_Bobee(to_net_doubles(sample))
|
|
2547
|
+
|
|
2548
|
+
@staticmethod
|
|
2549
|
+
def frequency_factor_kp(skewness: float, probability: float) -> float:
|
|
2550
|
+
"""Return the approximate Pearson type III frequency factor K(γ, p) (``GammaDistribution.FrequencyFactorKp``).
|
|
2551
|
+
|
|
2552
|
+
The Cornish-Fisher expansion for abs(γ) <= 2 and the modified Wilson-Hilferty transformation
|
|
2553
|
+
(Kirby, 1972) beyond; it approximates, and is not, the exact standardized quantile.
|
|
2554
|
+
|
|
2555
|
+
Parameters
|
|
2556
|
+
----------
|
|
2557
|
+
skewness : float
|
|
2558
|
+
The skewness γ.
|
|
2559
|
+
probability : float
|
|
2560
|
+
The non-exceedance probability.
|
|
2561
|
+
|
|
2562
|
+
Returns
|
|
2563
|
+
-------
|
|
2564
|
+
float
|
|
2565
|
+
The standardized quantile, so the quantile is ``mean + K * standard_deviation``.
|
|
2566
|
+
"""
|
|
2567
|
+
return float(_nd().GammaDistribution.FrequencyFactorKp(float(skewness), float(probability)))
|
|
2568
|
+
|
|
2569
|
+
@staticmethod
|
|
2570
|
+
def partial_kp(skewness: float, probability: float) -> float:
|
|
2571
|
+
"""Return the derivative of the frequency factor with respect to the skewness (``PartialKp``).
|
|
2572
|
+
|
|
2573
|
+
Parameters
|
|
2574
|
+
----------
|
|
2575
|
+
skewness : float
|
|
2576
|
+
The skewness γ.
|
|
2577
|
+
probability : float
|
|
2578
|
+
The non-exceedance probability.
|
|
2579
|
+
|
|
2580
|
+
Returns
|
|
2581
|
+
-------
|
|
2582
|
+
float
|
|
2583
|
+
The derivative dK/dγ at ``(skewness, probability)``.
|
|
2584
|
+
"""
|
|
2585
|
+
return float(_nd().GammaDistribution.PartialKp(float(skewness), float(probability)))
|
|
2586
|
+
|
|
2587
|
+
|
|
2588
|
+
@_register("GeneralizedBeta")
|
|
2589
|
+
class GeneralizedBeta(UnivariateDistribution):
|
|
2590
|
+
"""Beta distribution rescaled to [minimum, maximum] (``GeneralizedBeta``).
|
|
2591
|
+
|
|
2592
|
+
Parameters
|
|
2593
|
+
----------
|
|
2594
|
+
alpha : float, optional
|
|
2595
|
+
First shape α > 0; default 2.
|
|
2596
|
+
beta : float, optional
|
|
2597
|
+
Second shape β > 0; default 2.
|
|
2598
|
+
minimum : float, optional
|
|
2599
|
+
Lower end; default 0.
|
|
2600
|
+
maximum : float, optional
|
|
2601
|
+
Upper end; default 1.
|
|
2602
|
+
"""
|
|
2603
|
+
|
|
2604
|
+
def __init__(self, alpha: float = 2.0, beta: float = 2.0, minimum: float = 0.0, maximum: float = 1.0) -> None:
|
|
2605
|
+
super().__init__(_nd().GeneralizedBeta(float(alpha), float(beta), float(minimum), float(maximum)))
|
|
2606
|
+
|
|
2607
|
+
alpha = _net_property("Alpha", "The first shape α > 0 (``Alpha``); settable.")
|
|
2608
|
+
beta = _net_property("Beta", "The second shape β > 0 (``Beta``); settable.")
|
|
2609
|
+
minimum = _net_property("Min", "The lower end of the support (``Min``); settable.")
|
|
2610
|
+
maximum = _net_property("Max", "The upper end of the support (``Max``); settable.")
|
|
2611
|
+
|
|
2612
|
+
@classmethod
|
|
2613
|
+
def pert(cls, minimum: float, mode: float, maximum: float, scale: float | None = None) -> GeneralizedBeta:
|
|
2614
|
+
"""Return the PERT beta distribution through a mode (``GeneralizedBeta.PERT``).
|
|
2615
|
+
|
|
2616
|
+
Parameters
|
|
2617
|
+
----------
|
|
2618
|
+
minimum : float
|
|
2619
|
+
Lower end.
|
|
2620
|
+
mode : float
|
|
2621
|
+
The most likely value.
|
|
2622
|
+
maximum : float
|
|
2623
|
+
Upper end.
|
|
2624
|
+
scale : float, optional
|
|
2625
|
+
The PERT weight λ of the mode; None, the default, is Numerics' ``PERT(min, mode, max)``
|
|
2626
|
+
(λ = 4).
|
|
2627
|
+
|
|
2628
|
+
Returns
|
|
2629
|
+
-------
|
|
2630
|
+
GeneralizedBeta
|
|
2631
|
+
``α = 1 + λ (mode - min) / (max - min)``, ``β = 1 + λ (max - mode) / (max - min)``.
|
|
2632
|
+
"""
|
|
2633
|
+
pert = _nd().GeneralizedBeta.PERT
|
|
2634
|
+
net = (pert(float(minimum), float(mode), float(maximum)) if scale is None
|
|
2635
|
+
else pert(float(minimum), float(mode), float(maximum), float(scale)))
|
|
2636
|
+
instance = cls.__new__(cls)
|
|
2637
|
+
UnivariateDistribution.__init__(instance, net)
|
|
2638
|
+
return instance
|
|
2639
|
+
|
|
2640
|
+
def set_parameters_from_moments(self, mean: float, standard_deviation: float, minimum: float,
|
|
2641
|
+
maximum: float) -> None:
|
|
2642
|
+
"""Set the shapes in place from a mean and standard deviation on [minimum, maximum].
|
|
2643
|
+
|
|
2644
|
+
Parameters
|
|
2645
|
+
----------
|
|
2646
|
+
mean : float
|
|
2647
|
+
The mean.
|
|
2648
|
+
standard_deviation : float
|
|
2649
|
+
The standard deviation.
|
|
2650
|
+
minimum : float
|
|
2651
|
+
Lower end.
|
|
2652
|
+
maximum : float
|
|
2653
|
+
Upper end.
|
|
2654
|
+
|
|
2655
|
+
Raises
|
|
2656
|
+
------
|
|
2657
|
+
TypeError
|
|
2658
|
+
If the distribution is frozen.
|
|
2659
|
+
|
|
2660
|
+
Notes
|
|
2661
|
+
-----
|
|
2662
|
+
Calls ``SetParametersFromMoments``.
|
|
2663
|
+
"""
|
|
2664
|
+
self._ensure_mutable()
|
|
2665
|
+
self.net.SetParametersFromMoments(float(mean), float(standard_deviation), float(minimum), float(maximum))
|
|
2666
|
+
|
|
2667
|
+
|
|
2668
|
+
@_register("GeneralizedLogistic")
|
|
2669
|
+
class GeneralizedLogistic(_KappaMoments, UnivariateDistribution):
|
|
2670
|
+
"""Generalized logistic distribution (Hosking parameterization).
|
|
2671
|
+
|
|
2672
|
+
``F(x) = 1 / (1 + exp(-y))`` with ``y = -ln(1 - κ (x - ξ) / α) / κ`` (``y = (x - ξ) / α`` at κ = 0).
|
|
2673
|
+
|
|
2674
|
+
Parameters
|
|
2675
|
+
----------
|
|
2676
|
+
location : float, optional
|
|
2677
|
+
Location ξ; default 100.
|
|
2678
|
+
scale : float, optional
|
|
2679
|
+
Scale α > 0; default 10.
|
|
2680
|
+
shape : float, optional
|
|
2681
|
+
Shape κ (Hosking sign); default 0.
|
|
2682
|
+
"""
|
|
2683
|
+
|
|
2684
|
+
def __init__(self, location: float = 100.0, scale: float = 10.0, shape: float = 0.0) -> None:
|
|
2685
|
+
super().__init__(_nd().GeneralizedLogistic(float(location), float(scale), float(shape)))
|
|
2686
|
+
|
|
2687
|
+
location = _net_property("Xi", _LOCATION)
|
|
2688
|
+
scale = _net_property("Alpha", _SCALE)
|
|
2689
|
+
shape = _net_property("Kappa", _SHAPE)
|
|
2690
|
+
|
|
2691
|
+
@_FamilyMethod
|
|
2692
|
+
def moments_from_parameters(self, parameters: Any) -> np.ndarray:
|
|
2693
|
+
"""Return the product moments of a parameter vector (``MomentsFromParameters``).
|
|
2694
|
+
|
|
2695
|
+
Callable on the class without an instance or on an instance.
|
|
2696
|
+
|
|
2697
|
+
Parameters
|
|
2698
|
+
----------
|
|
2699
|
+
parameters : array_like
|
|
2700
|
+
``[location, scale, shape]``.
|
|
2701
|
+
|
|
2702
|
+
Returns
|
|
2703
|
+
-------
|
|
2704
|
+
numpy.ndarray
|
|
2705
|
+
``[mean, standard deviation, skewness, kurtosis]``.
|
|
2706
|
+
"""
|
|
2707
|
+
return to_numpy_doubles(_net_of(self).MomentsFromParameters(to_net_doubles(parameters)))
|
|
2708
|
+
|
|
2709
|
+
|
|
2710
|
+
@_register("GeneralizedNormal")
|
|
2711
|
+
class GeneralizedNormal(UnivariateDistribution):
|
|
2712
|
+
"""Generalized normal distribution (Hosking parameterization).
|
|
2713
|
+
|
|
2714
|
+
``F(x) = Φ(y)`` with ``y = -ln(1 - κ (x - ξ) / α) / κ`` (``y = (x - ξ) / α`` at κ = 0).
|
|
2715
|
+
|
|
2716
|
+
Parameters
|
|
2717
|
+
----------
|
|
2718
|
+
location : float, optional
|
|
2719
|
+
Location ξ; default 100.
|
|
2720
|
+
scale : float, optional
|
|
2721
|
+
Scale α > 0; default 10.
|
|
2722
|
+
shape : float, optional
|
|
2723
|
+
Shape κ (Hosking sign); default 0.
|
|
2724
|
+
"""
|
|
2725
|
+
|
|
2726
|
+
def __init__(self, location: float = 100.0, scale: float = 10.0, shape: float = 0.0) -> None:
|
|
2727
|
+
super().__init__(_nd().GeneralizedNormal(float(location), float(scale), float(shape)))
|
|
2728
|
+
|
|
2729
|
+
location = _net_property("Xi", _LOCATION)
|
|
2730
|
+
scale = _net_property("Alpha", _SCALE)
|
|
2731
|
+
shape = _net_property("Kappa", _SHAPE)
|
|
2732
|
+
|
|
2733
|
+
|
|
2734
|
+
@_register("Geometric")
|
|
2735
|
+
class Geometric(UnivariateDistribution):
|
|
2736
|
+
"""Geometric distribution: failures before the first success (discrete, support 0, 1, 2, ...).
|
|
2737
|
+
|
|
2738
|
+
Parameters
|
|
2739
|
+
----------
|
|
2740
|
+
probability : float, optional
|
|
2741
|
+
Probability of success per trial, in (0, 1]; default 0.5.
|
|
2742
|
+
"""
|
|
2743
|
+
|
|
2744
|
+
def __init__(self, probability: float = 0.5) -> None:
|
|
2745
|
+
super().__init__(_nd().Geometric(float(probability)))
|
|
2746
|
+
|
|
2747
|
+
probability = _net_property("ProbabilityOfSuccess", "The probability of success p (``ProbabilityOfSuccess``); "
|
|
2748
|
+
"settable.")
|
|
2749
|
+
|
|
2750
|
+
|
|
2751
|
+
@_register("InverseChiSquared")
|
|
2752
|
+
class InverseChiSquared(UnivariateDistribution):
|
|
2753
|
+
"""Scaled inverse chi-squared distribution (``InverseChiSquared``).
|
|
2754
|
+
|
|
2755
|
+
The inverse gamma distribution with shape ν/2 and scale ν τ²/2; the mean is ν τ²/(ν - 2) for ν > 2.
|
|
2756
|
+
|
|
2757
|
+
Parameters
|
|
2758
|
+
----------
|
|
2759
|
+
degrees_of_freedom : int, optional
|
|
2760
|
+
Degrees of freedom ν >= 1; default 10.
|
|
2761
|
+
scale : float, optional
|
|
2762
|
+
The scale τ² > 0 (Numerics ``Sigma``); default 1.
|
|
2763
|
+
"""
|
|
2764
|
+
|
|
2765
|
+
def __init__(self, degrees_of_freedom: int = 10, scale: float = 1.0) -> None:
|
|
2766
|
+
super().__init__(_nd().InverseChiSquared(int(degrees_of_freedom), float(scale)))
|
|
2767
|
+
|
|
2768
|
+
degrees_of_freedom = _net_property("DegreesOfFreedom", "The degrees of freedom ν (``DegreesOfFreedom``); "
|
|
2769
|
+
"settable.", kind=int)
|
|
2770
|
+
scale = _net_property("Sigma", "The scale τ² > 0 (``Sigma``); settable.")
|
|
2771
|
+
precision = _net_property("Precision", "The value 1 / τ⁴, the reciprocal of the squared scale (``Precision``).",
|
|
2772
|
+
settable=False)
|
|
2773
|
+
|
|
2774
|
+
|
|
2775
|
+
@_register("InverseGamma")
|
|
2776
|
+
class InverseGamma(UnivariateDistribution):
|
|
2777
|
+
"""Inverse gamma distribution with scale β and shape α (``InverseGamma``).
|
|
2778
|
+
|
|
2779
|
+
Parameters
|
|
2780
|
+
----------
|
|
2781
|
+
scale : float, optional
|
|
2782
|
+
Scale β > 0; default 0.5.
|
|
2783
|
+
shape : float, optional
|
|
2784
|
+
Shape α > 0; default 2.
|
|
2785
|
+
"""
|
|
2786
|
+
|
|
2787
|
+
def __init__(self, scale: float = 0.5, shape: float = 2.0) -> None:
|
|
2788
|
+
super().__init__(_nd().InverseGamma(float(scale), float(shape)))
|
|
2789
|
+
|
|
2790
|
+
scale = _net_property("Beta", "The scale β > 0 (``Beta``); settable.")
|
|
2791
|
+
shape = _net_property("Alpha", "The shape α > 0 (``Alpha``); settable.")
|
|
2792
|
+
|
|
2793
|
+
|
|
2794
|
+
@_register("Logistic")
|
|
2795
|
+
class Logistic(UnivariateDistribution):
|
|
2796
|
+
"""Logistic distribution.
|
|
2797
|
+
|
|
2798
|
+
Parameters
|
|
2799
|
+
----------
|
|
2800
|
+
location : float, optional
|
|
2801
|
+
Location ξ; default 0.
|
|
2802
|
+
scale : float, optional
|
|
2803
|
+
Scale α > 0; default 0.1.
|
|
2804
|
+
"""
|
|
2805
|
+
|
|
2806
|
+
def __init__(self, location: float = 0.0, scale: float = 0.1) -> None:
|
|
2807
|
+
super().__init__(_nd().Logistic(float(location), float(scale)))
|
|
2808
|
+
|
|
2809
|
+
location = _net_property("Xi", _LOCATION)
|
|
2810
|
+
scale = _net_property("Alpha", _SCALE)
|
|
2811
|
+
|
|
2812
|
+
|
|
2813
|
+
@_register("NoncentralT")
|
|
2814
|
+
class NoncentralT(UnivariateDistribution):
|
|
2815
|
+
"""Noncentral Student's t distribution.
|
|
2816
|
+
|
|
2817
|
+
Parameters
|
|
2818
|
+
----------
|
|
2819
|
+
degrees_of_freedom : float, optional
|
|
2820
|
+
Degrees of freedom ν > 0; default 10.
|
|
2821
|
+
noncentrality : float, optional
|
|
2822
|
+
Noncentrality μ; default 0.
|
|
2823
|
+
"""
|
|
2824
|
+
|
|
2825
|
+
def __init__(self, degrees_of_freedom: float = 10.0, noncentrality: float = 0.0) -> None:
|
|
2826
|
+
super().__init__(_nd().NoncentralT(float(degrees_of_freedom), float(noncentrality)))
|
|
2827
|
+
|
|
2828
|
+
degrees_of_freedom = _net_property("DegreesOfFreedom", "The degrees of freedom ν > 0 (``DegreesOfFreedom``); "
|
|
2829
|
+
"settable.")
|
|
2830
|
+
noncentrality = _net_property("Noncentrality", "The noncentrality μ (``Noncentrality``); settable.")
|
|
2831
|
+
|
|
2832
|
+
|
|
2833
|
+
@_register("Pareto")
|
|
2834
|
+
class Pareto(UnivariateDistribution):
|
|
2835
|
+
"""Pareto (type I) distribution: ``F(x) = 1 - (xm / x)^α`` for x >= xm.
|
|
2836
|
+
|
|
2837
|
+
Parameters
|
|
2838
|
+
----------
|
|
2839
|
+
scale : float, optional
|
|
2840
|
+
Scale xm > 0, the lower end of the support; default 1.
|
|
2841
|
+
shape : float, optional
|
|
2842
|
+
Shape α > 0; default 10.
|
|
2843
|
+
"""
|
|
2844
|
+
|
|
2845
|
+
def __init__(self, scale: float = 1.0, shape: float = 10.0) -> None:
|
|
2846
|
+
super().__init__(_nd().Pareto(float(scale), float(shape)))
|
|
2847
|
+
|
|
2848
|
+
scale = _net_property("Xm", "The scale xm > 0 (``Xm``); settable.")
|
|
2849
|
+
shape = _net_property("Alpha", "The shape α > 0 (``Alpha``); settable.")
|
|
2850
|
+
|
|
2851
|
+
|
|
2852
|
+
@_register("Pert")
|
|
2853
|
+
class Pert(UnivariateDistribution):
|
|
2854
|
+
"""PERT distribution: a beta on [minimum, maximum] weighted towards the most likely value.
|
|
2855
|
+
|
|
2856
|
+
Parameters
|
|
2857
|
+
----------
|
|
2858
|
+
minimum : float, optional
|
|
2859
|
+
Lower end a; default 0.
|
|
2860
|
+
most_likely : float, optional
|
|
2861
|
+
The mode c; default 0.5.
|
|
2862
|
+
maximum : float, optional
|
|
2863
|
+
Upper end b; default 1.
|
|
2864
|
+
"""
|
|
2865
|
+
|
|
2866
|
+
def __init__(self, minimum: float = 0.0, most_likely: float = 0.5, maximum: float = 1.0) -> None:
|
|
2867
|
+
super().__init__(_nd().Pert(float(minimum), float(most_likely), float(maximum)))
|
|
2868
|
+
|
|
2869
|
+
minimum = _net_property("Min", "The lower end a (``Min``); settable.")
|
|
2870
|
+
most_likely = _net_property("MostLikely", "The most likely value c (``MostLikely``); settable.")
|
|
2871
|
+
maximum = _net_property("Max", "The upper end b (``Max``); settable.")
|
|
2872
|
+
|
|
2873
|
+
@property
|
|
2874
|
+
def beta(self) -> GeneralizedBeta:
|
|
2875
|
+
"""The underlying beta distribution on [a, b], wrapped (``Beta``)."""
|
|
2876
|
+
return wrap(self.net.Beta)
|
|
2877
|
+
|
|
2878
|
+
|
|
2879
|
+
class _PertPercentiles:
|
|
2880
|
+
"""The percentile parameters shared by the percentile PERT families."""
|
|
2881
|
+
|
|
2882
|
+
percentile_5th = _net_property("Percentile5th", "The 5th percentile (``Percentile5th``); settable.")
|
|
2883
|
+
percentile_50th = _net_property("Percentile50th", "The 50th percentile (``Percentile50th``); settable.")
|
|
2884
|
+
percentile_95th = _net_property("Percentile95th", "The 95th percentile (``Percentile95th``); settable.")
|
|
2885
|
+
|
|
2886
|
+
def solve_parameters(self) -> None:
|
|
2887
|
+
"""Solve the PERT parameters that reproduce the three percentiles (``SolveParameters``).
|
|
2888
|
+
|
|
2889
|
+
Raises
|
|
2890
|
+
------
|
|
2891
|
+
TypeError
|
|
2892
|
+
If the distribution is frozen.
|
|
2893
|
+
"""
|
|
2894
|
+
self._ensure_mutable()
|
|
2895
|
+
self.net.SolveParameters()
|
|
2896
|
+
|
|
2897
|
+
def to_pert(self) -> Pert:
|
|
2898
|
+
"""Return the solved PERT distribution (``ToPert``).
|
|
2899
|
+
|
|
2900
|
+
Returns
|
|
2901
|
+
-------
|
|
2902
|
+
Pert
|
|
2903
|
+
The PERT distribution whose percentiles match.
|
|
2904
|
+
"""
|
|
2905
|
+
return wrap(self.net.ToPert())
|
|
2906
|
+
|
|
2907
|
+
|
|
2908
|
+
translate_class(_PertPercentiles)
|
|
2909
|
+
|
|
2910
|
+
|
|
2911
|
+
@_register("PertPercentile")
|
|
2912
|
+
class PertPercentile(_PertPercentiles, UnivariateDistribution):
|
|
2913
|
+
"""PERT distribution given by its 5th, 50th and 95th percentiles (``PertPercentile``).
|
|
2914
|
+
|
|
2915
|
+
Parameters
|
|
2916
|
+
----------
|
|
2917
|
+
percentile_5th : float, optional
|
|
2918
|
+
The 5th percentile; default 0.05.
|
|
2919
|
+
percentile_50th : float, optional
|
|
2920
|
+
The median; default 0.5.
|
|
2921
|
+
percentile_95th : float, optional
|
|
2922
|
+
The 95th percentile; default 0.95.
|
|
2923
|
+
"""
|
|
2924
|
+
|
|
2925
|
+
def __init__(self, percentile_5th: float = 0.05, percentile_50th: float = 0.5,
|
|
2926
|
+
percentile_95th: float = 0.95) -> None:
|
|
2927
|
+
super().__init__(_nd().PertPercentile(float(percentile_5th), float(percentile_50th), float(percentile_95th)))
|
|
2928
|
+
|
|
2929
|
+
min_allowable_value = _net_property("MinAllowableValue", "The smallest value the solved support may take "
|
|
2930
|
+
"(``MinAllowableValue``); settable.")
|
|
2931
|
+
max_allowable_value = _net_property("MaxAllowableValue", "The largest value the solved support may take "
|
|
2932
|
+
"(``MaxAllowableValue``); settable.")
|
|
2933
|
+
|
|
2934
|
+
|
|
2935
|
+
@_register("PertPercentileZ")
|
|
2936
|
+
class PertPercentileZ(_PertPercentiles, UnivariateDistribution):
|
|
2937
|
+
"""PERT distribution given by three percentiles, solved in a transformed space (``PertPercentileZ``).
|
|
2938
|
+
|
|
2939
|
+
Parameters
|
|
2940
|
+
----------
|
|
2941
|
+
percentile_5th : float, optional
|
|
2942
|
+
The 5th percentile, in (0, 1); default 0.05.
|
|
2943
|
+
percentile_50th : float, optional
|
|
2944
|
+
The median, in (0, 1); default 0.5.
|
|
2945
|
+
percentile_95th : float, optional
|
|
2946
|
+
The 95th percentile, in (0, 1); default 0.95.
|
|
2947
|
+
"""
|
|
2948
|
+
|
|
2949
|
+
def __init__(self, percentile_5th: float = 0.05, percentile_50th: float = 0.5,
|
|
2950
|
+
percentile_95th: float = 0.95) -> None:
|
|
2951
|
+
super().__init__(_nd().PertPercentileZ(float(percentile_5th), float(percentile_50th),
|
|
2952
|
+
float(percentile_95th)))
|
|
2953
|
+
|
|
2954
|
+
|
|
2955
|
+
@_register("Rayleigh")
|
|
2956
|
+
class Rayleigh(UnivariateDistribution):
|
|
2957
|
+
"""Rayleigh distribution.
|
|
2958
|
+
|
|
2959
|
+
Parameters
|
|
2960
|
+
----------
|
|
2961
|
+
scale : float, optional
|
|
2962
|
+
Scale σ > 0; default 10.
|
|
2963
|
+
"""
|
|
2964
|
+
|
|
2965
|
+
def __init__(self, scale: float = 10.0) -> None:
|
|
2966
|
+
super().__init__(_nd().Rayleigh(float(scale)))
|
|
2967
|
+
|
|
2968
|
+
scale = _net_property("Sigma", "The scale σ > 0 (``Sigma``); settable.")
|
|
2969
|
+
|
|
2970
|
+
|
|
2971
|
+
@_register("StudentT")
|
|
2972
|
+
class StudentT(UnivariateDistribution):
|
|
2973
|
+
"""Location-scale Student's t distribution.
|
|
2974
|
+
|
|
2975
|
+
Parameters
|
|
2976
|
+
----------
|
|
2977
|
+
location : float, optional
|
|
2978
|
+
Location μ; default 0.
|
|
2979
|
+
scale : float, optional
|
|
2980
|
+
Scale σ > 0; default 1.
|
|
2981
|
+
degrees_of_freedom : float, optional
|
|
2982
|
+
Degrees of freedom ν > 0; default 10.
|
|
2983
|
+
"""
|
|
2984
|
+
|
|
2985
|
+
def __init__(self, location: float = 0.0, scale: float = 1.0, degrees_of_freedom: float = 10.0) -> None:
|
|
2986
|
+
super().__init__(_nd().StudentT(float(location), float(scale), float(degrees_of_freedom)))
|
|
2987
|
+
|
|
2988
|
+
location = _net_property("Mu", "The location μ (``Mu``); settable.")
|
|
2989
|
+
scale = _net_property("Sigma", "The scale σ > 0 (``Sigma``); settable.")
|
|
2990
|
+
degrees_of_freedom = _net_property("DegreesOfFreedom", "The degrees of freedom ν > 0 (``DegreesOfFreedom``); "
|
|
2991
|
+
"settable.")
|
|
2992
|
+
|
|
2993
|
+
|
|
2994
|
+
@_register("Triangular")
|
|
2995
|
+
class Triangular(UnivariateDistribution):
|
|
2996
|
+
"""Triangular distribution on [minimum, maximum] with mode ``most_likely``.
|
|
2997
|
+
|
|
2998
|
+
Parameters
|
|
2999
|
+
----------
|
|
3000
|
+
minimum : float, optional
|
|
3001
|
+
Lower end a; default 0.
|
|
3002
|
+
most_likely : float, optional
|
|
3003
|
+
The mode c; default 0.5.
|
|
3004
|
+
maximum : float, optional
|
|
3005
|
+
Upper end b; default 1.
|
|
3006
|
+
"""
|
|
3007
|
+
|
|
3008
|
+
def __init__(self, minimum: float = 0.0, most_likely: float = 0.5, maximum: float = 1.0) -> None:
|
|
3009
|
+
super().__init__(_nd().Triangular(float(minimum), float(most_likely), float(maximum)))
|
|
3010
|
+
|
|
3011
|
+
minimum = _net_property("Min", "The lower end a (``Min``); settable.")
|
|
3012
|
+
most_likely = _net_property("MostLikely", "The mode c (``MostLikely``); settable.")
|
|
3013
|
+
maximum = _net_property("Max", "The upper end b (``Max``); settable.")
|
|
3014
|
+
|
|
3015
|
+
|
|
3016
|
+
class TruncatedDistribution(UnivariateDistribution):
|
|
3017
|
+
"""Any univariate distribution truncated to [lower_bound, upper_bound] (``TruncatedDistribution``).
|
|
3018
|
+
|
|
3019
|
+
The density is ``f(x) / (F(upper) - F(lower))`` on the interval. ``type_name`` on an instance is
|
|
3020
|
+
the base distribution's (Numerics reports the base type), so this class is not in :data:`FAMILIES`.
|
|
3021
|
+
|
|
3022
|
+
Parameters
|
|
3023
|
+
----------
|
|
3024
|
+
distribution : UnivariateDistribution
|
|
3025
|
+
The base distribution; Numerics keeps its own copy.
|
|
3026
|
+
lower_bound : float
|
|
3027
|
+
Lower truncation point.
|
|
3028
|
+
upper_bound : float
|
|
3029
|
+
Upper truncation point, above ``lower_bound``.
|
|
3030
|
+
"""
|
|
3031
|
+
|
|
3032
|
+
def __init__(self, distribution: UnivariateDistribution, lower_bound: float, upper_bound: float) -> None:
|
|
3033
|
+
super().__init__(_nd().TruncatedDistribution(distribution.net, float(lower_bound), float(upper_bound)))
|
|
3034
|
+
|
|
3035
|
+
lower_bound = _net_property("Min", "The lower truncation point (``Min``).", settable=False)
|
|
3036
|
+
upper_bound = _net_property("Max", "The upper truncation point (``Max``).", settable=False)
|
|
3037
|
+
|
|
3038
|
+
@property
|
|
3039
|
+
def base_distribution(self) -> UnivariateDistribution:
|
|
3040
|
+
"""The base distribution, wrapped (``BaseDistribution``)."""
|
|
3041
|
+
return wrap(self.net.BaseDistribution)
|
|
3042
|
+
|
|
3043
|
+
def _value_key(self) -> tuple:
|
|
3044
|
+
"""Return the truncated value key extended with the base distribution's family and state."""
|
|
3045
|
+
return super()._value_key() + self.base_distribution._value_key()
|
|
3046
|
+
|
|
3047
|
+
|
|
3048
|
+
_CLASS_BY_NET_NAME["TruncatedDistribution"] = TruncatedDistribution
|
|
3049
|
+
|
|
3050
|
+
|
|
3051
|
+
@_register("TruncatedNormal")
|
|
3052
|
+
class TruncatedNormal(UnivariateDistribution):
|
|
3053
|
+
"""Normal distribution truncated to [minimum, maximum] (``TruncatedNormal``).
|
|
3054
|
+
|
|
3055
|
+
``mu`` and ``sigma`` are the parameters of the parent normal; the moment properties
|
|
3056
|
+
(``mean``, ``standard_deviation``) are those of the truncated distribution.
|
|
3057
|
+
|
|
3058
|
+
Parameters
|
|
3059
|
+
----------
|
|
3060
|
+
mu : float, optional
|
|
3061
|
+
Mean of the parent normal; default 0.5.
|
|
3062
|
+
sigma : float, optional
|
|
3063
|
+
Standard deviation of the parent normal, > 0; default 0.2.
|
|
3064
|
+
minimum : float, optional
|
|
3065
|
+
Lower truncation point; default 0.
|
|
3066
|
+
maximum : float, optional
|
|
3067
|
+
Upper truncation point; default 1.
|
|
3068
|
+
"""
|
|
3069
|
+
|
|
3070
|
+
def __init__(self, mu: float = 0.5, sigma: float = 0.2, minimum: float = 0.0, maximum: float = 1.0) -> None:
|
|
3071
|
+
super().__init__(_nd().TruncatedNormal(float(mu), float(sigma), float(minimum), float(maximum)))
|
|
3072
|
+
|
|
3073
|
+
mu = _net_property("Mu", "The mean of the parent normal (``Mu``); settable.")
|
|
3074
|
+
sigma = _net_property("Sigma", "The standard deviation of the parent normal (``Sigma``); settable.")
|
|
3075
|
+
minimum = _net_property("Min", "The lower truncation point (``Min``); settable.")
|
|
3076
|
+
maximum = _net_property("Max", "The upper truncation point (``Max``); settable.")
|
|
3077
|
+
|
|
3078
|
+
|
|
3079
|
+
@_register("UniformDiscrete")
|
|
3080
|
+
class UniformDiscrete(UnivariateDistribution):
|
|
3081
|
+
"""Discrete uniform distribution on the integers minimum, ..., maximum.
|
|
3082
|
+
|
|
3083
|
+
Parameters
|
|
3084
|
+
----------
|
|
3085
|
+
minimum : float, optional
|
|
3086
|
+
Lowest value; default 0.
|
|
3087
|
+
maximum : float, optional
|
|
3088
|
+
Highest value; default 1.
|
|
3089
|
+
"""
|
|
3090
|
+
|
|
3091
|
+
def __init__(self, minimum: float = 0.0, maximum: float = 1.0) -> None:
|
|
3092
|
+
super().__init__(_nd().UniformDiscrete(float(minimum), float(maximum)))
|
|
3093
|
+
|
|
3094
|
+
minimum = _net_property("Min", "The lowest value (``Min``); settable.")
|
|
3095
|
+
maximum = _net_property("Max", "The highest value (``Max``); settable.")
|
|
3096
|
+
count = _net_property("N", "The number of values, maximum - minimum + 1 (``N``).", settable=False)
|
|
3097
|
+
|
|
3098
|
+
|
|
3099
|
+
@_register("VonMises")
|
|
3100
|
+
class VonMises(UnivariateDistribution):
|
|
3101
|
+
"""Von Mises (circular normal) distribution on [-π, π]; the CDF accumulates from -π.
|
|
3102
|
+
|
|
3103
|
+
Parameters
|
|
3104
|
+
----------
|
|
3105
|
+
mu : float, optional
|
|
3106
|
+
Mean direction μ in [-π, π]; default 0.
|
|
3107
|
+
kappa : float, optional
|
|
3108
|
+
Concentration κ >= 0; default 1.
|
|
3109
|
+
"""
|
|
3110
|
+
|
|
3111
|
+
def __init__(self, mu: float = 0.0, kappa: float = 1.0) -> None:
|
|
3112
|
+
super().__init__(_nd().VonMises(float(mu), float(kappa)))
|
|
3113
|
+
|
|
3114
|
+
mu = _net_property("Mu", "The mean direction μ (``Mu``); settable.")
|
|
3115
|
+
kappa = _net_property("Kappa", "The concentration κ (``Kappa``); settable.")
|
|
3116
|
+
|
|
3117
|
+
|
|
3118
|
+
@_register("Weibull")
|
|
3119
|
+
class Weibull(UnivariateDistribution):
|
|
3120
|
+
"""Two-parameter Weibull distribution: ``F(x) = 1 - exp(-(x / λ)^κ)``.
|
|
3121
|
+
|
|
3122
|
+
Parameters
|
|
3123
|
+
----------
|
|
3124
|
+
scale : float, optional
|
|
3125
|
+
Scale λ > 0; default 10.
|
|
3126
|
+
shape : float, optional
|
|
3127
|
+
Shape κ > 0; default 2.
|
|
3128
|
+
"""
|
|
3129
|
+
|
|
3130
|
+
def __init__(self, scale: float = 10.0, shape: float = 2.0) -> None:
|
|
3131
|
+
super().__init__(_nd().Weibull(float(scale), float(shape)))
|
|
3132
|
+
|
|
3133
|
+
scale = _net_property("Lambda", "The scale λ > 0 (``Lambda``); settable.")
|
|
3134
|
+
shape = _net_property("Kappa", "The shape κ > 0 (``Kappa``); settable.")
|
|
3135
|
+
|
|
3136
|
+
def solve_mle(self, sample: Any) -> np.ndarray:
|
|
3137
|
+
"""Return the maximum likelihood ``[scale, shape]`` from the profile-likelihood root (``SolveMLE``).
|
|
3138
|
+
|
|
3139
|
+
Parameters
|
|
3140
|
+
----------
|
|
3141
|
+
sample : array_like
|
|
3142
|
+
Positive observations.
|
|
3143
|
+
|
|
3144
|
+
Returns
|
|
3145
|
+
-------
|
|
3146
|
+
numpy.ndarray
|
|
3147
|
+
``[scale, shape]``; this distribution is unchanged.
|
|
3148
|
+
"""
|
|
3149
|
+
return to_numpy_doubles(self.net.SolveMLE(to_net_doubles(sample)))
|
|
3150
|
+
|
|
3151
|
+
|
|
3152
|
+
def _dependency_type() -> Any:
|
|
3153
|
+
"""Return the ``Probability.DependencyType`` enum type."""
|
|
3154
|
+
return namespace("Numerics.Data.Statistics").Probability.DependencyType
|
|
3155
|
+
|
|
3156
|
+
|
|
3157
|
+
@_register("CompetingRisks")
|
|
3158
|
+
class CompetingRisks(UnivariateDistribution):
|
|
3159
|
+
"""The minimum (or maximum) of several random variables, the competing-risks failure time.
|
|
3160
|
+
|
|
3161
|
+
With independent components the CDF of the minimum is ``1 - prod(1 - F_i(x))``; ``dependency``
|
|
3162
|
+
and ``correlation_matrix`` select a dependent combination. The CDF is tabulated on a grid and
|
|
3163
|
+
interpolated (``x_transform``, ``probability_transform``).
|
|
3164
|
+
|
|
3165
|
+
Parameters
|
|
3166
|
+
----------
|
|
3167
|
+
distributions : sequence of UnivariateDistribution
|
|
3168
|
+
The component distributions.
|
|
3169
|
+
"""
|
|
3170
|
+
|
|
3171
|
+
def __init__(self, distributions: Sequence[UnivariateDistribution]) -> None:
|
|
3172
|
+
super().__init__(_nd().CompetingRisks(_members_array(distributions)))
|
|
3173
|
+
|
|
3174
|
+
def __repr__(self) -> str:
|
|
3175
|
+
return f"CompetingRisks({self.distributions!r})"
|
|
3176
|
+
|
|
3177
|
+
@property
|
|
3178
|
+
def distributions(self) -> list[UnivariateDistribution]:
|
|
3179
|
+
"""The component distributions, wrapped (``Distributions``)."""
|
|
3180
|
+
return [wrap(component) for component in self.net.Distributions]
|
|
3181
|
+
|
|
3182
|
+
prng_seed = _net_property("PRNGSeed", "The seed of the random draws of dependent combinations (``PRNGSeed``); "
|
|
3183
|
+
"settable.", kind=int)
|
|
3184
|
+
minimum_of_random_variables = _net_property(
|
|
3185
|
+
"MinimumOfRandomVariables", "True for the minimum of the components (default), False for the maximum "
|
|
3186
|
+
"(``MinimumOfRandomVariables``); settable.", kind=bool)
|
|
3187
|
+
x_transform = _enum_property("XTransform", lambda: _data().Transform,
|
|
3188
|
+
"The X interpolation transform of the CDF table; settable.")
|
|
3189
|
+
probability_transform = _enum_property("ProbabilityTransform", lambda: _data().Transform,
|
|
3190
|
+
"The probability interpolation transform of the CDF table; settable.")
|
|
3191
|
+
dependency = _enum_property("Dependency", _dependency_type,
|
|
3192
|
+
"The dependency between components (``Probability.DependencyType`` name); settable.")
|
|
3193
|
+
|
|
3194
|
+
@property
|
|
3195
|
+
def correlation_matrix(self) -> np.ndarray | None:
|
|
3196
|
+
"""The correlation matrix of the components for a correlated dependency; settable."""
|
|
3197
|
+
matrix = self.net.CorrelationMatrix
|
|
3198
|
+
return None if matrix is None else to_numpy_matrix(matrix)
|
|
3199
|
+
|
|
3200
|
+
@correlation_matrix.setter
|
|
3201
|
+
def correlation_matrix(self, value: Any) -> None:
|
|
3202
|
+
self._ensure_mutable()
|
|
3203
|
+
self.net.CorrelationMatrix = None if value is None else to_net_matrix(value)
|
|
3204
|
+
|
|
3205
|
+
def set_parameters(self, parameters: Any) -> None:
|
|
3206
|
+
"""Replace the components (``SetParameters(UnivariateDistributionBase[])``) or set a parameter vector.
|
|
3207
|
+
|
|
3208
|
+
Parameters
|
|
3209
|
+
----------
|
|
3210
|
+
parameters : sequence of UnivariateDistribution or array_like
|
|
3211
|
+
New component distributions, or the parameter vector in Numerics order.
|
|
3212
|
+
|
|
3213
|
+
Raises
|
|
3214
|
+
------
|
|
3215
|
+
TypeError
|
|
3216
|
+
If the distribution is frozen.
|
|
3217
|
+
"""
|
|
3218
|
+
self._ensure_mutable()
|
|
3219
|
+
items = list(parameters) if not isinstance(parameters, np.ndarray) else []
|
|
3220
|
+
if items and all(isinstance(item, UnivariateDistribution) for item in items):
|
|
3221
|
+
self.net.SetParameters(_members_array(items))
|
|
3222
|
+
else:
|
|
3223
|
+
super().set_parameters(parameters)
|
|
3224
|
+
|
|
3225
|
+
def create_empirical_cdf(self) -> None:
|
|
3226
|
+
"""Build the tabulated CDF now rather than on first evaluation (``CreateEmpiricalCDF``)."""
|
|
3227
|
+
self.net.CreateEmpiricalCDF()
|
|
3228
|
+
|
|
3229
|
+
def cumulative_incidence_functions(self, bins: Any = None) -> list[Empirical]:
|
|
3230
|
+
"""Return the cumulative incidence function of each component (``CumulativeIncidenceFunctions``).
|
|
3231
|
+
|
|
3232
|
+
Parameters
|
|
3233
|
+
----------
|
|
3234
|
+
bins : pynumerics.sampling.StratificationBins, optional
|
|
3235
|
+
The X bins of the tables; None, the default, lets Numerics choose them.
|
|
3236
|
+
|
|
3237
|
+
Returns
|
|
3238
|
+
-------
|
|
3239
|
+
list of Empirical
|
|
3240
|
+
One table per component: the probability that it is the first to occur by x.
|
|
3241
|
+
"""
|
|
3242
|
+
functions = self.net.CumulativeIncidenceFunctions(None if bins is None else _bins_net(bins))
|
|
3243
|
+
return [wrap(function) for function in functions]
|
|
3244
|
+
|
|
3245
|
+
def generate_random_values_with_dependency(self, sample_size: int, seed: int = -1) -> np.ndarray:
|
|
3246
|
+
"""Draw the minimum (or maximum) of dependent component draws (``GenerateRandomValuesWithDependency``).
|
|
3247
|
+
|
|
3248
|
+
Parameters
|
|
3249
|
+
----------
|
|
3250
|
+
sample_size : int
|
|
3251
|
+
Number of values.
|
|
3252
|
+
seed : int, optional
|
|
3253
|
+
Mersenne Twister seed; zero or negative seeds from the clock.
|
|
3254
|
+
|
|
3255
|
+
Returns
|
|
3256
|
+
-------
|
|
3257
|
+
numpy.ndarray
|
|
3258
|
+
The sample.
|
|
3259
|
+
"""
|
|
3260
|
+
return to_numpy_doubles(self.net.GenerateRandomValuesWithDependency(int(sample_size), int(seed)))
|
|
3261
|
+
|
|
3262
|
+
|
|
3263
|
+
@_register("Mixture")
|
|
3264
|
+
class Mixture(UnivariateDistribution):
|
|
3265
|
+
"""Finite mixture of univariate distributions.
|
|
3266
|
+
|
|
3267
|
+
Parameters
|
|
3268
|
+
----------
|
|
3269
|
+
weights : array_like
|
|
3270
|
+
Component weights.
|
|
3271
|
+
distributions : sequence of UnivariateDistribution
|
|
3272
|
+
Components, one per weight.
|
|
3273
|
+
"""
|
|
3274
|
+
|
|
3275
|
+
def __init__(self, weights: Any, distributions: Sequence[UnivariateDistribution]) -> None:
|
|
3276
|
+
super().__init__(_nd().Mixture(to_net_doubles(weights), _members_array(distributions)))
|
|
3277
|
+
|
|
3278
|
+
@property
|
|
3279
|
+
def weights(self) -> np.ndarray:
|
|
3280
|
+
"""The component weights (``Weights``)."""
|
|
3281
|
+
return to_numpy_doubles(self.net.Weights)
|
|
3282
|
+
|
|
3283
|
+
@property
|
|
3284
|
+
def components(self) -> list[UnivariateDistribution]:
|
|
3285
|
+
"""The component distributions, wrapped (``Distributions``)."""
|
|
3286
|
+
return [wrap(component) for component in self.net.Distributions]
|
|
3287
|
+
|
|
3288
|
+
is_zero_inflated = _net_property("IsZeroInflated", "Whether a point mass at zero with weight ``zero_weight`` "
|
|
3289
|
+
"joins the components (``IsZeroInflated``); settable.",
|
|
3290
|
+
kind=bool)
|
|
3291
|
+
zero_weight = _net_property("ZeroWeight", "The weight of the point mass at zero (``ZeroWeight``); settable.")
|
|
3292
|
+
max_iterations = _net_property("MaxIterations", "The iteration limit of the maximum likelihood fit "
|
|
3293
|
+
"(``MaxIterations``); settable.", kind=int)
|
|
3294
|
+
tolerance = _net_property("Tolerance", "The convergence tolerance of the maximum likelihood fit (``Tolerance``); "
|
|
3295
|
+
"settable.")
|
|
3296
|
+
iterations = _net_property("Iterations", "The iterations the last fit used (``Iterations``).", kind=int,
|
|
3297
|
+
settable=False)
|
|
3298
|
+
x_transform = _enum_property("XTransform", lambda: _data().Transform,
|
|
3299
|
+
"The X interpolation transform of the inverse-CDF table; settable.")
|
|
3300
|
+
probability_transform = _enum_property("ProbabilityTransform", lambda: _data().Transform,
|
|
3301
|
+
"The probability interpolation transform of the inverse-CDF table; "
|
|
3302
|
+
"settable.")
|
|
3303
|
+
|
|
3304
|
+
def create_empirical_cdf(self) -> None:
|
|
3305
|
+
"""Build the tabulated CDF used by ``inverse_cdf`` now rather than on first use (``CreateEmpiricalCDF``)."""
|
|
3306
|
+
self.net.CreateEmpiricalCDF()
|
|
3307
|
+
|
|
3308
|
+
|
|
3309
|
+
@_register("EmpiricalDistribution", "Empirical")
|
|
3310
|
+
class Empirical(UnivariateDistribution):
|
|
3311
|
+
"""Empirical distribution defined by value and probability tables.
|
|
3312
|
+
|
|
3313
|
+
With the default orders the probabilities are non-exceedance probabilities
|
|
3314
|
+
(strictly ascending). With ``probability_order="descending"`` the table is a
|
|
3315
|
+
survival function: the probabilities are exceedance probabilities S(x) (strictly
|
|
3316
|
+
descending), ``cdf`` returns 1 − S(x) and ``inverse_cdf(q)`` reads the exceedance
|
|
3317
|
+
axis at 1 − q. Interpolation is in (X, Φ⁻¹(P)) space by default
|
|
3318
|
+
(``probability_transform="normal_z"``) and holds the endpoints.
|
|
3319
|
+
|
|
3320
|
+
Parameters
|
|
3321
|
+
----------
|
|
3322
|
+
values : array_like
|
|
3323
|
+
Ascending X values (ties allowed).
|
|
3324
|
+
probabilities : array_like
|
|
3325
|
+
Probability values in [0, 1], strictly monotone in ``probability_order``.
|
|
3326
|
+
x_order : SortOrder or str, optional
|
|
3327
|
+
Sort order of ``values``; must be ascending (the default).
|
|
3328
|
+
probability_order : SortOrder or str, optional
|
|
3329
|
+
``"ascending"`` (non-exceedance, default) or ``"descending"`` (survival).
|
|
3330
|
+
|
|
3331
|
+
Notes
|
|
3332
|
+
-----
|
|
3333
|
+
Uses ``EmpiricalDistribution(IList, IList, SortOrder, SortOrder)``.
|
|
3334
|
+
"""
|
|
3335
|
+
|
|
3336
|
+
def __init__(self, values: Any, probabilities: Any, x_order: enums.SortOrder | str = enums.SortOrder.ASCENDING,
|
|
3337
|
+
probability_order: enums.SortOrder | str = enums.SortOrder.ASCENDING) -> None:
|
|
3338
|
+
nd = _nd()
|
|
3339
|
+
data = _data()
|
|
3340
|
+
super().__init__(nd.EmpiricalDistribution(to_net_doubles(values), to_net_doubles(probabilities),
|
|
3341
|
+
net_enum(data.SortOrder, x_order),
|
|
3342
|
+
net_enum(data.SortOrder, probability_order)))
|
|
3343
|
+
|
|
3344
|
+
@classmethod
|
|
3345
|
+
def survival(cls, values: Any, exceedance: Any) -> Empirical:
|
|
3346
|
+
"""Build a survival-orientation table from exceedance probabilities.
|
|
3347
|
+
|
|
3348
|
+
Parameters
|
|
3349
|
+
----------
|
|
3350
|
+
values : array_like
|
|
3351
|
+
Ascending X values.
|
|
3352
|
+
exceedance : array_like
|
|
3353
|
+
Strictly descending exceedance probabilities S(x).
|
|
3354
|
+
|
|
3355
|
+
Returns
|
|
3356
|
+
-------
|
|
3357
|
+
Empirical
|
|
3358
|
+
The distribution with ``probability_order`` descending.
|
|
3359
|
+
"""
|
|
3360
|
+
return cls(values, exceedance, enums.SortOrder.ASCENDING, enums.SortOrder.DESCENDING)
|
|
3361
|
+
|
|
3362
|
+
@classmethod
|
|
3363
|
+
def from_sample(cls, sample: Any,
|
|
3364
|
+
plotting_position: enums.PlottingPosition | str = enums.PlottingPosition.WEIBULL) -> Empirical:
|
|
3365
|
+
"""Build the distribution of a sample with plotting-position probabilities.
|
|
3366
|
+
|
|
3367
|
+
Parameters
|
|
3368
|
+
----------
|
|
3369
|
+
sample : array_like
|
|
3370
|
+
Observations; sorted by Numerics.
|
|
3371
|
+
plotting_position : PlottingPosition or str, optional
|
|
3372
|
+
Plotting-position formula; default Weibull.
|
|
3373
|
+
|
|
3374
|
+
Returns
|
|
3375
|
+
-------
|
|
3376
|
+
Empirical
|
|
3377
|
+
``EmpiricalDistribution(IList, PlottingPostionType)``.
|
|
3378
|
+
"""
|
|
3379
|
+
nd = _nd()
|
|
3380
|
+
stats = namespace("Numerics.Data.Statistics")
|
|
3381
|
+
kind = net_enum(stats.PlottingPositions.PlottingPostionType, plotting_position)
|
|
3382
|
+
instance = cls.__new__(cls)
|
|
3383
|
+
UnivariateDistribution.__init__(instance, nd.EmpiricalDistribution(to_net_doubles(sample), kind))
|
|
3384
|
+
return instance
|
|
3385
|
+
|
|
3386
|
+
@classmethod
|
|
3387
|
+
def from_paired_data(cls, paired_data: Any) -> Empirical:
|
|
3388
|
+
"""Build the distribution from ordered paired data, keeping its orders and strictness.
|
|
3389
|
+
|
|
3390
|
+
Parameters
|
|
3391
|
+
----------
|
|
3392
|
+
paired_data : pynumerics.data.OrderedPairedData or Numerics.Data.OrderedPairedData
|
|
3393
|
+
X ascending; Y the probabilities.
|
|
3394
|
+
|
|
3395
|
+
Returns
|
|
3396
|
+
-------
|
|
3397
|
+
Empirical
|
|
3398
|
+
``EmpiricalDistribution(OrderedPairedData)``.
|
|
3399
|
+
"""
|
|
3400
|
+
net = getattr(paired_data, "net", paired_data)
|
|
3401
|
+
instance = cls.__new__(cls)
|
|
3402
|
+
UnivariateDistribution.__init__(instance, _nd().EmpiricalDistribution(net))
|
|
3403
|
+
return instance
|
|
3404
|
+
|
|
3405
|
+
@property
|
|
3406
|
+
def x_values(self) -> np.ndarray:
|
|
3407
|
+
"""The X table."""
|
|
3408
|
+
return to_numpy_doubles(self.net.XValues)
|
|
3409
|
+
|
|
3410
|
+
@property
|
|
3411
|
+
def probability_values(self) -> np.ndarray:
|
|
3412
|
+
"""The probability table as stored (exceedance for a survival table)."""
|
|
3413
|
+
return to_numpy_doubles(self.net.ProbabilityValues)
|
|
3414
|
+
|
|
3415
|
+
@property
|
|
3416
|
+
def probability_order(self) -> str:
|
|
3417
|
+
"""The probability sort order: ``"Ascending"``, ``"Descending"`` or ``"None"``."""
|
|
3418
|
+
return str(self.net.ProbabilityOrder)
|
|
3419
|
+
|
|
3420
|
+
@property
|
|
3421
|
+
def x_transform(self) -> str:
|
|
3422
|
+
"""The X interpolation transform; settable with a :class:`~pynumerics.enums.Transform` or name."""
|
|
3423
|
+
return str(self.net.XTransform)
|
|
3424
|
+
|
|
3425
|
+
@x_transform.setter
|
|
3426
|
+
def x_transform(self, value: enums.Transform | str) -> None:
|
|
3427
|
+
self.net.XTransform = net_enum(_data().Transform, value)
|
|
3428
|
+
|
|
3429
|
+
@property
|
|
3430
|
+
def probability_transform(self) -> str:
|
|
3431
|
+
"""The probability interpolation transform (default ``NormalZ``); settable."""
|
|
3432
|
+
return str(self.net.ProbabilityTransform)
|
|
3433
|
+
|
|
3434
|
+
@probability_transform.setter
|
|
3435
|
+
def probability_transform(self, value: enums.Transform | str) -> None:
|
|
3436
|
+
self.net.ProbabilityTransform = net_enum(_data().Transform, value)
|
|
3437
|
+
|
|
3438
|
+
@property
|
|
3439
|
+
def extrapolation(self) -> str:
|
|
3440
|
+
"""The X-space extrapolation sides (default ``None``: endpoint hold); settable."""
|
|
3441
|
+
return str(self.net.Extrapolation)
|
|
3442
|
+
|
|
3443
|
+
@extrapolation.setter
|
|
3444
|
+
def extrapolation(self, value: enums.Extrapolation | str) -> None:
|
|
3445
|
+
self.net.Extrapolation = net_enum(_data().ExtrapolationSides, value)
|
|
3446
|
+
|
|
3447
|
+
@property
|
|
3448
|
+
def x_value_order(self) -> str:
|
|
3449
|
+
"""The X sort order: ``"Ascending"`` (``XValueOrder``)."""
|
|
3450
|
+
return str(self.net.XValueOrder)
|
|
3451
|
+
|
|
3452
|
+
@property
|
|
3453
|
+
def strict_x_values(self) -> bool:
|
|
3454
|
+
"""Whether the X values are strictly monotone, with no ties (``StrictXValues``)."""
|
|
3455
|
+
return bool(self.net.StrictXValues)
|
|
3456
|
+
|
|
3457
|
+
@property
|
|
3458
|
+
def strict_probabilities(self) -> bool:
|
|
3459
|
+
"""Whether the probabilities are strictly monotone (``StrictProbabilities``)."""
|
|
3460
|
+
return bool(self.net.StrictProbabilities)
|
|
3461
|
+
|
|
3462
|
+
@classmethod
|
|
3463
|
+
def convolve(cls, distributions: Sequence[Empirical], number_of_points: int = 1024,
|
|
3464
|
+
log_spaced_output: bool = False) -> Empirical:
|
|
3465
|
+
"""Return the distribution of the sum of independent empirical variables (``Convolve``).
|
|
3466
|
+
|
|
3467
|
+
Parameters
|
|
3468
|
+
----------
|
|
3469
|
+
distributions : sequence of Empirical
|
|
3470
|
+
Two or more distributions; ``log_spaced_output`` needs exactly two.
|
|
3471
|
+
number_of_points : int, optional
|
|
3472
|
+
Points of the result table; default 1024.
|
|
3473
|
+
log_spaced_output : bool, optional
|
|
3474
|
+
Space the output X values logarithmically (the two-distribution overload); default False.
|
|
3475
|
+
|
|
3476
|
+
Returns
|
|
3477
|
+
-------
|
|
3478
|
+
Empirical
|
|
3479
|
+
The table of the sum.
|
|
3480
|
+
"""
|
|
3481
|
+
empirical = _nd().EmpiricalDistribution
|
|
3482
|
+
items = list(distributions)
|
|
3483
|
+
if log_spaced_output:
|
|
3484
|
+
if len(items) != 2:
|
|
3485
|
+
raise ValueError("log_spaced_output needs exactly two distributions.")
|
|
3486
|
+
result = empirical.Convolve(items[0].net, items[1].net, int(number_of_points), True)
|
|
3487
|
+
elif len(items) == 2:
|
|
3488
|
+
result = empirical.Convolve(items[0].net, items[1].net, int(number_of_points))
|
|
3489
|
+
else:
|
|
3490
|
+
from System.Collections.Generic import List # noqa: PLC0415 - the runtime is running
|
|
3491
|
+
|
|
3492
|
+
members = List[empirical]()
|
|
3493
|
+
for item in items:
|
|
3494
|
+
members.Add(item.net)
|
|
3495
|
+
result = empirical.Convolve(members, int(number_of_points))
|
|
3496
|
+
return wrap(result)
|
|
3497
|
+
|
|
3498
|
+
@staticmethod
|
|
3499
|
+
def convolve_discrete(values1: Any, masses1: Any, values2: Any, masses2: Any,
|
|
3500
|
+
lattice_points: int) -> tuple[np.ndarray, np.ndarray]:
|
|
3501
|
+
"""Convolve two discrete distributions on a common lattice (``ConvolveDiscrete``).
|
|
3502
|
+
|
|
3503
|
+
Parameters
|
|
3504
|
+
----------
|
|
3505
|
+
values1, masses1 : array_like
|
|
3506
|
+
Support points and probability masses of the first variable.
|
|
3507
|
+
values2, masses2 : array_like
|
|
3508
|
+
Support points and probability masses of the second variable.
|
|
3509
|
+
lattice_points : int
|
|
3510
|
+
Number of lattice points of the result.
|
|
3511
|
+
|
|
3512
|
+
Returns
|
|
3513
|
+
-------
|
|
3514
|
+
values : numpy.ndarray
|
|
3515
|
+
The lattice of the sum.
|
|
3516
|
+
masses : numpy.ndarray
|
|
3517
|
+
The probability masses on the lattice.
|
|
3518
|
+
"""
|
|
3519
|
+
values, masses = _nd().EmpiricalDistribution.ConvolveDiscrete(
|
|
3520
|
+
to_net_doubles(values1), to_net_doubles(masses1), to_net_doubles(values2), to_net_doubles(masses2),
|
|
3521
|
+
int(lattice_points), None, None)
|
|
3522
|
+
return to_numpy_doubles(values), to_numpy_doubles(masses)
|
|
3523
|
+
|
|
3524
|
+
|
|
3525
|
+
@_register("KernelDensity")
|
|
3526
|
+
class KernelDensity(UnivariateDistribution):
|
|
3527
|
+
"""Kernel density estimate of a sample (``Numerics.Distributions.KernelDensity``).
|
|
3528
|
+
|
|
3529
|
+
The density is ``f(x) = sum_i w_i K((x - x_i) / h) / (h sum_i w_i)`` (unit weights when none
|
|
3530
|
+
are given). The CDF and inverse CDF interpolate a 1000-bin cumulative table over the support,
|
|
3531
|
+
which is the sample range when ``bounded_by_data`` is True (the default) and the range widened
|
|
3532
|
+
by ``3 h`` on each side otherwise.
|
|
3533
|
+
|
|
3534
|
+
Parameters
|
|
3535
|
+
----------
|
|
3536
|
+
sample : array_like
|
|
3537
|
+
Finite sample values, in any order.
|
|
3538
|
+
kernel : KernelType or str, optional
|
|
3539
|
+
The kernel; default Gaussian.
|
|
3540
|
+
bandwidth : float, optional
|
|
3541
|
+
The bandwidth ``h`` (positive). None, the default, applies the Numerics rule of thumb
|
|
3542
|
+
``(4 / (3 n))^(1/5) s``: ``s`` is the sample standard deviation (divisor ``n - 1``), or
|
|
3543
|
+
with weights the weighted standard deviation with divisor ``sum_i w_i``.
|
|
3544
|
+
weights : array_like, optional
|
|
3545
|
+
Non-negative weights, one per value, with a positive total.
|
|
3546
|
+
|
|
3547
|
+
Notes
|
|
3548
|
+
-----
|
|
3549
|
+
Uses ``KernelDensity(IList, KernelType[, double])``, or
|
|
3550
|
+
``KernelDensity(IList, IList, KernelType, double?)`` with weights.
|
|
3551
|
+
"""
|
|
3552
|
+
|
|
3553
|
+
def __init__(self, sample: Any, kernel: enums.KernelType | str = enums.KernelType.GAUSSIAN,
|
|
3554
|
+
bandwidth: float | None = None, weights: Any = None) -> None:
|
|
3555
|
+
kd = _nd().KernelDensity
|
|
3556
|
+
kind, values = net_enum(kd.KernelType, kernel), to_net_doubles(sample)
|
|
3557
|
+
if weights is not None:
|
|
3558
|
+
net = kd(values, to_net_doubles(weights), kind, None if bandwidth is None else float(bandwidth))
|
|
3559
|
+
elif bandwidth is None:
|
|
3560
|
+
net = kd(values, kind)
|
|
3561
|
+
else:
|
|
3562
|
+
net = kd(values, kind, float(bandwidth))
|
|
3563
|
+
super().__init__(net)
|
|
3564
|
+
|
|
3565
|
+
def __repr__(self) -> str:
|
|
3566
|
+
return f"KernelDensity(kernel={self.kernel!r}, bandwidth={self.bandwidth!r}, sample_size={self.sample_size})"
|
|
3567
|
+
|
|
3568
|
+
@property
|
|
3569
|
+
def sample_data(self) -> np.ndarray:
|
|
3570
|
+
"""The sample values, in input order."""
|
|
3571
|
+
return to_numpy_doubles(self.net.SampleData)
|
|
3572
|
+
|
|
3573
|
+
@property
|
|
3574
|
+
def sample_size(self) -> int:
|
|
3575
|
+
"""The number of sample values."""
|
|
3576
|
+
return int(self.net.SampleSize)
|
|
3577
|
+
|
|
3578
|
+
@property
|
|
3579
|
+
def kernel(self) -> str:
|
|
3580
|
+
"""The kernel name (``KernelDistribution``); settable with a :class:`~pynumerics.enums.KernelType` or name."""
|
|
3581
|
+
return str(self.net.KernelDistribution)
|
|
3582
|
+
|
|
3583
|
+
@kernel.setter
|
|
3584
|
+
def kernel(self, value: enums.KernelType | str) -> None:
|
|
3585
|
+
self.net.KernelDistribution = net_enum(_nd().KernelDensity.KernelType, value)
|
|
3586
|
+
|
|
3587
|
+
@property
|
|
3588
|
+
def bandwidth(self) -> float:
|
|
3589
|
+
"""The bandwidth ``h``; settable with a positive finite value."""
|
|
3590
|
+
return float(self.net.Bandwidth)
|
|
3591
|
+
|
|
3592
|
+
@bandwidth.setter
|
|
3593
|
+
def bandwidth(self, value: float) -> None:
|
|
3594
|
+
self.net.Bandwidth = float(value)
|
|
3595
|
+
|
|
3596
|
+
@property
|
|
3597
|
+
def bounded_by_data(self) -> bool:
|
|
3598
|
+
"""Whether the support is the sample range (default) rather than the range widened by ``3 h``; settable."""
|
|
3599
|
+
return bool(self.net.BoundedByData)
|
|
3600
|
+
|
|
3601
|
+
@bounded_by_data.setter
|
|
3602
|
+
def bounded_by_data(self, value: bool) -> None:
|
|
3603
|
+
self.net.BoundedByData = bool(value)
|
|
3604
|
+
|
|
3605
|
+
@property
|
|
3606
|
+
def x_transform(self) -> str:
|
|
3607
|
+
"""The X interpolation transform of the CDF table (default ``None``); settable."""
|
|
3608
|
+
return str(self.net.XTransform)
|
|
3609
|
+
|
|
3610
|
+
@x_transform.setter
|
|
3611
|
+
def x_transform(self, value: enums.Transform | str) -> None:
|
|
3612
|
+
self.net.XTransform = net_enum(_data().Transform, value)
|
|
3613
|
+
|
|
3614
|
+
@property
|
|
3615
|
+
def probability_transform(self) -> str:
|
|
3616
|
+
"""The probability interpolation transform of the CDF table (default ``NormalZ``); settable."""
|
|
3617
|
+
return str(self.net.ProbabilityTransform)
|
|
3618
|
+
|
|
3619
|
+
@probability_transform.setter
|
|
3620
|
+
def probability_transform(self, value: enums.Transform | str) -> None:
|
|
3621
|
+
self.net.ProbabilityTransform = net_enum(_data().Transform, value)
|
|
3622
|
+
|
|
3623
|
+
def bandwidth_rule(self, sample: Any, weights: Any = None) -> float:
|
|
3624
|
+
"""Return the rule-of-thumb bandwidth of a sample (``BandwidthRule``).
|
|
3625
|
+
|
|
3626
|
+
Parameters
|
|
3627
|
+
----------
|
|
3628
|
+
sample : array_like
|
|
3629
|
+
Finite sample values.
|
|
3630
|
+
weights : array_like, optional
|
|
3631
|
+
Weights, one per value; the weighted standard deviation is used.
|
|
3632
|
+
|
|
3633
|
+
Returns
|
|
3634
|
+
-------
|
|
3635
|
+
float
|
|
3636
|
+
``(4 / (3 n))^(1/5)`` times the standard deviation (as for the ``bandwidth`` default),
|
|
3637
|
+
kept finite and positive for a constant sample.
|
|
3638
|
+
"""
|
|
3639
|
+
values = to_net_doubles(sample)
|
|
3640
|
+
if weights is None:
|
|
3641
|
+
return float(self.net.BandwidthRule(values))
|
|
3642
|
+
return float(self.net.BandwidthRule(values, to_net_doubles(weights)))
|
|
3643
|
+
|
|
3644
|
+
def set_sample_data(self, sample: Any, weights: Any = None) -> None:
|
|
3645
|
+
"""Replace the sample (and weights) in place, keeping the kernel and bandwidth (``SetSampleData``).
|
|
3646
|
+
|
|
3647
|
+
Parameters
|
|
3648
|
+
----------
|
|
3649
|
+
sample : array_like
|
|
3650
|
+
Finite sample values.
|
|
3651
|
+
weights : array_like, optional
|
|
3652
|
+
Non-negative weights, one per value; None, the default, gives unit weights.
|
|
3653
|
+
|
|
3654
|
+
Raises
|
|
3655
|
+
------
|
|
3656
|
+
TypeError
|
|
3657
|
+
If the distribution is frozen.
|
|
3658
|
+
"""
|
|
3659
|
+
self._ensure_mutable()
|
|
3660
|
+
if weights is None:
|
|
3661
|
+
self.net.SetSampleData(to_net_doubles(sample))
|
|
3662
|
+
else:
|
|
3663
|
+
self.net.SetSampleData(to_net_doubles(sample), to_net_doubles(weights))
|
|
3664
|
+
|
|
3665
|
+
degenerate_relative_bandwidth = _FamilyAttribute(
|
|
3666
|
+
lambda cls: float(_nd().KernelDensity.DegenerateRelativeBandwidth),
|
|
3667
|
+
lambda self: float(_nd().KernelDensity.DegenerateRelativeBandwidth),
|
|
3668
|
+
"""The bandwidth of a constant sample relative to its magnitude (``DegenerateRelativeBandwidth``).""")
|
|
3669
|
+
|
|
3670
|
+
degenerate_absolute_bandwidth = _FamilyAttribute(
|
|
3671
|
+
lambda cls: float(_nd().KernelDensity.DegenerateAbsoluteBandwidth),
|
|
3672
|
+
lambda self: float(_nd().KernelDensity.DegenerateAbsoluteBandwidth),
|
|
3673
|
+
"""The bandwidth of a constant sample at zero (``DegenerateAbsoluteBandwidth``).""")
|
|
3674
|
+
|
|
3675
|
+
|
|
3676
|
+
def create_distribution(type_name: str, parameters: Any = None) -> UnivariateDistribution:
|
|
3677
|
+
"""Create a distribution by type name through ``UnivariateDistributionFactory``.
|
|
3678
|
+
|
|
3679
|
+
``CreateDistribution(UnivariateDistributionType)`` builds the family at its default
|
|
3680
|
+
parameters; ``parameters``, when given, are then set through ``SetParameters``.
|
|
3681
|
+
Invalid parameters are recorded, not refused (``parameters_valid`` is False).
|
|
3682
|
+
|
|
3683
|
+
Parameters
|
|
3684
|
+
----------
|
|
3685
|
+
type_name : str
|
|
3686
|
+
A ``UnivariateDistributionType`` member name, for example ``"Gumbel"``; matched
|
|
3687
|
+
case-insensitively with underscores ignored.
|
|
3688
|
+
parameters : array_like, optional
|
|
3689
|
+
The parameters in Numerics order.
|
|
3690
|
+
|
|
3691
|
+
Returns
|
|
3692
|
+
-------
|
|
3693
|
+
UnivariateDistribution
|
|
3694
|
+
The wrapped distribution (its specific wrapper class when one exists).
|
|
3695
|
+
|
|
3696
|
+
Raises
|
|
3697
|
+
------
|
|
3698
|
+
ValueError
|
|
3699
|
+
If ``type_name`` is not a member of ``UnivariateDistributionType``, or ``parameters`` does
|
|
3700
|
+
not hold the family's number of parameters (the message names the expected count).
|
|
3701
|
+
"""
|
|
3702
|
+
nd = _nd()
|
|
3703
|
+
net = unwrap(nd.UnivariateDistributionFactory.CreateDistribution(
|
|
3704
|
+
net_enum(nd.UnivariateDistributionType, type_name)))
|
|
3705
|
+
if parameters is not None:
|
|
3706
|
+
net.SetParameters(to_net_doubles(_check_arity(net, parameters)))
|
|
3707
|
+
return wrap(net)
|
|
3708
|
+
|
|
3709
|
+
|
|
3710
|
+
def standard_z(probability: Any) -> Any:
|
|
3711
|
+
"""Return standard normal quantiles through ``Normal.StandardZ``.
|
|
3712
|
+
|
|
3713
|
+
Parameters
|
|
3714
|
+
----------
|
|
3715
|
+
probability : float or array_like
|
|
3716
|
+
Non-exceedance probabilities; 0 and 1 map to ±``double.Epsilon`` probabilities
|
|
3717
|
+
as in Numerics.
|
|
3718
|
+
|
|
3719
|
+
Returns
|
|
3720
|
+
-------
|
|
3721
|
+
float or numpy.ndarray
|
|
3722
|
+
Standard normal deviates, shaped like ``probability``.
|
|
3723
|
+
"""
|
|
3724
|
+
n = _nd().Normal
|
|
3725
|
+
return evaluate(probability, _static("StandardZ", n), _static_list("StandardZ", n))
|
|
3726
|
+
|
|
3727
|
+
|
|
3728
|
+
def standard_cdf(z: Any) -> Any:
|
|
3729
|
+
"""Return the standard normal CDF through ``Normal.StandardCDF``.
|
|
3730
|
+
|
|
3731
|
+
Parameters
|
|
3732
|
+
----------
|
|
3733
|
+
z : float or array_like
|
|
3734
|
+
Standard normal deviates.
|
|
3735
|
+
|
|
3736
|
+
Returns
|
|
3737
|
+
-------
|
|
3738
|
+
float or numpy.ndarray
|
|
3739
|
+
Non-exceedance probabilities, shaped like ``z``.
|
|
3740
|
+
"""
|
|
3741
|
+
n = _nd().Normal
|
|
3742
|
+
return evaluate(z, _static("StandardCDF", n), _static_list("StandardCDF", n))
|
|
3743
|
+
|
|
3744
|
+
|
|
3745
|
+
_STATIC_BOUND: dict[str, Any] = {}
|
|
3746
|
+
|
|
3747
|
+
|
|
3748
|
+
def _static(name: str, owner: Any) -> Any:
|
|
3749
|
+
"""Return a static member's ``double`` overload, bound once per process (see ``_util.bind``)."""
|
|
3750
|
+
member = _STATIC_BOUND.get(name)
|
|
3751
|
+
if member is None:
|
|
3752
|
+
member = _STATIC_BOUND[name] = bind(getattr(owner, name))
|
|
3753
|
+
return member
|
|
3754
|
+
|
|
3755
|
+
|
|
3756
|
+
_STATIC_LIST_BOUND: dict[str, Any] = {}
|
|
3757
|
+
|
|
3758
|
+
|
|
3759
|
+
def _static_list(name: str, owner: Any) -> Any:
|
|
3760
|
+
"""Return a static member's ``IList<double>`` overload, bound once per process (see ``_util.bind_list``)."""
|
|
3761
|
+
member = _STATIC_LIST_BOUND.get(name)
|
|
3762
|
+
if member is None:
|
|
3763
|
+
member = _STATIC_LIST_BOUND[name] = bind_list(getattr(owner, name))
|
|
3764
|
+
return member
|
|
3765
|
+
|
|
3766
|
+
|
|
3767
|
+
def standard_pdf(z: Any) -> Any:
|
|
3768
|
+
"""Return the standard normal density through ``Normal.StandardPDF``.
|
|
3769
|
+
|
|
3770
|
+
Parameters
|
|
3771
|
+
----------
|
|
3772
|
+
z : float or array_like
|
|
3773
|
+
Standard normal deviates.
|
|
3774
|
+
|
|
3775
|
+
Returns
|
|
3776
|
+
-------
|
|
3777
|
+
float or numpy.ndarray
|
|
3778
|
+
Densities, shaped like ``z``.
|
|
3779
|
+
"""
|
|
3780
|
+
n = _nd().Normal
|
|
3781
|
+
return evaluate(z, _static("StandardPDF", n), _static_list("StandardPDF", n))
|
|
3782
|
+
|
|
3783
|
+
|
|
3784
|
+
def try_create_distribution(type_name: str) -> UnivariateDistribution | None:
|
|
3785
|
+
"""Create a family at its default parameters, or return None when Numerics cannot (``TryCreateDistribution``).
|
|
3786
|
+
|
|
3787
|
+
Parameters
|
|
3788
|
+
----------
|
|
3789
|
+
type_name : str
|
|
3790
|
+
A ``UnivariateDistributionType`` member name, matched case-insensitively with underscores ignored.
|
|
3791
|
+
|
|
3792
|
+
Returns
|
|
3793
|
+
-------
|
|
3794
|
+
UnivariateDistribution or None
|
|
3795
|
+
The wrapped distribution; None for the families that need components (``Mixture``,
|
|
3796
|
+
``CompetingRisks``) and ``UserDefined``.
|
|
3797
|
+
|
|
3798
|
+
Raises
|
|
3799
|
+
------
|
|
3800
|
+
ValueError
|
|
3801
|
+
If ``type_name`` is not a member of ``UnivariateDistributionType``.
|
|
3802
|
+
"""
|
|
3803
|
+
nd = _nd()
|
|
3804
|
+
created, net = nd.UnivariateDistributionFactory.TryCreateDistribution(
|
|
3805
|
+
net_enum(nd.UnivariateDistributionType, type_name), None)
|
|
3806
|
+
return wrap(net) if created else None
|
|
3807
|
+
|
|
3808
|
+
|
|
3809
|
+
def from_xml(xml: str) -> UnivariateDistribution:
|
|
3810
|
+
"""Rebuild a distribution from the XML :meth:`UnivariateDistribution.to_xml` writes.
|
|
3811
|
+
|
|
3812
|
+
Calls ``UnivariateDistributionFactory.CreateDistribution(XElement)``, which dispatches to the
|
|
3813
|
+
family's own ``FromXElement`` for mixtures, competing risks, percentile PERT, empirical and
|
|
3814
|
+
kernel density tables.
|
|
3815
|
+
|
|
3816
|
+
Parameters
|
|
3817
|
+
----------
|
|
3818
|
+
xml : str
|
|
3819
|
+
The ``Distribution`` element as text.
|
|
3820
|
+
|
|
3821
|
+
Returns
|
|
3822
|
+
-------
|
|
3823
|
+
UnivariateDistribution
|
|
3824
|
+
The wrapped distribution.
|
|
3825
|
+
|
|
3826
|
+
Raises
|
|
3827
|
+
------
|
|
3828
|
+
ValueError
|
|
3829
|
+
If the type or a parameter is missing or invalid.
|
|
3830
|
+
"""
|
|
3831
|
+
return wrap(_nd().UnivariateDistributionFactory.CreateDistribution(_parse_xml(xml)))
|
|
3832
|
+
|
|
3833
|
+
|
|
3834
|
+
def standard_log_pdf(z: Any) -> Any:
|
|
3835
|
+
"""Return the log of the standard normal density through ``Normal.StandardLogPDF``.
|
|
3836
|
+
|
|
3837
|
+
Parameters
|
|
3838
|
+
----------
|
|
3839
|
+
z : float or array_like
|
|
3840
|
+
Standard normal deviates.
|
|
3841
|
+
|
|
3842
|
+
Returns
|
|
3843
|
+
-------
|
|
3844
|
+
float or numpy.ndarray
|
|
3845
|
+
Log densities, shaped like ``z``; Numerics has no array overload, so an array is evaluated
|
|
3846
|
+
element by element with the bound scalar member.
|
|
3847
|
+
"""
|
|
3848
|
+
return loop(z, _static("StandardLogPDF", _nd().Normal))
|
|
3849
|
+
|
|
3850
|
+
|
|
3851
|
+
def _elementwise(*arrays: Any) -> tuple[tuple[int, ...], list[np.ndarray]]:
|
|
3852
|
+
"""Broadcast the per-element arguments of a static array member and flatten them."""
|
|
3853
|
+
broadcast = np.broadcast_arrays(*(np.asarray(a) for a in arrays))
|
|
3854
|
+
shape = broadcast[0].shape
|
|
3855
|
+
return shape, [np.ascontiguousarray(b).ravel() for b in broadcast]
|
|
3856
|
+
|
|
3857
|
+
|
|
3858
|
+
def _shaped(values: Any, shape: tuple[int, ...]) -> Any:
|
|
3859
|
+
"""Return a .NET result array as a float for a scalar shape, otherwise as an array of ``shape``."""
|
|
3860
|
+
out = to_numpy_doubles(values)
|
|
3861
|
+
return float(out[0]) if shape == () else out.reshape(shape)
|
|
3862
|
+
|
|
3863
|
+
|
|
3864
|
+
def _poisson_static(name: str, rate: Any, values: Any) -> Any:
|
|
3865
|
+
"""Call the per-element static ``Poisson.<name>(double[], double[])`` on broadcast arguments."""
|
|
3866
|
+
shape, (rates, points) = _elementwise(np.asarray(rate, dtype=np.float64), np.asarray(values, dtype=np.float64))
|
|
3867
|
+
|
|
3868
|
+
def factory() -> Any:
|
|
3869
|
+
poisson = _nd().Poisson # starts the runtime before System is imported
|
|
3870
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
3871
|
+
|
|
3872
|
+
doubles = System.Array[System.Double]
|
|
3873
|
+
return getattr(poisson, name).Overloads[doubles, doubles]
|
|
3874
|
+
|
|
3875
|
+
member = cached_overload(f"Poisson.{name}(double[], double[])", factory)
|
|
3876
|
+
return _shaped(member(to_net_doubles(rates), to_net_doubles(points)), shape)
|
|
3877
|
+
|
|
3878
|
+
|
|
3879
|
+
def poisson_inverse_cdf(rate: Any, probability: Any) -> Any:
|
|
3880
|
+
"""Return Poisson quantiles with one rate per probability (``Poisson.InverseCDF(double[], double[])``).
|
|
3881
|
+
|
|
3882
|
+
Parameters
|
|
3883
|
+
----------
|
|
3884
|
+
rate : float or array_like
|
|
3885
|
+
Rates λ > 0, broadcast against ``probability``.
|
|
3886
|
+
probability : float or array_like
|
|
3887
|
+
Non-exceedance probabilities in [0, 1].
|
|
3888
|
+
|
|
3889
|
+
Returns
|
|
3890
|
+
-------
|
|
3891
|
+
float or numpy.ndarray
|
|
3892
|
+
The smallest integer k with P(X <= k) >= p for each element, shaped like the broadcast
|
|
3893
|
+
arguments; probability 0 gives 0 and 1 gives infinity. Each value is bitwise equal to
|
|
3894
|
+
``Poisson(rate).inverse_cdf(probability)``.
|
|
3895
|
+
|
|
3896
|
+
Raises
|
|
3897
|
+
------
|
|
3898
|
+
ValueError
|
|
3899
|
+
If a rate is not positive and finite, a probability is outside [0, 1], or the arguments
|
|
3900
|
+
do not broadcast.
|
|
3901
|
+
"""
|
|
3902
|
+
return _poisson_static("InverseCDF", rate, probability)
|
|
3903
|
+
|
|
3904
|
+
|
|
3905
|
+
def poisson_log_pmf(rate: Any, k: Any) -> Any:
|
|
3906
|
+
"""Return Poisson log probability masses with one rate per count (``Poisson.LogPDF(double[], double[])``).
|
|
3907
|
+
|
|
3908
|
+
Parameters
|
|
3909
|
+
----------
|
|
3910
|
+
rate : float or array_like
|
|
3911
|
+
Rates λ > 0, broadcast against ``k``.
|
|
3912
|
+
k : float or array_like
|
|
3913
|
+
Counts; a value that is not a nonnegative integer has log mass ``-inf``.
|
|
3914
|
+
|
|
3915
|
+
Returns
|
|
3916
|
+
-------
|
|
3917
|
+
float or numpy.ndarray
|
|
3918
|
+
The log masses, shaped like the broadcast arguments, each bitwise equal to
|
|
3919
|
+
``Poisson(rate).log_pdf(k)``.
|
|
3920
|
+
|
|
3921
|
+
Raises
|
|
3922
|
+
------
|
|
3923
|
+
ValueError
|
|
3924
|
+
If a rate is not positive and finite, or the arguments do not broadcast.
|
|
3925
|
+
"""
|
|
3926
|
+
return _poisson_static("LogPDF", rate, k)
|
|
3927
|
+
|
|
3928
|
+
|
|
3929
|
+
def poisson_cdf(rate: Any, k: Any) -> Any:
|
|
3930
|
+
"""Return Poisson cumulative probabilities with one rate per count (``Poisson.CDF(double[], double[])``).
|
|
3931
|
+
|
|
3932
|
+
Parameters
|
|
3933
|
+
----------
|
|
3934
|
+
rate : float or array_like
|
|
3935
|
+
Rates λ > 0, broadcast against ``k``.
|
|
3936
|
+
k : float or array_like
|
|
3937
|
+
Counts, floored when not integers.
|
|
3938
|
+
|
|
3939
|
+
Returns
|
|
3940
|
+
-------
|
|
3941
|
+
float or numpy.ndarray
|
|
3942
|
+
P(X <= k), shaped like the broadcast arguments, each bitwise equal to ``Poisson(rate).cdf(k)``.
|
|
3943
|
+
|
|
3944
|
+
Raises
|
|
3945
|
+
------
|
|
3946
|
+
ValueError
|
|
3947
|
+
If a rate is not positive and finite, or the arguments do not broadcast.
|
|
3948
|
+
"""
|
|
3949
|
+
return _poisson_static("CDF", rate, k)
|
|
3950
|
+
|
|
3951
|
+
|
|
3952
|
+
def _binomial_static(name: str, probability: Any, number_of_trials: Any, k: Any) -> Any:
|
|
3953
|
+
"""Call the per-element static ``Binomial.<name>(double[], int[], double[])`` on broadcast arguments."""
|
|
3954
|
+
trials = np.asarray(number_of_trials)
|
|
3955
|
+
if trials.dtype.kind not in "iuf" or (trials.dtype.kind == "f" and not np.array_equal(trials, np.trunc(trials))):
|
|
3956
|
+
raise ValueError("The number of trials must be an integer.")
|
|
3957
|
+
if trials.size and (trials.min() < np.iinfo(np.int32).min or trials.max() > np.iinfo(np.int32).max):
|
|
3958
|
+
raise ValueError("The number of trials must fit in a 32-bit integer.")
|
|
3959
|
+
shape, (p, n, points) = _elementwise(np.asarray(probability, dtype=np.float64), trials,
|
|
3960
|
+
np.asarray(k, dtype=np.float64))
|
|
3961
|
+
|
|
3962
|
+
def factory() -> Any:
|
|
3963
|
+
binomial = _nd().Binomial # starts the runtime before System is imported
|
|
3964
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
3965
|
+
|
|
3966
|
+
doubles = System.Array[System.Double]
|
|
3967
|
+
return getattr(binomial, name).Overloads[doubles, System.Array[System.Int32], doubles]
|
|
3968
|
+
|
|
3969
|
+
member = cached_overload(f"Binomial.{name}(double[], int[], double[])", factory)
|
|
3970
|
+
return _shaped(member(to_net_doubles(p), to_net_ints(n), to_net_doubles(points)), shape)
|
|
3971
|
+
|
|
3972
|
+
|
|
3973
|
+
def binomial_log_pmf(probability: Any, number_of_trials: Any, k: Any) -> Any:
|
|
3974
|
+
"""Return binomial log probability masses with one parameter set per count (``Binomial.LogPDF``).
|
|
3975
|
+
|
|
3976
|
+
Calls ``Binomial.LogPDF(double[], int[], double[])``.
|
|
3977
|
+
|
|
3978
|
+
Parameters
|
|
3979
|
+
----------
|
|
3980
|
+
probability : float or array_like
|
|
3981
|
+
Success probabilities in [0, 1], broadcast against the other arguments.
|
|
3982
|
+
number_of_trials : int or array_like
|
|
3983
|
+
Numbers of trials n >= 1.
|
|
3984
|
+
k : float or array_like
|
|
3985
|
+
Numbers of successes; a value that is not an integer in [0, n] has log mass ``-inf``.
|
|
3986
|
+
|
|
3987
|
+
Returns
|
|
3988
|
+
-------
|
|
3989
|
+
float or numpy.ndarray
|
|
3990
|
+
The log masses, shaped like the broadcast arguments, each bitwise equal to
|
|
3991
|
+
``Binomial(probability, number_of_trials).log_pdf(k)``.
|
|
3992
|
+
|
|
3993
|
+
Raises
|
|
3994
|
+
------
|
|
3995
|
+
ValueError
|
|
3996
|
+
If a probability is outside [0, 1], a number of trials is not a positive integer that fits
|
|
3997
|
+
in 32 bits (booleans, text and fractional values are refused), or the arguments do not broadcast.
|
|
3998
|
+
"""
|
|
3999
|
+
return _binomial_static("LogPDF", probability, number_of_trials, k)
|
|
4000
|
+
|
|
4001
|
+
|
|
4002
|
+
def binomial_cdf(probability: Any, number_of_trials: Any, k: Any) -> Any:
|
|
4003
|
+
"""Return binomial cumulative probabilities with one parameter set per count (``Binomial.CDF``).
|
|
4004
|
+
|
|
4005
|
+
Calls ``Binomial.CDF(double[], int[], double[])``.
|
|
4006
|
+
|
|
4007
|
+
Parameters
|
|
4008
|
+
----------
|
|
4009
|
+
probability : float or array_like
|
|
4010
|
+
Success probabilities in [0, 1], broadcast against the other arguments.
|
|
4011
|
+
number_of_trials : int or array_like
|
|
4012
|
+
Numbers of trials n >= 1.
|
|
4013
|
+
k : float or array_like
|
|
4014
|
+
Numbers of successes, floored when not integers.
|
|
4015
|
+
|
|
4016
|
+
Returns
|
|
4017
|
+
-------
|
|
4018
|
+
float or numpy.ndarray
|
|
4019
|
+
P(X <= k), shaped like the broadcast arguments, each bitwise equal to
|
|
4020
|
+
``Binomial(probability, number_of_trials).cdf(k)``.
|
|
4021
|
+
|
|
4022
|
+
Raises
|
|
4023
|
+
------
|
|
4024
|
+
ValueError
|
|
4025
|
+
If a probability is outside [0, 1], a number of trials is not a positive integer that fits
|
|
4026
|
+
in 32 bits (booleans, text and fractional values are refused), or the arguments do not broadcast.
|
|
4027
|
+
"""
|
|
4028
|
+
return _binomial_static("CDF", probability, number_of_trials, k)
|
|
4029
|
+
|
|
4030
|
+
|
|
4031
|
+
__all__ = [
|
|
4032
|
+
"FAMILIES", "FrozenDistribution", "Bernoulli", "Beta", "Binomial", "Cauchy", "ChiSquared", "CompetingRisks",
|
|
4033
|
+
"Deterministic", "Empirical", "Exponential", "GammaDistribution", "GeneralizedBeta", "GeneralizedExtremeValue",
|
|
4034
|
+
"GeneralizedLogistic", "GeneralizedNormal", "GeneralizedPareto", "Geometric", "Gumbel", "InverseChiSquared",
|
|
4035
|
+
"InverseGamma", "KappaFour", "KernelDensity", "LnNormal", "LogNormal", "LogPearsonTypeIII", "Logistic", "Mixture",
|
|
4036
|
+
"NoncentralT", "Normal", "Pareto", "PearsonTypeIII", "Pert", "PertPercentile", "PertPercentileZ", "Poisson",
|
|
4037
|
+
"Rayleigh", "StudentT", "Triangular", "TruncatedDistribution", "TruncatedNormal", "Uniform", "UniformDiscrete",
|
|
4038
|
+
"UnivariateDistribution", "VonMises", "Weibull", "binomial_cdf", "binomial_log_pmf", "create_distribution",
|
|
4039
|
+
"from_xml", "poisson_cdf", "poisson_inverse_cdf", "poisson_log_pmf", "standard_cdf", "standard_log_pdf",
|
|
4040
|
+
"standard_pdf", "standard_z", "try_create_distribution", "wrap",
|
|
4041
|
+
]
|
|
4042
|
+
|
|
4043
|
+
translate_public(globals())
|