# Plotting and reporting Bayes Factor given idata, var name, prior distribution and reference value
# pylint: disable=unbalanced-tuple-unpacking
import logging
from numpy import interp
from ..data.utils import extract
from .plot_utils import get_plotting_function
from ..stats.density_utils import _kde_linear
_log = logging.getLogger(__name__)
[docs]
def plot_bf(
idata,
var_name,
prior=None,
ref_val=0,
colors=("C0", "C1"),
figsize=None,
textsize=None,
hist_kwargs=None,
plot_kwargs=None,
ax=None,
backend=None,
backend_kwargs=None,
show=None,
):
r"""Approximated Bayes Factor for comparing hypothesis of two nested models.
The Bayes factor is estimated by comparing a model (H1) against a model in which the
parameter of interest has been restricted to be a point-null (H0). This computation
assumes the models are nested and thus H0 is a special case of H1.
Notes
-----
The bayes Factor is approximated as the Savage-Dickey density ratio
algorithm presented in [1]_.
Parameters
----------
idata : InferenceData
Any object that can be converted to an :class:`arviz.InferenceData` object
Refer to documentation of :func:`arviz.convert_to_dataset` for details.
var_name : str, optional
Name of variable we want to test.
prior : numpy.array, optional
In case we want to use different prior, for example for sensitivity analysis.
ref_val : int, default 0
Point-null for Bayes factor estimation.
colors : tuple, default ('C0', 'C1')
Tuple of valid Matplotlib colors. First element for the prior, second for the posterior.
figsize : (float, float), optional
Figure size. If `None` it will be defined automatically.
textsize : float, optional
Text size scaling factor for labels, titles and lines. If `None` it will be auto
scaled based on `figsize`.
plot_kwargs : dict, optional
Additional keywords passed to :func:`matplotlib.pyplot.plot`.
hist_kwargs : dict, optional
Additional keywords passed to :func:`arviz.plot_dist`. Only works for discrete variables.
ax : axes, optional
:class:`matplotlib.axes.Axes` or :class:`bokeh.plotting.Figure`.
backend : {"matplotlib", "bokeh"}, default "matplotlib"
Select plotting backend.
backend_kwargs : dict, optional
These are kwargs specific to the backend being used, passed to
:func:`matplotlib.pyplot.subplots` or :class:`bokeh.plotting.figure`.
For additional documentation check the plotting method of the backend.
show : bool, optional
Call backend show function.
Returns
-------
dict : A dictionary with BF10 (Bayes Factor 10 (H1/H0 ratio), and BF01 (H0/H1 ratio).
axes : matplotlib_axes or bokeh_figure
References
----------
.. [1] Heck, D., 2019. A caveat on the Savage-Dickey density ratio:
The case of computing Bayes factors for regression parameters.
Examples
--------
Moderate evidence indicating that the parameter "a" is different from zero.
.. plot::
:context: close-figs
>>> import numpy as np
>>> import arviz as az
>>> idata = az.from_dict(posterior={"a":np.random.normal(1, 0.5, 5000)},
... prior={"a":np.random.normal(0, 1, 5000)})
>>> az.plot_bf(idata, var_name="a", ref_val=0)
"""
posterior = extract(idata, var_names=var_name).values
if ref_val > posterior.max() or ref_val < posterior.min():
_log.warning(
"The reference value is outside of the posterior. "
"This translate into infinite support for H1, which is most likely an overstatement."
)
if posterior.ndim > 1:
_log.warning("Posterior distribution has {posterior.ndim} dimensions")
if prior is None:
prior = extract(idata, var_names=var_name, group="prior").values
if posterior.dtype.kind == "f":
posterior_grid, posterior_pdf = _kde_linear(posterior)
prior_grid, prior_pdf = _kde_linear(prior)
posterior_at_ref_val = interp(ref_val, posterior_grid, posterior_pdf)
prior_at_ref_val = interp(ref_val, prior_grid, prior_pdf)
elif posterior.dtype.kind == "i":
posterior_at_ref_val = (posterior == ref_val).mean()
prior_at_ref_val = (prior == ref_val).mean()
bf_10 = prior_at_ref_val / posterior_at_ref_val
bf_01 = 1 / bf_10
bfplot_kwargs = dict(
ax=ax,
bf_10=bf_10.item(),
bf_01=bf_01.item(),
prior=prior,
posterior=posterior,
ref_val=ref_val,
prior_at_ref_val=prior_at_ref_val,
posterior_at_ref_val=posterior_at_ref_val,
var_name=var_name,
colors=colors,
figsize=figsize,
textsize=textsize,
hist_kwargs=hist_kwargs,
plot_kwargs=plot_kwargs,
backend_kwargs=backend_kwargs,
show=show,
)
plot = get_plotting_function("plot_bf", "bfplot", backend)
axes = plot(**bfplot_kwargs)
return {"BF10": bf_10, "BF01": bf_01}, axes