Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft plot_bpv (bayesian p-value) #1222

Merged
merged 12 commits into from
Jun 29, 2020
Merged

Draft plot_bpv (bayesian p-value) #1222

merged 12 commits into from
Jun 29, 2020

Conversation

aloctavodia
Copy link
Contributor

@aloctavodia aloctavodia commented Jun 4, 2020

This is a new kind of posterior predictive check plot, focused toward p_values. This a super early draft (the code is still a mess). I have doubts about the arguments names, suggestions are more than welcome. What do you think of these examples. We could also add other like ecdf, but I prefer to concentrate first on getting these right.

BTW, the first too are the same as loo-pit, but using the data instead of the IS weights.

A few examples
kind="u_value", reference="analytical". We want a uniform distribution over the [0, 1] interval
u_value_analytical_None

kind="u_value", reference="samples" We want a uniform distribution over the [0, 1] interval
u_value_samples_None

kind="p_value", reference="analytical", t_stat=None. We want a symmetric distribution centered at 0.5
p_value_analytical_None

kind="p_value", reference="samples", t_stat=None We want a symmetric distribution centered at 0.5
p_value_samples_None

kind="t_stat", reference=None, t_stat="median", other values are "mean", "std", quantiles can be passed as string of number between 0 and 1. Finally the user can pass an arbitrary function. The density is the distribution of sampled T_stat. The dot represent the mean of the observed T_stat. The legend shows the bayesian p_value (bpv)
t_stat_None_median

  • Follows official PR format
  • Includes a sample plot to visually illustrate the changes (only for plot-related functions)
  • New features are properly documented (with an example if appropriate)?
  • Includes new or updated tests to cover the new feature
  • Code style correct (follows pylint and black guidelines)
  • Changes are listed in changelog

@aloctavodia aloctavodia changed the title [WIP] draft new plot (t_stat_ppc, p_values) [WIP] draft plot_bpv (bayesian p-value) Jun 8, 2020
@aloctavodia aloctavodia changed the title [WIP] draft plot_bpv (bayesian p-value) Draft plot_bpv (bayesian p-value) Jun 22, 2020
@aloctavodia
Copy link
Contributor Author

The basic elements are in place. So this is ready for review.

Copy link
Member

@ColCarroll ColCarroll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! I didn't check the bokeh code at all, and most of the comments were on naming.

