pygeodesy 24.5.2__py2.py3-none-any.whl → 24.5.8__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/fstats.py CHANGED
@@ -7,43 +7,37 @@ L{pygeodesy.Fsum}, precision floating point summation.
7
7
  # make sure int/int division yields float quotient, see .basics
8
8
  from __future__ import division as _; del _ # PYCHOK semicolon
9
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, _iadd_op_, Fmt
16
- from pygeodesy.interns import NN, _invalid_, _other_, _SPACE_
10
+ from pygeodesy.basics import isscalar, isodd, _xinstanceof, \
11
+ _xiterable, _xsubclassof, _zip
12
+ from pygeodesy.constants import _0_0, _1_0, _2_0, _3_0, _4_0, _6_0
13
+ from pygeodesy.errors import _AssertionError, _ValueError, _xError
14
+ from pygeodesy.fmath import Fsqrt
15
+ from pygeodesy.fsums import _2finite, _Float, Fsum, _iadd_op_, \
16
+ _isAn, _isFsumTuple, _Tuple, Fmt
17
+ from pygeodesy.interns import NN, _odd_, _SPACE_
17
18
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
18
19
  from pygeodesy.named import _Named, _NotImplemented, property_RO
19
20
  # from pygeodesy.props import property_RO # from .named
20
21
  # from pygeodesy.streprs import Fmt # from .fsums
21
22
 
22
- # from math import sqrt # from .fmath
23
-
24
23
  __all__ = _ALL_LAZY.fstats
25
- __version__ = '24.04.26'
26
-
27
- _Floats = Fsum, float
28
- _Scalar = _Floats + (int,) # XXX basics._Ints is ABCMeta in Python 2
29
- try:
30
- _Scalar += (long,)
31
- except NameError: # Python 3+
32
- pass
24
+ __version__ = '24.05.08'
33
25
 
34
26
 
35
- def _2Floats(xs, ys=False):
27
+ def _2Floats(**xs):
36
28
  '''(INTERNAL) Yield each value as C{float} or L{Fsum}.
37
29
  '''
38
- if ys:
39
- def _2f(i, x):
40
- return _2float(index=i, ys=x)
41
- else:
42
- def _2f(i, x): # PYCHOK redef
43
- return _2float(index=i, xs=x)
30
+ try:
31
+ name, xs = xs.popitem()
32
+ except Exception as X:
33
+ raise _AssertionError(xs=xs, cause=X)
44
34
 
45
- for i, x in enumerate(xs): # don't unravel Fsums
46
- yield x if isinstance(x, _Floats) else _2f(i, x)
35
+ try:
36
+ i, x = 0, None
37
+ for i, x in enumerate(xs): # don't unravel Fsums
38
+ yield x._Fsum if _isFsumTuple(x) else _2finite(x)
39
+ except Exception as X:
40
+ raise _xError(X, Fmt.INDEX(name, i), x)
47
41
 
48
42
 
49
43
  def _sampled(n, sample):
@@ -58,9 +52,11 @@ class _FstatsNamed(_Named):
58
52
  _n = 0
59
53
 
60
54
  def __add__(self, other):
61
- '''Sum of this and a scalar, an L{Fsum} or an other instance.
55
+ '''Sum of this and an other instance or a C{scalar} or an
56
+ L{Fsum}, L{Fsum2Tuple} or
57
+ .
62
58
  '''
63
- f = self.fcopy(name=self.__add__.__name__) # PYCHOK expected
59
+ f = self.copy(name=self.__add__.__name__) # PYCHOK expected
64
60
  f += other
65
61
  return f
66
62
 
@@ -73,7 +69,7 @@ class _FstatsNamed(_Named):
73
69
  return _NotImplemented(self)
74
70
 
75
71
  def __len__(self):
76
- '''Return the I{total} number of accumulated values (C{int}).
72
+ '''Return the I{total} number of accumulated C{Scalars} (C{int}).
77
73
  '''
78
74
  return self._n
79
75
 
@@ -86,16 +82,18 @@ class _FstatsNamed(_Named):
86
82
  return _NotImplemented(self, other)
87
83
 
88
84
  def __str__(self):
89
- return Fmt.SQUARE(self.named3, len(self))
85
+ n = self.name
86
+ n = _SPACE_(self.classname, n) if n else self.classname
87
+ return Fmt.SQUARE(n, len(self))
90
88
 
91
- def fcopy(self, deep=False, name=NN):
89
+ def copy(self, deep=False, name=NN):
92
90
  '''Copy this instance, C{shallow} or B{C{deep}}.
93
91
  '''
94
- n = name or self.fcopy.__name__
92
+ n = name or self.copy.__name__
95
93
  f = _Named.copy(self, deep=deep, name=n)
96
94
  return self._copy(f, self) # PYCHOK expected
97
95
 
98
- copy = fcopy
96
+ fcopy = copy # for backward compatibility
99
97
 
100
98
 
101
99
  class _FstatsBase(_FstatsNamed):
@@ -103,13 +101,13 @@ class _FstatsBase(_FstatsNamed):
103
101
  '''
104
102
  _Ms = ()
105
103
 
106
- def _copy(self, c, s):
104
+ def _copy(self, d, s):
107
105
  '''(INTERNAL) Copy C{B{c} = B{s}}.
