How do I create linked and responsive Layout of scatter and bar plot?

Hi

I have two time series (one observation per day per series for several years) that I would like to plot.

I would like to plot both the two series and the delta plot. The users would like the delta plot to be a bar plot.

Furthermore I need the combined plot to be responsive.

How do I achieve that?

(I have gone through the guides and galleries at the Holoviews site and done some Googling without luck).

Code

My current experiments are below.

  • I cannot get responsiveness from holoviews.Layout
    • I can get responsiveness if I put the two plots into a panel.Column.
  • I cannot get the two date (x-axis) linked.
    • With that I mean that they should be linked when I drag or zoom one of the plots.
    • The y-axis should not be linked as they will not have the same unit or scale.
import pandas as pd
import hvplot.pandas
from holoviews import opts
data = {
    "x": [pd.Timestamp("2020-01-01"),pd.Timestamp("2020-01-02"),pd.Timestamp("2020-01-03")],
    "y": [2,4,5]
}
dataframe = pd.DataFrame(data)
price_plot = dataframe.hvplot.scatter(x="x", y="y").opts(height=100, responsive=True)
bar_plot = dataframe.hvplot(x="x", y="y", kind="bar").opts(height=100, responsive=True)
layout = (price_plot + bar_plot).cols(1)
layout.opts(opts.Layout(shared_axes=True))

pn.Column(
    price_plot,
    bar_plot,
    layout,
    sizing_mode="stretch_width",
    background="yellow",
)

One Step towards a Solution

  • If I rename my dataframe columns for the x-axis the same and for the y-axis differently I get the linking I want.
  • If I don’t use a holoviews.Layout but a panel.Column I get responsiveness.

But still I’m not able to get the second plot to be a bar chart. If I set the kind to bar then the x-axis are no longer linked.

How do I solve that?

import pandas as pd
import hvplot.pandas
from holoviews import opts
price_data = {
    "date": [pd.Timestamp("2020-01-01"),pd.Timestamp("2020-01-02"),pd.Timestamp("2020-01-03")],
    "price": [2,4,5]
}
delta_data = {
    "date": [pd.Timestamp("2020-01-01"),pd.Timestamp("2020-01-02"),pd.Timestamp("2020-01-03")],
    "delta": [1,2,3]
}
price_df = pd.DataFrame(price_data)
delta_df = pd.DataFrame(delta_data)
price_plot = price_df.hvplot(x="date", y="price", label="Price").opts(height=300, responsive=True)
delta_plot = delta_df.hvplot(x="date", y="delta", kind="step", label="Delta").opts(height=300, responsive=True)
layout = (price_plot + delta_plot).cols(1)
layout.opts(opts.Layout(shared_axes=False))

pn.Column(
    price_plot,
    delta_plot,
    sizing_mode="stretch_width",
    background="yellow",
)

Just wondered if there was any news on this front as I’m still looking for a solution.

@Marc, I am facing the same issue.

The problem is that hvplot & holoviews resort to Bokeh’s FactorRange as x_range for Bars instead of Bokeh’s Range1d. So you can’t link them.

The only solution is, unfortunately, to make everything with bokeh. Or someone from the core developers could point us where we should get started to modify this behavior in the main library.

Meanwhile, using your data variable:

import bokeh as bk

s1 = bk.plotting.figure(plot_width=500, plot_height=250, title=None)
s1.scatter(source=data)
s1.xaxis.formatter=XFORMATTER
s2 = bk.plotting.figure(plot_width=500, plot_height=250, title=None,x_range=s1.x_range)
s2.vbar(x='x',top='y',bottom=0,source=data)
s2.xaxis.formatter=XFORMATTER

p = bk.layouts.gridplot([[s1], [s2]])



pn.Column(p)
1 Like

Thanks @hyamanieu

Just for reference I created a working example from your code

import panel as pn
import bokeh as bk
import pandas as pd
from datetime import timedelta
from bokeh.models.formatters import DatetimeTickFormatter

data = {
    "x": [pd.Timestamp("2020-01-01"),pd.Timestamp("2020-01-02"),pd.Timestamp("2020-01-03")],
    "y": [2,4,5]
}
XFORMATTER = DatetimeTickFormatter(days=["%m %d, %Y"], hours=["%Y-%m-%d %H:%M"])
s1 = bk.plotting.figure(plot_width=500, plot_height=250, title=None)
s1.scatter(source=data)
s1.xaxis.formatter=XFORMATTER
s2 = bk.plotting.figure(plot_width=500, plot_height=250, title=None,x_range=s1.x_range)
s2.vbar(x='x',top='y',bottom=0,source=data,)
s2.xaxis.formatter=XFORMATTER

p = bk.layouts.gridplot([[s1], [s2]])

pn.Column(p).servable()

Yes, I have been lazy :slight_smile: .

I have tried to change the x_range and xaxis in general using hooks, it didn’t help although the axis type did change.
Back to Bokeh like in 2016 :stuck_out_tongue: .

@hyamanieu. I think we should be using Histogram instead of Bars. It works as we would expect.

Unfortunately it won’t work in my case. I need to have stacked bars, 3 dimensions. I must use Bokeh.