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, property_RO

19# from pygeodesy.props import property_RO # from .named

20# from pygeodesy.streprs import Fmt # from .fsums

21

22# from math import sqrt # from .fmath

23

24__all__ = _ALL_LAZY.fstats

25__version__ = '24.04.07'

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

33

34

35 def _2Floats(xs, ys=False):

36 '''(INTERNAL) Yield each value as C{float} or L{Fsum}.

37 '''

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)

44

45 for i, x in enumerate(xs): # don't unravel Fsums

46 yield x if isinstance(x, _Floats) else _2f(i, x)

47

48

49 def _sampled(n, sample):

50 '''(INTERNAL) Return the sample or the entire count.

51 '''

52 return (n - 1) if sample and n > 0 else n

53

54

55 class _FstatsNamed(_Named):

56 '''(INTERNAL) Base class.

57 '''

58 _n = 0

59

60 def __add__(self, other):

61 '''Sum of this and a scalar, an L{Fsum} or an other instance.

62 '''

63 f = self.fcopy(name=self.__add__.__name__) # PYCHOK expected

64 f += other

65 return f

66

67 def __float__(self): # PYCHOK no cover

68 '''Not implemented.'''

69 return _NotImplemented(self)

70

71 def __int__(self): # PYCHOK no cover

72 '''Not implemented.'''

73 return _NotImplemented(self)

74

75 def __len__(self):

76 '''Return the I{total} number of accumulated values (C{int}).

77 '''

78 return self._n

79

80 def __neg__(self): # PYCHOK no cover

81 '''Not implemented.'''

82 return _NotImplemented(self)

83

84 def __radd__(self, other): # PYCHOK no cover

85 '''Not implemented.'''

86 return _NotImplemented(self, other)

87

88 def __str__(self):

89 return Fmt.SQUARE(self.named3, len(self))

90

91 def fcopy(self, deep=False, name=NN):

92 '''Copy this instance, C{shallow} or B{C{deep}}.

93 '''

94 n = name or self.fcopy.__name__

95 f = _Named.copy(self, deep=deep, name=n)

96 return self._copy(f, self) # PYCHOK expected

97

98 copy = fcopy

99

100

101 class _FstatsBase(_FstatsNamed):

102 '''(INTERNAL) Base running stats class.

103 '''

104 _Ms = ()

105

106 def _copy(self, c, s):

107 '''(INTERNAL) Copy C{B{c} = B{s}}.

108 '''

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

113

114 def fadd(self, xs, sample=False): # PYCHOK no cover

115 '''I{Must be overloaded}.'''

116 self._notOverloaded(xs, sample=sample)

117

118 def fadd_(self, *xs, **sample):

119 '''Accumulate and return the current count.

120

121 @see: Method C{fadd}.

122 '''

123 return self.fadd(xs, **sample)

124

125 def fmean(self, xs=None):

126 '''Accumulate and return the current mean.

127

128 @kwarg xs: Iterable with additional values (C{Scalar}s).

129

130 @return: Current, running mean (C{float}).

131

132 @see: Method C{fadd}.

133 '''

134 if xs:

135 self.fadd(xs)

136 return self._M1.fsum()

137

138 def fmean_(self, *xs):

139 '''Accumulate and return the current mean.

140

141 @see: Method C{fmean}.

142 '''

143 return self.fmean(xs)

144

145 def fstdev(self, xs=None, sample=False):

146 '''Accumulate and return the current standard deviation.

147

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}).

151

152 @return: Current, running (sample) standard deviation (C{float}).

153

154 @see: Method C{fadd}.

155 '''

156 v = self.fvariance(xs, sample=sample)

157 return sqrt(v) if v > 0 else _0_0

158

159 def fstdev_(self, *xs, **sample):

160 '''Accumulate and return the current standard deviation.

161

162 @see: Method C{fstdev}.

163 '''

164 return self.fstdev(xs, **sample)

165

166 def fvariance(self, xs=None, sample=False):

167 '''Accumulate and return the current variance.

168

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}).

172

173 @return: Current, running (sample) variance (C{float}).

174

175 @see: Method C{fadd}.

176 '''

177 n = self.fadd(xs, sample=sample)

178 return float(self._M2 / float(n)) if n > 0 else _0_0

179

180 def fvariance_(self, *xs, **sample):

181 '''Accumulate and return the current variance.

182

183 @see: Method C{fvariance}.

184 '''

185 return self.fvariance(xs, **sample)

186

187 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_))

196 self.fadd(other)

197 except Exception as x:

198 raise _xError(x, _SPACE_(self, _iadd_op_, repr(other)))

199

200 @property_RO

201 def _M1(self):

202 '''(INTERNAL) get the 1st Moment accumulator.'''

203 return self._Ms[0]

204

205 @property_RO

206 def _M2(self):

207 '''(INTERNAL) get the 2nd Moment accumulator.'''

208 return self._Ms[1]

209

210

211 class Fcook(_FstatsBase):

212 '''U{Cook<https://www.JohnDCook.com/blog/skewness_kurtosis>}'s

213 C{RunningStats} computing the running mean, median and

214 (sample) kurtosis, skewness, variance, standard deviation

215 and Jarque-Bera normality.

216

217 @see: L{Fwelford} and U{Higher-order statistics<https://

218 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.

219 '''

220 def __init__(self, xs=None, name=NN):

221 '''New L{Fcook} stats accumulator.

222

223 @kwarg xs: Iterable with initial values (C{Scalar}s).

224 @kwarg name: Optional name (C{str}).

225

226 @see: Method L{Fcook.fadd}.

227 '''

228 self._Ms = tuple(Fsum() for _ in range(4)) # 1st, 2nd ... Moment

229 if name:

230 self.name = name

231 if xs:

232 self.fadd(xs)

233

234 def __iadd__(self, other):

235 '''Add B{C{other}} to this L{Fcook} instance.

236

237 @arg other: An L{Fcook} instance or C{Scalar}s, meaning

238 one or more C{scalar} or L{Fsum} instances.

239

240 @return: This instance, updated (L{Fcook}).

241

242 @raise TypeError: Invalid B{C{other}} type.

243

244 @raise ValueError: Invalid B{C{other}}.

245

246 @see: Method L{Fcook.fadd}.

247 '''

248 if isinstance(other, Fcook):

249 nb = len(other)

250 if nb > 0:

251 na = len(self)

252 if na > 0:

253 A1, A2, A3, A4 = self._Ms

254 B1, B2, B3, B4 = other._Ms

255

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)

263

264 na2 = na**2

265 nb2 = nb**2

266 A4 += B4

267 A4 += (B3 * na - (A3 * nb)) * (Dn * _4_0)

268 A4 += (B2 * na2 + (A2 * nb2)) * (Dn2 * _6_0)

269 A4 += (Dn * Dn3) * (na2 - nab + nb2) # d**4 / n**3

270

271 A3 += B3

272 A3 += (A2 * na - (B2 * nb)) * (Dn * _3_0)

273 A3 += Dn3 * (na - nb)

274

275 A2 += B2

276 A2 += Dn2 * (nab / n_)

277

278 B1n = B1 * nb # if other is self

279 A1 *= na

280 A1 += B1n

281 A1 *= 1 / n_ # /= chokes PyChecker

282

283# self._Ms = A1, A2, A3, A4

284 self._n = n

285 else:

286 self._copy(self, other)

287 else:

288 self._iadd_other(other)

289 return self

290

291 def fadd(self, xs, sample=False):

292 '''Accumulate and return the current count.

293

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}).

298

299 @return: Current, running (sample) count (C{int}).

300

301 @raise OverflowError: Partial C{2sum} overflow.

302

303 @raise TypeError: Non-scalar B{C{xs}} value.

304

305 @raise ValueError: Invalid or non-finite B{C{xs}} value.

306

307 @see: U{online_kurtosis<https://WikiPedia.org/wiki/

308 Algorithms_for_calculating_variance>}.

309 '''

310 n = self._n

311 if xs:

312 M1, M2, M3, M4 = self._Ms

313 for x in _2Floats(xs):

