funcsim 0.2.7__tar.gz → 0.2.9__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 (38) hide show
  1. {funcsim-0.2.7/funcsim.egg-info → funcsim-0.2.9}/PKG-INFO +2 -2
  2. {funcsim-0.2.7 → funcsim-0.2.9}/README.md +1 -1
  3. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/__init__.py +4 -2
  4. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/dependence.py +73 -14
  5. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/kde.py +22 -1
  6. funcsim-0.2.9/funcsim/rteu.py +222 -0
  7. {funcsim-0.2.7 → funcsim-0.2.9/funcsim.egg-info}/PKG-INFO +2 -2
  8. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim.egg-info/SOURCES.txt +1 -0
  9. {funcsim-0.2.7 → funcsim-0.2.9}/setup.py +1 -1
  10. {funcsim-0.2.7 → funcsim-0.2.9}/LICENSE +0 -0
  11. {funcsim-0.2.7 → funcsim-0.2.9}/MANIFEST.in +0 -0
  12. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/addist.py +0 -0
  13. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/conversions.py +0 -0
  14. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/copcompare.py +0 -0
  15. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/copfit.py +0 -0
  16. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/core.py +0 -0
  17. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/cpt.py +0 -0
  18. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/cvmdist.py +0 -0
  19. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/distfit.py +0 -0
  20. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/ecdfgof.py +0 -0
  21. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/edf.py +0 -0
  22. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/eut.py +0 -0
  23. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/imanconover.py +0 -0
  24. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/ksdist.py +0 -0
  25. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/multicore.py +0 -0
  26. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/nearby.py +0 -0
  27. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/plotting.py +0 -0
  28. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/rdarrays.py +0 -0
  29. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/screen.py +0 -0
  30. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/shapiro.py +0 -0
  31. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/shrinkage.py +0 -0
  32. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/testsim.py +0 -0
  33. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/utests.py +0 -0
  34. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/vect.py +0 -0
  35. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim.egg-info/dependency_links.txt +0 -0
  36. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim.egg-info/requires.txt +0 -0
  37. {funcsim-0.2.7 → funcsim-0.2.9}/funcsim.egg-info/top_level.txt +0 -0
  38. {funcsim-0.2.7 → funcsim-0.2.9}/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.9
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.7').
53
+ The version string of the package (e.g., '0.2.9').
52
54
  """
53
- return "0.2.7"
55
+ return "0.2.9"
@@ -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:
@@ -0,0 +1,222 @@
1
+ """regret-theoretical expected utility (RTEU) and certainty equivalents"""
2
+
3
+ import math
4
+ from collections import namedtuple
5
+ from typing import Callable, Union
6
+ import numpy as np
7
+ from numpy.typing import NDArray
8
+ import scipy
9
+ from . import conversions
10
+
11
+
12
+ # container for results
13
+ RteuResult = namedtuple("RteuResult", "ExpectedValue CertaintyEquiv")
14
+
15
+
16
+ def regretPower(x: Union[float, NDArray],
17
+ exponent: float,
18
+ ) -> Union[float, NDArray]:
19
+ """
20
+ Compute power regret for a utility gap.
21
+
22
+ Parameters
23
+ ----------
24
+ x : float or numpy.ndarray
25
+ The utility gap psi(y_max) - psi(y), non-negative; an array is
26
+ evaluated elementwise.
27
+ exponent : float
28
+ Curvature of regret, at least 1.0. Values above one make the regret
29
+ function strictly convex, so a few large regrets weigh more than
30
+ many small ones; at exactly one, regret is linear in the gap and the
31
+ choice between scenarios coincides with expected utility's, although
32
+ the certainty equivalents do not.
33
+
34
+ Returns
35
+ -------
36
+ float or numpy.ndarray
37
+ ``x ** exponent``, a float for a float and an array for an array.
38
+
39
+ Raises
40
+ ------
41
+ ValueError
42
+ If ``x`` (or any element of it) is negative or ``exponent`` is below
43
+ 1.0.
44
+
45
+ Notes
46
+ -----
47
+ This is the one ready-made regret function; any function of one
48
+ non-negative float with g(0) = 0 may be passed as ``regretFunc``. It
49
+ accepts arrays as well as floats so that a user's own per-state code can
50
+ call it on whole vectors; ``rteu`` evaluates it through
51
+ ``numpy.vectorize`` like any other ``regretFunc``. There is no
52
+ canonical parameterization in the literature comparable to Tversky and
53
+ Kahneman's for cumulative prospect theory, so no preset analogous to
54
+ ``cptBV`` is provided.
55
+ """
56
+ if exponent < 1.0:
57
+ raise ValueError(f"exponent (={exponent}) must be at least 1.0")
58
+ xa = np.asarray(x, dtype=float)
59
+ if np.any(xa < 0.0):
60
+ raise ValueError(f"x (={x}) must be non-negative: the argument of "
61
+ "a regret function is a utility gap")
62
+ powered = xa ** exponent
63
+ return float(powered) if powered.ndim == 0 else powered
64
+
65
+
66
+ def _rtu(psiY, psiBest, vg, k):
67
+ # psi(y) - k g(psi(best) - psi(y)), elementwise, from utilities that
68
+ # have already been evaluated; vg is the vectorized regret function.
69
+ # psiY may be a scalar (a sure amount), in which case it broadcasts
70
+ return psiY - k * vg(psiBest - psiY)
71
+
72
+
73
+ def _finite_or_raise(values, where, label):
74
+ # raise if any evaluated utility is not finite, naming the first culprit
75
+ bad = ~np.isfinite(values)
76
+ if np.any(bad):
77
+ i = int(np.argmax(bad))
78
+ raise ValueError(f"utilFunc is not finite at {label} {where[i]} "
79
+ f"(position {i})")
80
+
81
+
82
+ def rteu(utilFunc: Callable[[float], float],
83
+ regretFunc: Callable[[float], float],
84
+ regretCoeff: float,
85
+ outcomes: conversions.VectorLike,
86
+ best: conversions.VectorLike,
87
+ ) -> RteuResult:
88
+ """
89
+ Compute regret-theoretical expected utility (RTEU) and the certainty
90
+ equivalent of one scenario, given the best attainable outcome in each
91
+ state of nature across the decision maker's menu.
92
+
93
+ Parameters
94
+ ----------
95
+ utilFunc : Callable
96
+ Utility function of the monetary outcome, psi, taking a float and
97
+ returning a float. It must be strictly increasing; the theory also
98
+ assumes it concave. See ``utilIsoelastic``.
99
+ regretFunc : Callable
100
+ Regret function of the utility gap, g, taking a non-negative float
101
+ and returning a float, with g(0) = 0, g' > 0, and g'' > 0. See
102
+ ``regretPower``.
103
+ regretCoeff : float
104
+ Regret coefficient k, non-negative. At zero the results are those
105
+ of expected utility theory.
106
+ outcomes : VectorLike
107
+ The scenario's outcome in each state of nature (one value per
108
+ simulation trial).
109
+ best : VectorLike
110
+ The best attainable outcome in the same state of nature across every
111
+ scenario in the menu, the same length as ``outcomes`` and no smaller
112
+ than it in any state: typically the row-wise maximum across the
113
+ scenario columns, which includes this scenario's own outcome.
114
+ Element n of ``outcomes`` and of ``best`` must belong to the same
115
+ state of nature, which ``simulate`` delivers when every scenario is
116
+ run at the same seed and number of trials.
117
+
118
+ Returns
119
+ -------
120
+ RteuResult
121
+ Named tuple with fields:
122
+
123
+ ExpectedValue : float
124
+ The regret-theoretical expected utility of the scenario, the
125
+ mean over states of psi(y) - k g(psi(best) - psi(y)).
126
+ CertaintyEquiv : float
127
+ The certainty equivalent of the scenario within the menu: the
128
+ sure amount c such that the decision maker is indifferent
129
+ between c and the scenario when both are in the menu, each
130
+ evaluated against the best attainable outcome max(best, c) in
131
+ every state.
132
+
133
+ Raises
134
+ ------
135
+ ValueError
136
+ If ``outcomes`` and ``best`` differ in length, ``best`` is smaller
137
+ than ``outcomes`` in any state, ``regretCoeff`` is negative,
138
+ ``regretFunc(0.0)`` is not zero, or ``utilFunc`` is not finite at
139
+ every outcome (for example a logarithmic psi applied to a
140
+ non-positive outcome).
141
+
142
+ Notes
143
+ -----
144
+ The per-state index follows Braun and Muermann (2004); measuring regret
145
+ against the best attainable outcome in each state is the form implied
146
+ by Quiggin's (1994) irrelevance of statewise dominated alternatives,
147
+ and it is why the rest of the menu enters only through ``best``. The
148
+ certainty equivalent is the menu-based definition of the regret theory
149
+ literature (Loomes and Sugden, 1983; Diecidue and Somasundaram, 2017;
150
+ Gollier, 2020). It is found by Brent's method between the scenario's
151
+ smallest outcome and the largest value of ``best``, where the
152
+ indifference condition has opposite signs; the sure-amount side is
153
+ strictly increasing in c and the scenario side non-increasing, so the
154
+ root is unique. At ``regretCoeff`` = 0 the certainty equivalent is the
155
+ expected utility certainty equivalent, psi^-1(E[psi(y)]), and
156
+ ``ExpectedValue`` is E[psi(y)]. States are equally likely. Passing the
157
+ scenario's own outcomes as ``best`` describes a menu of the scenario
158
+ and the sure amount alone, the two-element setting of Loomes and Sugden
159
+ (1983). Because a sure amount is itself regret-prone within the menu,
160
+ the gap between a scenario's expected outcome and its certainty
161
+ equivalent is not the risk premium of expected utility theory (Gollier,
162
+ 2020), and the certainty equivalents can order two scenarios
163
+ differently from the choice between them; the choice rests on
164
+ ``ExpectedValue``. The scale of ``utilFunc`` matters: k weights regret
165
+ measured in utility units, so k is not comparable across differently
166
+ scaled psi. The utility and regret functions are evaluated through
167
+ ``numpy.vectorize``, so any function of one float works.
168
+ """
169
+ if regretCoeff < 0.0:
170
+ raise ValueError(f"regretCoeff (={regretCoeff}) must be non-negative")
171
+ g0 = float(regretFunc(0.0))
172
+ if not math.isclose(g0, 0.0, abs_tol=1e-12):
173
+ raise ValueError(f"regretFunc(0.0) must be zero (got {g0})")
174
+
175
+ y = conversions.vlToArray(outcomes).astype(float)
176
+ b = conversions.vlToArray(best).astype(float)
177
+ if len(y) != len(b):
178
+ raise ValueError(f"outcomes (length {len(y)}) and best (length "
179
+ f"{len(b)}) must have the same length")
180
+ below = b < y
181
+ if np.any(below):
182
+ i = int(np.argmax(below))
183
+ raise ValueError("best must be the best attainable outcome in each "
184
+ "state, so no smaller than outcomes; at position "
185
+ f"{i} best={b[i]} is below outcomes={y[i]}")
186
+
187
+ vpsi = np.vectorize(utilFunc, otypes=[float])
188
+ vg = np.vectorize(regretFunc, otypes=[float])
189
+ with np.errstate(invalid="ignore", divide="ignore"):
190
+ psiY = vpsi(y)
191
+ psiB = vpsi(b)
192
+ _finite_or_raise(psiY, y, "outcome")
193
+ _finite_or_raise(psiB, b, "best")
194
+ k = float(regretCoeff)
195
+
196
+ expected = float(np.mean(_rtu(psiY, psiB, vg, k)))
197
+
198
+ # certainty equivalent: the sure amount c at which the decision maker is
199
+ # indifferent between c and the scenario, both in the menu, so that the
200
+ # best attainable outcome is max(best, c) in every state. Because psi
201
+ # is increasing, psi(max(best, c)) = max(psi(best), psi(c)), so psi is
202
+ # evaluated on one scalar per iteration and g on the vector
203
+ def gap(c):
204
+ psiC = float(utilFunc(c))
205
+ psiBC = np.maximum(psiB, psiC)
206
+ sure = np.mean(_rtu(psiC, psiBC, vg, k))
207
+ scenario = np.mean(_rtu(psiY, psiBC, vg, k))
208
+ return float(sure - scenario)
209
+
210
+ # the gap is <= 0 at the scenario's worst outcome and >= 0 at the menu's
211
+ # best, so the root lies between them; a sure scenario (constant
212
+ # outcomes) has gap(lo) == 0 exactly, which brentq would reject
213
+ lo = float(np.min(y))
214
+ hi = float(np.max(b))
215
+ if gap(lo) >= 0.0:
216
+ certequiv = lo
217
+ elif gap(hi) <= 0.0:
218
+ certequiv = hi
219
+ else:
220
+ certequiv = scipy.optimize.brentq(f=gap, a=lo, b=hi, maxiter=200)
221
+
222
+ return RteuResult(expected, float(certequiv))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: funcsim
3
- Version: 0.2.7
3
+ Version: 0.2.9
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
@@ -23,6 +23,7 @@ funcsim/multicore.py
23
23
  funcsim/nearby.py
24
24
  funcsim/plotting.py
25
25
  funcsim/rdarrays.py
26
+ funcsim/rteu.py
26
27
  funcsim/screen.py
27
28
  funcsim/shapiro.py
28
29
  funcsim/shrinkage.py
@@ -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.9",
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