Source code for pastas.plotting.bokeh

from bokeh.layouts import column, row
from bokeh.models import (
    ColumnDataSource,
    DataTable,
    RangeTool,
    ScientificFormatter,
    StringFormatter,
    TableColumn,
)
from bokeh.plotting import figure, show

from pastas.extensions import register_model_accessor


[docs]@register_model_accessor("bokeh") class Bokeh: """Extension class for interactive bokeh figures for pastas Models. Usage ----- >>> ps.extensions.register_bokeh() INFO: Registered bokeh plotting methods in Model class, e.g. `ml.bokeh.plot()`. >>> fig = ml.bokeh.results() >>> fig.write_html("results_figure.html") Methods ------- plot plot oseries and model simulation, interactive version of `ml.plot()` results plot oseries, model simulation, contribution, step responses and parameters table,interactive version of `ml.plots.results()` Notes ----- The `bokeh` extension is registered in the `Model` class by calling the `register_bokeh()` function. To work in Juptyer notebooks, the `bokeh.io.output_notebook()` function should be called before plotting. The `bokeh` extension is not registered by default, and should be called explicitly. Check the bokeh documentation for more information on how to interact with the plots. """
[docs] def __init__(self, model): self._model = model
[docs] def plot(self, tmin=None, tmax=None, height=300, width=600, show_plot=True): """Plot the observations and model simulation. Parameters ---------- tmin : pd.Timestamp, optional start time for model simulation, by default None tmax : pd.Timestamp, optional end time for model simulation, by default None height : int, optional height of the plot, by default 500 width : int, optional width of the plot, by default 800 show_plot : bool, optional Show the plot (i.e., in Jupyter Notebooks), by default True Returns ------- p : bokeh.plotting.figure Bokeh figure with the observations and model simulation. Examples -------- >>> ps.extensions.register_bokeh() INFO: Registered bokeh plotting methods in Model class, e.g. `ml.bokeh.plot()`. >>> >>> fig = ml.bokeh.plot() """ data = self._model.get_output_series(tmin=tmin, tmax=tmax, split=False) source = ColumnDataSource(data) rsq = self._model.stats.rsq(tmin=tmin, tmax=tmax) TOOLS = "zoom_in,zoom_out,reset,pan,xwheel_zoom,box_zoom,undo" p = figure( title="Pastas Model", y_axis_label="Head", x_axis_location=None, tools=TOOLS, width=width, height=height, x_axis_type="datetime", toolbar_location="above", ) p.scatter( "index", "Head_Calibration", source=source, legend_label="Observations", color="black", alpha=0.7, ) p.line( "index", "Simulation", source=source, legend_label=r"Simulation (R2 = {:.2f})".format(rsq), line_width=2, ) p.legend.ncols = 2 if show_plot: show(p) return p
[docs] def results(self, tmin=None, tmax=None, height=500, width=800, show_plot=True): """Overview of the results of the pastas model. Parameters ---------- tmin : pd.Timestamp, optional start time for model simulation, by default None tmax : pd.Timestamp, optional end time for model simulation, by default None height : int, optional height of the plot, by default 500 width : int, optional width of the plot, by default 800 show_plot : bool, optional Show the plot (i.e., in Jupyter Notebooks), by default True Returns ------- grid : bokeh.layouts.column Bokeh layout with the results of the pastas model. Examples -------- >>> ps.extensions.register_bokeh() INFO: Registered bokeh plotting methods in Model class, e.g. `ml.bokeh.plot()`. >>> fig = ml.bokeh.results() """ data = self._model.get_output_series(tmin=tmin, tmax=tmax, split=False) ranges = data.max() - data.min() ranges = ranges.drop([ranges.iloc[:2].idxmin(), "Noise"]) heights = (ranges / ranges.sum() * (height - 50)).values.astype(int) source = ColumnDataSource(data) rsq = self._model.stats.rsq(tmin=tmin, tmax=tmax) TOOLS = "zoom_in,zoom_out,reset,pan,xwheel_zoom,box_zoom,undo" p = figure( title="Pastas Model", y_axis_label="Head", x_axis_location=None, tools=TOOLS, width=int(0.75 * width), height=heights[0], x_axis_type="datetime", toolbar_location="above", ) p.scatter( "index", "Head_Calibration", source=source, legend_label="Observations", color="black", alpha=0.7, ) p.line( "index", "Simulation", source=source, legend_label=r"Simulation (R2 = {:.2f})".format(rsq), ) p.legend.ncols = 2 # Residuals res_plot = figure( y_axis_label="Residuals", toolbar_location=None, tools=TOOLS, x_range=p.x_range, width=int(0.75 * width), height=heights[2], x_axis_type="datetime", x_axis_location=None, ) res_plot.scatter( "index", "Residuals", source=source, color="black", alpha=0.7, legend_label="Residuals", ) res_plot.line( "index", "Residuals", source=source, color="black", alpha=0.7, legend_label="Residuals", ) if self._model.settings["noise"]: res_plot.line("index", "Noise", source=source, legend_label="Noise") res_plot.scatter("index", "Noise", source=source, legend_label="Noise") res_plot.legend.ncols = 2 # Parameter Table df = ColumnDataSource(self._model.parameters.loc[:, ["optimal"]]) columns = [ TableColumn( field="index", title="Name", formatter=StringFormatter(font_style="bold"), ), TableColumn( field="optimal", title="Optimal", formatter=ScientificFormatter(precision=2), ), ] table = DataTable( source=df, columns=columns, editable=False, index_position=None, width=int(0.25 * width), height=heights[0] + heights[2] - 10, ) left_column = [p, res_plot] right_column = [table] # Contributions rfunc_plot = None for i, smname in enumerate(self._model.stressmodels.keys(), start=2): if i == int(len(self._model.stressmodels) + 1): x_axis_location = "below" else: x_axis_location = None contrib_plot = figure( y_axis_label="Rise", toolbar_location=None, tools=TOOLS, x_axis_location=x_axis_location, width=int(0.75 * width), height=heights[i], x_axis_type="datetime", x_range=p.x_range, ) # xrange = False if rfunc_plot is None else rfunc_plot.x_range rfunc_plot = figure( x_axis_label=None, toolbar_location=None, x_axis_location=x_axis_location, width=int(0.25 * width), height=heights[i], ) contrib_plot.line("index", smname, source=source) response = self._model.get_step_response(smname) rfunc_plot.line(response.index, response.values) left_column.append(contrib_plot) right_column.append(rfunc_plot) select = figure( height=50, width=int(0.75 * width), y_range=p.y_range, x_axis_type="datetime", y_axis_type=None, tools="", toolbar_location=None, background_fill_color="#ffffff", ) range_tool = RangeTool(x_range=p.x_range) select.line("index", "Simulation", source=source) select.add_tools(range_tool) left_column.append(select) layout = row(column(left_column), column(right_column)) grid = column(layout, width=width, height=height) if show_plot: show(grid) return grid