314 n1 = n

315 n += 1

316 D = x - M1

317 Dn = D / n

318 if Dn:

319 Dn2 = Dn**2

320 if n1 > 1:

321 T1 = D * (Dn * n1)

322 T2 = T1 * (Dn * (n1 - 1))

323 T3 = T1 * (Dn2 * (n**2 - 3 * n1))

324 elif n1 > 0: # n1 == 1, n == 2

325 T1 = D * Dn

326 T2 = _0_0

327 T3 = T1 * Dn2

328 else:

329 T1 = T2 = T3 = _0_0

330 M4 += T3

331 M4 -= M3 * (Dn * _4_0)

332 M4 += M2 * (Dn2 * _6_0)

333

334 M3 += T2

335 M3 -= M2 * (Dn * _3_0)

336

337 M2 += T1

338 M1 += Dn

339# self._Ms = M1, M2, M3, M4

340 self._n = n

341 return _sampled(n, sample)

342

343 def fjb(self, xs=None, sample=True, excess=True):

344 '''Accumulate and compute the current U{Jarque-Bera

345 <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.

346

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.

350

351 @return: Current, running (sample) Jarque-Bera normality (C{float}).

352

353 @see: Method L{Fcook.fadd}.

354 '''

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

359

360 def fjb_(self, *xs, **sample_excess):

361 '''Accumulate and compute the current U{Jarque-Bera

362 <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.

363

364 @see: Method L{Fcook.fjb}.

365 '''

366 return self.fjb(xs, **sample_excess)

367

368 def fkurtosis(self, xs=None, sample=False, excess=True):

369 '''Accumulate and return the current kurtosis.

370

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}).

374 @kwarg excess: Return the I{excess} kurtosis (C{bool}), default.

375

376 @return: Current, running (sample) kurtosis or I{excess} kurtosis (C{float}).

377

378 @see: U{Kurtosis Formula<https://www.Macroption.com/kurtosis-formula>}

379 and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.

380

381 @see: Method L{Fcook.fadd}.

382 '''

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

397

398 def fkurtosis_(self, *xs, **sample_excess):

399 '''Accumulate and return the current kurtosis.

400

401 @see: Method L{Fcook.fkurtosis}.

402 '''

403 return self.fkurtosis(xs, **sample_excess)

404

405 def fmedian(self, xs=None):

406 '''Accumulate and return the current median.

407

408 @kwarg xs: Iterable with additional values (C{Scalar}s).

409

410 @return: Current, running median (C{float}).

411

412 @see: U{Pearson's Skewness Coefficients<https://MathWorld.Wolfram.com/

413 PearsonsSkewnessCoefficients.html>}, U{Skewness & Kurtosis Simplified

414 https://TowardsDataScience.com/skewness-kurtosis-simplified-1338e094fc85>}

415 and method L{Fcook.fadd}.

416 '''

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

421

422 def fmedian_(self, *xs):

423 '''Accumulate and return the current median.

424

425 @see: Method L{Fcook.fmedian}.

426 '''

427 return self.fmedian(xs)

428

429 def fskewness(self, xs=None, sample=False):

430 '''Accumulate and return the current skewness.

431

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}).

435

436 @return: Current, running (sample) skewness (C{float}).

437

438 @see: U{Skewness Formula<https://www.Macroption.com/skewness-formula/>}

439 and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.

440

441 @see: Method L{Fcook.fadd}.

442 '''

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

453

454 def fskewness_(self, *xs, **sample):

455 '''Accumulate and return the current skewness.

456

457 @see: Method L{Fcook.fskewness}.

458 '''

459 return self.fskewness(xs, **sample)

460

461 def toFwelford(self, name=NN):

462 '''Return an L{Fwelford} equivalent.

463 '''

464 f = Fwelford(name=name or self.name)

465 f._Ms = self._M1.fcopy(), self._M2.fcopy() # deep=False

466 f._n = self._n

467 return f

468

469

470 class Fwelford(_FstatsBase):

