funcsim 0.2.8__tar.gz → 0.2.10__tar.gz

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.
Files changed (42) hide show
  1. {funcsim-0.2.8/funcsim.egg-info → funcsim-0.2.10}/PKG-INFO +2 -2
  2. {funcsim-0.2.8 → funcsim-0.2.10}/README.md +1 -1
  3. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/__init__.py +4 -2
  4. funcsim-0.2.10/funcsim/bounds.py +44 -0
  5. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/dependence.py +122 -27
  6. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/distfit.py +3 -29
  7. funcsim-0.2.10/funcsim/kde.py +838 -0
  8. funcsim-0.2.10/funcsim/mvbandwidth.py +296 -0
  9. funcsim-0.2.10/funcsim/rteu.py +222 -0
  10. funcsim-0.2.10/funcsim/samplesize.py +102 -0
  11. {funcsim-0.2.8 → funcsim-0.2.10/funcsim.egg-info}/PKG-INFO +2 -2
  12. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim.egg-info/SOURCES.txt +4 -0
  13. {funcsim-0.2.8 → funcsim-0.2.10}/setup.py +1 -1
  14. funcsim-0.2.8/funcsim/kde.py +0 -268
  15. {funcsim-0.2.8 → funcsim-0.2.10}/LICENSE +0 -0
  16. {funcsim-0.2.8 → funcsim-0.2.10}/MANIFEST.in +0 -0
  17. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/addist.py +0 -0
  18. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/conversions.py +0 -0
  19. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/copcompare.py +0 -0
  20. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/copfit.py +0 -0
  21. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/core.py +0 -0
  22. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/cpt.py +0 -0
  23. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/cvmdist.py +0 -0
  24. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/ecdfgof.py +0 -0
  25. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/edf.py +0 -0
  26. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/eut.py +0 -0
  27. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/imanconover.py +0 -0
  28. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/ksdist.py +0 -0
  29. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/multicore.py +0 -0
  30. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/nearby.py +0 -0
  31. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/plotting.py +0 -0
  32. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/rdarrays.py +0 -0
  33. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/screen.py +0 -0
  34. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/shapiro.py +0 -0
  35. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/shrinkage.py +0 -0
  36. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/testsim.py +0 -0
  37. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/utests.py +0 -0
  38. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim/vect.py +0 -0
  39. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim.egg-info/dependency_links.txt +0 -0
  40. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim.egg-info/requires.txt +0 -0
  41. {funcsim-0.2.8 → funcsim-0.2.10}/funcsim.egg-info/top_level.txt +0 -0
  42. {funcsim-0.2.8 → funcsim-0.2.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: funcsim
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: functional, simple stochastic simulation
5
5
  Home-page: https://github.com/h-bryant/funcsim
6
6
  Author: Henry Bryant
@@ -55,7 +55,7 @@ Features:
55
55
  - painless multi-core simulations
56
56
  - fitting and comparison of univariate distributions, kernel density estimates, and goodness-of-fit tests
57
57
  - multivariate normal (fitted to data or specified by a mean vector and covariance matrix) and multivariate KDE distributions; Gaussian, Student's t, Clayton, Gumbel, and Frank copulas, fitted to pseudo-observations or specified by their parameters (implemented natively, no additional packages required); information-criterion comparison of copula families; Iman-Conover; covariance shrinkage
58
- - expected utility and cumulative prospect theory calculations
58
+ - expected utility, cumulative prospect theory, and regret-theoretical expected utility calculations
59
59
  - plotly-based fan charts and diagnostic plots
60
60
  - highly flexible, and as simple as possible (but no more)
61
61
  - functional paradigm
@@ -10,7 +10,7 @@ Features:
10
10
  - painless multi-core simulations
11
11
  - fitting and comparison of univariate distributions, kernel density estimates, and goodness-of-fit tests
12
12
  - multivariate normal (fitted to data or specified by a mean vector and covariance matrix) and multivariate KDE distributions; Gaussian, Student's t, Clayton, Gumbel, and Frank copulas, fitted to pseudo-observations or specified by their parameters (implemented natively, no additional packages required); information-criterion comparison of copula families; Iman-Conover; covariance shrinkage
13
- - expected utility and cumulative prospect theory calculations
13
+ - expected utility, cumulative prospect theory, and regret-theoretical expected utility calculations
14
14
  - plotly-based fan charts and diagnostic plots
15
15
  - highly flexible, and as simple as possible (but no more)
16
16
  - functional paradigm
@@ -39,6 +39,8 @@ from .cpt import weightPrelec2, cptBV
39
39
 
40
40
  from .eut import eut, utilIsoelastic, eutIsoelastic
41
41
 
42
+ from .rteu import rteu, regretPower
43
+
42
44
  from .plotting import fan, twofuncs, histpdf, dblscat, qqplot, show
43
45
 
44
46
  def version():
@@ -48,6 +50,6 @@ def version():
48
50
  Returns
49
51
  -------
50
52
  str
51
- The version string of the package (e.g., '0.2.8').
53
+ The version string of the package (e.g., '0.2.10').
52
54
  """
53
- return "0.2.8"
55
+ return "0.2.10"
@@ -0,0 +1,44 @@
1
+ """
2
+ Validation of optional support bounds, shared by `fit`, `compare`, and `Kde`.
3
+ """
4
+ import numbers
5
+ from typing import Optional
6
+ import numpy as np
7
+
8
+
9
+ def validate_bound(value, name: str) -> Optional[float]:
10
+ """
11
+ Return None (no bound) or the bound as a finite float.
12
+
13
+ Bools are rejected because they are the legacy "which group" flags of
14
+ `compare`.
15
+ """
16
+ if value is None:
17
+ return None
18
+ if isinstance(value, (bool, np.bool_)):
19
+ raise TypeError(f"{name} must be a float or None, not a bool")
20
+ if not isinstance(value, numbers.Real):
21
+ raise TypeError(f"{name} must be a float or None, "
22
+ f"not {type(value).__name__}")
23
+ bound = float(value)
24
+ if not np.isfinite(bound):
25
+ raise ValueError(f"{name} must be finite, not {value}")
26
+ return bound
27
+
28
+
29
+ def check_bounds_against_data(dataA: np.ndarray,
30
+ lower: Optional[float],
31
+ upper: Optional[float]) -> None:
32
+ """
33
+ Raise ValueError unless lower < upper and every observation lies in
34
+ [lower, upper] (observations exactly on a bound are allowed).
35
+ """
36
+ if lower is not None and upper is not None and not lower < upper:
37
+ raise ValueError(f"lowerBound ({lower}) must be strictly less than "
38
+ f"upperBound ({upper})")
39
+ if lower is not None and dataA.min() < lower:
40
+ raise ValueError(f"lowerBound ({lower}) exceeds the smallest "
41
+ f"observation ({dataA.min()})")
42
+ if upper is not None and dataA.max() > upper:
43
+ raise ValueError(f"upperBound ({upper}) is below the largest "
44
+ f"observation ({dataA.max()})")
@@ -5,6 +5,8 @@ from scipy import stats
5
5
  from typing import Generator, Optional, Sequence, Tuple, Union
6
6
  import warnings
7
7
  from . import conversions
8
+ from . import mvbandwidth
9
+ from .samplesize import check_sample_size, FIXED
8
10
  from . import copfit
9
11
  from . import nearby
10
12
  from . import shapiro
@@ -323,49 +325,130 @@ class MvKde():
323
325
  Input data array with variables in columns and observations
324
326
  in rows.
325
327
  bw : str or ArrayLike, optional
326
- Bandwidth selection method ('scott' or 'silverman'), or a K-by-K
327
- bandwidth covariance matrix in the units of the data, given as
328
- anything :func:`numpy.asarray` accepts (a nested list, a NumPy
329
- array, or a DataFrame). Default is 'scott'. The matrix in use is
330
- available as :attr:`bandwidth`.
328
+ Bandwidth selection method ('scott', 'silverman', 'lscv', or
329
+ 'scv'; see Notes), or a K-by-K bandwidth covariance matrix in the
330
+ units of the data, given as anything :func:`numpy.asarray`
331
+ accepts (a nested list, a NumPy array, or a DataFrame). Default
332
+ is 'scott'. The matrix in use is available as :attr:`bandwidth`.
333
+ full : bool, optional
334
+ If False (the default) a named method gives a diagonal bandwidth
335
+ matrix; if True it gives a full matrix, with off-diagonal entries
336
+ (see Notes). Ignored for a user-supplied matrix.
337
+
338
+ Raises
339
+ ------
340
+ ValueError
341
+ If `bw` is an unknown method name or a matrix of the wrong shape,
342
+ or if there are too few observations for the method and
343
+ structure chosen (see Notes).
344
+
345
+ Warns
346
+ -----
347
+ UserWarning
348
+ If there are enough observations to compute the bandwidth but too
349
+ few to trust the estimate (see Notes), if a cross-validation
350
+ criterion appears to have collapsed, or if more than a tenth of
351
+ the observations are tied.
331
352
 
332
353
  Notes
333
354
  -----
334
355
  Each call to :meth:`draw` consumes K + 1 values from ``ugen``, where K
335
356
  is the number of variables.
336
357
 
337
- Both rules of thumb give a diagonal bandwidth matrix whose entries are
338
- a common factor times the variance of each variable, with M
339
- observations and K variables:
340
-
341
- - Scott's rule: ``H = M ** (-2 / (K + 4)) * diag(s_1 ** 2, ..., s_K ** 2)``
342
- - Silverman's rule:
343
- ``H = ((K + 2) * M / 4) ** (-2 / (K + 4)) * diag(s_1 ** 2, ..., s_K ** 2)``
344
-
345
- where ``s_k`` is the standard deviation of variable k, computed with
346
- ``ddof=0`` (dividing by M). The two rules coincide when K = 2. These
347
- are the normal-reference rules of Scott (1992, eq. 6.42) and Silverman
348
- (1986, eq. 4.14), and the same expressions as
358
+ **Rules of thumb.** Both rules give a bandwidth matrix that is a
359
+ common factor times the sample (co)variance of the data, computed
360
+ with ``ddof=0`` (dividing by M), with M observations and K
361
+ variables: Scott's factor is ``M ** (-2 / (K + 4))`` and Silverman's
362
+ ``((K + 2) * M / 4) ** (-2 / (K + 4))``. With ``full=False`` the
363
+ factor multiplies ``diag(s_1 ** 2, ..., s_K ** 2)``; with
364
+ ``full=True`` it multiplies the full sample covariance matrix, so the
365
+ kernels are oriented along the data's correlation (the normal-scale
366
+ rule of Wand and Jones, 1995, section 4.3, which is also what
367
+ :class:`scipy.stats.gaussian_kde` uses, up to its ``ddof=1``
368
+ covariance). The two rules coincide when K = 2. These are the
369
+ normal-reference rules of Scott (1992, eq. 6.42) and Silverman (1986,
370
+ eq. 4.14), and the diagonal ones use the same factors as
349
371
  :class:`scipy.stats.gaussian_kde`'s ``scotts_factor`` and
350
372
  ``silverman_factor``. Before version 0.2.8 the Silverman factor was
351
373
  computed as ``(4 * M / (K + 2)) ** (-1 / (K + 4))``, which is correct
352
374
  only at K = 2.
353
375
 
376
+ **Cross-validation.** ``bw='lscv'`` minimizes the least-squares
377
+ cross-validation criterion (Sain, Baggerly, and Scott, 1994; the
378
+ multivariate form of :class:`funcsim.Kde`'s), an estimate of the
379
+ integrated squared error, over diagonal (``full=False``) or all
380
+ positive definite (``full=True``) matrices, by Nelder-Mead from the
381
+ Silverman matrix of the same structure. ``bw='scv'`` minimizes the
382
+ smoothed cross-validation criterion (Hall, Marron, and Park, 1992;
383
+ Duong and Hazelton, 2005), which replaces the leave-one-out term by
384
+ a pilot-smoothed estimate of the squared bias and is far less
385
+ variable than ``'lscv'``; its scalar pilot bandwidth is the
386
+ one-stage normal-reference rule of R's ``ks`` package (its
387
+ ``Hscv(..., nstage=1, pilot="dscalar")``), computed on the
388
+ standardized data (``full=False``) or the sphered data
389
+ (``full=True``). Both criteria are closed-form sums over all pairs
390
+ of observations, so each evaluation costs of order ``M ** 2 * K``
391
+ operations and the search several hundred evaluations: well under a
392
+ second for a few hundred observations, tens of seconds for a few
393
+ thousand. ``'scv'`` is the recommended data-driven choice;
394
+ ``'lscv'`` with ``full=True`` in particular is known to be
395
+ unstable. Results agree with ``ks::Hlscv`` and ``ks::Hscv`` to
396
+ about one percent in tests (the remaining differences are
397
+ optimizer tolerances and ``ks``'s ``ddof=1`` covariance in the
398
+ pilot).
399
+
400
+ **Sample size.** The constructor raises ``ValueError`` when the
401
+ bandwidth cannot be computed: fewer than two observations; with
402
+ ``full=True``, no more observations than variables (the sample
403
+ covariance is singular); and for ``'lscv'`` or ``'scv'``, fewer
404
+ observations than bandwidth parameters (K, or K (K + 1) / 2 with
405
+ ``full=True``) plus two. It warns when the bandwidth can be
406
+ computed but the estimate is unreliable: fewer observations than
407
+ Silverman's (1986, Table 4.2) requirement for a usable estimate of a
408
+ K-variate normal density (4, 19, 67, 223, 768, 2790, 10,700, 43,700,
409
+ 187,000, and 842,000 for K = 1 to 10; always beyond K = 10), raised
410
+ to ten observations per variable for a full-matrix rule and ten per
411
+ bandwidth parameter for ``'lscv'`` or ``'scv'``.
412
+
354
413
  References
355
414
  ----------
415
+ Duong, T., & Hazelton, M. L. (2005). Cross-validation bandwidth
416
+ matrices for multivariate kernel density estimation. *Scandinavian
417
+ Journal of Statistics*, 32(3), 485-506.
418
+
419
+ Hall, P., Marron, J. S., & Park, B. U. (1992). Smoothed
420
+ cross-validation. *Probability Theory and Related Fields*, 92, 1-20.
421
+
422
+ Sain, S. R., Baggerly, K. A., & Scott, D. W. (1994). Cross-validation
423
+ of multivariate densities. *Journal of the American Statistical
424
+ Association*, 89(427), 807-817.
425
+
356
426
  Scott, D.W. (1992). Multivariate Density Estimation: Theory, Practice,
357
427
  and Visualization. John Wiley & Sons.
358
428
 
359
429
  Silverman, B.W. (1986). Density Estimation for Statistics and Data
360
430
  Analysis. Chapman and Hall.
431
+
432
+ Wand, M. P., & Jones, M. C. (1995). *Kernel Smoothing*. Chapman and
433
+ Hall.
361
434
  """
362
435
  def __init__(self,
363
436
  data: conversions.ArrayLike,
364
- bw: Union[str, conversions.ArrayLike, None] = 'scott'
437
+ bw: Union[str, conversions.ArrayLike, None] = 'scott',
438
+ full: bool = False
365
439
  ) -> None:
366
440
  self._data = conversions.alToArray(data)
367
441
  self._names = conversions.alColNames(data)
368
442
  (self._M, self._K) = self._data.shape
443
+ full = bool(full)
444
+
445
+ # too few observations for the method and structure: an error if
446
+ # the bandwidth cannot be computed, a warning if it is unreliable
447
+ if bw is None:
448
+ bw = 'scott'
449
+ family = bw if isinstance(bw, str) else FIXED
450
+ check_sample_size(self._M, self._K, family,
451
+ full and family != FIXED)
369
452
 
370
453
  # standardize data (store mean/std to undo later)
371
454
  self._means = self._data.mean(axis=0)
@@ -386,20 +469,31 @@ class MvKde():
386
469
  smult = ((self._K+2.0) * self._M / 4.0)**(-1.0 / (self._K+4.0))
387
470
  self._silverman = np.square(smult * np.diagflat(stdevs))
388
471
 
472
+ # full-matrix versions: the squared factor times the sample
473
+ # covariance (ddof=0) of the standardized data, i.e. its
474
+ # correlation matrix
475
+ if full:
476
+ S0 = np.cov(self._data, rowvar=False, bias=True)
477
+ S0 = np.asarray(S0, dtype=float).reshape(self._K, self._K)
478
+ self._scott = mult ** 2 * S0
479
+ self._silverman = smult ** 2 * S0
480
+
389
481
  # dispatch on the type of bw before comparing it with anything: a
390
482
  # NumPy array compared with a string evaluates elementwise under
391
483
  # NumPy 2, and its truth value is ambiguous
392
- if bw is None:
393
- bw = 'scott'
394
484
  if isinstance(bw, str):
395
485
  if bw == 'scott':
396
486
  self._bw = self._scott
397
487
  elif bw == 'silverman':
398
488
  self._bw = self._silverman
489
+ elif bw in ('lscv', 'scv'):
490
+ self._bw = mvbandwidth.select(bw, self._data, full,
491
+ self._silverman)
399
492
  else:
400
493
  raise ValueError(f"unknown bandwidth method '{bw}'; "
401
- f"expected 'scott', 'silverman', or a "
402
- f"{self._K}-by-{self._K} bandwidth matrix")
494
+ f"expected 'scott', 'silverman', 'lscv', "
495
+ f"'scv', or a {self._K}-by-{self._K} "
496
+ f"bandwidth matrix")
403
497
  else:
