funcsim 0.2.7__tar.gz → 0.2.8__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 (37) hide show
  1. {funcsim-0.2.7/funcsim.egg-info → funcsim-0.2.8}/PKG-INFO +1 -1
  2. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/__init__.py +2 -2
  3. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/dependence.py +73 -14
  4. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/kde.py +22 -1
  5. {funcsim-0.2.7 → funcsim-0.2.8/funcsim.egg-info}/PKG-INFO +1 -1
  6. {funcsim-0.2.7 → funcsim-0.2.8}/setup.py +1 -1
  7. {funcsim-0.2.7 → funcsim-0.2.8}/LICENSE +0 -0
  8. {funcsim-0.2.7 → funcsim-0.2.8}/MANIFEST.in +0 -0
  9. {funcsim-0.2.7 → funcsim-0.2.8}/README.md +0 -0
  10. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/addist.py +0 -0
  11. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/conversions.py +0 -0
  12. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/copcompare.py +0 -0
  13. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/copfit.py +0 -0
  14. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/core.py +0 -0
  15. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/cpt.py +0 -0
  16. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/cvmdist.py +0 -0
  17. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/distfit.py +0 -0
  18. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/ecdfgof.py +0 -0
  19. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/edf.py +0 -0
  20. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/eut.py +0 -0
  21. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/imanconover.py +0 -0
  22. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/ksdist.py +0 -0
  23. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/multicore.py +0 -0
  24. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/nearby.py +0 -0
  25. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/plotting.py +0 -0
  26. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/rdarrays.py +0 -0
  27. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/screen.py +0 -0
  28. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/shapiro.py +0 -0
  29. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/shrinkage.py +0 -0
  30. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/testsim.py +0 -0
  31. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/utests.py +0 -0
  32. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim/vect.py +0 -0
  33. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim.egg-info/SOURCES.txt +0 -0
  34. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim.egg-info/dependency_links.txt +0 -0
  35. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim.egg-info/requires.txt +0 -0
  36. {funcsim-0.2.7 → funcsim-0.2.8}/funcsim.egg-info/top_level.txt +0 -0
  37. {funcsim-0.2.7 → funcsim-0.2.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: funcsim
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: functional, simple stochastic simulation
5
5
  Home-page: https://github.com/h-bryant/funcsim
6
6
  Author: Henry Bryant
@@ -48,6 +48,6 @@ def version():
48
48
  Returns
49
49
  -------
50
50
  str
51
- The version string of the package (e.g., '0.2.7').
51
+ The version string of the package (e.g., '0.2.8').
52
52
  """
53
- return "0.2.7"
53
+ return "0.2.8"
@@ -324,17 +324,44 @@ class MvKde():
324
324
  in rows.
325
325
  bw : str or ArrayLike, optional
326
326
  Bandwidth selection method ('scott' or 'silverman'), or a K-by-K
327
- bandwidth covariance matrix in the units of the data.
328
- Default is 'scott'.
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`.
329
331
 
330
332
  Notes
331
333
  -----
332
334
  Each call to :meth:`draw` consumes K + 1 values from ``ugen``, where K
333
335
  is the number of variables.
336
+
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
349
+ :class:`scipy.stats.gaussian_kde`'s ``scotts_factor`` and
350
+ ``silverman_factor``. Before version 0.2.8 the Silverman factor was
351
+ computed as ``(4 * M / (K + 2)) ** (-1 / (K + 4))``, which is correct
352
+ only at K = 2.
353
+
354
+ References
355
+ ----------
356
+ Scott, D.W. (1992). Multivariate Density Estimation: Theory, Practice,
357
+ and Visualization. John Wiley & Sons.
358
+
359
+ Silverman, B.W. (1986). Density Estimation for Statistics and Data
360
+ Analysis. Chapman and Hall.
334
361
  """
335
362
  def __init__(self,
336
363
  data: conversions.ArrayLike,
337
- bw: str = 'scott'
364
+ bw: Union[str, conversions.ArrayLike, None] = 'scott'
338
365
  ) -> None:
339
366
  self._data = conversions.alToArray(data)
340
367
  self._names = conversions.alColNames(data)
@@ -352,18 +379,27 @@ class MvKde():
352
379
  mult = self._M**(-1.0/(self._K+4.0))
353
380
  self._scott = np.square(mult * np.diagflat(stdevs))
354
381
 
355
- # rule-of-thumb silverman bandwidth
356
- smult = ((4.0 * self._M)/ (self._K+2.0))**(-1.0 / (self._K+4.0))
382
+ # rule-of-thumb silverman bandwidth (Silverman, 1986, eq. 4.14;
383
+ # the same expression as scipy's gaussian_kde.silverman_factor).
384
+ # Before 0.2.8 the (K + 2) / 4 factor was inverted, which agrees
385
+ # with this only at K = 2
386
+ smult = ((self._K+2.0) * self._M / 4.0)**(-1.0 / (self._K+4.0))
357
387
  self._silverman = np.square(smult * np.diagflat(stdevs))
358
388
 
359
- if bw is None or bw == 'scott':
360
- self._bw = self._scott
361
- elif bw == 'silverman':
362
- self._bw = self._silverman
363
- elif isinstance(bw, str):
364
- raise ValueError(f"unknown bandwidth method '{bw}'; expected "
365
- f"'scott', 'silverman', or a "
366
- f"{self._K}-by-{self._K} bandwidth matrix")
389
+ # dispatch on the type of bw before comparing it with anything: a
390
+ # NumPy array compared with a string evaluates elementwise under
391
+ # NumPy 2, and its truth value is ambiguous
392
+ if bw is None:
393
+ bw = 'scott'
394
+ if isinstance(bw, str):
395
+ if bw == 'scott':
396
+ self._bw = self._scott
397
+ elif bw == 'silverman':
398
+ self._bw = self._silverman
399
+ else:
400
+ raise ValueError(f"unknown bandwidth method '{bw}'; "
401
+ f"expected 'scott', 'silverman', or a "
402
+ f"{self._K}-by-{self._K} bandwidth matrix")
367
403
  else:
368
404
  H = np.asarray(bw, dtype=float)
369
405
  if H.shape != (self._K, self._K):
@@ -373,9 +409,32 @@ class MvKde():
373
409
  Dinv = np.diag(1.0 / self._stds)
374
410
  self._bw = Dinv @ H @ Dinv
375
411
 
412
+ # a bandwidth matrix that is not positive definite is repaired to
413
+ # the nearest one, and the repaired matrix is the one reported by
414
+ # the bandwidth property (nearestpd returns a positive definite
415
+ # input unchanged)
416
+ self._bw = nearby.nearestpd(self._bw)
417
+
376
418
  # cholesky decomp of the bandwidth (covariance) matrix, so that
377
419
  # draws apply covariance H (not H @ H.T)
378
- self._chol = np.linalg.cholesky(nearby.nearestpd(self._bw))
420
+ self._chol = np.linalg.cholesky(self._bw)
421
+
422
+ @property
423
+ def bandwidth(self) -> pd.DataFrame:
424
+ """
425
+ The bandwidth matrix ``H`` in use, in the units of the data, as a
426
+ K-by-K covariance DataFrame whose index and columns are the
427
+ variable names.
428
+
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.
434
+ """
435
+ D = np.diag(self._stds)
436
+ return pd.DataFrame(D @ self._bw @ D, index=self._names,
437
+ columns=self._names)
379
438
 
380
439
  def draw(self,
381
440
  ugen: Generator[float, None, None]
@@ -69,7 +69,8 @@ class Kde():
69
69
  Bandwidth selection method ('scott', 'silverman') or a positive
70
70
  float to use as the bandwidth (the standard deviation of the
71
71
  Gaussian kernel), in the same units as the data.
72
- Default is 'scott'.
72
+ Default is 'scott'. The bandwidth in use is available as
73
+ :attr:`bandwidth`.
73
74
  exact_ppf : bool, optional
74
75
  If True, :meth:`ppf` finds every value by root finding on
75
76
  :meth:`cdf`, as funcsim did before version 0.2.7 (about 0.3 ms per
@@ -82,6 +83,14 @@ class Kde():
82
83
  argument and a NumPy array of the same shape for an array-like argument
83
84
  (a pandas Series or a list gives an array).
84
85
 
86
+ The two rules of thumb set the bandwidth to a factor times the sample
87
+ standard deviation ``s`` of the M observations, computed with
88
+ ``ddof=1`` (dividing by M - 1, as :class:`scipy.stats.gaussian_kde`
89
+ does): Scott's rule gives ``M ** (-1 / 5) * s`` and Silverman's rule
90
+ ``(3 * M / 4) ** (-1 / 5) * s``. (:class:`funcsim.MvKde` applies the
91
+ same factors to a ``ddof=0`` standard deviation, so on the same single
92
+ column its bandwidth is smaller by the factor ``sqrt((M - 1) / M)``.)
93
+
85
94
  The inverse-CDF table evaluates the CDF on 4096 points spanning the data
86
95
  range plus eight kernel standard deviations on each side, and
87
96
  interpolates x against the normal score of the CDF with a
@@ -129,6 +138,18 @@ class Kde():
129
138
  self._exact_ppf = bool(exact_ppf)
130
139
  self._table = None # inverse-CDF table, built on first use
131
140
 
141
+ @property
142
+ def bandwidth(self) -> float:
143
+ """
144
+ The bandwidth in use, the standard deviation of the Gaussian
145
+ kernel, in the units of the data.
146
+
147
+ For ``bw='scott'`` or ``bw='silverman'`` this is the rule's factor
148
+ (``gkde.factor``) times the sample standard deviation of the data
149
+ with ``ddof=1``; for a float ``bw`` it is that value.
150
+ """
151
+ return math.sqrt(float(self.gkde.covariance[0, 0]))
152
+
132
153
  @staticmethod
133
154
  def _apply(func, x):
134
155
  # evaluate a 1-D-array function on scalar or array-like input:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: funcsim
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: functional, simple stochastic simulation
5
5
  Home-page: https://github.com/h-bryant/funcsim
6
6
  Author: Henry Bryant
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="funcsim",
8
- version="0.2.7",
8
+ version="0.2.8",
9
9
  license="bsd-3-clause",
10
10
  author="Henry Bryant",
11
11
  author_email="henry@tamu.edu",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes