pygeodesy 24.5.6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyGeodesy
3
- Version: 24.5.6
3
+ Version: 24.5.8
4
4
  Summary: Pure Python geodesy tools
5
5
  Home-page: https://GitHub.com/mrJean1/PyGeodesy
6
6
  Author: Jean M. Brouwers
@@ -114,7 +114,7 @@ line: ``epydoc --html --no-private --no-source --name=PyGeodesy --url=... -v pyg
114
114
  Tests
115
115
  =====
116
116
 
117
- The tests ran with Python 3.12.2 (with geographiclib_ 2.0), 3.11.5 (with geographiclib_ 2.0, numpy_
117
+ The tests ran with Python 3.12.3 (with geographiclib_ 2.0), 3.11.5 (with geographiclib_ 2.0, numpy_
118
118
  1.24.2 and scipy_ 1.10.1), Python 3.10.8 (with geographiclib_ 2.0, numpy_ 1.23.3, scipy_ 1.9.1,
119
119
  GeoConvert_ 2.2, GeodSolve_ 2.2 and RhumbSolve_ 2.2), Python 3.9.6 and Python 2.7.18 (with geographiclib_
120
120
  1.50, numpy_ 1.16.6, scipy_ 1.2.2, GeoConvert_ 2.2, GeodSolve_ 2.2 and RhumbSolve_ 2.2), all on macOS
@@ -137,7 +137,7 @@ Python 3.11.5, 3.10.10 and 2.7.18 (all with geographiclib_ 1.52) on `Windows 10`
137
137
  A single-File and single-Directory application with ``pygeodesy`` has been bundled using PyInstaller_
138
138
  3.4 and 64-bit Python 3.7.4 and 3.7.3 on macOS 10.13.6 High Sierra.
139
139
 
140
- Previously, the tests were run with Python 3.12.0-1, 3.11.2-4, 3.10.1-7, 3.9.1, 3.8.7, 3.7.1, 2.7.15, PyPy_
140
+ Previously, the tests were run with Python 3.12.0-2, 3.11.2-4, 3.10.1-7, 3.9.1, 3.8.7, 3.7.1, 2.7.15, PyPy_
141
141
  7.3.12 (Python 3.10.12), 7.3.1 (Python 3.6.9) and PyPy_ 7.1.1 (Python 2.7.13) (and geographiclib_ 1.52,
142
142
  numpy_ 1.16.3, 1.16.4, 1.16.6, 1.19.0, 1.19.4, 1.19.5 or 1.22.4 and scipy_ 1.2.1, 1.4.1, 1.5.2 or 1.8.1)
143
143
  on `Ubuntu 16.04`_, with Python 3.10.0-1, 3.9.0-5, 3.8.0-6, 3.7.2-6, 3.7.0, 3.6.2-5, 3.5.3, 2.7.13-17,
@@ -158,7 +158,7 @@ and McCabe_ using Python 2.7.18 and with Flake8_ using Python 3.11.5, both in 64
158
158
 
159
159
  For a summary of all *Karney*-based functionality in ``pygeodesy``, see module karney_.
160
160
 
161
- *Last updated: May 06, 2024.*
161
+ *Last updated: May 08, 2024.*
162
162
 
163
163
  License
164
164
  =======
@@ -1,9 +1,9 @@
1
1
  pygeodesy/LICENSE,sha256=YfgAiyxOwY6P9Kkb1_5XN81nueTLrpb3Ffkv3EuPgFU,1144
2
- pygeodesy/__init__.py,sha256=5Q85uqE7EHB5oDhKSCKkujvjELK-sTT7tomCX4OfaKE,40733
2
+ pygeodesy/__init__.py,sha256=aWftpUqOeH1Uul8uN_kDAXbFL0TqLXDhynBhCmgpxoQ,40733
3
3
  pygeodesy/__main__.py,sha256=qMFG3caY8ZXWu6uGiemzyT4OqTFZnsFtlxcGCAgkVJw,4637
4
4
  pygeodesy/albers.py,sha256=g2AVlpV8JO2AYFCthoIbRC2h1OJqjb9P3hpwF0C3TI8,30994
5
5
  pygeodesy/azimuthal.py,sha256=tc4JxgLi-0jzU08m4Bvi-t-kzHXYPeGuzL3j_tyVFUA,50125
6
- pygeodesy/basics.py,sha256=Mh70LwAH185gr8aYhdOhfqAa-SkHvAatlJEOFIH7N8Y,28162
6
+ pygeodesy/basics.py,sha256=GWsiv-Zl2qOZoW7wPwIG7yLd2FwYLUvsq8c6hZdgsFE,28453
7
7
  pygeodesy/booleans.py,sha256=HZbwoL-S7Ww9d4C2D1BVqXfmcuqqVpEVSU9_S0uyUyo,74204
8
8
  pygeodesy/cartesianBase.py,sha256=I3q29mRdBB3NCDmPoJsJ0QOFfLzkdMWc8X9zG4IwJyA,47264
9
9
  pygeodesy/clipy.py,sha256=VU3ynQ1IZ0v5hJlicqD48oW0imRgiL5_ZzRPrIjpfPw,27683
@@ -25,11 +25,11 @@ pygeodesy/elliptic.py,sha256=XRVpmpdm3hbztM4P-qhWKQizbgsXDUxWujgmOrIpgiQ,42428
25
25
  pygeodesy/epsg.py,sha256=ldHoLWqJWR4FUiBVnDTtrI_e7TNjjtr9OkxDlri1g5E,8165
26
26
  pygeodesy/errors.py,sha256=RHZ_f1zcOIyLQYProiQznaAOhojbDPDR4XbblXJMuS0,27232
27
27
  pygeodesy/etm.py,sha256=joEhU2lw9ehRv101nRpZvXq8hOYAEzdcYFpif9_obfk,44585
28
- pygeodesy/fmath.py,sha256=HCnndaXPwRIW8gGIVzbACftxIZZUUrRAiIAFfgVYLgw,33477
28
+ pygeodesy/fmath.py,sha256=MylYc4nkVtDWI6U-R4hGmXcq-9tUUBmnlM795weezec,33477
29
29
  pygeodesy/formy.py,sha256=Rces4Q5ecED0WVfuI9mQzeyHE54LDowYfI6faBHpyeA,74536
30
30
  pygeodesy/frechet.py,sha256=qgee-ISBtT7Ov3rJkcd_t-WeXTbeNoMSQuMa0j3MyQc,33512
31
- pygeodesy/fstats.py,sha256=UcaUuUpnQqaE8T7rqHCT6eptqa-IG23LsiCiOUh43U0,25681
32
- pygeodesy/fsums.py,sha256=VttiMEC02riVsWWh2SqhLD3rwo2af0RsAvxvpiNSqsw,77895
31
+ pygeodesy/fstats.py,sha256=23XqQRoegfeE4xEbbcgcsuw4JGEAe0OPvR8Jn2W8U-8,28414
32
+ pygeodesy/fsums.py,sha256=M9btYPykj5JHvO4gg6wkKPGKlg9R4ZfdoFFKcgmQVno,78284
33
33
  pygeodesy/gars.py,sha256=fCiWBJ4kOJxPfNOxadX-OzBGDXj7C9g02NuGHiZa_88,11342
34
34
  pygeodesy/geodesicw.py,sha256=5yjJ2rLekSsjT7e-Km6v592ZcFlA41flQP_E42jU9Sw,26901
35
35
  pygeodesy/geodsolve.py,sha256=3GLI2_4gglzTpZwKchNzAv-XhWFBzWRKjcWsjCC3dvI,21900
@@ -37,7 +37,7 @@ pygeodesy/geohash.py,sha256=HwukecfkWvyfz9LHKfoA-WddQHYot_mB04o0lyWrKyU,32382
37
37
  pygeodesy/geoids.py,sha256=EwZJ50qRXeZb1591N99hnamM_EC2NP6MIhLb5swVHf4,80605
38
38
  pygeodesy/hausdorff.py,sha256=LJULGbDop4xqiWeRH5-Uayvk5WVCZQpxK4PCYu4kqSs,34192
39
39
  pygeodesy/heights.py,sha256=tjr-5eb8UXn2MAp6-157sb6qxPREFk5dtDjHtBgIgZ8,42528
40
- pygeodesy/interns.py,sha256=LzgIw5JEMLMc_56v0n3LxgpcQ0cpdRghkus8yorcuGQ,30188
40
+ pygeodesy/interns.py,sha256=WuQe08kItPIwSJ9J1bUxI5yVOX_eNfpGIMpKHzoCqAw,30245
41
41
  pygeodesy/iters.py,sha256=CfU365eE9F4oWZbBx0qTEvfodMhpa8rGOepLIv67xF8,20181
42
42
  pygeodesy/karney.py,sha256=lYdsSHis8xQJVS1V0F3cphx6Dhgd77xTlMOdVR8MN8I,34994
43
43
  pygeodesy/ktm.py,sha256=sCvbLvJItavlruilAyjeZ0sOZx2yJumzGe_UiIVbGi4,27315
@@ -109,7 +109,7 @@ pygeodesy/rhumb/aux_.py,sha256=W4HkgoHAQz_aWaysfsmOA5010nCGvBfUs2Q-bymnHYU,16660
109
109
  pygeodesy/rhumb/bases.py,sha256=kzU_Dgt4FNPMgTg5rqbw-HiNpflDPKpmq9jhPbQmR4U,53851
110
110
  pygeodesy/rhumb/ekx.py,sha256=lF3tZ-ZY9fPJV8y1kgHW-7EOZCyb3gJr-kR-jj5Fbf8,23871
111
111
  pygeodesy/rhumb/solve.py,sha256=NZfwx7xv5UriQs7A0c7ZhoxxVUeAT15UwXK_jtwEXQw,27802
112
- PyGeodesy-24.5.6.dist-info/METADATA,sha256=5ijdMD-qHxonaJpVNVWA0GMecR-fjfEAVCcSvFwWfuc,19363
113
- PyGeodesy-24.5.6.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
114
- PyGeodesy-24.5.6.dist-info/top_level.txt,sha256=cEQPatCXzKZqrivpULC5V5fuy9_V_bAwaP_gUGid7pQ,10
115
- PyGeodesy-24.5.6.dist-info/RECORD,,
112
+ PyGeodesy-24.5.8.dist-info/METADATA,sha256=iiCbD7PToGDSIyOreeEG4YnJO2j2TZRnNvKeQdOVLQQ,19363
113
+ PyGeodesy-24.5.8.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
114
+ PyGeodesy-24.5.8.dist-info/top_level.txt,sha256=cEQPatCXzKZqrivpULC5V5fuy9_V_bAwaP_gUGid7pQ,10
115
+ PyGeodesy-24.5.8.dist-info/RECORD,,
pygeodesy/__init__.py CHANGED
@@ -121,7 +121,7 @@ C{epydoc --html --no-private --no-source --name=PyGeodesy --url=... -v pygeodesy
121
121
  Tests
122
122
  =====
123
123
 
124
- The tests ran with Python 3.12.2 (with U{geographiclib<https://PyPI.org/project/geographiclib>} 2.0,
124
+ The tests ran with Python 3.12.3 (with U{geographiclib<https://PyPI.org/project/geographiclib>} 2.0,
125
125
  Python 3.11.5 (with U{geographiclib<https://PyPI.org/project/geographiclib>} 2.0, U{numpy
126
126
  <https://PyPI.org/project/numpy>} 1.24.2 and U{scipy<https://PyPI.org/project/scipy>} 1.10.1), Python
127
127
  3.10.8 (with U{geographiclib <https://PyPI.org/project/geographiclib>} 2.0, U{numpy
@@ -155,7 +155,7 @@ U{Debian 11<https://Cirrus-CI.com/github/mrJean1/PyGeodesy/master>} in 64-bit on
155
155
  A single-File and single-Directory application with C{pygeodesy} has been bundled using U{PyInstaller
156
156
  <https://PyPI.org/project/pyinstaller>} 3.4 and 64-bit Python 3.7.3 on macOS 10.13.6 High Sierra.
157
157
 
158
- Previously, the tests were run with Python 3.12.0-1, 3.11.2-4, 3.10.1-7, 3.9.1, 3.8.7, 3.7.1, 2.7.15, U{PyPy
158
+ Previously, the tests were run with Python 3.12.0-2, 3.11.2-4, 3.10.1-7, 3.9.1, 3.8.7, 3.7.1, 2.7.15, U{PyPy
159
159
  <https://PyPy.org>} 7.3.12 (Python 3.10.12), 7.3.1 (Python 3.6.9) and U{PyPy<https://PyPy.org>} 7.1.1 (Python
160
160
  2.7.13) (and U{geographiclib <https://PyPI.org/project/geographiclib>} 1.52, U{numpy<https://PyPI.org/project/numpy>}
161
161
  1.16.3, 1.16.4, 1.16.6, 1.19.0, 1.19.4, 1.19.5 or 1.22.4 and U{scipy<https://PyPI.org/project/scipy>} 1.2.1, 1.4.1,
@@ -585,7 +585,7 @@ else:
585
585
  _init__all__ = False
586
586
 
587
587
  from pygeodesy.interns import _DOT_, _version2 # PYCHOK import
588
- __version__ = '24.05.06'
588
+ __version__ = '24.05.08'
589
589
  # see setup.py for similar logic
590
590
  version = _DOT_(*_version2(__version__, n=3))
591
591
 
pygeodesy/basics.py CHANGED
@@ -21,7 +21,8 @@ from pygeodesy.errors import _AttributeError, _ImportError, _NotImplementedError
21
21
  _xkwds_get
22
22
  from pygeodesy.interns import MISSING, NN, _1_, _by_, _COMMA_, _DOT_, _DEPRECATED_, \
23
23
  _ELLIPSIS4_, _enquote, _EQUAL_, _in_, _invalid_, _N_A_, \
24
- _not_scalar_, _SPACE_, _UNDER_, _version_, _version_info
24
+ _not_, _not_scalar_, _odd_, _SPACE_, _UNDER_, _version_, \
25
+ _version_info
25
26
  # from pygeodesy.latlonBase import LatLonBase # _MODS
26
27
  from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS, \
27
28
  _getenv, LazyImportError, _sys, _sys_version_info2
@@ -34,13 +35,12 @@ from math import copysign as _copysign
34
35
  import inspect as _inspect
35
36
 
36
37
  __all__ = _ALL_LAZY.basics
37
- __version__ = '24.04.28'
38
+ __version__ = '24.05.08'
38
39
 
39
40
  _0_0 = 0.0 # in .constants
40
41
  _below_ = 'below'
41
42
  _list_tuple_types = (list, tuple)
42
43
  _list_tuple_set_types = (list, tuple, set)
43
- _odd_ = 'odd'
44
44
  _PYGEODESY_XPACKAGES_ = 'PYGEODESY_XPACKAGES'
45
45
  _required_ = 'required'
46
46
 
@@ -786,6 +786,15 @@ def _xisscalar(**names_values):
786
786
  raise _TypeError(n, v, txt=_not_scalar_)
787
787
 
788
788
 
789
+ def _xiterable(obj):
790
+ '''(INTERNAL) Raise C{TypeError} if C{obj} is not iterable.
791
+ '''
792
+ # https://PyPI.org/project/isiterable/
793
+ if not (hasattr(obj, '__iter__') or
794
+ hasattr(obj, '__getitem__')):
795
+ raise TypeError(_not_(_xiterable.__name__[2:]))
796
+
797
+
789
798
  def _xnumpy(where, *required):
790
799
  '''(INTERNAL) Import C{numpy} and check required version.
791
800
  '''
pygeodesy/fmath.py CHANGED
@@ -24,7 +24,7 @@ from math import fabs, sqrt # pow
24
24
  import operator as _operator # in .datums, .trf, .utm
25
25
 
26
26
  __all__ = _ALL_LAZY.fmath
27
- __version__ = '24.05.06'
27
+ __version__ = '24.05.07'
28
28
 
29
29
  # sqrt(2) <https://WikiPedia.org/wiki/Square_root_of_2>
30
30
  _0_4142 = 0.41421356237309504880 # ... sqrt(2) - 1
@@ -539,7 +539,7 @@ def fidw(xs, ds, beta=2):
539
539
  i += 1 # len(xs) < i < len(ds)
540
540
  except Exception as X:
541
541
  _I = Fmt.INDEX
542
- raise _xError(X, _I(ds=i), d, _I(xs=i), x)
542
+ raise _xError(X, _I(xs=i), x, _I(ds=i), d)
543
543
  else: # b == 0
544
544
  x = fsum(xs) / n # fmean(xs)
545
545
  i = n
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
13
- from pygeodesy.errors import _AssertionError, _xError
14
- from pygeodesy.fmath import hypot2, sqrt
15
- from pygeodesy.fsums import _Float, _2float, Fsum, _iadd_op_, \
16
- _isAn, _Fsum_Fsum2Tuple_types, Fmt
17
- 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_
18
18
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
19
19
  from pygeodesy.named import _Named, _NotImplemented, property_RO
20
20
  # from pygeodesy.props import property_RO # from .named
21
21
  # from pygeodesy.streprs import Fmt # from .fsums
22
22
 
23
- # from math import sqrt # from .fmath
24
-
25
23
  __all__ = _ALL_LAZY.fstats
26
- __version__ = '24.05.06'
27
-
28
- _Floats = _Fsum_Fsum2Tuple_types + (_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
24
+ __version__ = '24.05.08'
34
25
 
35
26
 
36
27
  def _2Floats(**xs):
37
28
  '''(INTERNAL) Yield each value as C{float} or L{Fsum}.
38
29
  '''
39
30
  try:
40
- _s, xs = xs.popitem()
41
- except Exception as x:
42
- raise _AssertionError(xs=xs, cause=x)
31
+ name, xs = xs.popitem()
32
+ except Exception as X:
33
+ raise _AssertionError(xs=xs, cause=X)
43
34
 
44
- for i, x in enumerate(xs): # don't unravel Fsums
45
- yield x if _isAn(x, _Floats) else \
46
- _2float(index=i, **{_s: 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
 
@@ -90,14 +86,14 @@ class _FstatsNamed(_Named):
90
86
  n = _SPACE_(self.classname, n) if n else self.classname
91
87
  return Fmt.SQUARE(n, len(self))
92
88
 
93
- def fcopy(self, deep=False, name=NN):
89
+ def copy(self, deep=False, name=NN):
94
90
  '''Copy this instance, C{shallow} or B{C{deep}}.
95
91
  '''
96
- n = name or self.fcopy.__name__
92
+ n = name or self.copy.__name__
97
93
  f = _Named.copy(self, deep=deep, name=n)
98
94
  return self._copy(f, self) # PYCHOK expected
99
95
 
100
- copy = fcopy
96
+ fcopy = copy # for backward compatibility
101
97
 
102
98
 
103
99
  class _FstatsBase(_FstatsNamed):
@@ -105,13 +101,13 @@ class _FstatsBase(_FstatsNamed):
105
101
  '''
106
102
  _Ms = ()
107
103
 
108
- def _copy(self, c, s):
104
+ def _copy(self, d, s):
109
105
  '''(INTERNAL) Copy C{B{c} = B{s}}.
110
106
  '''
111
- _xinstanceof(self.__class__, c=c, s=s)
112
- c._Ms = tuple(M.fcopy() for M in s._Ms) # deep=False
113
- c._n = s._n
114
- 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
115
111
 
116
112
  def fadd(self, xs, sample=False): # PYCHOK no cover
117
113
  '''I{Must be overloaded}.'''
@@ -120,84 +116,85 @@ class _FstatsBase(_FstatsNamed):
120
116
  def fadd_(self, *xs, **sample):
121
117
  '''Accumulate and return the current count.
122
118
 
123
- @see: Method C{fadd}.
119
+ @see: Method C{fadd} for further details.
124
120
  '''
125
121
  return self.fadd(xs, **sample)
126
122
 
127
123
  def fmean(self, xs=None):
128
124
  '''Accumulate and return the current mean.
129
125
 
130
- @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).
131
128
 
132
129
  @return: Current, running mean (C{float}).
133
130
 
134
131
  @see: Method C{fadd}.
135
132
  '''
136
- if xs:
137
- self.fadd(xs)
138
- return self._M1.fsum()
133
+ return _Float(self._Mean(xs))
139
134
 
140
135
  def fmean_(self, *xs):
141
136
  '''Accumulate and return the current mean.
142
137
 
143
- @see: Method C{fmean}.
138
+ @see: Method C{fmean} for further details.
144
139
  '''
145
140
  return self.fmean(xs)
146
141
 
147
- def fstdev(self, xs=None, sample=False):
142
+ def fstdev(self, xs=None, **sample):
148
143
  '''Accumulate and return the current standard deviation.
149
144
 
150
- @kwarg xs: Iterable with additional values (C{Scalar}).
151
- @kwarg sample: Return the I{sample} instead of the entire
152
- 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}).
153
149
 
154
150
  @return: Current, running (sample) standard deviation (C{float}).
155
151
 
156
152
  @see: Method C{fadd}.
157
153
  '''
158
- v = self.fvariance(xs, sample=sample)
159
- return sqrt(v) if v > 0 else _0_0
154
+ return _Float(self._Stdev(xs, **sample))
160
155
 
161
156
  def fstdev_(self, *xs, **sample):
162
157
  '''Accumulate and return the current standard deviation.
163
158
 
164
- @see: Method C{fstdev}.
159
+ @see: Method C{fstdev} for further details.
165
160
  '''
166
161
  return self.fstdev(xs, **sample)
167
162
 
168
- def fvariance(self, xs=None, sample=False):
163
+ def fvariance(self, xs=None, **sample):
169
164
  '''Accumulate and return the current variance.
170
165
 
171
- @kwarg xs: Iterable with additional values (C{Scalar}s).
172
- @kwarg sample: Return the I{sample} instead of the entire
173
- 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}).
174
170
 
175
171
  @return: Current, running (sample) variance (C{float}).
176
172
 
177
173
  @see: Method C{fadd}.
178
174
  '''
179
- n = self.fadd(xs, sample=sample)
180
- return _Float(self._M2 / _Float(n)) if n > 0 else _0_0
175
+ return _Float(self._Variance(xs, **sample))
181
176
 
182
177
  def fvariance_(self, *xs, **sample):
183
178
  '''Accumulate and return the current variance.
184
179
 
185
- @see: Method C{fvariance}.
180
+ @see: Method C{fvariance} for further details.
186
181
  '''
187
182
  return self.fvariance(xs, **sample)
188
183
 
189
184
  def _iadd_other(self, other):
190
- '''(INTERNAL) Add Scalar or Scalars.
191
- '''
192
- if _isAn(other, _Scalar):
193
- self.fadd_(other)
194
- else:
195
- try:
196
- if not islistuple(other):
197
- 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)
198
194
  self.fadd(other)
199
- except Exception as x:
200
- 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)
201
198
 
202
199
  @property_RO
203
200
  def _M1(self):
@@ -209,6 +206,25 @@ class _FstatsBase(_FstatsNamed):
209
206
  '''(INTERNAL) get the 2nd Moment accumulator.'''
210
207
  return self._Ms[1]
211
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
+
212
228
 
213
229
  class Fcook(_FstatsBase):
214
230
  '''U{Cook<https://www.JohnDCook.com/blog/skewness_kurtosis>}'s
@@ -222,12 +238,13 @@ class Fcook(_FstatsBase):
222
238
  def __init__(self, xs=None, name=NN):
223
239
  '''New L{Fcook} stats accumulator.
224
240
 
225
- @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).
226
243
  @kwarg name: Optional name (C{str}).
227
244
 
228
245
  @see: Method L{Fcook.fadd}.
229
246
  '''
230
- self._Ms = tuple(Fsum() for _ in range(4)) # 1st, 2nd ... Moment
247
+ self._Ms = _Tuple(Fsum() for _ in range(4)) # 1st, 2nd ... Moment
231
248
  if name:
232
249
  self.name = name
233
250
  if xs:
@@ -236,14 +253,15 @@ class Fcook(_FstatsBase):
236
253
  def __iadd__(self, other):
237
254
  '''Add B{C{other}} to this L{Fcook} instance.
238
255
 
239
- @arg other: An L{Fcook} instance or C{Scalar}s, meaning
240
- 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).
241
259
 
242
260
  @return: This instance, updated (L{Fcook}).
243
261
 
244
- @raise TypeError: Invalid B{C{other}} type.
262
+ @raise TypeError: Invalid B{C{other}}.
245
263
 
246
- @raise ValueError: Invalid B{C{other}}.
264
+ @raise ValueError: Invalid or non-finite B{C{other}}.
247
265
 
248
266
  @see: Method L{Fcook.fadd}.
249
267
  '''
@@ -256,9 +274,9 @@ class Fcook(_FstatsBase):
256
274
  B1, B2, B3, B4 = other._Ms
257
275
 
258
276
  n = na + nb
259
- n_ = _Float(n)
277
+ _n = _1_0 / n
260
278
  D = A1 - B1 # b1 - a1
261
- Dn = D / n_
279
+ Dn = D * _n
262
280
  Dn2 = Dn**2 # d**2 / n**2
263
281
  nab = na * nb
264
282
  Dn3 = Dn2 * (D * nab)
@@ -271,16 +289,16 @@ class Fcook(_FstatsBase):
271
289
  A4 += (Dn * Dn3) * (na2 - nab + nb2) # d**4 / n**3
272
290
 
273
291
  A3 += B3
274
- A3 += (A2 * na - (B2 * nb)) * (Dn * _3_0)
292
+ A3 += (A2 * na - (B2 * nb)) * (Dn * _3_0)
275
293
  A3 += Dn3 * (na - nb)
276
294
 
277
295
  A2 += B2
278
- A2 += Dn2 * (nab / n_)
296
+ A2 += Dn2 * (nab * _n)
279
297
 
280
298
  B1n = B1 * nb # if other is self
281
299
  A1 *= na
282
300
  A1 += B1n
283
- A1 *= 1 / n_ # /= chokes PyChecker
301
+ A1 *= _n
284
302
 
285
303
  # self._Ms = A1, A2, A3, A4
286
304
  self._n = n
@@ -293,18 +311,18 @@ class Fcook(_FstatsBase):
293
311
  def fadd(self, xs, sample=False):
294
312
  '''Accumulate and return the current count.
295
313
 
296
- @arg xs: Iterable with additional values (C{Scalar}s,
297
- meaning C{scalar} or L{Fsum} instances).
298
- @kwarg sample: Return the I{sample} instead of the entire
299
- 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}).
300
318
 
301
319
  @return: Current, running (sample) count (C{int}).
302
320
 
303
321
  @raise OverflowError: Partial C{2sum} overflow.
304
322
 
305
- @raise TypeError: Non-scalar B{C{xs}} value.
323
+ @raise TypeError: Invalid B{C{xs}}.
306
324
 
307
- @raise ValueError: Invalid or non-finite B{C{xs}} value.
325
+ @raise ValueError: Invalid or non-finite B{C{xs}}.
308
326
 
309
327
  @see: U{online_kurtosis<https://WikiPedia.org/wiki/
310
328
  Algorithms_for_calculating_variance>}.
@@ -312,7 +330,7 @@ class Fcook(_FstatsBase):
312
330
  n = self._n
313
331
  if xs:
314
332
  M1, M2, M3, M4 = self._Ms
315
- for x in _2Floats(xs=xs):
333
+ for x in _2Floats(xs=xs): # PYCHOK yield
316
334
  n1 = n
317
335
  n += 1
318
336
  D = x - M1
@@ -342,38 +360,38 @@ class Fcook(_FstatsBase):
342
360
  self._n = n
343
361
  return _sampled(n, sample)
344
362
 
345
- def fjb(self, xs=None, sample=True, excess=True):
363
+ def fjb(self, xs=None, excess=True, sample=True):
346
364
  '''Accumulate and compute the current U{Jarque-Bera
347
365
  <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
348
366
 
349
- @kwarg xs: Iterable with additional values (C{Scalar}s).
350
- @kwarg sample: Return the I{sample} normality (C{bool}), default.
351
- @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}).
352
372
 
353
373
  @return: Current, running (sample) Jarque-Bera normality (C{float}).
354
374
 
355
375
  @see: Method L{Fcook.fadd}.
356
376
  '''
357
- n = self.fadd(xs, sample=sample)
358
- k = self.fkurtosis(sample=sample, excess=excess) / _2_0
359
- s = self.fskewness(sample=sample)
360
- return n * hypot2(k, s) / _6_0
377
+ return _Float(self._JarqueBera(xs, excess, sample=sample))
361
378
 
362
379
  def fjb_(self, *xs, **sample_excess):
363
380
  '''Accumulate and compute the current U{Jarque-Bera
364
381
  <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
365
382
 
366
- @see: Method L{Fcook.fjb}.
383
+ @see: Method L{Fcook.fjb} for further details.
367
384
  '''
368
385
  return self.fjb(xs, **sample_excess)
369
386
 
370
- def fkurtosis(self, xs=None, sample=False, excess=True):
387
+ def fkurtosis(self, xs=None, excess=True, **sample):
371
388
  '''Accumulate and return the current kurtosis.
372
389
 
373
- @kwarg xs: Iterable with additional values (C{Scalar}s).
374
- @kwarg sample: Return the I{sample} instead of the entire
375
- I{population} kurtosis (C{bool}).
390
+ @arg xs: Iterable of additional values (each C{scalar} or an
391
+ L{Fsum} or L{Fsum2Tuple} instance).
376
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}).
377
395
 
378
396
  @return: Current, running (sample) kurtosis or I{excess} kurtosis (C{float}).
379
397
 
@@ -382,32 +400,21 @@ class Fcook(_FstatsBase):
382
400
 
383
401
  @see: Method L{Fcook.fadd}.
384
402
  '''
385
- k, n = _0_0, self.fadd(xs, sample=sample)
386
- if n > 0:
387
- _, M2, _, M4 = self._Ms
388
- m2 = _Float(M2 * M2)
389
- if m2:
390
- K, x = (M4 * (n / m2)), _3_0
391
- if sample and 2 < n < len(self):
392
- d = _Float((n - 1) * (n - 2))
393
- K *= (n + 1) * (n + 2) / d
394
- x *= n**2 / d
395
- if excess:
396
- K -= x
397
- k = K.fsum()
398
- return k
403
+ n = self.fadd(xs, **sample)
404
+ return _Float(self._Kurtosis(n, excess, **sample))
399
405
 
400
- def fkurtosis_(self, *xs, **sample_excess):
406
+ def fkurtosis_(self, *xs, **excess_sample):
401
407
  '''Accumulate and return the current kurtosis.
402
408
 
403
- @see: Method L{Fcook.fkurtosis}.
409
+ @see: Method L{Fcook.fkurtosis} for further details.
404
410
  '''
405
- return self.fkurtosis(xs, **sample_excess)
411
+ return self.fkurtosis(xs, **excess_sample)
406
412
 
407
413
  def fmedian(self, xs=None):
408
414
  '''Accumulate and return the current median.
409
415
 
410
- @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).
411
418
 
412
419
  @return: Current, running median (C{float}).
413
420
 
@@ -416,24 +423,22 @@ class Fcook(_FstatsBase):
416
423
  https://TowardsDataScience.com/skewness-kurtosis-simplified-1338e094fc85>}
417
424
  and method L{Fcook.fadd}.
418
425
  '''
419
- # skewness = 3 * (mean - median) / stdev, i.e.
420
- # median = mean - skewness * stdef / 3
421
- m = _Float(self._M1) if xs is None else self.fmean(xs)
422
- return m - self.fskewness() * self.fstdev() / _3_0
426
+ return _Float(self._Median(xs))
423
427
 
424
428
  def fmedian_(self, *xs):
425
429
  '''Accumulate and return the current median.
426
430
 
427
- @see: Method L{Fcook.fmedian}.
431
+ @see: Method L{Fcook.fmedian} for further details.
428
432
  '''
429
433
  return self.fmedian(xs)
430
434
 
431
- def fskewness(self, xs=None, sample=False):
435
+ def fskewness(self, xs=None, **sample):
432
436
  '''Accumulate and return the current skewness.
433
437
 
434
- @kwarg xs: Iterable with additional values (C{Scalar}s).
435
- @kwarg sample: Return the I{sample} instead of the entire
436
- 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}).
437
442
 
438
443
  @return: Current, running (sample) skewness (C{float}).
439
444
 
@@ -442,29 +447,70 @@ class Fcook(_FstatsBase):
442
447
 
443
448
  @see: Method L{Fcook.fadd}.
444
449
  '''
445
- s, n = _0_0, self.fadd(xs, sample=sample)
446
- if n > 0:
447
- _, M2, M3, _ = self._Ms
448
- m = _Float(M2**3)
449
- if m > 0:
450
- S = M3 * sqrt(_Float(n) / m)
451
- if sample and 1 < n < len(self):
452
- S *= (n + 1) / _Float(n - 1)
453
- s = S.fsum()
454
- return s
450
+ n = self.fadd(xs, **sample)
451
+ return _Float(self._Skewness(n, **sample))
455
452
 
456
453
  def fskewness_(self, *xs, **sample):
457
454
  '''Accumulate and return the current skewness.
458
455
 
459
- @see: Method L{Fcook.fskewness}.
456
+ @see: Method L{Fcook.fskewness} for further details.
460
457
  '''
461
458
  return self.fskewness(xs, **sample)
462
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
+
463
509
  def toFwelford(self, name=NN):
464
510
  '''Return an L{Fwelford} equivalent.
465
511
  '''
466
512
  f = Fwelford(name=name or self.name)
467
- f._Ms = self._M1.fcopy(), self._M2.fcopy() # deep=False
513
+ f._Ms = self._M1.copy(), self._M2.copy() # deep=False
468
514
  f._n = self._n
469
515
  return f
470
516
 
@@ -478,7 +524,8 @@ class Fwelford(_FstatsBase):
478
524
  def __init__(self, xs=None, name=NN):
479
525
  '''New L{Fwelford} stats accumulator.
480
526
 
481
- @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).
482
529
  @kwarg name: Optional name (C{str}).
483
530
 
484
531
  @see: Method L{Fwelford.fadd}.
@@ -492,12 +539,13 @@ class Fwelford(_FstatsBase):
492
539
  def __iadd__(self, other):
493
540
  '''Add B{C{other}} to this L{Fwelford} instance.
494
541
 
495
- @arg other: An L{Fwelford} or L{Fcook} instance or C{Scalar}s,
496
- 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).
497
545
 
498
546
  @return: This instance, updated (L{Fwelford}).
499
547
 
500
- @raise TypeError: Invalid B{C{other}} type.
548
+ @raise TypeError: Invalid B{C{other}}.
501
549
 
502
550
  @raise ValueError: Invalid B{C{other}}.
503
551
 
@@ -513,18 +561,18 @@ class Fwelford(_FstatsBase):
513
561
  M_, S_ = other._Ms
514
562
 
515
563
  n = na + nb
516
- n_ = _Float(n)
564
+ _n = _1_0 / n
517
565
 
518
566
  D = M_ - M
519
567
  D *= D # D**2
520
- D *= na * nb / n_
568
+ D *= na * nb * _n
521
569
  S += D
522
570
  S += S_
523
571
 
524
572
  Mn = M_ * nb # if other is self
525
573
  M *= na
526
574
  M += Mn
527
- M *= 1 / n_ # /= chokes PyChecker
575
+ M *= _n
528
576
 
529
577
  # self._Ms = M, S
530
578
  self._n = n
@@ -540,23 +588,23 @@ class Fwelford(_FstatsBase):
540
588
  def fadd(self, xs, sample=False):
541
589
  '''Accumulate and return the current count.
542
590
 
543
- @arg xs: Iterable with additional values (C{Scalar}s,
544
- meaning C{scalar} or L{Fsum} instances).
545
- @kwarg sample: Return the I{sample} instead of the entire
546
- 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}).
547
595
 
548
596
  @return: Current, running (sample) count (C{int}).
549
597
 
550
598
  @raise OverflowError: Partial C{2sum} overflow.
551
599
 
552
- @raise TypeError: Non-scalar B{C{xs}} value.
600
+ @raise TypeError: Invalid B{C{xs}}.
553
601
 
554
- @raise ValueError: Invalid or non-finite B{C{xs}} value.
602
+ @raise ValueError: Invalid or non-finite B{C{xs}}.
555
603
  '''
556
604
  n = self._n
557
605
  if xs:
558
606
  M, S = self._Ms
559
- for x in _2Floats(xs=xs):
607
+ for x in _2Floats(xs=xs): # PYCHOK yield
560
608
  n += 1
561
609
  D = x - M
562
610
  M += D / n
@@ -575,14 +623,16 @@ class Flinear(_FstatsNamed):
575
623
  def __init__(self, xs=None, ys=None, Fstats=Fwelford, name=NN):
576
624
  '''New L{Flinear} regression accumulator.
577
625
 
578
- @kwarg xs: Iterable with initial C{x} values (C{Scalar}s).
579
- @kwarg ys: Iterable with initial C{y} values (C{Scalar}s).
580
- @kwarg Fstats: Stats class for C{x} and C{y} values (L{Fcook}
581
- 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}).
582
632
  @kwarg name: Optional name (C{str}).
583
633
 
584
- @raise TypeError: Invalid B{C{Fs}}, not L{Fcook} or
585
- L{Fwelford}.
634
+ @raise TypeError: B{C{Fstats}} not L{Fcook} or L{Fwelford}.
635
+
586
636
  @see: Method L{Flinear.fadd}.
587
637
  '''
588
638
  _xsubclassof(Fcook, Fwelford, Fstats=Fstats)
@@ -598,8 +648,8 @@ class Flinear(_FstatsNamed):
598
648
  def __iadd__(self, other):
599
649
  '''Add B{C{other}} to this instance.
600
650
 
601
- @arg other: An L{Flinear} instance or C{Scalar} pairs,
602
- 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_}.
603
653
 
604
654
  @return: This instance, updated (L{Flinear}).
605
655
 
@@ -614,126 +664,139 @@ class Flinear(_FstatsNamed):
614
664
  if _isAn(other, Flinear):
615
665
  if len(other) > 0:
616
666
  if len(self) > 0:
617
- n = other._n
618
- S = other._S
619
- X = other._X
620
- Y = other._Y
621
- D = (X._M1 - self._X._M1) * \
622
- (Y._M1 - self._Y._M1) * \
623
- (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
624
674
  self._n += n
625
- self._S += S + D
626
- self._X += X
627
- self._Y += Y
628
675
  else:
629
676
  self._copy(self, other)
630
677
  else:
631
678
  try:
632
- if not islistuple(other):
633
- raise TypeError(_SPACE_(_invalid_, _other_))
634
- elif isodd(len(other)):
635
- raise ValueError(Fmt.PAREN(isodd=Fmt.PAREN(len=_other_)))
679
+ _xiterable(other)
636
680
  self.fadd_(*other)
637
681
  except Exception as x:
638
- raise _xError(x, _SPACE_(self, _iadd_op_, repr(other)))
682
+ op = _SPACE_(self, _iadd_op_, repr(other))
683
+ raise _xError(x, op)
639
684
  return self
640
685
 
641
- def _copy(self, c, s):
642
- '''(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}.
643
698
  '''
644
- _xinstanceof(Flinear, c=c, s=s)
645
- c._n = s._n
646
- c._S = s._S.fcopy(deep=False)
647
- c._X = s._X.fcopy(deep=False)
648
- c._Y = s._Y.fcopy(deep=False)
649
- return c
699
+ return self._Sampled(self._X._Stdev(**sample) *
700
+ self._Y._Stdev(**sample), **sample)
650
701
 
651
702
  def fadd(self, xs, ys, sample=False):
652
703
  '''Accumulate and return the current count.
653
704
 
654
- @arg xs: Iterable with additional C{x} values (C{Scalar}s),
655
- meaning C{scalar} or L{Fsum} instances).
656
- @arg ys: Iterable with additional C{y} values (C{Scalar}s,
657
- meaning C{scalar} or L{Fsum} instances).
658
- @kwarg sample: Return the I{sample} instead of the entire
659
- 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}).
660
711
 
661
712
  @return: Current, running (sample) count (C{int}).
662
713
 
663
714
  @raise OverflowError: Partial C{2sum} overflow.
664
715
 
665
- @raise TypeError: Non-scalar B{C{xs}} or B{C{ys}} value.
716
+ @raise TypeError: Invalid B{C{xs}} or B{C{ys}}.
666
717
 
667
- @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}}.
668
719
  '''
669
720
  n = self._n
670
721
  if xs and ys:
671
722
  S = self._S
672
723
  X = self._X
673
724
  Y = self._Y
674
- for x, y in _zip(_2Floats(xs=xs), _2Floats(ys=ys)): # strict=True
725
+ for x, y in _zip(_2Floats(xs=xs), _2Floats(ys=ys)): # PYCHOK strict=True
675
726
  n1 = n
676
727
  n += 1
677
728
  if n1 > 0:
678
- S += (X._M1 - x) * (Y._M1 - y) * (n1 / _Float(n))
729
+ S += (X._M1 - x) * (Y._M1 - y) * (n1 / n)
679
730
  X += x
680
731
  Y += y
681
- self._n = n
732
+ self._n = n
682
733
  return _sampled(n, sample)
683
734
 
684
735
  def fadd_(self, *x_ys, **sample):
685
736
  '''Accumulate and return the current count.
686
737
 
687
- @arg x_ys: Individual, alternating C{x, y, x, y, ...}
688
- 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).
689
741
 
690
- @see: Method C{Flinear.fadd}.
742
+ @see: Method C{Flinear.fadd} for further details.
691
743
  '''
744
+ if isodd(len(x_ys)):
745
+ t = _SPACE_(_odd_, len.__name__)
746
+ raise _ValueError(t, len(x_ys))
692
747
  return self.fadd(x_ys[0::2], x_ys[1::2], **sample)
693
748
 
694
- def fcorrelation(self, sample=False):
749
+ def fcorrelation(self, **sample):
695
750
  '''Return the current, running (sample) correlation (C{float}).
696
751
 
697
- @kwarg sample: Return the I{sample} instead of the entire
698
- 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}).
699
754
  '''
700
- return self._sampled(self.x.fstdev(sample=sample) *
701
- self.y.fstdev(sample=sample), sample)
755
+ return _Float(self._Correlation(**sample))
702
756
 
703
- def fintercept(self, sample=False):
757
+ def fintercept(self, **sample):
704
758
  '''Return the current, running (sample) intercept (C{float}).
705
759
 
706
- @kwarg sample: Return the I{sample} instead of the entire
707
- 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}).
708
762
  '''
709
- return _Float(self.y._M1 -
710
- (self.x._M1 * self.fslope(sample=sample)))
763
+ return _Float(self._Intercept(**sample))
711
764
 
712
- def fslope(self, sample=False):
765
+ def fslope(self, **sample):
713
766
  '''Return the current, running (sample) slope (C{float}).
714
767
 
715
- @kwarg sample: Return the I{sample} instead of the entire
716
- 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}).
717
770
  '''
718
- return self._sampled(self.x.fvariance(sample=sample), sample)
771
+ return _Float(self._Slope(**sample))
719
772
 
720
- 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):
721
779
  '''(INTERNAL) Compute the sampled or entire population result.
722
780
  '''
723
- t *= _Float(_sampled(self._n, sample))
724
- 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)
725
788
 
726
789
  @property_RO
727
790
  def x(self):
728
791
  '''Get the C{x} accumulator (L{Fcook} or L{Fwelford}).
729
792
  '''
730
- return self._X
793
+ return self._X # .copy()
731
794
 
732
795
  @property_RO
733
796
  def y(self):
734
797
  '''Get the C{y} accumulator (L{Fcook} or L{Fwelford}).
735
798
  '''
736
- return self._Y
799
+ return self._Y # .copy()
737
800
 
738
801
 
739
802
  __all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)
pygeodesy/fsums.py CHANGED
@@ -43,7 +43,7 @@ from pygeodesy.streprs import Fmt, fstr, unstr
43
43
  from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
44
44
 
45
45
  __all__ = _ALL_LAZY.fsums
46
- __version__ = '24.05.06'
46
+ __version__ = '24.05.08'
47
47
 
48
48
  _abs = abs
49
49
  _add_op_ = _PLUS_ # in .auxilats.auxAngle
@@ -71,7 +71,7 @@ _significant_ = 'significant'
71
71
  _sub_op_ = _DASH_ # in .auxilats.auxAngle
72
72
  _threshold_ = 'threshold'
73
73
  _truediv_op_ = _SLASH_
74
- _Tuple = tuple
74
+ _Tuple = tuple # in .fstats
75
75
  _divmod_op_ = _floordiv_op_ + _mod_op_
76
76
  _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle
77
77
 
@@ -86,19 +86,25 @@ def _2delta(*ab):
86
86
  return _Float(a if fabs(a) > fabs(b) else b)
87
87
 
88
88
 
89
- def _2error(unused):
89
+ def _2error(unused): # in .fstats
90
90
  '''(INTERNAL) Throw a C{not-finite} exception.
91
91
  '''
92
92
  raise ValueError(_not_finite_)
93
93
 
94
94
 
95
+ def _2finite(x):
96
+ '''(INTERNAL) return C{float(x)} if finite.
97
+ '''
98
+ x = _Float(x)
99
+ return x if _isfinite(x) else _2error(x)
100
+
101
+
95
102
  def _2float(index=None, **name_value): # in .fmath, .fstats
96
103
  '''(INTERNAL) Raise C{TypeError} or C{ValueError} if not scalar or infinite.
97
104
  '''
98
105
  n, v = name_value.popitem() # _xkwds_item2(name_value)
99
106
  try:
100
- v = _Float(v)
101
- return v if _isfinite(v) else _2error(v)
107
+ return _2finite(v)
102
108
  except Exception as X:
103
109
  raise _xError(X, Fmt.INDEX(n, index), v)
104
110
 
@@ -838,7 +844,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
838
844
  c = _ceil(s) + int(r) - 1
839
845
  while r > (c - s): # (s + r) > c
840
846
  c += 1
841
- return c
847
+ return c # _ceil(self._n_d)
842
848
 
843
849
  cmp = __cmp__
844
850
 
@@ -1149,7 +1155,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1149
1155
  f = _floor(s) + _floor(r) + 1
1150
1156
  while (f - s) > r: # f > (s + r)
1151
1157
  f -= 1
1152
- return f
1158
+ return f # _floor(self._n_d)
1153
1159
 
1154
1160
  # ffloordiv = __ifloordiv__ # for naming consistency
1155
1161
  # floordiv = __floordiv__ # for naming consistency
@@ -1222,7 +1228,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1222
1228
  @note: The precision running C{fsum} after a C{//=} or
1223
1229
  C{//} C{floor} division is C{int} in Python 3+.
1224
1230
  '''
1225
- return self._fprs2.fsum
1231
+ s, _ = self._fprs2
1232
+ return s # ._fprs2.fsum
1226
1233
 
1227
1234
  @Property_RO
1228
1235
  def _fprs2(self):
@@ -1259,7 +1266,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1259
1266
 
1260
1267
  @see: Method L{Fsum.fadd} for further details.
1261
1268
  '''
1262
- f = Fsum(*xs) if xs else _0_0
1269
+ f = xs[0] if len(xs) == 1 else (
1270
+ Fsum(*xs) if xs else _0_0)
1263
1271
  return self._fset(f)
1264
1272
 
1265
1273
  def _fset(self, other, as_is=True, n=0, up=True):
@@ -1356,7 +1364,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1356
1364
  return self._facc_1(xs)._fprs
1357
1365
 
1358
1366
  @property_RO
1359
- def _Fsum(self): # like L{Fsum2Tuple._Fsum}, for C{_2floats}.
1367
+ def _Fsum(self): # like L{Fsum2Tuple._Fsum}, for C{_2floats}, .fstats
1360
1368
  return self # NOT @Property_RO, see .copy and ._copy_2
1361
1369
 
1362
1370
  def Fsum_(self, *xs, **name):
@@ -1559,13 +1567,18 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1559
1567
  '''
1560
1568
  # assert isscalar(factor)
1561
1569
  if self._ps and self._finite(factor, op):
1562
- f = self if factor == _1_0 else (
1570
+ f = self if factor == _1_0 else (
1563
1571
  self._neg if factor == _N_1_0 else
1564
1572
  self._ps_mul(op, factor).as_iscalar)
1565
1573
  else:
1566
1574
  f = _0_0
1567
1575
  return f
1568
1576
 
1577
+ # @property_RO
1578
+ # def _n_d(self):
1579
+ # n, d = self.as_integer_ratio()
1580
+ # return n / d
1581
+
1569
1582
  @property_RO
1570
1583
  def _neg(self):
1571
1584
  '''(INTERNAL) Return C{Fsum(-self)} or scalar C{NEG0}.
@@ -1751,8 +1764,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1751
1764
  return ps
1752
1765
 
1753
1766
  def _ps_mul(self, op, *factors):
1754
- '''(INTERNAL) Multiply this instance' C{partials} with each
1755
- of the scalar B{C{factors}} and accumulate.
1767
+ '''(INTERNAL) Multiply this instance' C{partials} with
1768
+ each scalar C{factor} and accumulate into an C{Fsum}.
1756
1769
  '''
1757
1770
  def _pfs(ps, fs):
1758
1771
  if _len(ps) < _len(fs):
@@ -1763,7 +1776,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1763
1776
  p *= f
1764
1777
  yield p if _fin(p) else self._finite(p, op)
1765
1778
 
1766
- return _Psum(self._ps_acc([], _pfs(self._ps, factors), up=False))
1779
+ return Fsum()._facc_scalar(_pfs(self._ps, factors), up=False)
1767
1780
 
1768
1781
  @property_RO
1769
1782
  def _ps_neg(self):
@@ -1773,7 +1786,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1773
1786
  yield -p
1774
1787
 
1775
1788
  def _ps_1sum(self, *less):
1776
- '''(INTERNAL) Return the partials sum, 1-primed C{less} any scalars.
1789
+ '''(INTERNAL) Return the partials sum, 1-primed C{less} some scalars.
1777
1790
  '''
1778
1791
  def _1pls(ps, ls):
1779
1792
  yield _1_0
@@ -1800,6 +1813,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1800
1813
  return dict(ratio=q, R=t)
1801
1814
  return {}
1802
1815
 
1816
+ rdiv = __rtruediv__
1817
+
1803
1818
  @property_RO
1804
1819
  def real(self):
1805
1820
  '''Get the C{real} part of this instance (C{float}).
@@ -1966,7 +1981,7 @@ class DivMod2Tuple(_NamedTuple):
1966
1981
  _Units_ = (_Float_Int, Fsum)
1967
1982
 
1968
1983
 
1969
- class Fsum2Tuple(_NamedTuple):
1984
+ class Fsum2Tuple(_NamedTuple): # in .fstats
1970
1985
  '''2-Tuple C{(fsum, residual)} with the precision running C{fsum}
1971
1986
  and the C{residual}, the sum of the remaining partials. Each
1972
1987
  item is C{float} or C{int}.
@@ -2031,7 +2046,7 @@ class Fsum2Tuple(_NamedTuple):
2031
2046
  return self._Fsum._fprs2
2032
2047
 
2033
2048
  @Property_RO
2034
- def _Fsum(self): # this C{Fsum2Tuple} as L{Fsum}
2049
+ def _Fsum(self): # this C{Fsum2Tuple} as L{Fsum}, in .fstats
2035
2050
  s, r = _s_r(*self)
2036
2051
  ps = (r, s) if r else (s,)
2037
2052
  return _Psum(ps, name=self.name)
@@ -2085,7 +2100,7 @@ class Fsum2Tuple(_NamedTuple):
2085
2100
  '''
2086
2101
  return Fmt.PAREN(fstr(self, fmt=fmt, strepr=str, force=False, **prec_sep))
2087
2102
 
2088
- _Fsum_Fsum2Tuple_types = Fsum, Fsum2Tuple # PYCHOK in .fstats
2103
+ _Fsum_Fsum2Tuple_types = Fsum, Fsum2Tuple # PYCHOK lines
2089
2104
 
2090
2105
 
2091
2106
  class ResidualError(_ValueError):
pygeodesy/interns.py CHANGED
@@ -332,6 +332,7 @@ _null_ = 'null' # PYCHOK OK
332
332
  _number_ = 'number' # PYCHOK OK
333
333
  _numpy_ = 'numpy' # PYCHOK OK
334
334
  _Nv00_ = 'Nv00' # PYCHOK OK
335
+ _odd_ = 'odd' # PYCHOK OK
335
336
  _of_ = 'of' # PYCHOK OK
336
337
  _on_ = 'on' # PYCHOK OK
337
338
  _opposite_ = 'opposite' # PYCHOK OK
@@ -647,7 +648,7 @@ def _version_ints(vs):
647
648
  __all__ = (_NN_, # not MISSING!
648
649
  Str_.__name__, # classes
649
650
  machine.__name__) # in .lazily
650
- __version__ = '24.05.06'
651
+ __version__ = '24.05.08'
651
652
 
652
653
  if __name__ == '__main__':
653
654