Skip to content

Commit

Permalink
More fixes thanks to tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tomicapretto committed Apr 14, 2024
1 parent 358bce1 commit e7e0a6d
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 86 deletions.
21 changes: 16 additions & 5 deletions bambi/backend/pymc.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,12 +371,12 @@ def _run_laplace(self, draws, omit_offsets, include_mean):
Parameters
----------
draws: int
draws : int
The number of samples to draw from the posterior distribution.
omit_offsets: bool
omit_offsets : bool
Omits offset terms in the `InferenceData` object returned when the model includes
group specific effects.
include_mean: bool
include_mean : bool
Compute the posterior of the mean response.
Returns
Expand All @@ -386,6 +386,14 @@ def _run_laplace(self, draws, omit_offsets, include_mean):
with self.model:
maps = pm.find_MAP()
n_maps = deepcopy(maps)

# Remove deterministics for parent parameters
n_maps = {
key: value
for key, value in n_maps.items()
if key not in self.spec.family.likelihood.params
}

for m in maps:
if pm.util.is_transformed_name(m):
n_maps.pop(pm.util.get_untransformed_name(m))
Expand All @@ -398,6 +406,9 @@ def _run_laplace(self, draws, omit_offsets, include_mean):
cov = np.linalg.inv(hessian)
modes = np.concatenate([np.atleast_1d(v) for v in n_maps.values()])

print(cov.shape)
print(modes.shape)

samples = np.random.multivariate_normal(modes, cov, size=draws)

idata = _posterior_samples_to_idata(samples, self.model)
Expand All @@ -422,9 +433,9 @@ def _posterior_samples_to_idata(samples, model):
Parameters
----------
samples: array
samples : array
Posterior samples
model: PyMC model
model : PyMC model
Returns
-------
Expand Down
3 changes: 3 additions & 0 deletions bambi/backend/terms.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ def build(self, spec):
dist = dist(label, dims=dims, **self.term.prior.args)[np.newaxis, :]
else:
dist = dist(label, **self.term.prior.args)
# TODO: check if this brings the desired result
# Multiply it by vector of ones so it then has the proper length
dist = dist * np.ones((spec.response_component.term.data.shape[0],))
return dist

@property
Expand Down
2 changes: 1 addition & 1 deletion bambi/families/family.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ def _make_dist_kwargs_and_coords(self, model, posterior, **kwargs):
* A dictionary that maps the names of the likelihood parameters to draws from the
posterior distribtuion.
* An `xr.Coordinates` object with the coordinates required for the response. For example:
`(chain, draw, y_obs)` or `(chain, draw, y_obs, y_dim)`.
`(chain, draw, __obs__)` or `(chain, draw, __obs__, y_dim)`.
It was created to abstract repetitive logic used in both `.posterior_predictive()` and
`log_likelihood()`.
Expand Down
33 changes: 26 additions & 7 deletions bambi/interpret/effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class ResponseInfo:
computing uncertainty intervals, and creating the summary dataframe in
'PredictiveDifferences'.
FIXME: is the documentation outdated?
Parameters
----------
model : Model
Expand Down Expand Up @@ -65,7 +67,9 @@ def __post_init__(self):
if self.target is None:
self.name_target = self.name
else:
self.name_target = f"{self.name}_{self.target}"
# TODO: verify this is the correct update
# self.name_target = f"{self.name}_{self.target}"
self.name_target = self.target

