Utilities¶
pyem.utils collects the shared helpers used throughout the fitting
pipeline: the Gaussian-space parameter transforms and objective-function
helper (pyem.utils.math), group-level fit metrics for model comparison
(pyem.utils.stats), and convenience plotting functions
(pyem.utils.plotting).
Math transforms¶
These functions map between the unconstrained Gaussian space that the EM
loop optimizes over and the natural (bounded) parameter ranges that models
actually use. norm2alpha/alpha2norm are inverses mapping R to (0, 1)
(e.g. a learning rate), and norm2beta/beta2norm are inverses mapping R
to (0, max_val) (e.g. an inverse-temperature capped at max_val).
softmax is a numerically stabilized choice rule (subtracting the max
expected value before exponentiating) used to turn expected values into
choice probabilities. calc_fval builds the scalar objective minimized
during subject-level fitting — the negative log-posterior ("npl", negative
log-likelihood plus negative log-prior) or just the negative log-likelihood
("nll") — and caps non-finite values at 1e7 so gradient-based optimizers
keep making progress instead of hitting inf/nan.
softmax ¶
norm2alpha ¶
alpha2norm ¶
norm2beta ¶
beta2norm ¶
calc_fval ¶
Return objective value given a negative log-likelihood.
Parameters¶
negll : float
Negative log-likelihood of the data under the model.
params : array-like
Parameter vector passed to the prior. Only used when prior is
provided and output is "npl".
prior : object or None, optional
Object with a logpdf method returning the log prior density.
When None the function reduces to returning negll.
output : {"npl", "nll"}
Indicates whether to return the negative posterior likelihood
("npl") or just the negative log-likelihood ("nll").
Returns¶
float
Objective value suitable for minimisation. If the prior results in
inf the value is capped at a large constant to keep optimisers
stable.
Source code in pyem/utils/math.py
Statistics & model-fit metrics¶
These functions turn the raw output of an EM fit into the model-comparison
metrics used by pyem.core.compare.ModelComparison (and are usually called
through it rather than directly). calc_LME computes the Laplace-approximate
log model evidence from each subject's inverse-Hessian and negative-log-posterior,
summed across subjects and penalized for the number of group-level
parameters. calc_BICint computes the integrated BIC by Monte Carlo
integration over the group posterior — for each subject it draws parameter
samples from N(mu, sigma), re-evaluates fit_func at each sample, and
combines the resulting negative log-likelihoods via a numerically stable
logsumexp before applying the usual k*log(n) complexity penalty.
pseudo_r2_from_nll computes a McFadden-style pseudo-R² comparing a model's
(median or mean) negative log-likelihood against the negative log-likelihood
of chance performance among noptions alternatives.
calc_LME ¶
Source code in pyem/utils/stats.py
calc_BICint ¶
calc_BICint(all_data, param_names, mu, sigma, fit_func, nsamples: int = 2000, func_output: str = 'all', nll_key: str = 'nll', ntrials_total: int | None = None, njobs: int = -1) -> float
Integrated BIC via Monte Carlo integration over the group posterior.
ntrials_total is the number of independent trials contributing to a
single subject's likelihood, used for the k*log(n) complexity
penalty. If not supplied, it is auto-detected from the first subject's
data, assuming every array-like field in that subject's data is
trial-aligned with identical shape (true for the RW/Bayes families,
e.g. [choices, rewards] both shaped (nblocks, ntrials)). For
families with heterogeneous per-trial shapes (e.g. a GLM's [X, Y]
where X has an extra feature-count dimension), pass
ntrials_total explicitly rather than relying on auto-detection.
Source code in pyem/utils/stats.py
pseudo_r2_from_nll ¶
pseudo_r2_from_nll(nll: ndarray, ntrials_total: int, noptions: int, metric: str = 'median') -> float
Source code in pyem/utils/stats.py
Plotting¶
Convenience plotting helpers built on matplotlib and seaborn. These
functions require the optional seaborn dependency — install it with the
viz extra: pip install -e ".[viz]". plot_choices plots subject-averaged
choice frequency over trials for a two-alternative task. plot_scatter
draws an x/y scatter plot (e.g. true vs. estimated parameters from a
parameter-recovery check) with an optional dashed x=y reference line and a
Pearson-r annotation; it draws onto (and returns) a matplotlib Axes
without calling plt.show(), so callers can compose multiple panels before
displaying or saving the figure.
plot_choices ¶
Plots the subject average EV over trials
Inputs
- choices_A (np.array): subject choices for option A
- filename (str): filename to save figure to (if None, figure is not saved)
Source code in pyem/utils/plotting.py
plot_scatter ¶
plot_scatter(x, xlabel, y, ylabel, *, ax=None, show_line=True, equal_limits=True, s=75, alpha=0.6, colorname='royalblue', annotate=True)
Scatter plot of x vs y with optional Pearson r annotation and
x=y reference line. The function deliberately does not call
plt.show() so that callers can aggregate multiple subplots before
displaying the figure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
array - like
|
x-axis data |
required |
xlabel
|
str
|
x-axis label |
required |
y
|
array - like
|
y-axis data |
required |
ylabel
|
str
|
y-axis label |
required |
ax
|
Axes | None
|
Axes to draw on. If None, creates a new figure/axes. |
None
|
show_line
|
bool
|
Whether to draw dashed x=y line. |
True
|
equal_limits
|
bool
|
If True, sets identical limits and equal aspect. |
True
|
s
|
float
|
Marker size. |
75
|
alpha
|
float
|
Marker opacity. |
0.6
|
colorname
|
str
|
Marker color. |
'royalblue'
|
annotate
|
bool
|
If True, adds Pearson r annotation. |
True
|
Returns:
| Type | Description |
|---|---|
|
matplotlib.axes.Axes: The axes the plot was drawn on. |