471 '''U{Welford<https://WikiPedia.org/wiki/Algorithms_for_calculating_variance>}'s

472 accumulator computing the running mean, (sample) variance and standard deviation.

473

474 @see: U{Cook<https://www.JohnDCook.com/blog/standard_deviation/>} and L{Fcook}.

475 '''

476 def __init__(self, xs=None, name=NN):

477 '''New L{Fwelford} stats accumulator.

478

479 @kwarg xs: Iterable with initial values (C{Scalar}s).

480 @kwarg name: Optional name (C{str}).

481

482 @see: Method L{Fwelford.fadd}.

483 '''

484 self._Ms = Fsum(), Fsum() # 1st and 2nd Moment

485 if name:

486 self.name = name

487 if xs:

488 self.fadd(xs)

489

490 def __iadd__(self, other):

491 '''Add B{C{other}} to this L{Fwelford} instance.

492

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.

495

496 @return: This instance, updated (L{Fwelford}).

497

498 @raise TypeError: Invalid B{C{other}} type.

499

500 @raise ValueError: Invalid B{C{other}}.

501

502 @see: Method L{Fwelford.fadd} and U{Parallel algorithm<https//

503 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.

504 '''

505 if isinstance(other, Fwelford):

506 nb = len(other)

507 if nb > 0:

508 na = len(self)

509 if na > 0:

510 M, S = self._Ms

511 M_, S_ = other._Ms

512

513 n = na + nb

514 n_ = float(n)

515

516 D = M_ - M

517 D *= D # D**2

518 D *= na * nb / n_

519 S += D

520 S += S_

521

522 Mn = M_ * nb # if other is self

523 M *= na

524 M += Mn

525 M *= 1 / n_ # /= chokes PyChecker

526

527# self._Ms = M, S

528 self._n = n

529 else:

530 self._copy(self, other)

531

532 elif isinstance(other, Fcook):

533 self += other.toFwelford()

534 else:

535 self._iadd_other(other)

536 return self

537

538 def fadd(self, xs, sample=False):

539 '''Accumulate and return the current count.

540

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}).

545

546 @return: Current, running (sample) count (C{int}).

547

548 @raise OverflowError: Partial C{2sum} overflow.

549

550 @raise TypeError: Non-scalar B{C{xs}} value.

551

552 @raise ValueError: Invalid or non-finite B{C{xs}} value.

553 '''

554 n = self._n

555 if xs:

556 M, S = self._Ms

557 for x in _2Floats(xs):

558 n += 1

559 D = x - M

560 M += D / n

561 D *= x - M

562 S += D

563# self._Ms = M, S

564 self._n = n

565 return _sampled(n, sample)

566

567

568 class Flinear(_FstatsNamed):

569 '''U{Cook<https://www.JohnDCook.com/blog/running_regression>}'s

570 C{RunningRegression} computing the running slope, intercept

571 and correlation of a linear regression.

572 '''

573 def __init__(self, xs=None, ys=None, Fstats=Fwelford, name=NN):

574 '''New L{Flinear} regression accumulator.

575

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}).

580 @kwarg name: Optional name (C{str}).

581

582 @raise TypeError: Invalid B{C{Fs}}, not L{Fcook} or

583 L{Fwelford}.

584 @see: Method L{Flinear.fadd}.

585 '''

586 _xsubclassof(Fcook, Fwelford, Fstats=Fstats)

587 if name:

588 self.name = name

589

590 self._S = Fsum(name=name)

591 self._X = Fstats(name=name)

592 self._Y = Fstats(name=name)

593 if xs and ys:

594 self.fadd(xs, ys)

595

596 def __iadd__(self, other):

597 '''Add B{C{other}} to this instance.

598

599 @arg other: An L{Flinear} instance or C{Scalar} pairs,

600 meaning C{scalar} or L{Fsum} instances.

601

602 @return: This instance, updated (L{Flinear}).

603

604 @raise TypeError: Invalid B{C{other}} or the B{C{other}}

605 and these C{x} and C{y} accumulators

606 are not compatible.

607

608 @raise ValueError: Invalid or odd-length B{C{other}}.

609

610 @see: Method L{Flinear.fadd_}.

611 '''

612 if isinstance(other, Flinear):