404
498
  H = np.asarray(bw, dtype=float)
405
499
  if H.shape != (self._K, self._K):
@@ -426,11 +520,12 @@ class MvKde():
426
520
  K-by-K covariance DataFrame whose index and columns are the
427
521
  variable names.
428
522
 
429
- For ``bw='scott'`` or ``bw='silverman'`` this is diagonal, the
430
- rule's factor squared times the variance of each variable with
431
- ``ddof=0`` (see the class notes); for a user-supplied matrix it is
432
- that matrix, or the nearest positive definite matrix to it if it
433
- was not positive definite.
523
+ For ``bw='scott'`` or ``bw='silverman'`` this is the rule's factor
524
+ times the variances (``full=False``, a diagonal matrix) or the
525
+ covariance matrix (``full=True``) of the data with ``ddof=0`` (see
526
+ the class notes); for ``'lscv'`` or ``'scv'`` the selected matrix;
527
+ for a user-supplied matrix that matrix, or the nearest positive
528
+ definite matrix to it if it was not positive definite.
434
529
  """
435
530
  D = np.diag(self._stds)
436
531
  return pd.DataFrame(D @ self._bw @ D, index=self._names,
@@ -1,5 +1,4 @@
1
1
 
2
- import numbers
3
2
  from collections import namedtuple
4
3
  import scipy.stats as stats
5
4
  from scipy import optimize
@@ -9,6 +8,7 @@ import warnings
9
8
  from collections.abc import Mapping
10
9
  from .ecdfgof import adtest, cvmtest
11
10
  from . import conversions
11
+ from .bounds import validate_bound, check_bounds_against_data
12
12
  from typing import Optional, Tuple
13
13
 
14
14
 
@@ -166,34 +166,8 @@ _PENALTY = 100.0 * np.log(np.finfo(float).max)
166
166
  # argument validation
167
167
 
168
168
 
169
- def _validate_bound(value, name: str) -> Optional[float]:
170
- # None (bound not fixed) or a finite real number; bools are rejected
171
- # because they are the legacy "which group" flags of `compare`
172
- if value is None:
173
- return None
174
- if isinstance(value, (bool, np.bool_)):
175
- raise TypeError(f"{name} must be a float or None, not a bool")
176
- if not isinstance(value, numbers.Real):
177
- raise TypeError(f"{name} must be a float or None, "
178
- f"not {type(value).__name__}")
179
- bound = float(value)
180
- if not np.isfinite(bound):
181
- raise ValueError(f"{name} must be finite, not {value}")
182
- return bound
183
-
184
-
185
- def _check_bounds_against_data(dataA: np.ndarray,
186
- lower: Optional[float],
187
- upper: Optional[float]) -> None:
188
- if lower is not None and upper is not None and not lower < upper:
189
- raise ValueError(f"lowerBound ({lower}) must be strictly less than "
190
- f"upperBound ({upper})")
191
- if lower is not None and dataA.min() < lower:
192
- raise ValueError(f"lowerBound ({lower}) exceeds the smallest "
193
- f"observation ({dataA.min()})")
194
- if upper is not None and dataA.max() > upper:
195
- raise ValueError(f"upperBound ({upper}) is below the largest "
196
- f"observation ({dataA.max()})")
169
+ _validate_bound = validate_bound
170
+ _check_bounds_against_data = check_bounds_against_data
197
171
 
198
172
 
199
173
  # ---------------------------------------------------------------------------