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

Add layout properties for Bokeh backend. #1089

Merged
merged 28 commits into from
Feb 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* **Experimental Feature**: Added `reloo` function to ArviZ
* ArviZ version to InferenceData attributes. (#1086)
* Add `log_likelihood` argument to `from_pymc3`
* Integrated rcParams for `plot.bokeh.layout` and `plot.backend`. (#1089)


### Maintenance and fixes
Expand Down
5 changes: 5 additions & 0 deletions arviz/plots/autocorrplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
filter_plotters_list,
get_plotting_function,
)
from ..rcparams import rcParams
from ..utils import _var_names


Expand Down Expand Up @@ -127,6 +128,10 @@ def plot_autocorr(
show=show,
)

if backend is None:
backend = rcParams["plot.backend"]
backend = backend.lower()

if backend == "bokeh":

autocorr_plot_args.pop("xt_labelsize")
Expand Down
74 changes: 74 additions & 0 deletions arviz/plots/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# pylint: disable=no-member,invalid-name,redefined-outer-name
"""ArviZ plotting backends."""
import re
import numpy as np
from pandas import DataFrame

from ...rcparams import rcParams


def to_cds(
data,
Expand Down Expand Up @@ -102,6 +106,76 @@ def ColumnDataSource(*args, **kwargs):
return ColumnDataSource(*args, **kwargs)


def create_layout(ax, force_layout=False):
"""Transform bokeh array of figures to layout."""
ax = np.atleast_2d(ax)
subplot_order = rcParams["plot.bokeh.layout.order"]
if force_layout:
from bokeh.layouts import gridplot as layout

ax = ax.tolist()
layout_args = {
"sizing_mode": rcParams["plot.bokeh.layout.sizing_mode"],
"toolbar_location": rcParams["plot.bokeh.layout.toolbar_location"],
}
elif any(item in subplot_order for item in ("row", "column")):
# check number of rows
match = re.match(r"(\d*)(row|column)", subplot_order)
n = int(match.group(1)) if match.group(1) is not None else 1
subplot_order = match.group(2)
# set up 1D list of axes
ax = [item for item in ax.ravel().tolist() if item is not None]
layout_args = {"sizing_mode": rcParams["plot.bokeh.layout.sizing_mode"]}
if subplot_order == "row" and n == 1:
from bokeh.layouts import row as layout
elif subplot_order == "column" and n == 1:
from bokeh.layouts import column as layout
else:
from bokeh.layouts import layout

if n != 1:
ax = np.array(ax + [None for _ in range(int(np.ceil(len(ax) / n)) - len(ax))])
if subplot_order == "row":
ax = ax.reshape(n, -1)
else:
ax = ax.reshape(-1, n)
ax = ax.tolist()
else:
if subplot_order in ("square", "square_trimmed"):
ax = [item for item in ax.ravel().tolist() if item is not None]
n = int(np.ceil(len(ax) ** 0.5))
ax = ax + [None for _ in range(n ** 2 - len(ax))]
ax = np.array(ax).reshape(n, n)
ax = ax.tolist()
if (subplot_order == "square_trimmed") and any(
all(item is None for item in row) for row in ax
):
from bokeh.layouts import layout

ax = [row for row in ax if not all(item is None for item in row)]
layout_args = {"sizing_mode": rcParams["plot.bokeh.layout.sizing_mode"]}
else:
from bokeh.layouts import gridplot as layout

layout_args = {
"sizing_mode": rcParams["plot.bokeh.layout.sizing_mode"],
"toolbar_location": rcParams["plot.bokeh.layout.toolbar_location"],
}

return layout(ax, **layout_args)


def show_layout(ax, show=True, force_layout=False):
"""Create a layout and call bokeh show."""
if show is None:
show = rcParams["plot.bokeh.show"]
if show:
import bokeh.plotting as bkp

layout = create_layout(ax, force_layout=force_layout)
bkp.show(layout)


def _copy_docstring(lib, function):
"""Extract docstring from function."""
import importlib
Expand Down
21 changes: 13 additions & 8 deletions arviz/plots/backends/bokeh/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# pylint: disable=wrong-import-position
"""Bokeh Plotting Backend."""
from packaging import version
from ....rcparams import rcParams


def backend_kwarg_defaults(*args, **kwargs):
Expand All @@ -12,14 +13,19 @@ def backend_kwarg_defaults(*args, **kwargs):
# add needed default args from arviz.rcParams
for key, arg in args:
defaults.setdefault(key, rcParams[arg])
return defaults


def backend_show(show):
"""Set default behaviour for show if not explicitly defined."""
if show is None:
show = rcParams["plot.bokeh.show"]
return show
for key, arg in {
"toolbar_location": "plot.bokeh.layout.toolbar_location",
"tools": "plot.bokeh.tools",
"output_backend": "plot.bokeh.output_backend",
"height": "plot.bokeh.figure.height",
"width": "plot.bokeh.figure.width",
}.items():
# by default, ignore height and width if dpi is used
if key in ("height", "width") and "dpi" in defaults:
continue
defaults.setdefault(key, rcParams[arg])
return defaults


from .autocorrplot import plot_autocorr
Expand All @@ -43,7 +49,6 @@ def backend_show(show):
from .rankplot import plot_rank
from .traceplot import plot_trace
from .violinplot import plot_violin
from ....rcparams import rcParams


def check_bokeh_version():
Expand Down
13 changes: 6 additions & 7 deletions arviz/plots/backends/bokeh/autocorrplot.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"""Bokeh Autocorrplot."""
import bokeh.plotting as bkp
import numpy as np
from bokeh.layouts import gridplot
from bokeh.models import DataRange1d
from bokeh.models.annotations import Title
import numpy as np

from . import backend_kwarg_defaults, backend_show
from . import backend_kwarg_defaults
from .. import show_layout
from ...plot_utils import _create_axes_grid, make_label
from ....stats import autocorr

Expand Down Expand Up @@ -39,7 +38,7 @@ def plot_autocorr(
backend_kwargs = {}

backend_kwargs = {
**backend_kwarg_defaults(),
**backend_kwarg_defaults(("dpi", "plot.bokeh.figure.dpi"),),
**backend_kwargs,
}

Expand Down Expand Up @@ -89,6 +88,6 @@ def plot_autocorr(
ax.x_range = data_range_x
ax.y_range = data_range_y

if backend_show(show):
bkp.show(gridplot(axes.tolist(), toolbar_location="above"))
show_layout(axes, show)

return axes
16 changes: 7 additions & 9 deletions arviz/plots/backends/bokeh/compareplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import bokeh.plotting as bkp
from bokeh.models import Span

from . import backend_kwarg_defaults, backend_show
from . import backend_kwarg_defaults
from .. import show_layout


def plot_compare(
Expand All @@ -26,17 +27,15 @@ def plot_compare(
backend_kwargs = {}

backend_kwargs = {
**backend_kwarg_defaults(
("tools", "plot.bokeh.tools"),
("output_backend", "plot.bokeh.output_backend"),
("dpi", "plot.bokeh.figure.dpi"),
),
**backend_kwarg_defaults(("dpi", "plot.bokeh.figure.dpi"),),
**backend_kwargs,
}
dpi = backend_kwargs.pop("dpi")

if ax is None:
ax = bkp.figure(width=figsize[0] * dpi, height=figsize[1] * dpi, **backend_kwargs)
backend_kwargs.setdefault("width", int(figsize[0] * dpi))
backend_kwargs.setdefault("height", int(figsize[1] * dpi))
ax = bkp.figure(**backend_kwargs)

yticks_pos = list(yticks_pos)

Expand Down Expand Up @@ -130,7 +129,6 @@ def plot_compare(
ax.y_range._property_values["start"] = -1 + step # pylint: disable=protected-access
ax.y_range._property_values["end"] = 0 - step # pylint: disable=protected-access

if backend_show(show):
bkp.show(ax, toolbar_location="above")
show_layout(ax, show)

return ax
14 changes: 6 additions & 8 deletions arviz/plots/backends/bokeh/densityplot.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""Bokeh Densityplot."""
from collections import defaultdict
import numpy as np
import bokeh.plotting as bkp
from bokeh.layouts import gridplot

from bokeh.models.annotations import Title, Legend
import numpy as np

from . import backend_kwarg_defaults, backend_show
from . import backend_kwarg_defaults
from .. import show_layout
from ...plot_utils import (
make_label,
_create_axes_grid,
Expand Down Expand Up @@ -43,7 +43,7 @@ def plot_density(
backend_kwargs = {}

backend_kwargs = {
**backend_kwarg_defaults(),
**backend_kwarg_defaults(("dpi", "plot.bokeh.figure.dpi"),),
**backend_kwargs,
}

Expand Down Expand Up @@ -99,9 +99,7 @@ def plot_density(
ax1.add_layout(legend, "above")
ax1.legend.click_policy = "hide"

if backend_show(show):
grid = gridplot(ax.tolist(), toolbar_location="above")
bkp.show(grid)
show_layout(ax, show)

return ax

Expand Down
14 changes: 5 additions & 9 deletions arviz/plots/backends/bokeh/distplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import bokeh.plotting as bkp
import numpy as np

from . import backend_kwarg_defaults, backend_show
from . import backend_kwarg_defaults
from .. import show_layout
from ...kdeplot import plot_kde
from ...plot_utils import get_bins

Expand Down Expand Up @@ -37,12 +38,7 @@ def plot_dist(
backend_kwargs = {}

backend_kwargs = {
**backend_kwarg_defaults(
("tools", "plot.bokeh.tools"),
("output_backend", "plot.bokeh.output_backend"),
("width", "plot.bokeh.figure.width"),
("height", "plot.bokeh.figure.height"),
),
**backend_kwarg_defaults(),
**backend_kwargs,
}
if ax is None:
Expand Down Expand Up @@ -88,8 +84,8 @@ def plot_dist(
else:
raise TypeError('Invalid "kind":{}. Select from {{"auto","kde","hist"}}'.format(kind))

if backend_show(show):
bkp.show(ax, toolbar_location="above")
show_layout(ax, show)

return ax


Expand Down
25 changes: 10 additions & 15 deletions arviz/plots/backends/bokeh/elpdplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import warnings

import bokeh.plotting as bkp
import numpy as np
from bokeh.layouts import gridplot
from bokeh.models.annotations import Title
from bokeh.models import ColumnDataSource
import bokeh.models.markers as mk
import numpy as np

from . import backend_kwarg_defaults, backend_show
from . import backend_kwarg_defaults
from .. import show_layout
from ...plot_utils import _scale_fig_size
from ....rcparams import rcParams, _validate_bokeh_marker

Expand All @@ -34,11 +34,7 @@ def plot_elpd(
backend_kwargs = {}

backend_kwargs = {
**backend_kwarg_defaults(
("tools", "plot.bokeh.tools"),
("output_backend", "plot.bokeh.output_backend"),
("dpi", "plot.bokeh.figure.dpi"),
),
**backend_kwarg_defaults(("dpi", "plot.bokeh.figure.dpi"),),
**backend_kwargs,
}
dpi = backend_kwargs.pop("dpi")
Expand All @@ -49,16 +45,15 @@ def plot_elpd(
plot_kwargs.setdefault("s", markersize)

if ax is None:
ax = bkp.figure(
width=int(figsize[0] * dpi), height=int(figsize[1] * dpi), **backend_kwargs
)
backend_kwargs.setdefault("width", int(figsize[0] * dpi))
backend_kwargs.setdefault("height", int(figsize[1] * dpi))
ax = bkp.figure(**backend_kwargs)
ydata = pointwise_data[0] - pointwise_data[1]
_plot_atomic_elpd(
ax, xdata, ydata, *models, threshold, coord_labels, xlabels, True, True, plot_kwargs
)

if backend_show(show):
bkp.show(ax, toolbar_location="above")
show_layout(ax, show)

else:
max_plots = (
Expand Down Expand Up @@ -129,8 +124,8 @@ def plot_elpd(
plot_kwargs,
)

if backend_show(show):
bkp.show(gridplot(ax.tolist(), toolbar_location="above"))
show_layout(ax, show)

return ax


Expand Down
Loading