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-2023 -- 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.