613 if len(other) > 0:

614 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))

622 self._n += n

623 self._S += S + D

624 self._X += X

625 self._Y += Y

626 else:

627 self._copy(self, other)

628 else:

629 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_)))

634 self.fadd_(*other)

635 except Exception as x:

636 raise _xError(x, _SPACE_(self, _iadd_op_, repr(other)))

637 return self

638

639 def _copy(self, c, s):

640 '''(INTERNAL) Copy C{B{c} = B{s}}.

641 '''

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

648

649 def fadd(self, xs, ys, sample=False):

650 '''Accumulate and return the current count.

651

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}).

658

659 @return: Current, running (sample) count (C{int}).

660

661 @raise OverflowError: Partial C{2sum} overflow.

662

663 @raise TypeError: Non-scalar B{C{xs}} or B{C{ys}} value.

664

665 @raise ValueError: Invalid or non-finite B{C{xs}} or B{C{ys}} value.

666 '''

667 n = self._n

668 if xs and ys:

669 S = self._S

670 X = self._X

671 Y = self._Y

672 for x, y in _zip(_2Floats(xs), _2Floats(ys, ys=True)): # strict=True

673 n1 = n

674 n += 1

675 if n1 > 0:

676 S += (X._M1 - x) * (Y._M1 - y) * (n1 / float(n))

677 X += x

678 Y += y

679 self._n = n

680 return _sampled(n, sample)

681

682 def fadd_(self, *x_ys, **sample):

683 '''Accumulate and return the current count.

684

685 @arg x_ys: Individual, alternating C{x, y, x, y, ...}

686 positional values (C{Scalar}s).

687

688 @see: Method C{Flinear.fadd}.

689 '''

690 return self.fadd(x_ys[0::2], x_ys[1::2], **sample)

691

692 def fcorrelation(self, sample=False):

693 '''Return the current, running (sample) correlation (C{float}).

694

695 @kwarg sample: Return the I{sample} instead of the entire

696 I{population} correlation (C{bool}).

697 '''

698 return self._sampled(self.x.fstdev(sample=sample) *

699 self.y.fstdev(sample=sample), sample)

700

701 def fintercept(self, sample=False):

702 '''Return the current, running (sample) intercept (C{float}).

703

704 @kwarg sample: Return the I{sample} instead of the entire

705 I{population} intercept (C{bool}).

706 '''

707 return float(self.y._M1 -

708 (self.x._M1 * self.fslope(sample=sample)))

709

710 def fslope(self, sample=False):

711 '''Return the current, running (sample) slope (C{float}).

712

713 @kwarg sample: Return the I{sample} instead of the entire

714 I{population} slope (C{bool}).

715 '''

716 return self._sampled(self.x.fvariance(sample=sample), sample)

717

718 def _sampled(self, t, sample):

719 '''(INTERNAL) Compute the sampled or entire population result.

720 '''

721 t *= float(_sampled(self._n, sample))

722 return float(self._S / t) if t else _0_0

723

724 @property_RO

725 def x(self):

726 '''Get the C{x} accumulator (L{Fcook} or L{Fwelford}).

727 '''

728 return self._X

729

730 @property_RO

731 def y(self):

732 '''Get the C{y} accumulator (L{Fcook} or L{Fwelford}).

733 '''

734 return self._Y

735

736

737__all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)

738

739# **) MIT License

740#

741# Copyright (C) 2021-2024 -- mrJean1 at Gmail -- All Rights Reserved.

742#

743# Permission is hereby granted, free of charge, to any person obtaining a

744# copy of this software and associated documentation files (the "Software"),

745# to deal in the Software without restriction, including without limitation

746# the rights to use, copy, modify, merge, publish, distribute, sublicense,

747# and/or sell copies of the Software, and to permit persons to whom the

748# Software is furnished to do so, subject to the following conditions:

749#

750# The above copyright notice and this permission notice shall be included

751# in all copies or substantial portions of the Software.

752#

753# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS

754# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

755# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL

756# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR

757# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,

758# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR

759# OTHER DEALINGS IN THE SOFTWARE.