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.
- {funcsim-0.2.7/funcsim.egg-info → funcsim-0.2.9}/PKG-INFO +2 -2
- {funcsim-0.2.7 → funcsim-0.2.9}/README.md +1 -1
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/__init__.py +4 -2
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/dependence.py +73 -14
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/kde.py +22 -1
- funcsim-0.2.9/funcsim/rteu.py +222 -0
- {funcsim-0.2.7 → funcsim-0.2.9/funcsim.egg-info}/PKG-INFO +2 -2
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim.egg-info/SOURCES.txt +1 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/setup.py +1 -1
- {funcsim-0.2.7 → funcsim-0.2.9}/LICENSE +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/MANIFEST.in +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/addist.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/conversions.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/copcompare.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/copfit.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/core.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/cpt.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/cvmdist.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/distfit.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/ecdfgof.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/edf.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/eut.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/imanconover.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/ksdist.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/multicore.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/nearby.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/plotting.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/rdarrays.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/screen.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/shapiro.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/shrinkage.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/testsim.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/utests.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim/vect.py +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim.egg-info/dependency_links.txt +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim.egg-info/requires.txt +0 -0
- {funcsim-0.2.7 → funcsim-0.2.9}/funcsim.egg-info/top_level.txt +0 -0
- {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.
|
|
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
|
|
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
|
|
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.
|
|
53
|
+
The version string of the package (e.g., '0.2.9').
|
|
52
54
|
"""
|
|
53
|
-
return "0.2.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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(
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|