108
106
  '''
109
- _xinstanceof(self.__class__, c=c, s=s)
110
- c._Ms = tuple(M.fcopy() for M in s._Ms) # deep=False
111
- c._n = s._n
112
- return c
107
+ _xinstanceof(self.__class__, d=d, s=s)
108
+ d._Ms = _Tuple(M.copy() for M in s._Ms) # deep=False
109
+ d._n = s._n
110
+ return d
113
111
 
114
112
  def fadd(self, xs, sample=False): # PYCHOK no cover
115
113
  '''I{Must be overloaded}.'''
@@ -118,84 +116,85 @@ class _FstatsBase(_FstatsNamed):
118
116
  def fadd_(self, *xs, **sample):
119
117
  '''Accumulate and return the current count.
120
118
 
121
- @see: Method C{fadd}.
119
+ @see: Method C{fadd} for further details.
122
120
  '''
123
121
  return self.fadd(xs, **sample)
124
122
 
125
123
  def fmean(self, xs=None):
126
124
  '''Accumulate and return the current mean.
127
125
 
128
- @kwarg xs: Iterable with additional values (C{Scalar}s).
126
+ @kwarg xs: Iterable of additional values (each C{scalar} or
127
+ an L{Fsum} or L{Fsum2Tuple} instance).
129
128
 
130
129
  @return: Current, running mean (C{float}).
131
130
 
132
131
  @see: Method C{fadd}.
133
132
  '''
134
- if xs:
135
- self.fadd(xs)
136
- return self._M1.fsum()
133
+ return _Float(self._Mean(xs))
137
134
 
138
135
  def fmean_(self, *xs):
139
136
  '''Accumulate and return the current mean.
140
137
 
141
- @see: Method C{fmean}.
138
+ @see: Method C{fmean} for further details.
142
139
  '''
143
140
  return self.fmean(xs)
144
141
 
145
- def fstdev(self, xs=None, sample=False):
142
+ def fstdev(self, xs=None, **sample):
146
143
  '''Accumulate and return the current standard deviation.
147
144
 
148
- @kwarg xs: Iterable with additional values (C{Scalar}).
149
- @kwarg sample: Return the I{sample} instead of the entire
150
- I{population} standard deviation (C{bool}).
145
+ @arg xs: Iterable of additional values (each C{scalar} or an
146
+ L{Fsum} or L{Fsum2Tuple} instance).
147
+ @kwarg sample: Use C{B{sample}=True} for the I{sample} deviation
148
+ instead of the I{population} deviation (C{bool}).
151
149
 
152
150
  @return: Current, running (sample) standard deviation (C{float}).
153
151
 
154
152
  @see: Method C{fadd}.
155
153
  '''
156
- v = self.fvariance(xs, sample=sample)
157
- return sqrt(v) if v > 0 else _0_0
154
+ return _Float(self._Stdev(xs, **sample))
158
155
 
159
156
  def fstdev_(self, *xs, **sample):
160
157
  '''Accumulate and return the current standard deviation.
161
158
 
162
- @see: Method C{fstdev}.
159
+ @see: Method C{fstdev} for further details.
163
160
  '''
164
161
  return self.fstdev(xs, **sample)
165
162
 
166
- def fvariance(self, xs=None, sample=False):
163
+ def fvariance(self, xs=None, **sample):
167
164
  '''Accumulate and return the current variance.
168
165
 
169
- @kwarg xs: Iterable with additional values (C{Scalar}s).
170
- @kwarg sample: Return the I{sample} instead of the entire
171
- I{population} variance (C{bool}).
166
+ @arg xs: Iterable of additional values (each C{scalar} or an
167
+ L{Fsum} or L{Fsum2Tuple} instance).
168
+ @kwarg sample: Use C{B{sample}=True} for the I{sample} variance
169
+ instead of the I{population} variance (C{bool}).
172
170
 
173
171
  @return: Current, running (sample) variance (C{float}).
174
172
 
175
173
  @see: Method C{fadd}.
176
174
  '''
177
- n = self.fadd(xs, sample=sample)
178
- return float(self._M2 / float(n)) if n > 0 else _0_0
175
+ return _Float(self._Variance(xs, **sample))
179
176
 
180
177
  def fvariance_(self, *xs, **sample):
181
178
  '''Accumulate and return the current variance.
182
179
 