self.name_obs = "__obs__"
self.lower_bound_name = f"lower_{self.lower_bound * 100}%"
Expand Down Expand Up @@ -277,6 +281,9 @@ def get_estimate(

if self.variable.values.ndim == 1:
self.variable.values = np.array(self.variable.values).reshape(-1, 1)

print("name_target", self.response.name_target)
print("name_obs", self.response.name_obs)

draws = {}
variable_data = {}
Expand Down Expand Up @@ -515,11 +522,15 @@ def predictions(
target = None
else:
# use the default response "y" and append target
response_name = get_aliased_name(model.response_component.response_term)
response_name = get_aliased_name(model.response_component.term)
else:
response_name = get_aliased_name(model.response_component.response_term)
response_name = get_aliased_name(model.response_component.term)

response = ResponseInfo(response_name, target)
if target == "mean":
target_ = model.family.likelihood.parent
else:
target_ = target
response = ResponseInfo(response_name, target_)
response_transform = transforms.get(response_name, identity)

if pps:
Expand Down Expand Up @@ -680,10 +691,18 @@ def comparisons(
conditional_info = ConditionalInfo(model, conditional)

transforms = transforms if transforms is not None else {}
response_name = get_aliased_name(model.response_component.response_term)
response_name = get_aliased_name(model.response_component.term)

# TODO: see if this is appropriate
response = ResponseInfo(
response_name, target="mean", lower_bound=lower_bound, upper_bound=upper_bound
response_name,
target=model.family.likelihood.parent,
lower_bound=lower_bound,
upper_bound=upper_bound
)
# response = ResponseInfo(
# response_name, target="mean", lower_bound=lower_bound, upper_bound=upper_bound
# )
response_transform = transforms.get(response_name, identity)

comparisons_data = create_differences_data(
Expand Down Expand Up @@ -844,7 +863,7 @@ def slopes(
upper_bound = 1 - lower_bound

transforms = transforms if transforms is not None else {}
response_name = get_aliased_name(model.response_component.response_term)
response_name = get_aliased_name(model.response_component.term)
response = ResponseInfo(response_name, "mean", lower_bound, upper_bound)
response_transform = transforms.get(response_name, identity)

Expand Down
84 changes: 42 additions & 42 deletions bambi/interpret/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def _plot_differences(
if transforms is None:
transforms = {}

response_name = get_aliased_name(model.response_component.response_term)
response_name = get_aliased_name(model.response_component.term)

if ax is None:
fig_kwargs = {} if fig_kwargs is None else fig_kwargs
Expand Down Expand Up @@ -73,7 +73,7 @@ def _plot_differences(
else:
raise TypeError("Main covariate must be numeric or categoric.")

response_name = get_aliased_name(model.response_component.response_term)
response_name = get_aliased_name(model.response_component.term)
for ax in axes.ravel(): # pylint: disable = redefined-argument-from-local
ax.set(xlabel=covariates.main, ylabel=response_name)

Expand Down Expand Up @@ -111,37 +111,37 @@ def plot_predictions(
average_by: str, list, bool, optional
The covariates we would like to average by. The passed covariate(s) will marginalize
over the other covariates in the model. If True, it averages over all covariates
in the model to obtain the average estimate. Defaults to ``None``.
in the model to obtain the average estimate. Defaults to `None`.
target : str
Which model parameter to plot. Defaults to 'mean'. Passing a parameter into target only
works when pps is False as the target may not be available in the posterior predictive
distribution.
sample_new_groups : bool, optional
If the model contains group-level effects, and data is passed for unseen groups, whether
to sample from the new groups. Defaults to ``False``.
to sample from the new groups. Defaults to `False`.
pps: bool, optional
Whether to plot the posterior predictive samples. Defaults to ``False``.
Whether to plot the posterior predictive samples. Defaults to `False`.
use_hdi : bool, optional
Whether to compute the highest density interval (defaults to True) or the quantiles.
prob : float, optional
The probability for the credibility intervals. Must be between 0 and 1. Defaults to 0.94.
Changing the global variable ``az.rcParam["stats.hdi_prob"]`` affects this default.
Changing the global variable `az.rcParam["stats.hdi_prob"]` affects this default.
legend : bool, optional
Whether to automatically include a legend in the plot. Defaults to ``True``.
Whether to automatically include a legend in the plot. Defaults to `True`.
transforms : dict, optional
Transformations that are applied to each of the variables being plotted. The keys are the
name of the variables, and the values are functions to be applied. Defaults to ``None``.
name of the variables, and the values are functions to be applied. Defaults to `None`.
ax : matplotlib.axes._subplots.AxesSubplot, optional
A matplotlib axes object or a sequence of them. If None, this function instantiates a
new axes object. Defaults to ``None``.
new axes object. Defaults to `None`.
fig_kwargs : optional
Keyword arguments passed to the matplotlib figure function as a dict. For example,
``fig_kwargs=dict(figsize=(11, 8)), sharey=True`` would make the figure 11 inches wide
`fig_kwargs=dict(figsize=(11, 8)), sharey=True` would make the figure 11 inches wide
by 8 inches high and would share the y-axis values.
subplot_kwargs : optional
Keyword arguments used to determine the covariates used for the horizontal, group,
and panel axes. For example, ``subplot_kwargs=dict(main="x", group="y", panel="z")`` would
plot the horizontal axis as ``x``, the color (hue) as ``y``, and the panel axis as ``z``.
and panel axes. For example, `subplot_kwargs=dict(main="x", group="y", panel="z")` would
plot the horizontal axis as `x`, the color (hue) as `y`, and the panel axis as `z`.
Returns
-------
Expand All @@ -151,8 +151,8 @@ def plot_predictions(
Raises
------
ValueError
If ``conditional`` and ``average_by`` are both ``None``.
If length of ``conditional`` is greater than 3 and ``average_by`` is ``None``.
If `conditional` and `average_by` are both `None`.
If length of `conditional` is greater than 3 and `average_by` is `None`.
If main covariate is not numeric or categoric.
"""
if conditional is None and average_by is None:
Expand Down Expand Up @@ -207,7 +207,7 @@ def plot_predictions(
else:
covariates = get_covariates(conditional_info.covariates)

response_name = get_aliased_name(model.response_component.response_term)
response_name = get_aliased_name(model.response_component.term)

if ax is None:
fig_kwargs = {} if fig_kwargs is None else fig_kwargs
Expand Down Expand Up @@ -270,33 +270,33 @@ def plot_comparisons(
values are the values to condition on.
average_by: str, list, optional
The covariates we would like to average by. The passed covariate(s) will marginalize
over the other covariates in the model. Defaults to ``None``.
over the other covariates in the model. Defaults to `None`.
comparison_type : str, optional
The type of comparison to plot. Defaults to 'diff'.
sample_new_groups : bool, optional
If the model contains group-level effects, and data is passed for unseen groups, whether
to sample from the new groups. Defaults to ``False``.
to sample from the new groups. Defaults to `False`.
use_hdi : bool, optional
Whether to compute the highest density interval (defaults to True) or the quantiles.
prob : float, optional
The probability for the credibility intervals. Must be between 0 and 1. Defaults to 0.94.
Changing the global variable ``az.rcParam["stats.hdi_prob"]`` affects this default.
Changing the global variable `az.rcParam["stats.hdi_prob"]` affects this default.
legend : bool, optional
Whether to automatically include a legend in the plot. Defaults to ``True``.
Whether to automatically include a legend in the plot. Defaults to `True`.
transforms : dict, optional
Transformations that are applied to each of the variables being plotted. The keys are the
name of the variables, and the values are functions to be applied. Defaults to ``None``.
name of the variables, and the values are functions to be applied. Defaults to `None`.
ax : matplotlib.axes._subplots.AxesSubplot, optional
A matplotlib axes object or a sequence of them. If None, this function instantiates a
new axes object. Defaults to ``None``.
new axes object. Defaults to `None`.
fig_kwargs : optional
Keyword arguments passed to the matplotlib figure function as a dict. For example,
``fig_kwargs=dict(figsize=(11, 8)), sharey=True`` would make the figure 11 inches wide
`fig_kwargs=dict(figsize=(11, 8)), sharey=True` would make the figure 11 inches wide
by 8 inches high and would share the y-axis values.
subplot_kwargs : optional
Keyword arguments used to determine the covariates used for the horizontal, group,
and panel axes. For example, ``subplot_kwargs=dict(main="x", group="y", panel="z")`` would
plot the horizontal axis as ``x``, the color (hue) as ``y``, and the panel axis as ``z``.
and panel axes. For example, `subplot_kwargs=dict(main="x", group="y", panel="z")` would
plot the horizontal axis as `x`, the color (hue) as `y`, and the panel axis as `z`.
Returns
-------
Expand All @@ -306,10 +306,10 @@ def plot_comparisons(
Raises
------
ValueError
If the number of contrast levels is greater than 2 and ``average_by`` is ``None``.
If ``conditional`` and ``average_by`` are both ``None``.
If length of ``conditional`` is greater than 3 and ``average_by`` is ``None``.
If ``average_by`` is ``True``.
If the number of contrast levels is greater than 2 and `average_by` is `None`.
If `conditional` and `average_by` are both `None`.
If length of `conditional` is greater than 3 and `average_by` is `None`.
If `average_by` is `True`.
If main covariate is not numeric or categoric.
"""
contrast_name = contrast
Expand Down Expand Up @@ -416,7 +416,7 @@ def plot_slopes(
average_by: str, list, bool, optional
The covariates we would like to average by. The passed covariate(s) will marginalize
over the other covariates in the model. If True, it averages over all covariates
in the model to obtain the average estimate. Defaults to ``None``.
in the model to obtain the average estimate. Defaults to `None`.
eps : float, optional
To compute the slope, 'wrt' is evaluated at wrt +/- 'eps'. The rate of change is then
computed as the difference between the two values divided by 'eps'. Defaults to 1e-4.
Expand All @@ -432,28 +432,28 @@ def plot_slopes(
in the response.
sample_new_groups : bool, optional
If the model contains group-level effects, and data is passed for unseen groups, whether
to sample from the new groups. Defaults to ``False``.
to sample from the new groups. Defaults to `False`.
use_hdi : bool, optional
Whether to compute the highest density interval (defaults to True) or the quantiles.
prob : float, optional
The probability for the credibility intervals. Must be between 0 and 1. Defaults to 0.94.
Changing the global variable ``az.rcParam["stats.hdi_prob"]`` affects this default.
Changing the global variable `az.rcParam["stats.hdi_prob"]` affects this default.
transforms : dict, optional
Transformations that are applied to each of the variables being plotted. The keys are the
name of the variables, and the values are functions to be applied. Defaults to ``None``.
name of the variables, and the values are functions to be applied. Defaults to `None`.
legend : bool, optional
Whether to automatically include a legend in the plot. Defaults to ``True``.
Whether to automatically include a legend in the plot. Defaults to `True`.
ax : matplotlib.axes._subplots.AxesSubplot, optional
A matplotlib axes object or a sequence of them. If None, this function instantiates a
new axes object. Defaults to ``None``.
new axes object. Defaults to `None`.
fig_kwargs : optional
Keyword arguments passed to the matplotlib figure function as a dict. For example,
``fig_kwargs=dict(figsize=(11, 8)), sharey=True`` would make the figure 11 inches wide
`fig_kwargs=dict(figsize=(11, 8)), sharey=True` would make the figure 11 inches wide
by 8 inches high and would share the y-axis values.
subplot_kwargs : optional
Keyword arguments used to determine the covariates used for the horizontal, group,
and panel axes. For example, ``subplot_kwargs=dict(main="x", group="y", panel="z")`` would
plot the horizontal axis as ``x``, the color (hue) as ``y``, and the panel axis as ``z``.
and panel axes. For example, `subplot_kwargs=dict(main="x", group="y", panel="z")` would
plot the horizontal axis as `x`, the color (hue) as `y`, and the panel axis as `z`.
Returns
-------
Expand All @@ -463,11 +463,11 @@ def plot_slopes(
Raises
------
ValueError
If the number of ``wrt`` values is greater than 2 and ``average_by`` is ``None``.
If ``conditional`` and ``average_by`` are both ``None``.
If length of ``conditional`` is greater than 3 and ``average_by`` is ``None``.
If ``average_by`` is ``True``.
If ``slope`` is not one of ('dydx', 'dyex', 'eyex', 'eydx').
If the number of `wrt` values is greater than 2 and `average_by` is `None`.
If `conditional` and `average_by` are both `None`.
If length of `conditional` is greater than 3 and `average_by` is `None`.
If `average_by` is `True`.
If `slope` is not one of ('dydx', 'dyex', 'eyex', 'eydx').
If main covariate is not numeric or categoric.
"""
wrt_name = wrt
Expand Down
1 change: 0 additions & 1 deletion tests/test_alternative_samplers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ def data_n100():
)
return data


def test_laplace():
data = pd.DataFrame(np.repeat((0, 1), (30, 60)), columns=["w"])
priors = {"Intercept": bmb.Prior("Uniform", lower=0, upper=1)}
Expand Down
Loading

0 comments on commit e7e0a6d

Please sign in to comment.