pygeodesy 24.3.24__py2.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.
- PyGeodesy-24.3.24.dist-info/METADATA +272 -0
- PyGeodesy-24.3.24.dist-info/RECORD +115 -0
- PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
- PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
- pygeodesy/LICENSE +21 -0
- pygeodesy/__init__.py +615 -0
- pygeodesy/__main__.py +103 -0
- pygeodesy/albers.py +867 -0
- pygeodesy/auxilats/_CX_4.py +218 -0
- pygeodesy/auxilats/_CX_6.py +314 -0
- pygeodesy/auxilats/_CX_8.py +475 -0
- pygeodesy/auxilats/__init__.py +54 -0
- pygeodesy/auxilats/__main__.py +86 -0
- pygeodesy/auxilats/auxAngle.py +548 -0
- pygeodesy/auxilats/auxDLat.py +302 -0
- pygeodesy/auxilats/auxDST.py +296 -0
- pygeodesy/auxilats/auxLat.py +848 -0
- pygeodesy/auxilats/auxily.py +272 -0
- pygeodesy/azimuthal.py +1150 -0
- pygeodesy/basics.py +892 -0
- pygeodesy/booleans.py +2031 -0
- pygeodesy/cartesianBase.py +1062 -0
- pygeodesy/clipy.py +704 -0
- pygeodesy/constants.py +516 -0
- pygeodesy/css.py +660 -0
- pygeodesy/datums.py +752 -0
- pygeodesy/deprecated/__init__.py +61 -0
- pygeodesy/deprecated/bases.py +40 -0
- pygeodesy/deprecated/classes.py +262 -0
- pygeodesy/deprecated/consterns.py +54 -0
- pygeodesy/deprecated/datum.py +40 -0
- pygeodesy/deprecated/functions.py +375 -0
- pygeodesy/deprecated/nvector.py +48 -0
- pygeodesy/deprecated/rhumbBase.py +32 -0
- pygeodesy/deprecated/rhumbaux.py +33 -0
- pygeodesy/deprecated/rhumbsolve.py +33 -0
- pygeodesy/deprecated/rhumbx.py +33 -0
- pygeodesy/dms.py +986 -0
- pygeodesy/ecef.py +1348 -0
- pygeodesy/elevations.py +279 -0
- pygeodesy/ellipsoidalBase.py +1224 -0
- pygeodesy/ellipsoidalBaseDI.py +913 -0
- pygeodesy/ellipsoidalExact.py +343 -0
- pygeodesy/ellipsoidalGeodSolve.py +343 -0
- pygeodesy/ellipsoidalKarney.py +403 -0
- pygeodesy/ellipsoidalNvector.py +685 -0
- pygeodesy/ellipsoidalVincenty.py +590 -0
- pygeodesy/ellipsoids.py +2476 -0
- pygeodesy/elliptic.py +1198 -0
- pygeodesy/epsg.py +243 -0
- pygeodesy/errors.py +804 -0
- pygeodesy/etm.py +1190 -0
- pygeodesy/fmath.py +1013 -0
- pygeodesy/formy.py +1818 -0
- pygeodesy/frechet.py +865 -0
- pygeodesy/fstats.py +760 -0
- pygeodesy/fsums.py +1898 -0
- pygeodesy/gars.py +358 -0
- pygeodesy/geodesicw.py +581 -0
- pygeodesy/geodesicx/_C4_24.py +1699 -0
- pygeodesy/geodesicx/_C4_27.py +2395 -0
- pygeodesy/geodesicx/_C4_30.py +3301 -0
- pygeodesy/geodesicx/__init__.py +48 -0
- pygeodesy/geodesicx/__main__.py +91 -0
- pygeodesy/geodesicx/gx.py +1382 -0
- pygeodesy/geodesicx/gxarea.py +535 -0
- pygeodesy/geodesicx/gxbases.py +154 -0
- pygeodesy/geodesicx/gxline.py +669 -0
- pygeodesy/geodsolve.py +426 -0
- pygeodesy/geohash.py +914 -0
- pygeodesy/geoids.py +1884 -0
- pygeodesy/hausdorff.py +892 -0
- pygeodesy/heights.py +1155 -0
- pygeodesy/interns.py +687 -0
- pygeodesy/iters.py +545 -0
- pygeodesy/karney.py +919 -0
- pygeodesy/ktm.py +633 -0
- pygeodesy/latlonBase.py +1766 -0
- pygeodesy/lazily.py +960 -0
- pygeodesy/lcc.py +684 -0
- pygeodesy/ltp.py +1107 -0
- pygeodesy/ltpTuples.py +1563 -0
- pygeodesy/mgrs.py +721 -0
- pygeodesy/named.py +1324 -0
- pygeodesy/namedTuples.py +683 -0
- pygeodesy/nvectorBase.py +695 -0
- pygeodesy/osgr.py +781 -0
- pygeodesy/points.py +1686 -0
- pygeodesy/props.py +628 -0
- pygeodesy/resections.py +1048 -0
- pygeodesy/rhumb/__init__.py +46 -0
- pygeodesy/rhumb/aux_.py +397 -0
- pygeodesy/rhumb/bases.py +1148 -0
- pygeodesy/rhumb/ekx.py +563 -0
- pygeodesy/rhumb/solve.py +572 -0
- pygeodesy/simplify.py +647 -0
- pygeodesy/solveBase.py +472 -0
- pygeodesy/sphericalBase.py +724 -0
- pygeodesy/sphericalNvector.py +1264 -0
- pygeodesy/sphericalTrigonometry.py +1447 -0
- pygeodesy/streprs.py +627 -0
- pygeodesy/trf.py +2079 -0
- pygeodesy/triaxials.py +1484 -0
- pygeodesy/units.py +969 -0
- pygeodesy/unitsBase.py +349 -0
- pygeodesy/ups.py +538 -0
- pygeodesy/utily.py +1231 -0
- pygeodesy/utm.py +762 -0
- pygeodesy/utmups.py +318 -0
- pygeodesy/utmupsBase.py +517 -0
- pygeodesy/vector2d.py +785 -0
- pygeodesy/vector3d.py +968 -0
- pygeodesy/vector3dBase.py +1049 -0
- pygeodesy/webmercator.py +383 -0
- pygeodesy/wgrs.py +439 -0
pygeodesy/fstats.py
ADDED
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
u'''Classes for I{running} statistics and regressions based on
|
|
5
|
+
L{pygeodesy.Fsum}, precision floating point summation.
|
|
6
|
+
'''
|
|
7
|
+
# make sure int/int division yields float quotient, see .basics
|
|
8
|
+
from __future__ import division as _; del _ # PYCHOK semicolon
|
|
9
|
+
|
|
10
|
+
from pygeodesy.basics import isodd, islistuple, _xinstanceof, \
|
|
11
|
+
_xsubclassof, _zip
|
|
12
|
+
from pygeodesy.constants import _0_0, _2_0, _3_0, _4_0, _6_0, _xError
|
|
13
|
+
# from pygeodesy.errors import _xError # from .constants
|
|
14
|
+
from pygeodesy.fmath import hypot2, sqrt
|
|
15
|
+
from pygeodesy.fsums import _2float, Fsum, Fmt
|
|
16
|
+
from pygeodesy.interns import NN, _iadd_op_, _invalid_, _other_, _SPACE_
|
|
17
|
+
from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
|
|
18
|
+
from pygeodesy.named import _Named, _NotImplemented, notOverloaded, \
|
|
19
|
+
property_RO
|
|
20
|
+
# from pygeodesy.props import property_RO # from .named
|
|
21
|
+
# from pygeodesy.streprs import Fmt # from .fsums
|
|
22
|
+
|
|
23
|
+
# from math import sqrt # from .fmath
|
|
24
|
+
|
|
25
|
+
__all__ = _ALL_LAZY.fstats
|
|
26
|
+
__version__ = '23.09.22'
|
|
27
|
+
|
|
28
|
+
_Floats = Fsum, float
|
|
29
|
+
_Scalar = _Floats + (int,) # XXX basics._Ints is ABCMeta in Python 2
|
|
30
|
+
try:
|
|
31
|
+
_Scalar += (long,)
|
|
32
|
+
except NameError: # Python 3+
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _2Floats(xs, ys=False):
|
|
37
|
+
'''(INTERNAL) Yield each value as C{float} or L{Fsum}.
|
|
38
|
+
'''
|
|
39
|
+
if ys:
|
|
40
|
+
def _2f(i, x):
|
|
41
|
+
return _2float(index=i, ys=x)
|
|
42
|
+
else:
|
|
43
|
+
def _2f(i, x): # PYCHOK redef
|
|
44
|
+
return _2float(index=i, xs=x)
|
|
45
|
+
|
|
46
|
+
for i, x in enumerate(xs): # don't unravel Fsums
|
|
47
|
+
yield x if isinstance(x, _Floats) else _2f(i, x)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _sampled(n, sample):
|
|
51
|
+
'''(INTERNAL) Return the sample or the entire count.
|
|
52
|
+
'''
|
|
53
|
+
return (n - 1) if sample and n > 0 else n
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class _FstatsNamed(_Named):
|
|
57
|
+
'''(INTERNAL) Base class.
|
|
58
|
+
'''
|
|
59
|
+
_n = 0
|
|
60
|
+
|
|
61
|
+
def __add__(self, other):
|
|
62
|
+
'''Sum of this and a scalar, an L{Fsum} or an other instance.
|
|
63
|
+
'''
|
|
64
|
+
f = self.fcopy(name=self.__add__.__name__) # PYCHOK expected
|
|
65
|
+
f += other
|
|
66
|
+
return f
|
|
67
|
+
|
|
68
|
+
def __float__(self): # PYCHOK no cover
|
|
69
|
+
'''Not implemented.'''
|
|
70
|
+
return _NotImplemented(self)
|
|
71
|
+
|
|
72
|
+
def __int__(self): # PYCHOK no cover
|
|
73
|
+
'''Not implemented.'''
|
|
74
|
+
return _NotImplemented(self)
|
|
75
|
+
|
|
76
|
+
def __len__(self):
|
|
77
|
+
'''Return the I{total} number of accumulated values (C{int}).
|
|
78
|
+
'''
|
|
79
|
+
return self._n
|
|
80
|
+
|
|
81
|
+
def __neg__(self): # PYCHOK no cover
|
|
82
|
+
'''Not implemented.'''
|
|
83
|
+
return _NotImplemented(self)
|
|
84
|
+
|
|
85
|
+
def __radd__(self, other): # PYCHOK no cover
|
|
86
|
+
'''Not implemented.'''
|
|
87
|
+
return _NotImplemented(self, other)
|
|
88
|
+
|
|
89
|
+
def __str__(self):
|
|
90
|
+
return Fmt.SQUARE(self.named3, len(self))
|
|
91
|
+
|
|
92
|
+
def fcopy(self, deep=False, name=NN):
|
|
93
|
+
'''Copy this instance, C{shallow} or B{C{deep}}.
|
|
94
|
+
'''
|
|
95
|
+
n = name or self.fcopy.__name__
|
|
96
|
+
f = _Named.copy(self, deep=deep, name=n)
|
|
97
|
+
return self._copy(f, self) # PYCHOK expected
|
|
98
|
+
|
|
99
|
+
copy = fcopy
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class _FstatsBase(_FstatsNamed):
|
|
103
|
+
'''(INTERNAL) Base running stats class.
|
|
104
|
+
'''
|
|
105
|
+
_Ms = ()
|
|
106
|
+
|
|
107
|
+
def _copy(self, c, s):
|
|
108
|
+
'''(INTERNAL) Copy C{B{c} = B{s}}.
|
|
109
|
+
'''
|
|
110
|
+
_xinstanceof(self.__class__, c=c, s=s)
|
|
111
|
+
c._Ms = tuple(M.fcopy() for M in s._Ms) # deep=False
|
|
112
|
+
c._n = s._n
|
|
113
|
+
return c
|
|
114
|
+
|
|
115
|
+
def fadd(self, xs, sample=False): # PYCHOK no cover
|
|
116
|
+
'''I{Must be overloaded}.'''
|
|
117
|
+
notOverloaded(self, xs, sample=sample)
|
|
118
|
+
|
|
119
|
+
def fadd_(self, *xs, **sample):
|
|
120
|
+
'''Accumulate and return the current count.
|
|
121
|
+
|
|
122
|
+
@see: Method C{fadd}.
|
|
123
|
+
'''
|
|
124
|
+
return self.fadd(xs, **sample)
|
|
125
|
+
|
|
126
|
+
def fmean(self, xs=None):
|
|
127
|
+
'''Accumulate and return the current mean.
|
|
128
|
+
|
|
129
|
+
@kwarg xs: Iterable with additional values (C{Scalar}s).
|
|
130
|
+
|
|
131
|
+
@return: Current, running mean (C{float}).
|
|
132
|
+
|
|
133
|
+
@see: Method C{fadd}.
|
|
134
|
+
'''
|
|
135
|
+
if xs:
|
|
136
|
+
self.fadd(xs)
|
|
137
|
+
return self._M1.fsum()
|
|
138
|
+
|
|
139
|
+
def fmean_(self, *xs):
|
|
140
|
+
'''Accumulate and return the current mean.
|
|
141
|
+
|
|
142
|
+
@see: Method C{fmean}.
|
|
143
|
+
'''
|
|
144
|
+
return self.fmean(xs)
|
|
145
|
+
|
|
146
|
+
def fstdev(self, xs=None, sample=False):
|
|
147
|
+
'''Accumulate and return the current standard deviation.
|
|
148
|
+
|
|
149
|
+
@kwarg xs: Iterable with additional values (C{Scalar}).
|
|
150
|
+
@kwarg sample: Return the I{sample} instead of the entire
|
|
151
|
+
I{population} standard deviation (C{bool}).
|
|
152
|
+
|
|
153
|
+
@return: Current, running (sample) standard deviation (C{float}).
|
|
154
|
+
|
|
155
|
+
@see: Method C{fadd}.
|
|
156
|
+
'''
|
|
157
|
+
v = self.fvariance(xs, sample=sample)
|
|
158
|
+
return sqrt(v) if v > 0 else _0_0
|
|
159
|
+
|
|
160
|
+
def fstdev_(self, *xs, **sample):
|
|
161
|
+
'''Accumulate and return the current standard deviation.
|
|
162
|
+
|
|
163
|
+
@see: Method C{fstdev}.
|
|
164
|
+
'''
|
|
165
|
+
return self.fstdev(xs, **sample)
|
|
166
|
+
|
|
167
|
+
def fvariance(self, xs=None, sample=False):
|
|
168
|
+
'''Accumulate and return the current variance.
|
|
169
|
+
|
|
170
|
+
@kwarg xs: Iterable with additional values (C{Scalar}s).
|
|
171
|
+
@kwarg sample: Return the I{sample} instead of the entire
|
|
172
|
+
I{population} variance (C{bool}).
|
|
173
|
+
|
|
174
|
+
@return: Current, running (sample) variance (C{float}).
|
|
175
|
+
|
|
176
|
+
@see: Method C{fadd}.
|
|
177
|
+
'''
|
|
178
|
+
n = self.fadd(xs, sample=sample)
|
|
179
|
+
return float(self._M2 / float(n)) if n > 0 else _0_0
|
|
180
|
+
|
|
181
|
+
def fvariance_(self, *xs, **sample):
|
|
182
|
+
'''Accumulate and return the current variance.
|
|
183
|
+
|
|
184
|
+
@see: Method C{fvariance}.
|
|
185
|
+
'''
|
|
186
|
+
return self.fvariance(xs, **sample)
|
|
187
|
+
|
|
188
|
+
def _iadd_other(self, other):
|
|
189
|
+
'''(INTERNAL) Add Scalar or Scalars.
|
|
190
|
+
'''
|
|
191
|
+
if isinstance(other, _Scalar):
|
|
192
|
+
self.fadd_(other)
|
|
193
|
+
else:
|
|
194
|
+
try:
|
|
195
|
+
if not islistuple(other):
|
|
196
|
+
raise TypeError(_SPACE_(_invalid_, _other_))
|
|
197
|
+
self.fadd(other)
|
|
198
|
+
except Exception as x:
|
|
199
|
+
raise _xError(x, _SPACE_(self, _iadd_op_, repr(other)))
|
|
200
|
+
|
|
201
|
+
@property_RO
|
|
202
|
+
def _M1(self):
|
|
203
|
+
'''(INTERNAL) get the 1st Moment accumulator.'''
|
|
204
|
+
return self._Ms[0]
|
|
205
|
+
|
|
206
|
+
@property_RO
|
|
207
|
+
def _M2(self):
|
|
208
|
+
'''(INTERNAL) get the 2nd Moment accumulator.'''
|
|
209
|
+
return self._Ms[1]
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class Fcook(_FstatsBase):
|
|
213
|
+
'''U{Cook<https://www.JohnDCook.com/blog/skewness_kurtosis>}'s
|
|
214
|
+
C{RunningStats} computing the running mean, median and
|
|
215
|
+
(sample) kurtosis, skewness, variance, standard deviation
|
|
216
|
+
and Jarque-Bera normality.
|
|
217
|
+
|
|
218
|
+
@see: L{Fwelford} and U{Higher-order statistics<https://
|
|
219
|
+
WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.
|
|
220
|
+
'''
|
|
221
|
+
def __init__(self, xs=None, name=NN):
|
|
222
|
+
'''New L{Fcook} stats accumulator.
|
|
223
|
+
|
|
224
|
+
@kwarg xs: Iterable with initial values (C{Scalar}s).
|
|
225
|
+
@kwarg name: Optional name (C{str}).
|
|
226
|
+
|
|
227
|
+
@see: Method L{Fcook.fadd}.
|
|
228
|
+
'''
|
|
229
|
+
self._Ms = tuple(Fsum() for _ in range(4)) # 1st, 2nd ... Moment
|
|
230
|
+
if name:
|
|
231
|
+
self.name = name
|
|
232
|
+
if xs:
|
|
233
|
+
self.fadd(xs)
|
|
234
|
+
|
|
235
|
+
def __iadd__(self, other):
|
|
236
|
+
'''Add B{C{other}} to this L{Fcook} instance.
|
|
237
|
+
|
|
238
|
+
@arg other: An L{Fcook} instance or C{Scalar}s, meaning
|
|
239
|
+
one or more C{scalar} or L{Fsum} instances.
|
|
240
|
+
|
|
241
|
+
@return: This instance, updated (L{Fcook}).
|
|
242
|
+
|
|
243
|
+
@raise TypeError: Invalid B{C{other}} type.
|
|
244
|
+
|
|
245
|
+
@raise ValueError: Invalid B{C{other}}.
|
|
246
|
+
|
|
247
|
+
@see: Method L{Fcook.fadd}.
|
|
248
|
+
'''
|
|
249
|
+
if isinstance(other, Fcook):
|
|
250
|
+
nb = len(other)
|
|
251
|
+
if nb > 0:
|
|
252
|
+
na = len(self)
|
|
253
|
+
if na > 0:
|
|
254
|
+
A1, A2, A3, A4 = self._Ms
|
|
255
|
+
B1, B2, B3, B4 = other._Ms
|
|
256
|
+
|
|
257
|
+
n = na + nb
|
|
258
|
+
n_ = float(n)
|
|
259
|
+
D = A1 - B1 # b1 - a1
|
|
260
|
+
Dn = D / n_
|
|
261
|
+
Dn2 = Dn**2 # d**2 / n**2
|
|
262
|
+
nab = na * nb
|
|
263
|
+
Dn3 = Dn2 * (D * nab)
|
|
264
|
+
|
|
265
|
+
na2 = na**2
|
|
266
|
+
nb2 = nb**2
|
|
267
|
+
A4 += B4
|
|
268
|
+
A4 += (B3 * na - (A3 * nb)) * (Dn * _4_0)
|
|
269
|
+
A4 += (B2 * na2 + (A2 * nb2)) * (Dn2 * _6_0)
|
|
270
|
+
A4 += (Dn * Dn3) * (na2 - nab + nb2) # d**4 / n**3
|
|
271
|
+
|
|
272
|
+
A3 += B3
|
|
273
|
+
A3 += (A2 * na - (B2 * nb)) * (Dn * _3_0)
|
|
274
|
+
A3 += Dn3 * (na - nb)
|
|
275
|
+
|
|
276
|
+
A2 += B2
|
|
277
|
+
A2 += Dn2 * (nab / n_)
|
|
278
|
+
|
|
279
|
+
B1n = B1 * nb # if other is self
|
|
280
|
+
A1 *= na
|
|
281
|
+
A1 += B1n
|
|
282
|
+
A1 *= 1 / n_ # /= chokes PyChecker
|
|
283
|
+
|
|
284
|
+
# self._Ms = A1, A2, A3, A4
|
|
285
|
+
self._n = n
|
|
286
|
+
else:
|
|
287
|
+
self._copy(self, other)
|
|
288
|
+
else:
|
|
289
|
+
self._iadd_other(other)
|
|
290
|
+
return self
|
|
291
|
+
|
|
292
|
+
def fadd(self, xs, sample=False):
|
|
293
|
+
'''Accumulate and return the current count.
|
|
294
|
+
|
|
295
|
+
@arg xs: Iterable with additional values (C{Scalar}s,
|
|
296
|
+
meaning C{scalar} or L{Fsum} instances).
|
|
297
|
+
@kwarg sample: Return the I{sample} instead of the entire
|
|
298
|
+
I{population} count (C{bool}).
|
|
299
|
+
|
|
300
|
+
@return: Current, running (sample) count (C{int}).
|
|
301
|
+
|
|
302
|
+
@raise OverflowError: Partial C{2sum} overflow.
|
|
303
|
+
|
|
304
|
+
@raise TypeError: Non-scalar B{C{xs}} value.
|
|
305
|
+
|
|
306
|
+
@raise ValueError: Invalid or non-finite B{C{xs}} value.
|
|
307
|
+
|
|
308
|
+
@see: U{online_kurtosis<https://WikiPedia.org/wiki/
|
|
309
|
+
Algorithms_for_calculating_variance>}.
|
|
310
|
+
'''
|
|
311
|
+
n = self._n
|
|
312
|
+
if xs:
|
|
313
|
+
M1, M2, M3, M4 = self._Ms
|
|
314
|
+
for x in _2Floats(xs):
|
|
315
|
+
n1 = n
|
|
316
|
+
n += 1
|
|
317
|
+
D = x - M1
|
|
318
|
+
Dn = D / n
|
|
319
|
+
if Dn:
|
|
320
|
+
Dn2 = Dn**2
|
|
321
|
+
if n1 > 1:
|
|
322
|
+
T1 = D * (Dn * n1)
|
|
323
|
+
T2 = T1 * (Dn * (n1 - 1))
|
|
324
|
+
T3 = T1 * (Dn2 * (n**2 - 3 * n1))
|
|
325
|
+
elif n1 > 0: # n1 == 1, n == 2
|
|
326
|
+
T1 = D * Dn
|
|
327
|
+
T2 = _0_0
|
|
328
|
+
T3 = T1 * Dn2
|
|
329
|
+
else:
|
|
330
|
+
T1 = T2 = T3 = _0_0
|
|
331
|
+
M4 += T3
|
|
332
|
+
M4 -= M3 * (Dn * _4_0)
|
|
333
|
+
M4 += M2 * (Dn2 * _6_0)
|
|
334
|
+
|
|
335
|
+
M3 += T2
|
|
336
|
+
M3 -= M2 * (Dn * _3_0)
|
|
337
|
+
|
|
338
|
+
M2 += T1
|
|
339
|
+
M1 += Dn
|
|
340
|
+
# self._Ms = M1, M2, M3, M4
|
|
341
|
+
self._n = n
|
|
342
|
+
return _sampled(n, sample)
|
|
343
|
+
|
|
344
|
+
def fjb(self, xs=None, sample=True, excess=True):
|
|
345
|
+
'''Accumulate and compute the current U{Jarque-Bera
|
|
346
|
+
<https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
|
|
347
|
+
|
|
348
|
+
@kwarg xs: Iterable with additional values (C{Scalar}s).
|
|
349
|
+
@kwarg sample: Return the I{sample} normality (C{bool}), default.
|
|
350
|
+
@kwarg excess: Return the I{excess} kurtosis (C{bool}), default.
|
|
351
|
+
|
|
352
|
+
@return: Current, running (sample) Jarque-Bera normality (C{float}).
|
|
353
|
+
|
|
354
|
+
@see: Method L{Fcook.fadd}.
|
|
355
|
+
'''
|
|
356
|
+
n = self.fadd(xs, sample=sample)
|
|
357
|
+
k = self.fkurtosis(sample=sample, excess=excess) / _2_0
|
|
358
|
+
s = self.fskewness(sample=sample)
|
|
359
|
+
return n * hypot2(k, s) / _6_0
|
|
360
|
+
|
|
361
|
+
def fjb_(self, *xs, **sample_excess):
|
|
362
|
+
'''Accumulate and compute the current U{Jarque-Bera
|
|
363
|
+
<https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
|
|
364
|
+
|
|
365
|
+
@see: Method L{Fcook.fjb}.
|
|
366
|
+
'''
|
|
367
|
+
return self.fjb(xs, **sample_excess)
|
|
368
|
+
|
|
369
|
+
def fkurtosis(self, xs=None, sample=False, excess=True):
|
|
370
|
+
'''Accumulate and return the current kurtosis.
|
|
371
|
+
|
|
372
|
+
@kwarg xs: Iterable with additional values (C{Scalar}s).
|
|
373
|
+
@kwarg sample: Return the I{sample} instead of the entire
|
|
374
|
+
I{population} kurtosis (C{bool}).
|
|
375
|
+
@kwarg excess: Return the I{excess} kurtosis (C{bool}), default.
|
|
376
|
+
|
|
377
|
+
@return: Current, running (sample) kurtosis or I{excess} kurtosis (C{float}).
|
|
378
|
+
|
|
379
|
+
@see: U{Kurtosis Formula<https://www.Macroption.com/kurtosis-formula>}
|
|
380
|
+
and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.
|
|
381
|
+
|
|
382
|
+
@see: Method L{Fcook.fadd}.
|
|
383
|
+
'''
|
|
384
|
+
k, n = _0_0, self.fadd(xs, sample=sample)
|
|
385
|
+
if n > 0:
|
|
386
|
+
_, M2, _, M4 = self._Ms
|
|
387
|
+
m2 = float(M2 * M2)
|
|
388
|
+
if m2:
|
|
389
|
+
K, x = (M4 * (n / m2)), _3_0
|
|
390
|
+
if sample and 2 < n < len(self):
|
|
391
|
+
d = float((n - 1) * (n - 2))
|
|
392
|
+
K *= (n + 1) * (n + 2) / d
|
|
393
|
+
x *= n**2 / d
|
|
394
|
+
if excess:
|
|
395
|
+
K -= x
|
|
396
|
+
k = K.fsum()
|
|
397
|
+
return k
|
|
398
|
+
|
|
399
|
+
def fkurtosis_(self, *xs, **sample_excess):
|
|
400
|
+
'''Accumulate and return the current kurtosis.
|
|
401
|
+
|
|
402
|
+
@see: Method L{Fcook.fkurtosis}.
|
|
403
|
+
'''
|
|
404
|
+
return self.fkurtosis(xs, **sample_excess)
|
|
405
|
+
|
|
406
|
+
def fmedian(self, xs=None):
|
|
407
|
+
'''Accumulate and return the current median.
|
|
408
|
+
|
|
409
|
+
@kwarg xs: Iterable with additional values (C{Scalar}s).
|
|
410
|
+
|
|
411
|
+
@return: Current, running median (C{float}).
|
|
412
|
+
|
|
413
|
+
@see: U{Pearson's Skewness Coefficients<https://MathWorld.Wolfram.com/
|
|
414
|
+
PearsonsSkewnessCoefficients.html>}, U{Skewness & Kurtosis Simplified
|
|
415
|
+
https://TowardsDataScience.com/skewness-kurtosis-simplified-1338e094fc85>}
|
|
416
|
+
and method L{Fcook.fadd}.
|
|
417
|
+
'''
|
|
418
|
+
# skewness = 3 * (mean - median) / stdev, i.e.
|
|
419
|
+
# median = mean - skewness * stdef / 3
|
|
420
|
+
m = float(self._M1) if xs is None else self.fmean(xs)
|
|
421
|
+
return m - self.fskewness() * self.fstdev() / _3_0
|
|
422
|
+
|
|
423
|
+
def fmedian_(self, *xs):
|
|
424
|
+
'''Accumulate and return the current median.
|
|
425
|
+
|
|
426
|
+
@see: Method L{Fcook.fmedian}.
|
|
427
|
+
'''
|
|
428
|
+
return self.fmedian(xs)
|
|
429
|
+
|
|
430
|
+
def fskewness(self, xs=None, sample=False):
|
|
431
|
+
'''Accumulate and return the current skewness.
|
|
432
|
+
|
|
433
|
+
@kwarg xs: Iterable with additional values (C{Scalar}s).
|
|
434
|
+
@kwarg sample: Return the I{sample} instead of the entire
|
|
435
|
+
I{population} skewness (C{bool}).
|
|
436
|
+
|
|
437
|
+
@return: Current, running (sample) skewness (C{float}).
|
|
438
|
+
|
|
439
|
+
@see: U{Skewness Formula<https://www.Macroption.com/skewness-formula/>}
|
|
440
|
+
and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.
|
|
441
|
+
|
|
442
|
+
@see: Method L{Fcook.fadd}.
|
|
443
|
+
'''
|
|
444
|
+
s, n = _0_0, self.fadd(xs, sample=sample)
|
|
445
|
+
if n > 0:
|
|
446
|
+
_, M2, M3, _ = self._Ms
|
|
447
|
+
m = float(M2**3)
|
|
448
|
+
if m > 0:
|
|
449
|
+
S = M3 * sqrt(float(n) / m)
|
|
450
|
+
if sample and 1 < n < len(self):
|
|
451
|
+
S *= (n + 1) / float(n - 1)
|
|
452
|
+
s = S.fsum()
|
|
453
|
+
return s
|
|
454
|
+
|
|
455
|
+
def fskewness_(self, *xs, **sample):
|
|
456
|
+
'''Accumulate and return the current skewness.
|
|
457
|
+
|
|
458
|
+
@see: Method L{Fcook.fskewness}.
|
|
459
|
+
'''
|
|
460
|
+
return self.fskewness(xs, **sample)
|
|
461
|
+
|
|
462
|
+
def toFwelford(self, name=NN):
|
|
463
|
+
'''Return an L{Fwelford} equivalent.
|
|
464
|
+
'''
|
|
465
|
+
f = Fwelford(name=name or self.name)
|
|
466
|
+
f._Ms = self._M1.fcopy(), self._M2.fcopy() # deep=False
|
|
467
|
+
f._n = self._n
|
|
468
|
+
return f
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
class Fwelford(_FstatsBase):
|
|
472
|
+
'''U{Welford<https://WikiPedia.org/wiki/Algorithms_for_calculating_variance>}'s
|
|
473
|
+
accumulator computing the running mean, (sample) variance and standard deviation.
|
|
474
|
+
|
|
475
|
+
@see: U{Cook<https://www.JohnDCook.com/blog/standard_deviation/>} and L{Fcook}.
|
|
476
|
+
'''
|
|
477
|
+
def __init__(self, xs=None, name=NN):
|
|
478
|
+
'''New L{Fwelford} stats accumulator.
|
|
479
|
+
|
|
480
|
+
@kwarg xs: Iterable with initial values (C{Scalar}s).
|
|
481
|
+
@kwarg name: Optional name (C{str}).
|
|
482
|
+
|
|
483
|
+
@see: Method L{Fwelford.fadd}.
|
|
484
|
+
'''
|
|
485
|
+
self._Ms = Fsum(), Fsum() # 1st and 2nd Moment
|
|
486
|
+
if name:
|
|
487
|
+
self.name = name
|
|
488
|
+
if xs:
|
|
489
|
+
self.fadd(xs)
|
|
490
|
+
|
|
491
|
+
def __iadd__(self, other):
|
|
492
|
+
'''Add B{C{other}} to this L{Fwelford} instance.
|
|
493
|
+
|
|
494
|
+
@arg other: An L{Fwelford} or L{Fcook} instance or C{Scalar}s,
|
|
495
|
+
meaning one or more C{scalar} or L{Fsum} instances.
|
|
496
|
+
|
|
497
|
+
@return: This instance, updated (L{Fwelford}).
|
|
498
|
+
|
|
499
|
+
@raise TypeError: Invalid B{C{other}} type.
|
|
500
|
+
|
|
501
|
+
@raise ValueError: Invalid B{C{other}}.
|
|
502
|
+
|
|
503
|
+
@see: Method L{Fwelford.fadd} and U{Parallel algorithm<https//
|
|
504
|
+
WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.
|
|
505
|
+
'''
|
|
506
|
+
if isinstance(other, Fwelford):
|
|
507
|
+
nb = len(other)
|
|
508
|
+
if nb > 0:
|
|
509
|
+
na = len(self)
|
|
510
|
+
if na > 0:
|
|
511
|
+
M, S = self._Ms
|
|
512
|
+
M_, S_ = other._Ms
|
|
513
|
+
|
|
514
|
+
n = na + nb
|
|
515
|
+
n_ = float(n)
|
|
516
|
+
|
|
517
|
+
D = M_ - M
|
|
518
|
+
D *= D # D**2
|
|
519
|
+
D *= na * nb / n_
|
|
520
|
+
S += D
|
|
521
|
+
S += S_
|
|
522
|
+
|
|
523
|
+
Mn = M_ * nb # if other is self
|
|
524
|
+
M *= na
|
|
525
|
+
M += Mn
|
|
526
|
+
M *= 1 / n_ # /= chokes PyChecker
|
|
527
|
+
|
|
528
|
+
# self._Ms = M, S
|
|
529
|
+
self._n = n
|
|
530
|
+
else:
|
|
531
|
+
self._copy(self, other)
|
|
532
|
+
|
|
533
|
+
elif isinstance(other, Fcook):
|
|
534
|
+
self += other.toFwelford()
|
|
535
|
+
else:
|
|
536
|
+
self._iadd_other(other)
|
|
537
|
+
return self
|
|
538
|
+
|
|
539
|
+
def fadd(self, xs, sample=False):
|
|
540
|
+
'''Accumulate and return the current count.
|
|
541
|
+
|
|
542
|
+
@arg xs: Iterable with additional values (C{Scalar}s,
|
|
543
|
+
meaning C{scalar} or L{Fsum} instances).
|
|
544
|
+
@kwarg sample: Return the I{sample} instead of the entire
|
|
545
|
+
I{population} count (C{bool}).
|
|
546
|
+
|
|
547
|
+
@return: Current, running (sample) count (C{int}).
|
|
548
|
+
|
|
549
|
+
@raise OverflowError: Partial C{2sum} overflow.
|
|
550
|
+
|
|
551
|
+
@raise TypeError: Non-scalar B{C{xs}} value.
|
|
552
|
+
|
|
553
|
+
@raise ValueError: Invalid or non-finite B{C{xs}} value.
|
|
554
|
+
'''
|
|
555
|
+
n = self._n
|
|
556
|
+
if xs:
|
|
557
|
+
M, S = self._Ms
|
|
558
|
+
for x in _2Floats(xs):
|
|
559
|
+
n += 1
|
|
560
|
+
D = x - M
|
|
561
|
+
M += D / n
|
|
562
|
+
D *= x - M
|
|
563
|
+
S += D
|
|
564
|
+
# self._Ms = M, S
|
|
565
|
+
self._n = n
|
|
566
|
+
return _sampled(n, sample)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
class Flinear(_FstatsNamed):
|
|
570
|
+
'''U{Cook<https://www.JohnDCook.com/blog/running_regression>}'s
|
|
571
|
+
C{RunningRegression} computing the running slope, intercept
|
|
572
|
+
and correlation of a linear regression.
|
|
573
|
+
'''
|
|
574
|
+
def __init__(self, xs=None, ys=None, Fstats=Fwelford, name=NN):
|
|
575
|
+
'''New L{Flinear} regression accumulator.
|
|
576
|
+
|
|
577
|
+
@kwarg xs: Iterable with initial C{x} values (C{Scalar}s).
|
|
578
|
+
@kwarg ys: Iterable with initial C{y} values (C{Scalar}s).
|
|
579
|
+
@kwarg Fstats: Stats class for C{x} and C{y} values (L{Fcook}
|
|
580
|
+
or L{Fwelford}).
|
|
581
|
+
@kwarg name: Optional name (C{str}).
|
|
582
|
+
|
|
583
|
+
@raise TypeError: Invalid B{C{Fs}}, not L{Fcook} or
|
|
584
|
+
L{Fwelford}.
|
|
585
|
+
@see: Method L{Flinear.fadd}.
|
|
586
|
+
'''
|
|
587
|
+
_xsubclassof(Fcook, Fwelford, Fstats=Fstats)
|
|
588
|
+
if name:
|
|
589
|
+
self.name = name
|
|
590
|
+
|
|
591
|
+
self._S = Fsum(name=name)
|
|
592
|
+
self._X = Fstats(name=name)
|
|
593
|
+
self._Y = Fstats(name=name)
|
|
594
|
+
if xs and ys:
|
|
595
|
+
self.fadd(xs, ys)
|
|
596
|
+
|
|
597
|
+
def __iadd__(self, other):
|
|
598
|
+
'''Add B{C{other}} to this instance.
|
|
599
|
+
|
|
600
|
+
@arg other: An L{Flinear} instance or C{Scalar} pairs,
|
|
601
|
+
meaning C{scalar} or L{Fsum} instances.
|
|
602
|
+
|
|
603
|
+
@return: This instance, updated (L{Flinear}).
|
|
604
|
+
|
|
605
|
+
@raise TypeError: Invalid B{C{other}} or the B{C{other}}
|
|
606
|
+
and these C{x} and C{y} accumulators
|
|
607
|
+
are not compatible.
|
|
608
|
+
|
|
609
|
+
@raise ValueError: Invalid or odd-length B{C{other}}.
|
|
610
|
+
|
|
611
|
+
@see: Method L{Flinear.fadd_}.
|
|
612
|
+
'''
|
|
613
|
+
if isinstance(other, Flinear):
|
|
614
|
+
if len(other) > 0:
|
|
615
|
+
if len(self) > 0:
|
|
616
|
+
n = other._n
|
|
617
|
+
S = other._S
|
|
618
|
+
X = other._X
|
|
619
|
+
Y = other._Y
|
|
620
|
+
D = (X._M1 - self._X._M1) * \
|
|
621
|
+
(Y._M1 - self._Y._M1) * \
|
|
622
|
+
(n * self._n / float(n + self._n))
|
|
623
|
+
self._n += n
|
|
624
|
+
self._S += S + D
|
|
625
|
+
self._X += X
|
|
626
|
+
self._Y += Y
|
|
627
|
+
else:
|
|
628
|
+
self._copy(self, other)
|
|
629
|
+
else:
|
|
630
|
+
try:
|
|
631
|
+
if not islistuple(other):
|
|
632
|
+
raise TypeError(_SPACE_(_invalid_, _other_))
|
|
633
|
+
elif isodd(len(other)):
|
|
634
|
+
raise ValueError(Fmt.PAREN(isodd=Fmt.PAREN(len=_other_)))
|
|
635
|
+
self.fadd_(*other)
|
|
636
|
+
except Exception as x:
|
|
637
|
+
raise _xError(x, _SPACE_(self, _iadd_op_, repr(other)))
|
|
638
|
+
return self
|
|
639
|
+
|
|
640
|
+
def _copy(self, c, s):
|
|
641
|
+
'''(INTERNAL) Copy C{B{c} = B{s}}.
|
|
642
|
+
'''
|
|
643
|
+
_xinstanceof(Flinear, c=c, s=s)
|
|
644
|
+
c._n = s._n
|
|
645
|
+
c._S = s._S.fcopy(deep=False)
|
|
646
|
+
c._X = s._X.fcopy(deep=False)
|
|
647
|
+
c._Y = s._Y.fcopy(deep=False)
|
|
648
|
+
return c
|
|
649
|
+
|
|
650
|
+
def fadd(self, xs, ys, sample=False):
|
|
651
|
+
'''Accumulate and return the current count.
|
|
652
|
+
|
|
653
|
+
@arg xs: Iterable with additional C{x} values (C{Scalar}s),
|
|
654
|
+
meaning C{scalar} or L{Fsum} instances).
|
|
655
|
+
@arg ys: Iterable with additional C{y} values (C{Scalar}s,
|
|
656
|
+
meaning C{scalar} or L{Fsum} instances).
|
|
657
|
+
@kwarg sample: Return the I{sample} instead of the entire
|
|
658
|
+
I{population} count (C{bool}).
|
|
659
|
+
|
|
660
|
+
@return: Current, running (sample) count (C{int}).
|
|
661
|
+
|
|
662
|
+
@raise OverflowError: Partial C{2sum} overflow.
|
|
663
|
+
|
|
664
|
+
@raise TypeError: Non-scalar B{C{xs}} or B{C{ys}} value.
|
|
665
|
+
|
|
666
|
+
@raise ValueError: Invalid or non-finite B{C{xs}} or B{C{ys}} value.
|
|
667
|
+
'''
|
|
668
|
+
n = self._n
|
|
669
|
+
if xs and ys:
|
|
670
|
+
S = self._S
|
|
671
|
+
X = self._X
|
|
672
|
+
Y = self._Y
|
|
673
|
+
for x, y in _zip(_2Floats(xs), _2Floats(ys, ys=True)): # strict=True
|
|
674
|
+
n1 = n
|
|
675
|
+
n += 1
|
|
676
|
+
if n1 > 0:
|
|
677
|
+
S += (X._M1 - x) * (Y._M1 - y) * (n1 / float(n))
|
|
678
|
+
X += x
|
|
679
|
+
Y += y
|
|
680
|
+
self._n = n
|
|
681
|
+
return _sampled(n, sample)
|
|
682
|
+
|
|
683
|
+
def fadd_(self, *x_ys, **sample):
|
|
684
|
+
'''Accumulate and return the current count.
|
|
685
|
+
|
|
686
|
+
@arg x_ys: Individual, alternating C{x, y, x, y, ...}
|
|
687
|
+
positional values (C{Scalar}s).
|
|
688
|
+
|
|
689
|
+
@see: Method C{Flinear.fadd}.
|
|
690
|
+
'''
|
|
691
|
+
return self.fadd(x_ys[0::2], x_ys[1::2], **sample)
|
|
692
|
+
|
|
693
|
+
def fcorrelation(self, sample=False):
|
|
694
|
+
'''Return the current, running (sample) correlation (C{float}).
|
|
695
|
+
|
|
696
|
+
@kwarg sample: Return the I{sample} instead of the entire
|
|
697
|
+
I{population} correlation (C{bool}).
|
|
698
|
+
'''
|
|
699
|
+
return self._sampled(self.x.fstdev(sample=sample) *
|
|
700
|
+
self.y.fstdev(sample=sample), sample)
|
|
701
|
+
|
|
702
|
+
def fintercept(self, sample=False):
|
|
703
|
+
'''Return the current, running (sample) intercept (C{float}).
|
|
704
|
+
|
|
705
|
+
@kwarg sample: Return the I{sample} instead of the entire
|
|
706
|
+
I{population} intercept (C{bool}).
|
|
707
|
+
'''
|
|
708
|
+
return float(self.y._M1 -
|
|
709
|
+
(self.x._M1 * self.fslope(sample=sample)))
|
|
710
|
+
|
|
711
|
+
def fslope(self, sample=False):
|
|
712
|
+
'''Return the current, running (sample) slope (C{float}).
|
|
713
|
+
|
|
714
|
+
@kwarg sample: Return the I{sample} instead of the entire
|
|
715
|
+
I{population} slope (C{bool}).
|
|
716
|
+
'''
|
|
717
|
+
return self._sampled(self.x.fvariance(sample=sample), sample)
|
|
718
|
+
|
|
719
|
+
def _sampled(self, t, sample):
|
|
720
|
+
'''(INTERNAL) Compute the sampled or entire population result.
|
|
721
|
+
'''
|
|
722
|
+
t *= float(_sampled(self._n, sample))
|
|
723
|
+
return float(self._S / t) if t else _0_0
|
|
724
|
+
|
|
725
|
+
@property_RO
|
|
726
|
+
def x(self):
|
|
727
|
+
'''Get the C{x} accumulator (L{Fcook} or L{Fwelford}).
|
|
728
|
+
'''
|
|
729
|
+
return self._X
|
|
730
|
+
|
|
731
|
+
@property_RO
|
|
732
|
+
def y(self):
|
|
733
|
+
'''Get the C{y} accumulator (L{Fcook} or L{Fwelford}).
|
|
734
|
+
'''
|
|
735
|
+
return self._Y
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
__all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)
|
|
739
|
+
|
|
740
|
+
# **) MIT License
|
|
741
|
+
#
|
|
742
|
+
# Copyright (C) 2021-2024 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
743
|
+
#
|
|
744
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
745
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
746
|
+
# to deal in the Software without restriction, including without limitation
|
|
747
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
748
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
749
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
750
|
+
#
|
|
751
|
+
# The above copyright notice and this permission notice shall be included
|
|
752
|
+
# in all copies or substantial portions of the Software.
|
|
753
|
+
#
|
|
754
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
755
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
756
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
757
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
758
|
+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
759
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
760
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|