183
- @see: Method C{fvariance}.
180
+ @see: Method C{fvariance} for further details.
184
181
  '''
185
182
  return self.fvariance(xs, **sample)
186
183
 
187
184
  def _iadd_other(self, other):
188
- '''(INTERNAL) Add Scalar or Scalars.
189
- '''
190
- if isinstance(other, _Scalar):
191
- self.fadd_(other)
192
- else:
193
- try:
194
- if not islistuple(other):
195
- raise TypeError(_SPACE_(_invalid_, _other_))
185
+ '''(INTERNAL) Add one or several values.
186
+ '''
187
+ try:
188
+ if _isFsumTuple(other):
189
+ self.fadd_(other._Fsum)
190
+ elif isscalar(other):
191
+ self.fadd_(_2finite(other))
192
+ else: # iterable?
193
+ _xiterable(other)
196
194
  self.fadd(other)
197
- except Exception as x:
198
- raise _xError(x, _SPACE_(self, _iadd_op_, repr(other)))
195
+ except Exception as x:
196
+ t = _SPACE_(self, _iadd_op_, repr(other))
197
+ raise _xError(x, t)
199
198
 
200
199
  @property_RO
201
200
  def _M1(self):
@@ -207,6 +206,25 @@ class _FstatsBase(_FstatsNamed):
207
206
  '''(INTERNAL) get the 2nd Moment accumulator.'''
208
207
  return self._Ms[1]
209
208
 
209
+ def _Mean(self, xs=None):
210
+ '''(INTERNAL) Return the current mean as L{Fsum}.
211
+ '''
212
+ if xs:
213
+ self.fadd(xs)
214
+ return self._M1 # .copy()
215
+
216
+ def _Stdev(self, xs=None, **sample):
217
+ '''(INTERNAL) Return the current (sample) standard deviation as L{Fsum}.
218
+ '''
219
+ V = self._Variance(xs, **sample)
220
+ return Fsqrt(V) if V > 0 else _0_0
221
+
222
+ def _Variance(self, xs=None, **sample):
223
+ '''(INTERNAL) Return the current (sample) variance as L{Fsum}.
224
+ '''
225
+ n = self.fadd(xs, **sample)
226
+ return (self._M2 / n) if n > 0 else _0_0
227
+
210
228
 
211
229
  class Fcook(_FstatsBase):
212
230
  '''U{Cook<https://www.JohnDCook.com/blog/skewness_kurtosis>}'s
@@ -220,12 +238,13 @@ class Fcook(_FstatsBase):
220
238
  def __init__(self, xs=None, name=NN):
221
239
  '''New L{Fcook} stats accumulator.
222
240
 
223
- @kwarg xs: Iterable with initial values (C{Scalar}s).
241
+ @arg xs: Iterable of additional values (each C{scalar} or
242
+ an L{Fsum} or L{Fsum2Tuple} instance).
224
243
  @kwarg name: Optional name (C{str}).
225
244
 
226
245
  @see: Method L{Fcook.fadd}.
227
246
  '''
228
- self._Ms = tuple(Fsum() for _ in range(4)) # 1st, 2nd ... Moment
247
+ self._Ms = _Tuple(Fsum() for _ in range(4)) # 1st, 2nd ... Moment
229
248
  if name:
230
249
  self.name = name
231
250
  if xs:
@@ -234,18 +253,19 @@ class Fcook(_FstatsBase):
234
253
  def __iadd__(self, other):
235
254
  '''Add B{C{other}} to this L{Fcook} instance.
236
255
 
237
- @arg other: An L{Fcook} instance or C{Scalar}s, meaning
238
- one or more C{scalar} or L{Fsum} instances.
256
+ @arg other: An L{Fcook} instance or value or iterable
257
+ of values (each C{scalar} or an L{Fsum}
258
+ or L{Fsum2Tuple} instance).
239
259
 
240
260
  @return: This instance, updated (L{Fcook}).
241
261
 
242
- @raise TypeError: Invalid B{C{other}} type.
262
+ @raise TypeError: Invalid B{C{other}}.
243
263
 
244
- @raise ValueError: Invalid B{C{other}}.
264
+ @raise ValueError: Invalid or non-finite B{C{other}}.
245
265
 
246
266
  @see: Method L{Fcook.fadd}.
247
267
  '''
248
- if isinstance(other, Fcook):
268
+ if _isAn(other, Fcook):
249
269
  nb = len(other)
250
270
  if nb > 0:
251
271
  na = len(self)
@@ -253,13 +273,13 @@ class Fcook(_FstatsBase):
253
273
  A1, A2, A3, A4 = self._Ms
254
274
  B1, B2, B3, B4 = other._Ms
255
275
 
256
- n = na + nb
257
- n_ = float(n)
258
- D = A1 - B1 # b1 - a1
259
- Dn = D / n_
260
- Dn2 = Dn**2 # d**2 / n**2
261
- nab = na * nb
262
- Dn3 = Dn2 * (D * nab)
276
+ n = na + nb
277
+ _n = _1_0 / n
278
+ D = A1 - B1 # b1 - a1
279
+ Dn = D * _n
280
+ Dn2 = Dn**2 # d**2 / n**2
281
+ nab = na * nb
282
+ Dn3 = Dn2 * (D * nab)
263
283
 
264
284
  na2 = na**2
265
285
  nb2 = nb**2
@@ -269,16 +289,16 @@ class Fcook(_FstatsBase):
269
289
  A4 += (Dn * Dn3) * (na2 - nab + nb2) # d**4 / n**3
270
290
 
271
291
  A3 += B3
272
- A3 += (A2 * na - (B2 * nb)) * (Dn * _3_0)
273
- A3 += Dn3 * (na - nb)
292
+ A3 += (A2 * na - (B2 * nb)) * (Dn * _3_0)
293
+ A3 += Dn3 * (na - nb)
274
294
 
275
295
  A2 += B2
276
- A2 += Dn2 * (nab / n_)
296
+ A2 += Dn2 * (nab * _n)
277
297
 
278
298
  B1n = B1 * nb # if other is self
279
299
  A1 *= na
280
300
  A1 += B1n
281
- A1 *= 1 / n_ # /= chokes PyChecker
301
+ A1 *= _n
282
302
 
283
303
  # self._Ms = A1, A2, A3, A4
284
304
  self._n = n
@@ -291,18 +311,18 @@ class Fcook(_FstatsBase):
291
311
  def fadd(self, xs, sample=False):
292
312
  '''Accumulate and return the current count.
293
313
 
294
- @arg xs: Iterable with additional values (C{Scalar}s,
295
- meaning C{scalar} or L{Fsum} instances).
296
- @kwarg sample: Return the I{sample} instead of the entire
297
- I{population} count (C{bool}).
314
+ @arg xs: Iterable of additional values (each C{scalar} or an
315
+ L{Fsum} or L{Fsum2Tuple} instance).
316
+ @kwarg sample: Use C{B{sample}=True} for the I{sample} count
317
+ instead of the I{population} count (C{bool}).
298
318
 
299
319
  @return: Current, running (sample) count (C{int}).
300
320
 
301
321
  @raise OverflowError: Partial C{2sum} overflow.
302
322
 
303
- @raise TypeError: Non-scalar B{C{xs}} value.
323
+ @raise TypeError: Invalid B{C{xs}}.
304
324
 
305
- @raise ValueError: Invalid or non-finite B{C{xs}} value.
325
+ @raise ValueError: Invalid or non-finite B{C{xs}}.
306
326
 
307
327
  @see: U{online_kurtosis<https://WikiPedia.org/wiki/
308
328
  Algorithms_for_calculating_variance>}.
@@ -310,7 +330,7 @@ class Fcook(_FstatsBase):
310
330
  n = self._n
311
331
  if xs:
312
332
  M1, M2, M3, M4 = self._Ms
313
- for x in _2Floats(xs):
333
+ for x in _2Floats(xs=xs): # PYCHOK yield
314
334
  n1 = n
315
335
  n += 1
316
336
  D = x - M1
@@ -340,38 +360,38 @@ class Fcook(_FstatsBase):
340
360
  self._n = n
341
361
  return _sampled(n, sample)
342
362
 
343
- def fjb(self, xs=None, sample=True, excess=True):
363
+ def fjb(self, xs=None, excess=True, sample=True):
344
364
  '''Accumulate and compute the current U{Jarque-Bera
345
365
  <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
346
366
 
347
- @kwarg xs: Iterable with additional values (C{Scalar}s).
348
- @kwarg sample: Return the I{sample} normality (C{bool}), default.
349
- @kwarg excess: Return the I{excess} kurtosis (C{bool}), default.
367
+ @kwarg xs: Iterable of additional values (each C{scalar} or an
368
+ L{Fsum} or L{Fsum2Tuple}).
369
+ @kwarg excess: Apply the I{excess} kurtosis (C{bool}), default.
370
+ @kwarg sample: Use C{B{sample}=False} for the I{population}
371
+ normality instead of the I{sample} one (C{bool}).
350
372
 
351
373
  @return: Current, running (sample) Jarque-Bera normality (C{float}).
352
374
 
353
375
  @see: Method L{Fcook.fadd}.
354
376
  '''
355
- n = self.fadd(xs, sample=sample)
356
- k = self.fkurtosis(sample=sample, excess=excess) / _2_0
357
- s = self.fskewness(sample=sample)
358
- return n * hypot2(k, s) / _6_0
377
+ return _Float(self._JarqueBera(xs, excess, sample=sample))
359
378
 
360
379
  def fjb_(self, *xs, **sample_excess):
361
380
  '''Accumulate and compute the current U{Jarque-Bera
362
381
  <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
363
382
 
364
- @see: Method L{Fcook.fjb}.
383
+ @see: Method L{Fcook.fjb} for further details.
365
384
  '''
366
385
  return self.fjb(xs, **sample_excess)
367
386
 
368
- def fkurtosis(self, xs=None, sample=False, excess=True):
387
+ def fkurtosis(self, xs=None, excess=True, **sample):
369
388
  '''Accumulate and return the current kurtosis.
370
389
 
371
- @kwarg xs: Iterable with additional values (C{Scalar}s).
372
- @kwarg sample: Return the I{sample} instead of the entire
373
- I{population} kurtosis (C{bool}).
390
+ @arg xs: Iterable of additional values (each C{scalar} or an
391
+ L{Fsum} or L{Fsum2Tuple} instance).
374
392
  @kwarg excess: Return the I{excess} kurtosis (C{bool}), default.
393
+ @kwarg sample: Use C{B{sample}=True} for the I{sample} kurtosis
394
+ instead of the I{population} kurtosis (C{bool}).
375
395
 
376
396
  @return: Current, running (sample) kurtosis or I{excess} kurtosis (C{float}).
377
397
 
@@ -380,32 +400,21 @@ class Fcook(_FstatsBase):
380
400
 
381
401
  @see: Method L{Fcook.fadd}.
382
402
  '''
383
- k, n = _0_0, self.fadd(xs, sample=sample)
384
- if n > 0:
385
- _, M2, _, M4 = self._Ms
386
- m2 = float(M2 * M2)
387
- if m2:
388
- K, x = (M4 * (n / m2)), _3_0
389
- if sample and 2 < n < len(self):
390
- d = float((n - 1) * (n - 2))
391
- K *= (n + 1) * (n + 2) / d
392
- x *= n**2 / d
393
- if excess:
394
- K -= x
395
- k = K.fsum()
396
- return k
403
+ n = self.fadd(xs, **sample)
404
+ return _Float(self._Kurtosis(n, excess, **sample))
397
405
 
398
- def fkurtosis_(self, *xs, **sample_excess):
406
+ def fkurtosis_(self, *xs, **excess_sample):
399
407
  '''Accumulate and return the current kurtosis.
400
408
 
401
- @see: Method L{Fcook.fkurtosis}.
409
+ @see: Method L{Fcook.fkurtosis} for further details.
402
410
  '''
403
- return self.fkurtosis(xs, **sample_excess)
411
+ return self.fkurtosis(xs, **excess_sample)
404
412
 
405
413
  def fmedian(self, xs=None):
406
414
  '''Accumulate and return the current median.
407
415
 
408
- @kwarg xs: Iterable with additional values (C{Scalar}s).
416
+ @arg xs: Iterable of additional values (each C{scalar} or an
417
+ L{Fsum} or L{Fsum2Tuple} instance).
409
418
 
410
419
  @return: Current, running median (C{float}).
411
420
 
@@ -414,24 +423,22 @@ class Fcook(_FstatsBase):
414
423
  https://TowardsDataScience.com/skewness-kurtosis-simplified-1338e094fc85>}
415
424
  and method L{Fcook.fadd}.
416
425
  '''
417
- # skewness = 3 * (mean - median) / stdev, i.e.
418
- # median = mean - skewness * stdef / 3
419
- m = float(self._M1) if xs is None else self.fmean(xs)
420
- return m - self.fskewness() * self.fstdev() / _3_0
426
+ return _Float(self._Median(xs))
421
427
 
422
428
  def fmedian_(self, *xs):
423
429
  '''Accumulate and return the current median.
424
430
 
425
- @see: Method L{Fcook.fmedian}.
431
+ @see: Method L{Fcook.fmedian} for further details.
426
432
  '''
427
433
  return self.fmedian(xs)
428
434
 
429
- def fskewness(self, xs=None, sample=False):
435
+ def fskewness(self, xs=None, **sample):
430
436
  '''Accumulate and return the current skewness.
431
437
 
432
- @kwarg xs: Iterable with additional values (C{Scalar}s).
433
- @kwarg sample: Return the I{sample} instead of the entire
434
- I{population} skewness (C{bool}).
438
+ @arg xs: Iterable of additional values (each C{scalar} or an
439
+ L{Fsum} or L{Fsum2Tuple} instance).
440
+ @kwarg sample: Use C{B{sample}=True} for the I{sample} skewness
441
+ instead of the I{population} skewness (C{bool}).
435
442
 
436
443
  @return: Current, running (sample) skewness (C{float}).
437
444
 
@@ -440,29 +447,70 @@ class Fcook(_FstatsBase):
440
447
 
441
448
  @see: Method L{Fcook.fadd}.
442
449
  '''
443
- s, n = _0_0, self.fadd(xs, sample=sample)
444
- if n > 0:
445
- _, M2, M3, _ = self._Ms
446
- m = float(M2**3)
447
- if m > 0:
448
- S = M3 * sqrt(float(n) / m)
449
- if sample and 1 < n < len(self):
450
- S *= (n + 1) / float(n - 1)
451
- s = S.fsum()
452
- return s
450
+ n = self.fadd(xs, **sample)
451
+ return _Float(self._Skewness(n, **sample))
453
452
 
454
453
  def fskewness_(self, *xs, **sample):
455
454
  '''Accumulate and return the current skewness.
456
455
 
457
- @see: Method L{Fcook.fskewness}.
456
+ @see: Method L{Fcook.fskewness} for further details.
458
457
  '''
459
458
  return self.fskewness(xs, **sample)
460
459
 
460
+ def _JarqueBera(self, xs, excess, **sample):
461
+ '''(INTERNAL) Return the (sample) Jarque-Bera normality as L{Fsum}.
462
+ '''
463
+ N, n = _0_0, self.fadd(xs, **sample)
464
+ if n > 0:
465
+ K = self._Kurtosis(n, excess, **sample) / _2_0
466
+ S = self._Skewness(n, **sample)
467
+ N = (K**2 + S**2) * (n / _6_0) # Fpowers(2, K, S) * ...
468
+ return N
469
+
470
+ def _Kurtosis(self, n, excess, sample=False):
471
+ '''(INTERNAL) Return the (sample) kurtosis as L{Fsum} or C{0.0}.
472
+ '''
473
+ K = _0_0
474
+ if n > 0:
475
+ _, M2, _, M4 = self._Ms
476
+ M = M2**2
477
+ if M > 0:
478
+ K, x = M.rdiv(M4 * n, raiser=False), _3_0
479
+ if sample and 2 < n < len(self):
480
+ d = (n - 1) * (n - 2)
481
+ K *= (n + 1) * (n + 2) / d
482
+ x *= n**2 / d
483
+ if excess:
484
+ K -= x
485
+ return K
486
+
487
+ def _Median(self, xs=None):
488
+ '''(INTERNAL) Return the median as L{Fsum}.
489
+ '''
490
+ # skewness = 3 * (mean - median) / stdev, i.e.
491
+ # median = mean - (skewness * stdef) / 3
492
+ return self._Mean(xs) - (self._Skewness(self._n) *
493
+ self._Stdev()) / _3_0
494
+
495
+ def _Skewness(self, n, sample=False):
496
+ '''(INTERNAL) Return the (sample) skewness as L{Fsum} or C{0.0}.
497
+ '''
498
+ S = _0_0
499
+ if n > 0:
500
+ _, M2, M3, _ = self._Ms
501
+ M = M2**3
502
+ if M > 0:
503
+ M = M.rdiv(n, raiser=False)
504
+ S = M3 * Fsqrt(M, raiser=False)
505
+ if sample and 1 < n < len(self):
506
+ S *= (n + 1) / (n - 1)
507
+ return S
508
+
461
509
  def toFwelford(self, name=NN):
462
510
  '''Return an L{Fwelford} equivalent.
463
511
  '''
464
512
  f = Fwelford(name=name or self.name)
465
- f._Ms = self._M1.fcopy(), self._M2.fcopy() # deep=False
513
+ f._Ms = self._M1.copy(), self._M2.copy() # deep=False
466
514
  f._n = self._n
467
515
  return f
468
516
 
@@ -476,7 +524,8 @@ class Fwelford(_FstatsBase):
476
524
  def __init__(self, xs=None, name=NN):
477
525
  '''New L{Fwelford} stats accumulator.
478
526
 
479
- @kwarg xs: Iterable with initial values (C{Scalar}s).
527
+ @arg xs: Iterable of initial values (each C{scalar} or an
528
+ L{Fsum} or L{Fsum2Tuple} instance).
480
529
  @kwarg name: Optional name (C{str}).
481
530
 
482
531
  @see: Method L{Fwelford.fadd}.
@@ -490,19 +539,20 @@ class Fwelford(_FstatsBase):
490
539
  def __iadd__(self, other):
491
540
  '''Add B{C{other}} to this L{Fwelford} instance.
492
541
 
493
- @arg other: An L{Fwelford} or L{Fcook} instance or C{Scalar}s,
494
- meaning one or more C{scalar} or L{Fsum} instances.
542
+ @arg other: An L{Fwelford} or L{Fcook} instance or value
543
+ or an iterable of values (each C{scalar} or
544
+ an L{Fsum} or L{Fsum2Tuple} instance).
495
545
 
496
546
  @return: This instance, updated (L{Fwelford}).
497
547
 
498
- @raise TypeError: Invalid B{C{other}} type.
548
+ @raise TypeError: Invalid B{C{other}}.
499
549
 
500
550
  @raise ValueError: Invalid B{C{other}}.
501
551
 
502
552
  @see: Method L{Fwelford.fadd} and U{Parallel algorithm<https//
503
553
  WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.
504
554
  '''
505
- if isinstance(other, Fwelford):
555
+ if _isAn(other, Fwelford):
506
556
  nb = len(other)
507
557
  if nb > 0:
508
558
  na = len(self)
@@ -510,26 +560,26 @@ class Fwelford(_FstatsBase):
510
560
  M, S = self._Ms
511
561
  M_, S_ = other._Ms
512
562
 
513
- n = na + nb
514
- n_ = float(n)
563
+ n = na + nb
564
+ _n = _1_0 / n
515
565
 
516
566
  D = M_ - M
517
567
  D *= D # D**2
518
- D *= na * nb / n_
568
+ D *= na * nb * _n
519
569
  S += D
520
570
  S += S_
521
571
 
522
572
  Mn = M_ * nb # if other is self
523
573
  M *= na
524
574
  M += Mn
525
- M *= 1 / n_ # /= chokes PyChecker
575
+ M *= _n
526
576
 
527
577
  # self._Ms = M, S
528
578
  self._n = n
529
579
  else:
530
580
  self._copy(self, other)
531
581
 
532
- elif isinstance(other, Fcook):
582
+ elif _isAn(other, Fcook):
533
583
  self += other.toFwelford()
534
584
  else:
535
585
  self._iadd_other(other)
@@ -538,23 +588,23 @@ class Fwelford(_FstatsBase):
538
588
  def fadd(self, xs, sample=False):
539
589
  '''Accumulate and return the current count.
540
590
 
541
- @arg xs: Iterable with additional values (C{Scalar}s,
542
- meaning C{scalar} or L{Fsum} instances).
543
- @kwarg sample: Return the I{sample} instead of the entire
544
- I{population} count (C{bool}).
591
+ @arg xs: Iterable of additional values (each C{scalar} or an
592
+ L{Fsum} or L{Fsum2Tuple} instance).
593
+ @kwarg sample: Use C{B{sample}=True} for the I{sample} count
594
+ instead of the I{population} count (C{bool}).
545
595
 
546
596
  @return: Current, running (sample) count (C{int}).
547
597
 
548
598
  @raise OverflowError: Partial C{2sum} overflow.
549
599
 
550
- @raise TypeError: Non-scalar B{C{xs}} value.
600
+ @raise TypeError: Invalid B{C{xs}}.
551
601
 
552
- @raise ValueError: Invalid or non-finite B{C{xs}} value.
602
+ @raise ValueError: Invalid or non-finite B{C{xs}}.
553
603
  '''
554
604
  n = self._n
555
605
  if xs:
556
606
  M, S = self._Ms
557
- for x in _2Floats(xs):
607
+ for x in _2Floats(xs=xs): # PYCHOK yield
558
608
  n += 1
559
609
  D = x - M
560
610
  M += D / n
@@ -573,14 +623,16 @@ class Flinear(_FstatsNamed):
573
623
  def __init__(self, xs=None, ys=None, Fstats=Fwelford, name=NN):
574
624
  '''New L{Flinear} regression accumulator.
575
625
 
576
- @kwarg xs: Iterable with initial C{x} values (C{Scalar}s).
577
- @kwarg ys: Iterable with initial C{y} values (C{Scalar}s).
578
- @kwarg Fstats: Stats class for C{x} and C{y} values (L{Fcook}
579
- or L{Fwelford}).
626
+ @kwarg xs: Iterable of initial C{x} values (each C{scalar} or
627
+ an L{Fsum} or L{Fsum2Tuple} instance).
628
+ @kwarg ys: Iterable of initial C{y} values (each C{scalar} or
629
+ an L{Fsum} or L{Fsum2Tuple} instance).
630
+ @kwarg Fstats: Class for C{xs} and C{ys} values (L{Fcook} or
631
+ L{Fwelford}).
580
632
  @kwarg name: Optional name (C{str}).
581
633
 
582
- @raise TypeError: Invalid B{C{Fs}}, not L{Fcook} or
583
- L{Fwelford}.
634
+ @raise TypeError: B{C{Fstats}} not L{Fcook} or L{Fwelford}.
635
+
584
636
  @see: Method L{Flinear.fadd}.
585
637
  '''
586
638
  _xsubclassof(Fcook, Fwelford, Fstats=Fstats)
@@ -596,8 +648,8 @@ class Flinear(_FstatsNamed):
596
648
  def __iadd__(self, other):
597
649
  '''Add B{C{other}} to this instance.
598
650
 
599
- @arg other: An L{Flinear} instance or C{Scalar} pairs,
600
- meaning C{scalar} or L{Fsum} instances.
651
+ @arg other: An L{Flinear} instance or an iterable of
652
+ C{x_ys} values, see method C{fadd_}.
601
653
 
602
654
  @return: This instance, updated (L{Flinear}).
603
655
 
@@ -609,129 +661,142 @@ class Flinear(_FstatsNamed):
609
661
 
610
662
  @see: Method L{Flinear.fadd_}.
611
663
  '''
612
- if isinstance(other, Flinear):
664
+ if _isAn(other, Flinear):
613
665
  if len(other) > 0:
614
666
  if len(self) > 0:
615
- n = other._n
616
- S = other._S
617
- X = other._X
618
- Y = other._Y
619
- D = (X._M1 - self._X._M1) * \
620
- (Y._M1 - self._Y._M1) * \
621
- (n * self._n / float(n + self._n))
667
+ n = other._n
668
+ D = (other._X._M1 - self._X._M1) * \
669
+ (other._Y._M1 - self._Y._M1) * \
670
+ (n * self._n / (self._n + n))
671
+ self._S += other._S + D
672
+ self._X += other._X
673
+ self._Y += other._Y
622
674
  self._n += n
623
- self._S += S + D
624
- self._X += X
625
- self._Y += Y
626
675
  else:
627
676
  self._copy(self, other)
628
677
  else:
629
678
  try:
630
- if not islistuple(other):
631
- raise TypeError(_SPACE_(_invalid_, _other_))
632
- elif isodd(len(other)):
633
- raise ValueError(Fmt.PAREN(isodd=Fmt.PAREN(len=_other_)))
679
+ _xiterable(other)
634
680
  self.fadd_(*other)
635
681
  except Exception as x:
636
- raise _xError(x, _SPACE_(self, _iadd_op_, repr(other)))
682
+ op = _SPACE_(self, _iadd_op_, repr(other))
683
+ raise _xError(x, op)
637
684
  return self
638
685
 
639
- def _copy(self, c, s):
640
- '''(INTERNAL) Copy C{B{c} = B{s}}.
686
+ def _copy(self, d, s):
687
+ '''(INTERNAL) Copy C{B{d} = B{s}}.
688
+ '''
689
+ _xinstanceof(Flinear, d=d, s=s)
690
+ d._S = s._S.copy(deep=False)
691
+ d._X = s._X.copy(deep=False)
692
+ d._Y = s._Y.copy(deep=False)
693
+ d._n = s._n
694
+ return d
695
+
696
+ def _Correlation(self, **sample):
697
+ '''(INTERNAL) Return the current (sample) correlation as L{Fsum}.
641
698
  '''
642
- _xinstanceof(Flinear, c=c, s=s)
643
- c._n = s._n
644
- c._S = s._S.fcopy(deep=False)
645
- c._X = s._X.fcopy(deep=False)
646
- c._Y = s._Y.fcopy(deep=False)
647
- return c
699
+ return self._Sampled(self._X._Stdev(**sample) *
700
+ self._Y._Stdev(**sample), **sample)
648
701
 
649
702
  def fadd(self, xs, ys, sample=False):
650
703
  '''Accumulate and return the current count.
651
704
 
652
- @arg xs: Iterable with additional C{x} values (C{Scalar}s),
653
- meaning C{scalar} or L{Fsum} instances).
654
- @arg ys: Iterable with additional C{y} values (C{Scalar}s,
655
- meaning C{scalar} or L{Fsum} instances).
656
- @kwarg sample: Return the I{sample} instead of the entire
657
- I{population} count (C{bool}).
705
+ @arg xs: Iterable of additional C{x} values (each C{scalar}
706
+ or an L{Fsum} or L{Fsum2Tuple} instance).
707
+ @arg ys: Iterable of additional C{y} values (each C{scalar}
708
+ or an L{Fsum} or L{Fsum2Tuple} instance).
709
+ @kwarg sample: Use C{B{sample}=True} for the I{sample} count
710
+ instead of the I{population} count (C{bool}).
658
711
 
659
712
  @return: Current, running (sample) count (C{int}).
660
713
 
661
714
  @raise OverflowError: Partial C{2sum} overflow.
662
715
 
663
- @raise TypeError: Non-scalar B{C{xs}} or B{C{ys}} value.
716
+ @raise TypeError: Invalid B{C{xs}} or B{C{ys}}.
664
717
 
665
- @raise ValueError: Invalid or non-finite B{C{xs}} or B{C{ys}} value.
718
+ @raise ValueError: Invalid or non-finite B{C{xs}} or B{C{ys}}.
666
719
  '''
667
720
  n = self._n
668
721
  if xs and ys:
669
722
  S = self._S
670
723
  X = self._X
671
724
  Y = self._Y
672
- for x, y in _zip(_2Floats(xs), _2Floats(ys, ys=True)): # strict=True
725
+ for x, y in _zip(_2Floats(xs=xs), _2Floats(ys=ys)): # PYCHOK strict=True
673
726
  n1 = n
674
727
  n += 1
675
728
  if n1 > 0:
676
- S += (X._M1 - x) * (Y._M1 - y) * (n1 / float(n))
729
+ S += (X._M1 - x) * (Y._M1 - y) * (n1 / n)
677
730
  X += x
678
731
  Y += y
679
- self._n = n
732
+ self._n = n
680
733
  return _sampled(n, sample)
681
734
 
682
735
  def fadd_(self, *x_ys, **sample):
683
736
  '''Accumulate and return the current count.
684
737
 
685
- @arg x_ys: Individual, alternating C{x, y, x, y, ...}
686
- positional values (C{Scalar}s).
738
+ @arg x_ys: Individual, alternating C{x, y, x, y, ...} values
739
+ (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
740
+ instance).
687
741
 
688
- @see: Method C{Flinear.fadd}.
742
+ @see: Method C{Flinear.fadd} for further details.
689
743
  '''
744
+ if isodd(len(x_ys)):
745
+ t = _SPACE_(_odd_, len.__name__)
746
+ raise _ValueError(t, len(x_ys))
690
747
  return self.fadd(x_ys[0::2], x_ys[1::2], **sample)
691
748
 
692
- def fcorrelation(self, sample=False):
749
+ def fcorrelation(self, **sample):
693
750
  '''Return the current, running (sample) correlation (C{float}).
694
751
 
695
- @kwarg sample: Return the I{sample} instead of the entire
696
- I{population} correlation (C{bool}).
752
+ @kwarg sample: Use C{B{sample}=True} for the I{sample} correlation
753
+ instead of the I{population} correlation (C{bool}).
697
754
  '''
698
- return self._sampled(self.x.fstdev(sample=sample) *
699
- self.y.fstdev(sample=sample), sample)
755
+ return _Float(self._Correlation(**sample))
700
756
 
701
- def fintercept(self, sample=False):
757
+ def fintercept(self, **sample):
702
758
  '''Return the current, running (sample) intercept (C{float}).
703
759
 
704
- @kwarg sample: Return the I{sample} instead of the entire
705
- I{population} intercept (C{bool}).
760
+ @kwarg sample: Use C{B{sample}=True} for the I{sample} intercept
761
+ instead of the I{population} intercept (C{bool}).
706
762
  '''
707
- return float(self.y._M1 -
708
- (self.x._M1 * self.fslope(sample=sample)))
763
+ return _Float(self._Intercept(**sample))
709
764
 
710
- def fslope(self, sample=False):
765
+ def fslope(self, **sample):
711
766
  '''Return the current, running (sample) slope (C{float}).
712
767
 
713
- @kwarg sample: Return the I{sample} instead of the entire
714
- I{population} slope (C{bool}).
768
+ @kwarg sample: Use C{B{sample}=True} for the I{sample} slope
769
+ instead of the I{population} slope (C{bool}).
715
770
  '''
716
- return self._sampled(self.x.fvariance(sample=sample), sample)
771
+ return _Float(self._Slope(**sample))
717
772
 
718
- def _sampled(self, t, sample):
773
+ def _Intercept(self, **sample):
774
+ '''(INTERNAL) Return the current (sample) intercept as L{Fsum}.
775
+ '''
776
+ return self._Y._M1 - self._X._M1 * self._Slope(**sample)
777
+
778
+ def _Sampled(self, T, sample=False):
719
779
  '''(INTERNAL) Compute the sampled or entire population result.
720
780
  '''
721
- t *= float(_sampled(self._n, sample))
722
- return float(self._S / t) if t else _0_0
781
+ T *= _sampled(self._n, sample)
782
+ return self._S.copy().fdiv(T, raiser=False) if T else T
783
+
784
+ def _Slope(self, **sample):
785
+ '''(INTERNAL) Return the current (sample) slope as L{Fsum}.
786
+ '''
787
+ return self._Sampled(self._X._Variance(**sample), **sample)
723
788
 
724
789
  @property_RO
725
790
  def x(self):
726
791
  '''Get the C{x} accumulator (L{Fcook} or L{Fwelford}).
727
792
  '''
728
- return self._X
793
+ return self._X # .copy()
729
794
 
730
795
  @property_RO
731
796
  def y(self):
732
797
  '''Get the C{y} accumulator (L{Fcook} or L{Fwelford}).
733
798
  '''
734
- return self._Y
799
+ return self._Y # .copy()
735
800
 
736
801
 
737
802
  __all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)