funcsim 0.2.3__tar.gz → 0.2.5__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.3/funcsim.egg-info → funcsim-0.2.5}/PKG-INFO +25 -17
  2. funcsim-0.2.5/README.md +47 -0
  3. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/__init__.py +2 -2
  4. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/core.py +36 -16
  5. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/cpt.py +2 -2
  6. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/dependence.py +151 -33
  7. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/distfit.py +4 -1
  8. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/ecdfgof.py +21 -9
  9. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/eut.py +2 -2
  10. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/imanconover.py +3 -3
  11. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/nearby.py +2 -2
  12. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/plotting.py +17 -12
  13. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/rdarrays.py +15 -2
  14. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/screen.py +17 -4
  15. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/shrinkage.py +46 -17
  16. {funcsim-0.2.3 → funcsim-0.2.5/funcsim.egg-info}/PKG-INFO +25 -17
  17. {funcsim-0.2.3 → funcsim-0.2.5}/setup.py +1 -1
  18. funcsim-0.2.3/README.md +0 -39
  19. {funcsim-0.2.3 → funcsim-0.2.5}/LICENSE +0 -0
  20. {funcsim-0.2.3 → funcsim-0.2.5}/MANIFEST.in +0 -0
  21. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/addist.py +0 -0
  22. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/conversions.py +0 -0
  23. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/copfit.py +0 -0
  24. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/cvmdist.py +0 -0
  25. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/edf.py +0 -0
  26. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/kde.py +0 -0
  27. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/ksdist.py +0 -0
  28. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/multicore.py +0 -0
  29. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/shapiro.py +0 -0
  30. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/testsim.py +0 -0
  31. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/utests.py +0 -0
  32. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim/vect.py +0 -0
  33. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim.egg-info/SOURCES.txt +0 -0
  34. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim.egg-info/dependency_links.txt +0 -0
  35. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim.egg-info/requires.txt +0 -0
  36. {funcsim-0.2.3 → funcsim-0.2.5}/funcsim.egg-info/top_level.txt +0 -0
  37. {funcsim-0.2.3 → funcsim-0.2.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: funcsim
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: functional, simple stochastic simulation
5
5
  Home-page: https://github.com/h-bryant/funcsim
6
6
  Author: Henry Bryant
@@ -46,35 +46,43 @@ Dynamic: summary
46
46
  # funcsim
47
47
  Lightweight, functional stochastic simulation
48
48
 
49
+ Documentation: https://funcsim.readthedocs.io/
50
+
49
51
  Features:
50
- - cross-sectional and recursive dynamic simulations
51
- - pseudo-random numbers (so that results are comparable across multiple runs or scenarios)
52
+ - static (cross-sectional) and recursive-dynamic simulations from a single user-written function
53
+ - pseudo-random numbers with an explicit seed (so that results are reproducible and comparable across runs or scenarios)
52
54
  - stratified/Latin hypercube sampling
53
55
  - painless multi-core simulations
56
+ - fitting and comparison of univariate distributions, kernel density estimates, and goodness-of-fit tests
57
+ - multivariate normal and KDE distributions; Gaussian, Student's t, Clayton, Gumbel, and Frank copulas (implemented natively, no additional packages required); Iman-Conover; covariance shrinkage
58
+ - expected utility and cumulative prospect theory calculations
59
+ - plotly-based fan charts and diagnostic plots
54
60
  - highly flexible, and as simple as possible (but no more)
55
61
  - functional paradigm
56
62
 
57
- Developed using:
58
- - python >= 3.10
59
- - numpy 2.2.0
60
- - scipy 1.16.0
61
- - pandas 2.2.0 (pandas 2.2+ and 3.x are both supported)
62
- - xarray 2025.4.0
63
-
64
- Copulas (Gaussian, Student's t, Clayton, Gumbel, and Frank) are supported
65
- natively; no additional packages are required.
63
+ Requirements:
64
+ - python >= 3.11
65
+ - numpy >= 2.2.0
66
+ - scipy >= 1.16.0
67
+ - pandas >= 2.2.0 (pandas 2.2+ and 3.x are both supported)
68
+ - xarray >= 2025.4.0
66
69
 
67
- For optional functionality, additinoal packages are needed:
68
- - plotting: plotly
70
+ For optional functionality, additional packages are needed:
71
+ - plotting: plotly (`pip install "funcsim[plotting]"`); kaleido for static image export
69
72
 
70
- Additionally, to run the demos, run tests, and build docs:
73
+ Additionally, to run the tests and build the docs:
71
74
  - pytest
72
- - sphinx, sphinx-autodoc-typehints
75
+ - sphinx, sphinx-autodoc-typehints (`pip install "funcsim[docs]"`)
76
+
77
+ ### installation
78
+ ```bash
79
+ pip install funcsim
80
+ ```
73
81
 
74
82
  ### building html docs
75
83
  From the top-level funcsim directory...
76
84
  ```bash
77
- sphinx-build -b html docs/source/ docs/build/
85
+ sphinx-build -W -b html docs/source/ docs/build/
78
86
  ```
79
87
 
80
88
  ### running tests
@@ -0,0 +1,47 @@
1
+ # funcsim
2
+ Lightweight, functional stochastic simulation
3
+
4
+ Documentation: https://funcsim.readthedocs.io/
5
+
6
+ Features:
7
+ - static (cross-sectional) and recursive-dynamic simulations from a single user-written function
8
+ - pseudo-random numbers with an explicit seed (so that results are reproducible and comparable across runs or scenarios)
9
+ - stratified/Latin hypercube sampling
10
+ - painless multi-core simulations
11
+ - fitting and comparison of univariate distributions, kernel density estimates, and goodness-of-fit tests
12
+ - multivariate normal and KDE distributions; Gaussian, Student's t, Clayton, Gumbel, and Frank copulas (implemented natively, no additional packages required); Iman-Conover; covariance shrinkage
13
+ - expected utility and cumulative prospect theory calculations
14
+ - plotly-based fan charts and diagnostic plots
15
+ - highly flexible, and as simple as possible (but no more)
16
+ - functional paradigm
17
+
18
+ Requirements:
19
+ - python >= 3.11
20
+ - numpy >= 2.2.0
21
+ - scipy >= 1.16.0
22
+ - pandas >= 2.2.0 (pandas 2.2+ and 3.x are both supported)
23
+ - xarray >= 2025.4.0
24
+
25
+ For optional functionality, additional packages are needed:
26
+ - plotting: plotly (`pip install "funcsim[plotting]"`); kaleido for static image export
27
+
28
+ Additionally, to run the tests and build the docs:
29
+ - pytest
30
+ - sphinx, sphinx-autodoc-typehints (`pip install "funcsim[docs]"`)
31
+
32
+ ### installation
33
+ ```bash
34
+ pip install funcsim
35
+ ```
36
+
37
+ ### building html docs
38
+ From the top-level funcsim directory...
39
+ ```bash
40
+ sphinx-build -W -b html docs/source/ docs/build/
41
+ ```
42
+
43
+ ### running tests
44
+ From the top-level funcsim directory...
45
+ ```bash
46
+ python3 -m pytest
47
+ ```
@@ -46,6 +46,6 @@ def version():
46
46
  Returns
47
47
  -------
48
48
  str
49
- The version string of the package (e.g., '0.1.5').
49
+ The version string of the package (e.g., '0.2.5').
50
50
  """
51
- return "0.2.3"
51
+ return "0.2.5"
@@ -183,17 +183,20 @@ def simulate(f: Callable[[Generator[int, float, None],
183
183
 
184
184
  Parameters
185
185
  ----------
186
- f : function
187
- Function that performs a single trial in a static simulation or a s
188
- single step through time in a recursive dynamic simulation. Should take
189
- 'ugen' as a first argument in either case, where 'ugen' will be a
190
- generator that emits standard uniform draws (or standard normal draws,
191
- if `stdnorm` is True) that will be passed to f by ``simulate``.
192
- In the case of a recursive dynamic simulation that employs past values,
193
- f should take 'data' as a second argument, where this will be a type of
194
- array that is also provided by ``simulate.`` This function should return
195
- a dict with variable names (as strings) as keys and values for those
196
- variables (as floats) as values.
186
+ f : callable
187
+ Function that performs a single trial in a static simulation, or a
188
+ single step through time in a recursive-dynamic simulation. Its first
189
+ argument (conventionally ``ugen``) receives a generator that emits
190
+ independent standard uniform draws (or standard normal draws, if
191
+ `stdnorm` is True); obtain each draw with ``next(ugen)``. If `f`
192
+ takes a second argument (conventionally ``hist``), it receives a
193
+ per-trial history object whose ``recall(varname, lag)`` method returns
194
+ the value of a variable `lag` steps earlier; see
195
+ :class:`funcsim.rdarrays.RDdata`. `f` must return a dict with
196
+ variable names (strings) as keys and floats as values. The number of
197
+ times `f` calls ``next(ugen)`` must be the same in every trial and
198
+ step, because draws are pre-allocated across trials for stratified
199
+ sampling.
197
200
  ntrials : int, optional
198
201
  The number of trials to perform. Default is 500.
199
202
  nsteps : int, optional
@@ -207,12 +210,14 @@ def simulate(f: Callable[[Generator[int, float, None],
207
210
  Use multiple processes/cores for the simulation. Default is False.
208
211
  When True, `f` must be picklable (e.g., defined at the top level of
209
212
  a module, not inside another function), because worker processes
210
- are spawned on most platforms.
213
+ are spawned on most platforms. For the same reason, a script that
214
+ calls ``simulate(..., multi=True)`` must guard the call with
215
+ ``if __name__ == "__main__":``.
211
216
  seed : int, optional
212
217
  Seed for pseudo-random number generation. Default is 6.
213
- stdnorm : book, optional
214
- If False, ``next(draw)`` within `trialf` will return standard uniform
215
- random draws. If True, ``next(draw)`` will return standard normal draws.
218
+ stdnorm : bool, optional
219
+ If False, ``next(ugen)`` within `f` returns standard uniform random
220
+ draws. If True, ``next(ugen)`` returns standard normal draws.
216
221
  Default is False.
217
222
  sampling : {'lh', 'mc'}, optional
218
223
  If 'lh', Latin Hypercube sampling is employed. If 'mc', simple
@@ -221,7 +226,22 @@ def simulate(f: Callable[[Generator[int, float, None],
221
226
  Returns
222
227
  -------
223
228
  xarray.DataArray
224
- 3-D xarray.DataArray with dimensions 'trials', 'variables', and 'steps'.
229
+ 3-D xarray.DataArray with dimensions 'trials', 'variables', and
230
+ 'steps'. The 'steps' coordinate continues the index of `hist0` (or
231
+ counts from 0 when `hist0` is not given), and includes any historical
232
+ steps from `hist0` ahead of the `nsteps` simulated steps.
233
+
234
+ Raises
235
+ ------
236
+ ValueError
237
+ If `hist0` is malformed, `f` is not callable or takes more than two
238
+ arguments, `f` does not return a dict keyed by strings, or `sampling`
239
+ is not one of 'lh' or 'mc'.
240
+ RuntimeError
241
+ If `f` consumes a different number of draws in some trial or step
242
+ than it did when first probed.
243
+ funcsim.rdarrays.MissingValue
244
+ If `f` recalls a lagged value that is not available.
225
245
  """
226
246
  if hist0 is not None:
227
247
  _checkhist0(hist0)
@@ -315,7 +315,7 @@ def cpt(utilFunc: Callable,
315
315
  Sequence of possible stochastic outcomes.
316
316
  refOutcome : float
317
317
  Reference point for gains and losses.
318
- probabilities : float or Iterable, optional
318
+ probabilities : Iterable of float, optional
319
319
  Probabilities for each outcome. If None, outcomes are assumed
320
320
  equally likely.
321
321
  precision : float, optional
@@ -436,7 +436,7 @@ def cptBV(outcomes: Iterable,
436
436
  Sequence of possible stochastic outcomes.
437
437
  refOutcome : float
438
438
  Reference point for gains and losses.
439
- probabilities : float or Iterable, optional
439
+ probabilities : Iterable of float, optional
440
440
  Probabilities for each outcome. If None, outcomes are assumed equally
441
441
  likely.
442
442
 
@@ -2,7 +2,7 @@ import math
2
2
  import numpy as np
3
3
  import pandas as pd
4
4
  from scipy import stats
5
- from typing import Generator, Optional, Tuple
5
+ from typing import Generator, Optional, Sequence, Tuple
6
6
  import warnings
7
7
  from . import conversions
8
8
  from . import copfit
@@ -191,12 +191,17 @@ class MvKde():
191
191
  Parameters
192
192
  ----------
193
193
  data : ArrayLike
194
- Input data array of with variables in columns and observations
194
+ Input data array with variables in columns and observations
195
195
  in rows.
196
196
  bw : str or ArrayLike, optional
197
197
  Bandwidth selection method ('scott' or 'silverman'), or a K-by-K
198
198
  bandwidth covariance matrix in the units of the data.
199
199
  Default is 'scott'.
200
+
201
+ Notes
202
+ -----
203
+ Each call to :meth:`draw` consumes K + 1 values from ``ugen``, where K
204
+ is the number of variables.
200
205
  """
201
206
  def __init__(self,
202
207
  data: conversions.ArrayLike,
@@ -279,16 +284,24 @@ class MvKde():
279
284
 
280
285
  class MvNorm():
281
286
  """
282
- A multivariate normal distribution object. A vector of means
283
- and a covariance matrix are computed from the input data. If the sample
284
- covariance matrix is not positive definite, the Higham
285
- method is used to calculate the nearest positive definite matrix.
287
+ A multivariate normal distribution object. Constructed from data, a
288
+ vector of means and a covariance matrix are computed from the input.
289
+ Constructed with :meth:`from_params`, the mean vector and covariance
290
+ matrix are supplied directly. Either way, if the covariance matrix is
291
+ not positive definite, the Higham method is used to calculate the
292
+ nearest positive definite matrix.
286
293
 
287
294
  Parameters
288
295
  ----------
289
296
  data : ArrayLike
290
- Input data array of with variables in columns and observations
297
+ Input data array with variables in columns and observations
291
298
  in rows.
299
+
300
+ Notes
301
+ -----
302
+ Each call to :meth:`draw` consumes K values from ``ugen``, where K is
303
+ the number of variables. A warning is issued for any variable whose
304
+ Shapiro-Wilk test rejects normality at the 5% level.
292
305
  """
293
306
  def __init__(self,
294
307
  data: conversions.ArrayLike,
@@ -312,10 +325,80 @@ class MvNorm():
312
325
  swp = shapiro.swtest(self._data[:, k])[1]
313
326
  if not swp > 0.05:
314
327
  msg = (f"variable '{self._names[k]}' may not be "
315
- f" normally distributed. (Shapiro-Wilk p-value"
328
+ f"normally distributed. (Shapiro-Wilk p-value"
316
329
  f"={swp:.3f})")
317
330
  warnings.warn(msg, UserWarning)
318
331
 
332
+ @classmethod
333
+ def from_params(cls,
334
+ mu: conversions.VectorLike,
335
+ sigma: conversions.ArrayLike,
336
+ names: Optional[Sequence[str]] = None,
337
+ ) -> "MvNorm":
338
+ """
339
+ Create a multivariate normal distribution object from a mean vector
340
+ and a covariance matrix, rather than from data.
341
+
342
+ Parameters
343
+ ----------
344
+ mu : VectorLike
345
+ Mean vector of length K.
346
+ sigma : ArrayLike
347
+ K-by-K covariance matrix (a nested list is also accepted). It
348
+ must be symmetric. If it is not positive definite, the nearest
349
+ positive definite matrix (Higham, 1988) is substituted with a
350
+ warning.
351
+ names : sequence of str, optional
352
+ Variable names, length K. If omitted, the index of `mu` (if it
353
+ is a pandas Series) or the column labels of `sigma` (if it is a
354
+ pandas DataFrame) are used; otherwise the variables are named
355
+ 'v0', 'v1', ....
356
+
357
+ Returns
358
+ -------
359
+ MvNorm
360
+ A distribution object whose :meth:`draw` method returns joint
361
+ draws with the given means and covariances.
362
+
363
+ Notes
364
+ -----
365
+ No normality check is performed, since there are no data to check.
366
+
367
+ Examples
368
+ --------
369
+ >>> dist = fs.MvNorm.from_params([3.0, 4.0], [[1.0, 0.5], [0.5, 1.0]])
370
+ """
371
+ muA = np.asarray(conversions.vlToArray(mu), dtype=float)
372
+ if isinstance(sigma, (list, tuple)):
373
+ sigmaA = np.asarray(sigma, dtype=float)
374
+ else:
375
+ sigmaA = np.asarray(conversions.alToArray(sigma), dtype=float)
376
+ K = muA.shape[0]
377
+ if sigmaA.shape != (K, K):
378
+ raise ValueError(f"sigma must be {K}-by-{K} to match mu; "
379
+ f"got shape {sigmaA.shape}")
380
+ if not np.allclose(sigmaA, sigmaA.T):
381
+ raise ValueError("sigma must be symmetric")
382
+ if names is not None:
383
+ nms = pd.Index(list(names))
384
+ elif isinstance(mu, pd.Series):
385
+ nms = pd.Index(mu.index)
386
+ elif isinstance(sigma, pd.DataFrame):
387
+ nms = pd.Index(sigma.columns)
388
+ else:
389
+ nms = pd.Index([f"v{k}" for k in range(K)])
390
+ if len(nms) != K:
391
+ raise ValueError(f"names must have length {K}; got {len(nms)}")
392
+
393
+ obj = cls.__new__(cls)
394
+ obj._data = None
395
+ obj._names = nms
396
+ (obj._M, obj._K) = (0, K)
397
+ obj._mu = muA
398
+ obj._sigma = nearby.nearestpd(sigmaA)
399
+ obj._A = np.linalg.cholesky(obj._sigma)
400
+ return obj
401
+
319
402
  def draw(self,
320
403
  ugen: Generator[float, None, None]
321
404
  ) -> pd.Series:
@@ -330,11 +413,12 @@ class MvNorm():
330
413
  Returns
331
414
  -------
332
415
  pd.Series
333
- A pandas Series representing a joint draw from the KDE. The index
334
- values are the variable names, and the values are the random
335
- values. If no variable names were provided in the input data,
336
- the variables will be named 'v0', 'v1', ..., reflecting the
337
- oreder of the columns in the input data.
416
+ A pandas Series representing a joint draw from the multivariate
417
+ normal distribution. The index values are the variable names,
418
+ and the values are the random values. If no variable names were
419
+ provided (as column labels of the input data, or through the
420
+ `names` argument of :meth:`from_params`), the variables are
421
+ named 'v0', 'v1', ..., in column order.
338
422
  """
339
423
  uvec = [next(ugen) for i in range(self._K)]
340
424
  retA = self._mu + np.dot(self._A, stats.norm.ppf(uvec))
@@ -353,10 +437,15 @@ class CopulaGauss():
353
437
  columns and observations in rows. That is, each column
354
438
  represents the result of applying the fitted CDF of some marginal
355
439
  distribution to the raw data for that variable. The
356
- values in each column should be in the range [0, 1]. The parameters
357
- are fit using the method of moments. If the sample covariance
358
- matrix is not positive definite, the Higham method is used to
359
- calculate the nearest positive definite matrix.
440
+ values in each column should be in the range (0, 1). The correlation
441
+ matrix is fit using the method of moments on the normal scores of
442
+ the pseudo-observations. If it is not positive definite, the Higham
443
+ method is used to calculate the nearest positive definite matrix.
444
+
445
+ Notes
446
+ -----
447
+ Each call to :meth:`draw` consumes K values from ``ugen``, where K is
448
+ the number of variables.
360
449
  """
361
450
 
362
451
  def __init__(self,
@@ -412,7 +501,7 @@ class CopulaGauss():
412
501
  standard uniform draws are the values in the Series.
413
502
  If no variable names were provided in the input data,
414
503
  the variables will be named 'v0', 'v1', ..., reflecting the
415
- oreder of the columns in the input data.
504
+ order of the columns in the input data.
416
505
 
417
506
  """
418
507
  uvec = [next(ugen) for i in range(self._K)]
@@ -432,9 +521,14 @@ class CopulaStudent():
432
521
  columns and observations in rows. That is, each column
433
522
  represents the result of applying the fitted CDF of some marginal
434
523
  distribution to the raw data for that variable. The
435
- values in each column should be in the range [0, 1]. The correlation
524
+ values in each column should be in the range (0, 1). The correlation
436
525
  matrix is fit by pairwise Kendall's-tau inversion, and the degrees
437
- of freedom are then fit by profile maximum likelihood.
526
+ of freedom are then fit by profile maximum pseudo-likelihood.
527
+
528
+ Notes
529
+ -----
530
+ Each call to :meth:`draw` consumes K + 1 values from ``ugen``, where K
531
+ is the number of variables.
438
532
  """
439
533
 
440
534
  def __init__(self,
@@ -477,7 +571,7 @@ class CopulaStudent():
477
571
  standard uniform draws are the values in the Series.
478
572
  If no variable names were provided in the input data,
479
573
  the variables will be named 'v0', 'v1', ..., reflecting the
480
- oreder of the columns in the input data.
574
+ order of the columns in the input data.
481
575
 
482
576
  """
483
577
  uvec = [next(ugen) for i in range(self._K)]
@@ -500,10 +594,18 @@ class CopulaClayton():
500
594
  columns and observations in rows. That is, each column
501
595
  represents the result of applying the fitted CDF of some marginal
502
596
  distribution to the raw data for that variable. The
503
- values in each column should be in the range [0, 1]. Parameters are
504
- fit using maximum likelihood. This implementation accomodates
597
+ values in each column should be in the range (0, 1). The dependence
598
+ parameter theta is fit by maximum pseudo-likelihood, starting from
599
+ the Kendall's-tau inversion estimate. This implementation accommodates
505
600
  only positive dependence, so the fitted value for theta is
506
601
  constrained to be > 0.
602
+
603
+ Notes
604
+ -----
605
+ Each call to :meth:`draw` consumes K + 1 values from ``ugen``, where K
606
+ is the number of variables. If the data exhibit negative dependence
607
+ (mean pairwise Kendall's tau <= 0), theta is set to its minimum
608
+ (near independence) and a warning is issued.
507
609
  """
508
610
 
509
611
  def __init__(self,
@@ -525,7 +627,7 @@ class CopulaClayton():
525
627
  (self._theta, taub, clamped) = copfit.fit_clayton(self._data)
526
628
  if clamped:
527
629
  warnings.warn(f"The Clayton copula implementation in funcsim "
528
- f"accomodates only positive dependence, but the "
630
+ f"accommodates only positive dependence, but the "
529
631
  f"data exhibit negative dependence (mean pairwise "
530
632
  f"Kendall's tau = {taub:.3f}). A theta value of "
531
633
  f"{copfit.THETA_MIN_CLAYTON} is being used rather "
@@ -557,7 +659,7 @@ class CopulaClayton():
557
659
  standard uniform draws are the values in the Series.
558
660
  If no variable names were provided in the input data,
559
661
  the variables will be named 'v0', 'v1', ..., reflecting the
560
- oreder of the columns in the input data.
662
+ order of the columns in the input data.
561
663
 
562
664
  """
563
665
  # floor the gamma frailty: for large theta the shape parameter is
@@ -582,10 +684,18 @@ class CopulaGumbel():
582
684
  columns and observations in rows. That is, each column
583
685
  represents the result of applying the fitted CDF of some marginal
584
686
  distribution to the raw data for that variable. The
585
- values in each column should be in the range [0, 1]. Parameters are
586
- fit using maximum likelihood. This implementation accomodates
687
+ values in each column should be in the range (0, 1). The dependence
688
+ parameter theta is fit by maximum pseudo-likelihood, starting from
689
+ the Kendall's-tau inversion estimate. This implementation accommodates
587
690
  only positive dependence, so the fitted value for theta is
588
691
  constrained to be > 1.0.
692
+
693
+ Notes
694
+ -----
695
+ Each call to :meth:`draw` consumes K + 2 values from ``ugen``, where K
696
+ is the number of variables. If the data exhibit negative dependence
697
+ (mean pairwise Kendall's tau <= 0), theta is set to its minimum
698
+ (near independence) and a warning is issued.
589
699
  """
590
700
 
591
701
  def __init__(self,
@@ -607,7 +717,7 @@ class CopulaGumbel():
607
717
  (self._theta, taub, clamped) = copfit.fit_gumbel(self._data)
608
718
  if clamped:
609
719
  warnings.warn(f"The Gumbel copula implementation in funcsim "
610
- f"accomodates only positive dependence, but the "
720
+ f"accommodates only positive dependence, but the "
611
721
  f"data exhibit negative dependence (mean pairwise "
612
722
  f"Kendall's tau = {taub:.3f}). A theta value of "
613
723
  f"(approximately) 1.0 is being used rather than a "
@@ -639,7 +749,7 @@ class CopulaGumbel():
639
749
  standard uniform draws are the values in the Series.
640
750
  If no variable names were provided in the input data,
641
751
  the variables will be named 'v0', 'v1', ..., reflecting the
642
- oreder of the columns in the input data.
752
+ order of the columns in the input data.
643
753
 
644
754
  """
645
755
  gamma = math.cos(0.5 * math.pi / self._theta)**self._theta
@@ -662,10 +772,18 @@ class CopulaFrank():
662
772
  columns and observations in rows. That is, each column
663
773
  represents the result of applying the fitted CDF of some marginal
664
774
  distribution to the raw data for that variable. The
665
- values in each column should be in the range [0, 1]. Parameters are
666
- fit using maximum likelihood. This implementation accomodates
775
+ values in each column should be in the range (0, 1). The dependence
776
+ parameter theta is fit by maximum pseudo-likelihood, starting from
777
+ the Kendall's-tau inversion estimate. This implementation accommodates
667
778
  only positive dependence, so the fitted value for theta is
668
779
  constrained to be > 0.
780
+
781
+ Notes
782
+ -----
783
+ Each call to :meth:`draw` consumes K + 2 values from ``ugen``, where K
784
+ is the number of variables. If the data exhibit negative dependence
785
+ (mean pairwise Kendall's tau <= 0), theta is set to its minimum
786
+ (near independence) and a warning is issued.
669
787
  """
670
788
 
671
789
  def __init__(self,
@@ -687,7 +805,7 @@ class CopulaFrank():
687
805
  (self._theta, taub, clamped) = copfit.fit_frank(self._data)
688
806
  if clamped:
689
807
  warnings.warn(f"The Frank copula implementation in funcsim "
690
- f"accomodates only positive dependence, but the "
808
+ f"accommodates only positive dependence, but the "
691
809
  f"data exhibit negative dependence (mean pairwise "
692
810
  f"Kendall's tau = {taub:.3f}). A theta value of "
693
811
  f"{copfit.THETA_MIN_FRANK} is being used rather "
@@ -716,7 +834,7 @@ class CopulaFrank():
716
834
  standard uniform draws are the values in the Series.
717
835
  If no variable names were provided in the input data,
718
836
  the variables will be named 'v0', 'v1', ..., reflecting the
719
- oreder of the columns in the input data.
837
+ order of the columns in the input data.
720
838
 
721
839
  """
722
840
  # Generate d uniform random variables
@@ -414,7 +414,8 @@ def fit(data: conversions.VectorLike,
414
414
  scipydist : scipy.stats.rv_continuous
415
415
  The scipy.stats distribution object to fit (e.g., stats.norm).
416
416
  distName : str, optional
417
- Name of the distribution. If None, the distribution's name is used.
417
+ Name recorded in the ``distName`` field of the result (for example,
418
+ the label used by ``compare``). Default None.
418
419
  lowerBound : float, optional
419
420
  If given, the lower bound of the support of the fitted distribution
420
421
  is fixed at this value rather than estimated. The distribution must
@@ -446,6 +447,8 @@ def fit(data: conversions.VectorLike,
446
447
  The frozen fitted distribution object.
447
448
  distName : str
448
449
  Name of the fitted distribution.
450
+ warnings : list
451
+ Warnings captured during fitting (empty for a clean fit).
449
452
 
450
453
  Raises
451
454
  ------
@@ -118,10 +118,14 @@ def kstest(data: conversions.VectorLike,
118
118
  ----------
119
119
  data : VectorLike
120
120
  Sample data vector.
121
- dist : str or frozen distribution object
122
- Scipy distribution name or frozen distribution object.
121
+ dist : str, scipy.stats distribution, or frozen distribution
122
+ The hypothesized distribution: the name of a ``scipy.stats``
123
+ distribution (e.g., ``"norm"``), an unfrozen distribution object
124
+ (e.g., ``stats.norm``), or a frozen distribution (e.g.,
125
+ ``stats.norm(0.0, 1.0)``).
123
126
  args : tuple, optional
124
- Parameters for the distribution if it is not a string.
127
+ Shape, location, and scale parameters, applied when `dist` is a name
128
+ or an unfrozen distribution. Ignored for a frozen distribution.
125
129
  assume_sorted : bool, optional
126
130
  If True, assumes that `data` is already sorted. Defaults to False.
127
131
 
@@ -146,10 +150,14 @@ def cvmtest(data: conversions.VectorLike,
146
150
  ----------
147
151
  data : VectorLike
148
152
  Sample data vector.
149
- dist : str or frozen distribution object
150
- Scipy distribution name or frozen distribution object.
153
+ dist : str, scipy.stats distribution, or frozen distribution
154
+ The hypothesized distribution: the name of a ``scipy.stats``
155
+ distribution (e.g., ``"norm"``), an unfrozen distribution object
156
+ (e.g., ``stats.norm``), or a frozen distribution (e.g.,
157
+ ``stats.norm(0.0, 1.0)``).
151
158
  args : tuple, optional
152
- Parameters for the distribution if it is not a string.
159
+ Shape, location, and scale parameters, applied when `dist` is a name
160
+ or an unfrozen distribution. Ignored for a frozen distribution.
153
161
  assume_sorted : bool, optional
154
162
  If True, assumes that `data` is already sorted. Defaults to False.
155
163
 
@@ -173,10 +181,14 @@ def adtest(data: conversions.VectorLike,
173
181
  ----------
174
182
  data : VectorLike
175
183
  Sample data vector.
176
- dist : str or frozen distribution object
177
- Scipy distribution name or frozen distribution object.
184
+ dist : str, scipy.stats distribution, or frozen distribution
185
+ The hypothesized distribution: the name of a ``scipy.stats``
186
+ distribution (e.g., ``"norm"``), an unfrozen distribution object
187
+ (e.g., ``stats.norm``), or a frozen distribution (e.g.,
188
+ ``stats.norm(0.0, 1.0)``).
178
189
  args : tuple, optional
179
- Parameters for the distribution if it is not a string.
190
+ Shape, location, and scale parameters, applied when `dist` is a name
191
+ or an unfrozen distribution. Ignored for a frozen distribution.
180
192
  assume_sorted : bool, optional
181
193
  If True, assumes that `data` is already sorted. Defaults to False.
182
194
 
@@ -53,7 +53,7 @@ def eut(util: Callable[[float], float],
53
53
  Compute expected utility and certainty equivalent income.
54
54
 
55
55
  This can operate on an arbitrary utility function, numerically solving
56
- for the certainty equivalent. For isoelastic expected utiliy and
56
+ for the certainty equivalent. For isoelastic expected utility and
57
57
  certainty equivalents, the function "eutIsoelastic" will be less
58
58
  likely to suffer from numerical problems.
59
59
 
@@ -74,7 +74,7 @@ def eut(util: Callable[[float], float],
74
74
  Named tuple with fields:
75
75
 
76
76
  ExpectedValue : float
77
- The mean isoelastic utility of the outcomes.
77
+ The mean utility of the outcomes.
78
78
  CertaintyEquiv : float
79
79
  The certainty equivalent income.
80
80
  """
@@ -40,12 +40,12 @@ def imanconover(spear: conversions.ArrayLike,
40
40
  )-> pd.DataFrame:
41
41
 
42
42
  """
43
- Induce a Spearman correlation stucture using the Iman & Conover method.
43
+ Induce a Spearman correlation structure using the Iman & Conover method.
44
44
 
45
45
  Parameters
46
46
  ----------
47
47
  spear : ArrayLike
48
- Desired Spearman correlation matrix (must be symetric and
48
+ Desired Spearman correlation matrix (must be symmetric and
49
49
  positive definite).
50
50
  vectors : list of VectorLike
51
51
  List of vectors, each representing draws for one variable.
@@ -59,7 +59,7 @@ def imanconover(spear: conversions.ArrayLike,
59
59
  -------
60
60
  pandas.DataFrame
61
61
  DataFrame with one column for each vector/variable, where the
62
- data refelct the desired Spearman correlation structure.
62
+ data reflect the desired Spearman correlation structure.
63
63
 
64
64
  References
65
65
  ----------
@@ -72,7 +72,7 @@ def nearestpd(array):
72
72
 
73
73
  if is_positive_definite(A3):
74
74
  msg = ("Higham's (1988) first method was employed to compute "
75
- "the pos. def. matrix nearest to the sample covariance matrix.")
75
+ "the positive definite matrix nearest to the input matrix.")
76
76
  warnings.warn(msg, UserWarning)
77
77
  return repackage(array, A3)
78
78
 
@@ -94,6 +94,6 @@ def nearestpd(array):
94
94
  A3 += Id * (-mineig * k ** 2 + spacing)
95
95
  k += 1
96
96
  msg = ("Higham's (1988) second method was employed to compute "
97
- "the pos. def. matrix nearest to the sample covariance matrix.")
97
+ "the positive definite matrix nearest to the input matrix.")
98
98
  warnings.warn(msg, UserWarning)
99
99
  return repackage(array, A3)
@@ -51,15 +51,18 @@ def fan(da: xr.DataArray,
51
51
  varname : str
52
52
  Name of the variable to plot from the "variables" dimension.
53
53
  filepath : str, optional
54
- Path to save the chart. Supports HTML and image formats. Default None.
54
+ Path to save the chart. A ``.html`` extension writes an interactive
55
+ HTML file; ``.png``, ``.jpg``, ``.jpeg``, ``.webp``, ``.svg``, and
56
+ ``.pdf`` write a static image, which requires the optional
57
+ ``kaleido`` package. Default None (do not save).
55
58
  line_color : str, optional
56
59
  Color of the mean line. Default is 'blue'.
57
60
  title : str, optional
58
61
  Title for the figure. Default is "" (no title).
59
62
  width : int, optional
60
- Width of the chart in pixels. Default is 800.
63
+ Width of the chart in pixels. Default is 750.
61
64
  height : int, optional
62
- Height of the chart in pixels. Default is 500.
65
+ Height of the chart in pixels. Default is 400.
63
66
 
64
67
  Returns
65
68
  -------
@@ -244,12 +247,12 @@ def twofuncs(f0: Callable[[float], float],
244
247
  Label for the first function. Default is "f0".
245
248
  name1 : str, optional
246
249
  Label for the second function. Default is "f1".
247
- title : str, optinal
248
- Title for the top of the figure. Defaults to None.
250
+ title : str, optional
251
+ Title for the figure. Default is "" (no title).
249
252
  width : int, optional
250
- Width of the chart in pixels. Default is 800.
253
+ Width of the chart in pixels. Default is 750.
251
254
  height : int, optional
252
- Height of the chart in pixels. Default is 500.
255
+ Height of the chart in pixels. Default is 400.
253
256
 
254
257
  Returns
255
258
  -------
@@ -309,9 +312,9 @@ def histpdf(data: conversions.VectorLike,
309
312
  title : str, optional
310
313
  Title for the figure. Default is "" (no title).
311
314
  width : int, optional
312
- Width of the chart in pixels. Default is 800.
315
+ Width of the chart in pixels. Default is 750.
313
316
  height : int, optional
314
- Height of the chart in pixels. Default is 500.
317
+ Height of the chart in pixels. Default is 400.
315
318
 
316
319
  Returns
317
320
  -------
@@ -389,7 +392,7 @@ def dblscat(a0: conversions.ArrayLike,
389
392
  title : str, optional
390
393
  Title for the figure. Default is "" (no title).
391
394
  width : int, optional
392
- Width of the chart in pixels. Default is 800.
395
+ Width of the chart in pixels. Default is 500.
393
396
  height : int, optional
394
397
  Height of the chart in pixels. Default is 500.
395
398
 
@@ -530,8 +533,10 @@ def show(fig):
530
533
  """
531
534
  Flexibly display a plotly plot in a Jupyter notebook.
532
535
 
533
- Invokes plotly's fig.show() with a `renderer` argument set to the value
534
- in the environment variable FUNCSIM_PLOTLY_RENDERER.
536
+ Invokes plotly's ``fig.show()`` with a `renderer` argument set to the
537
+ value of the environment variable ``FUNCSIM_PLOTLY_RENDERER`` (for
538
+ example ``"notebook"``, ``"vscode"``, ``"browser"``, or ``"png"``).
539
+ If the variable is not set, plotly's default renderer is used.
535
540
 
536
541
  Parameters
537
542
  ----------
@@ -3,10 +3,21 @@ import xarray as xr
3
3
 
4
4
 
5
5
  class MissingValue(Exception):
6
+ """Raised by :meth:`RDdata.recall` when a requested value is unavailable."""
6
7
  pass
7
8
 
8
9
 
9
10
  class RDdata():
11
+ """
12
+ Per-trial history for a recursive-dynamic simulation.
13
+
14
+ ``simulate`` passes an instance of this class as the second argument
15
+ (conventionally ``hist``) to a step function that accepts two
16
+ arguments. It holds the historical values supplied via ``hist0``
17
+ followed by the values the step function has returned so far in the
18
+ current trial. Step functions should only call :meth:`recall`; the
19
+ class is not meant to be instantiated by users.
20
+ """
10
21
  # container for an np array with pre-allocated memory for future values
11
22
  # for a single trial in an RD sim
12
23
  def __init__(self, a, steps, namePositions):
@@ -48,7 +59,9 @@ class RDdata():
48
59
  varname : str
49
60
  Name of the variable to recall.
50
61
  lag : int, optional
51
- Number of steps to lag (default is 0).
62
+ Number of steps to look back. ``lag=1`` is the previous step
63
+ (the most recent value available while a step is being
64
+ computed). Default is 0.
52
65
 
53
66
  Returns
54
67
  -------
@@ -73,7 +86,7 @@ class RDdata():
73
86
  f'simulation, no (non-NaN) value for the '
74
87
  f'variable "{varname}" with {lag} lag(s) is '
75
88
  f'available. Be sure that you passed a '
76
- f'a "data0" array to "simulate" and that it '
89
+ f'"hist0" array to "simulate" and that it '
77
90
  f'contains this value.')
78
91
  else:
79
92
  raise MissingValue(f'In time step {self._currStep} in the '
@@ -351,8 +351,17 @@ def screen(data: conversions.VectorLike,
351
351
  Returns
352
352
  -------
353
353
  str
354
- A formatted string summarizing the results of stationarity,
354
+ A formatted string summarizing the results of stationarity,
355
355
  heteroskedasticity, autocorrelation, and randomness tests.
356
+
357
+ Notes
358
+ -----
359
+ Line A is an augmented Dickey-Fuller (ADF) test whose equation includes
360
+ a constant, so its alternative is stationarity around a constant
361
+ (possibly nonzero) mean. Line B adds a linear time trend, so its
362
+ alternative is trend stationarity. Lines C (White) and D (Ljung-Box)
363
+ are applied to the residuals from a regression of the series on the
364
+ observation number. Lines E and F test the overall i.i.d. null.
356
365
  """
357
366
 
358
367
  if alpha <= 0 or alpha >= 1:
@@ -369,9 +378,13 @@ def screen(data: conversions.VectorLike,
369
378
  y_hat = X @ beta
370
379
  resid = y - y_hat
371
380
 
372
- # ADF tests: line A has no deterministic terms; line B includes a
373
- # constant and linear trend, so it is a genuine trend-stationarity test
374
- p_adf = adf_test_no_const(seriesA, max_lag=2, n_sim=1000, seed=42)[1]
381
+ # ADF tests: line A includes a constant, so it tests for stationarity
382
+ # around a constant mean (a no-constant equation has zero-mean
383
+ # stationarity as its alternative, so it fails to reject the unit root,
384
+ # and the report warns, for an i.i.d. series whose mean is large
385
+ # relative to its standard deviation); line B includes a constant and
386
+ # a linear trend, so it is a genuine trend-stationarity test
387
+ p_adf = adf_test_with_const(seriesA, max_lag=2, n_sim=1000, seed=42)[1]
375
388
  p_adf_t = adf_test_const_trend(seriesA, max_lag=2, n_sim=1000, seed=42)[1]
376
389
 
377
390
  # White test for heteroskedasticity (constant is added inside
@@ -6,6 +6,7 @@ Genetics and Molecular Biology//, vol 4(2005), issue 1.
6
6
  """
7
7
 
8
8
  import itertools
9
+ from typing import Tuple, Union
9
10
  import numpy as np
10
11
  import pandas as pd
11
12
  import xarray as xr
@@ -207,7 +208,10 @@ def _target_f(x):
207
208
 
208
209
 
209
210
  def shrink(data: conversions.ArrayLike,
210
- target: str ) -> conversions.ArrayLike:
211
+ target: str,
212
+ return_intensity: bool = False
213
+ ) -> Union[conversions.ArrayLike,
214
+ Tuple[conversions.ArrayLike, float]]:
211
215
  """
212
216
  Covariance shrinkage estimator.
213
217
 
@@ -216,22 +220,43 @@ def shrink(data: conversions.ArrayLike,
216
220
  data : ArrayLike
217
221
  Data array with variables in columns and observations in rows.
218
222
  target : str
219
- Shrinkage target, one of: 'A', 'B', 'C', 'D', 'E', 'F'. These
220
- correspond to different shrinkage estimation methods as defined in
221
- Shafer & Strimmer, "A shrinkage approach to large-scale covariance
222
- matrix estimation and implications for Functional Genomics."
223
- //Statistical Applications in Genetics and Molecular Biology//,
224
- vol 4(2005), issue 1.
223
+ Shrinkage target, one of 'A' through 'F', following Table 2 of
224
+ Schafer and Strimmer (2005):
225
+
226
+ - 'A': diagonal, unit variance
227
+ - 'B': diagonal, common variance
228
+ - 'C': common variance and common covariance
229
+ - 'D': diagonal, unequal variance
230
+ - 'E': perfect positive correlation
231
+ - 'F': constant correlation
232
+
233
+ Targets 'B', 'E', and 'F' originate in Ledoit and Wolf (2003,
234
+ 2004). The shrinkage intensity is estimated analytically in each
235
+ case.
236
+ return_intensity : bool, optional
237
+ If True, also return the estimated shrinkage intensity. Default is
238
+ False.
225
239
 
226
240
  Returns
227
241
  -------
228
- ArrayLike
229
- The estimated covariance matrix, with a type matching the data array
242
+ ArrayLike or tuple
243
+ The estimated covariance matrix, with a type matching the data
244
+ array. If `return_intensity` is True, a tuple ``(sigma, lam)``,
245
+ where ``lam`` is the shrinkage intensity, a float in [0, 1]: 0
246
+ leaves the sample covariance matrix unchanged and 1 replaces it
247
+ with the target.
230
248
 
231
249
  Raises
232
250
  ------
233
251
  ValueError
234
252
  If target is not one of: 'A', 'B', 'C', 'D', 'E', 'F'.
253
+
254
+ References
255
+ ----------
256
+ Schafer, J., & Strimmer, K. (2005). A shrinkage approach to large-scale
257
+ covariance matrix estimation and implications for functional genomics.
258
+ Statistical Applications in Genetics and Molecular Biology, 4(1),
259
+ Article 32.
235
260
  """
236
261
  if target not in ['A', 'B', 'C', 'D', 'E', 'F']:
237
262
  raise ValueError("target must be one of: A, B, C, D, E, F")
@@ -245,26 +270,30 @@ def shrink(data: conversions.ArrayLike,
245
270
  'E': _target_e,
246
271
  'F': _target_f}
247
272
 
248
- sig = target_map[target](a_np)[0]
273
+ sig, lam = target_map[target](a_np)
249
274
 
250
275
  # return the same type of array that was passed. the result is a
251
276
  # p-by-p covariance matrix, so both axes are labeled with the
252
277
  # variable (column) labels of the input, not the observation index
253
278
  if isinstance(data, pd.DataFrame):
254
- df = pd.DataFrame(data=sig, index=data.columns, columns=data.columns)
255
- return df
279
+ result = pd.DataFrame(data=sig, index=data.columns,
280
+ columns=data.columns)
256
281
  elif isinstance(data, xr.DataArray):
257
282
  coldim = str(data.dims[-1])
258
283
  if coldim in data.coords:
259
284
  varnames = np.asarray(data.coords[coldim].values)
260
285
  else:
261
286
  varnames = np.arange(sig.shape[0])
262
- da = xr.DataArray(sig,
263
- dims=(coldim, coldim + "_"),
264
- coords={coldim: varnames, coldim + "_": varnames})
265
- return da
287
+ result = xr.DataArray(sig,
288
+ dims=(coldim, coldim + "_"),
289
+ coords={coldim: varnames,
290
+ coldim + "_": varnames})
266
291
  else:
267
- return sig
292
+ result = sig
293
+
294
+ if return_intensity:
295
+ return result, float(lam)
296
+ return result
268
297
 
269
298
 
270
299
  if __name__ == '__main__':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: funcsim
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: functional, simple stochastic simulation
5
5
  Home-page: https://github.com/h-bryant/funcsim
6
6
  Author: Henry Bryant
@@ -46,35 +46,43 @@ Dynamic: summary
46
46
  # funcsim
47
47
  Lightweight, functional stochastic simulation
48
48
 
49
+ Documentation: https://funcsim.readthedocs.io/
50
+
49
51
  Features:
50
- - cross-sectional and recursive dynamic simulations
51
- - pseudo-random numbers (so that results are comparable across multiple runs or scenarios)
52
+ - static (cross-sectional) and recursive-dynamic simulations from a single user-written function
53
+ - pseudo-random numbers with an explicit seed (so that results are reproducible and comparable across runs or scenarios)
52
54
  - stratified/Latin hypercube sampling
53
55
  - painless multi-core simulations
56
+ - fitting and comparison of univariate distributions, kernel density estimates, and goodness-of-fit tests
57
+ - multivariate normal and KDE distributions; Gaussian, Student's t, Clayton, Gumbel, and Frank copulas (implemented natively, no additional packages required); Iman-Conover; covariance shrinkage
58
+ - expected utility and cumulative prospect theory calculations
59
+ - plotly-based fan charts and diagnostic plots
54
60
  - highly flexible, and as simple as possible (but no more)
55
61
  - functional paradigm
56
62
 
57
- Developed using:
58
- - python >= 3.10
59
- - numpy 2.2.0
60
- - scipy 1.16.0
61
- - pandas 2.2.0 (pandas 2.2+ and 3.x are both supported)
62
- - xarray 2025.4.0
63
-
64
- Copulas (Gaussian, Student's t, Clayton, Gumbel, and Frank) are supported
65
- natively; no additional packages are required.
63
+ Requirements:
64
+ - python >= 3.11
65
+ - numpy >= 2.2.0
66
+ - scipy >= 1.16.0
67
+ - pandas >= 2.2.0 (pandas 2.2+ and 3.x are both supported)
68
+ - xarray >= 2025.4.0
66
69
 
67
- For optional functionality, additinoal packages are needed:
68
- - plotting: plotly
70
+ For optional functionality, additional packages are needed:
71
+ - plotting: plotly (`pip install "funcsim[plotting]"`); kaleido for static image export
69
72
 
70
- Additionally, to run the demos, run tests, and build docs:
73
+ Additionally, to run the tests and build the docs:
71
74
  - pytest
72
- - sphinx, sphinx-autodoc-typehints
75
+ - sphinx, sphinx-autodoc-typehints (`pip install "funcsim[docs]"`)
76
+
77
+ ### installation
78
+ ```bash
79
+ pip install funcsim
80
+ ```
73
81
 
74
82
  ### building html docs
75
83
  From the top-level funcsim directory...
76
84
  ```bash
77
- sphinx-build -b html docs/source/ docs/build/
85
+ sphinx-build -W -b html docs/source/ docs/build/
78
86
  ```
79
87
 
80
88
  ### running tests
@@ -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.3",
8
+ version="0.2.5",
9
9
  license="bsd-3-clause",
10
10
  author="Henry Bryant",
11
11
  author_email="henry@tamu.edu",
funcsim-0.2.3/README.md DELETED
@@ -1,39 +0,0 @@
1
- # funcsim
2
- Lightweight, functional stochastic simulation
3
-
4
- Features:
5
- - cross-sectional and recursive dynamic simulations
6
- - pseudo-random numbers (so that results are comparable across multiple runs or scenarios)
7
- - stratified/Latin hypercube sampling
8
- - painless multi-core simulations
9
- - highly flexible, and as simple as possible (but no more)
10
- - functional paradigm
11
-
12
- Developed using:
13
- - python >= 3.10
14
- - numpy 2.2.0
15
- - scipy 1.16.0
16
- - pandas 2.2.0 (pandas 2.2+ and 3.x are both supported)
17
- - xarray 2025.4.0
18
-
19
- Copulas (Gaussian, Student's t, Clayton, Gumbel, and Frank) are supported
20
- natively; no additional packages are required.
21
-
22
- For optional functionality, additinoal packages are needed:
23
- - plotting: plotly
24
-
25
- Additionally, to run the demos, run tests, and build docs:
26
- - pytest
27
- - sphinx, sphinx-autodoc-typehints
28
-
29
- ### building html docs
30
- From the top-level funcsim directory...
31
- ```bash
32
- sphinx-build -b html docs/source/ docs/build/
33
- ```
34
-
35
- ### running tests
36
- From the top-level funcsim directory...
37
- ```bash
38
- python3 -m pytest
39
- ```
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