CHANGELOG.md Outdated
@@ -10,6 +10,7 @@
* `plot_trace` now supports multiple aesthetics to identify chain and variable
shape and support matplotlib aliases (#1253)
* `plot_hdi` can now take already computed HDI values (#1241)
* `plot_bpv`. A new plot intented to plot Bayesian p-values (#1222)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `plot_bpv`. A new plot intented to plot Bayesian p-values (#1222)
* `plot_bpv`. A new plot for Bayesian p-values (#1222)

data : az.InferenceData object
InferenceData object containing the observed and posterior/prior predictive data.
kind : str
Type of plot to display (u_value, p_value, t_stat). Defaults to u_value.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add what u_value, p_value, t_stat are (or references)

acepted, see examples section for details.
bpv : bool
If True (default) add the bayesian p_value to the legend when kind = t_stat.
mean : bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a strong feeling, but maybe change to plot_mean?

Whether or not to plot the mean T statistic. Defaults to True.
reference : str
How to compute the distributions used as reference for u_values or p_values. Allowed values
are "analytical" (default) and "samples". Use `None` to do not plot any reference.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
are "analytical" (default) and "samples". Use `None` to do not plot any reference.
are "analytical" (default) and "samples". Use `None` to do not plot any reference. Defaults to "samples".

How to compute the distributions used as reference for u_values or p_values. Allowed values
are "analytical" (default) and "samples". Use `None` to do not plot any reference.
n_ref : int, optional
Number of reference distributions to sample when `reference=samples`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Number of reference distributions to sample when `reference=samples`
Number of reference distributions to sample when `reference=samples`. Defaults to 100.

"""Check if value is a number between 0 and 1."""
try:
value = float(value)
return 0 < value < 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want this return after the except, since you are watching for a ValueError while casting to float, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this works ok.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will work as written

import arviz as az

data = az.load_arviz_data("regression1d")
az.plot_bpv(data, kind="t_stat", t_stat="0.5", backend="bokeh")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
az.plot_bpv(data, kind="t_stat", t_stat="0.5", backend="bokeh")
az.plot_bpv(data, kind="t_stat", t_stat=0.5, backend="bokeh")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing a string is actually valid

az.style.use("arviz-darkgrid")

data = az.load_arviz_data("regression1d")
az.plot_bpv(data, kind="t_stat", t_stat="0.5")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
az.plot_bpv(data, kind="t_stat", t_stat="0.5")
az.plot_bpv(data, kind="t_stat", t_stat=0.5)

@@ -0,0 +1,10 @@
"""
Bayesian p-value Posterior plot
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this have a different name from the other, to indicate it is using a T stat?

@@ -0,0 +1,15 @@
"""
Bayesian p-value Posterior plot
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question on naming here

@aloctavodia
Copy link
Contributor Author

maybe @ahartikainen wants to improve the bokeh code in a future PR ;-)

@codecov
Copy link

codecov bot commented Jun 25, 2020

Codecov Report

Merging #1222 into master will decrease coverage by 0.13%.
The diff coverage is 81.99%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1222      +/-   ##
==========================================
- Coverage   93.21%   93.08%   -0.14%     
==========================================
  Files          98      101       +3     
  Lines        9734     9995     +261     
==========================================
+ Hits         9074     9304     +230     
- Misses        660      691      +31     
Impacted Files Coverage Δ
arviz/plots/bpvplot.py 77.14% <77.14%> (ø)
arviz/plots/backends/matplotlib/bpvplot.py 82.14% <82.14%> (ø)
arviz/plots/backends/bokeh/bpvplot.py 84.26% <84.26%> (ø)
arviz/plots/plot_utils.py 94.20% <87.50%> (-0.42%) ⬇️
arviz/plots/__init__.py 100.00% <100.00%> (ø)
arviz/plots/backends/matplotlib/__init__.py 97.05% <100.00%> (+0.08%) ⬆️
arviz/plots/backends/matplotlib/posteriorplot.py 98.09% <0.00%> (ø)
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e5e4eab...73c89b6. Read the comment docs.

@aloctavodia aloctavodia merged commit 8099e93 into master Jun 29, 2020
@aloctavodia aloctavodia deleted the plot_bpv branch June 29, 2020 11:33
Copy link
Member

@OriolAbril OriolAbril left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great and very useful, looking forward to the section on EABM too :)

There are many documentation nits that do not need to be addressed straight away, they could well be recommendations for future functions to slowly move towards numpydoc best practices and recommendations. On noting the default for example, I have noticed pandas uses both:

engine : {‘c’, ‘python’}, optional
Parser engine to use. The C engine is faster while the python engine is currently more feature-complete.

where the user has to understand that c is the default. And

compression : {‘infer’, ‘gzip’, ‘bz2’, ‘zip’, ‘xz’, None}, default ‘infer’

where the user has to understand that having a default means its optional.

----------
data : az.InferenceData object
InferenceData object containing the observed and posterior/prior predictive data.
kind : str
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I would modify this following numpydoc advise:

When a parameter can only assume one of a fixed set of values, those values can be listed in braces, with the default appearing first:

order : {'C', 'F', 'A'}
   Description of `order`.

If True (default) add the bayesian p_value to the legend when kind = t_stat.
plot_mean : bool
Whether or not to plot the mean T statistic. Defaults to True.
reference : str
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Comment on lines +76 to +77
computing u_values. Should be in the interval (0, 1]. Defaults to
0.94.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaults to stats.hdi_prob rcParam

For "u_value" we compute pi := p(yi* ≤ yi | y). i.e. like a p_value but per observation yi.
This is also known as marginal p_value. The ideal distribution is uniform. This is similar
to the LOO-pit calculation/plot, the difference is than in LOO-pit plot we compute
pi = p(yi* r ≤ yi | y-i ), where y-i, is all other data except yi.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is one extra r before the lower or equal than sign

backend_kwargs : bool, optional
These are kwargs specific to the backend being used. For additional documentation
check the plotting method of the backend.
group : {"prior", "posterior"}, optional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

along the idea of comments above, this should be inverted to show the default first

ax : numpy array-like of matplotlib axes or bokeh figures, optional
A 2D array of locations into which to plot the densities. If not supplied, Arviz will create
its own array of plot areas (and return it).
backend : str, optional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

@@ -0,0 +1,294 @@
"""Bayesian p-value Posterior/Prior predictive plot."""
import numpy as np
from matplotlib.colors import to_hex
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use az.plots.plot_utils.vectorized_to_hex. It basically calls matplotlib.colors.to_hex but works with lists of colors too (not relevant here) and we could at some point (far future) make a version of the function not requiring matplotlib, i.e. try importing from matplotlib and if not possible use bokeh version (as far as I know bokeh has no function that does this yet)

Comment on lines +714 to +718
for idx in range(shape[1]):
density, xmin, xmax = _fast_kde(dist_rvs[:, idx])
x_s = np.linspace(xmin, xmax, len(density))
x_ss.append(x_s)
densities.append(density)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should check memory performance and see if its possible to preallocate. Not sure how it will perform, but specially the append looks suspicious.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my memory is foggy but I think that was my first attempt, but then I changed because the size of the grid used by _fast_kde is not fixed and this make cause trouble. That should be fixed with the new upcoming kde. Nevertheless I agree this is something we need to revisit.

@@ -966,3 +967,18 @@ def test_plot_rank(models, kwargs):
def test_plot_dist_comparison_warn(models):
with pytest.raises(NotImplementedError, match="The bokeh backend.+Use matplotlib bakend."):
plot_dist_comparison(models.model_1, backend="bokeh")


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think everything will work with multidimensional data (that is chain, draw, *shape with shape being multidimensional) thanks to the skip_dims and careful reshaping, however, it would be great to test on multidimensional models too. Should be the same as test below but using multidim_models fixture instead, probably a couple cases are enough for multidim.

pi = p(yi* r ≤ yi | y-i ), where y-i, is all other data except yi.
For "t_stat" we compute := p(T(y)* ≤ T(y) | y) where T is any T statistic. See t_stat
argument below for details of available options.
t_stat : str, float, or callable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding a "identity" option too to encourage users to calculate T statistics manually and store them in idata object and then use these new variables as variable names with t_stat="identity"?

I know it is already possible to do using t_stat=lambda x: x, hence the encourage above. This would be helpful if T statistic function was expensive to calculate or for convenience to use T statistic with both plot_bpv and loo_pit

@aloctavodia
Copy link
Contributor Author

Thanks for the comments @OriolAbril!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants