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

Vertical or horizontal frame alignment of plot elements in a layout. #5673

Closed
hali-geoviz opened this issue Apr 4, 2023 · 6 comments
Closed

Comments

@hali-geoviz
Copy link

hali-geoviz commented Apr 4, 2023

Hi, I wanted to share a need that I sometime come across and one which I think could be common for more users. If tackled it could potentially simplify working with plotting in holoviews/hvplot even more.

Problem description

Mostly plots align quite well when composing holoviews elements into a layout, but sometimes I experience that axes are not aligned and it can be needs for cumbersome workarounds to make the axes/plots align. When composing a layout of two or more elements and these share one axis/dimension I kind of have an intuitive expectation that the axes would align.

The typical situation where the problem arises is when there is two or more plot elements and the width of the formatted label ticks are different on the dimensions that are not shared. This could be due to different number scales (e.g. ratio vs. thousands), custom tick formatting or no ticks in selected dimensions. This basically means different space taken by the ticks, ticks labels and axis label for the dimension not shared and thus the frames and axes are not auto-aligned anymore.

Code illustrating the issue:

# Imports
import pandas as pd
import numpy as np
import holoviews as hv
from holoviews import opts
import panel as pn
pn.extension()

# load bokeh extension
hv.extension('bokeh')

# Create dummy data
xs = np.linspace(10,20, 11)
y1 = np.random.random(len(xs)) # .cumsum()
y2 = y1.cumsum()
y3 = -xs**1/3

data = {
    'x': xs, 
    'y1': y1,
    'y2': y2,
    'y3': y3
}

df = pd.DataFrame(data)

df['y1_category'] = pd.qcut(
    y1,
    q=[0, .05, .25, .75, .95, 1.0],
    labels=['Very low', 'Low', 'Intermediate', 'High', 'Very high']
)

df content:
image

size_opts = dict(frame_width=800, frame_height=90)
p1 = hv.Curve(df, kdims='x', vdims='y1_category')
p2 = hv.Curve(df, kdims='x', vdims='y1')
p3 = hv.Scatter(df, 'x', 'y2').opts(size=9)
p4 = hv.Curve(df, 'x', 'y3').opts(
    yticks=[(-7,'lower'), (-5, 'some center'), (-3, 'top')],
    fontsize={'ylabel': '12pt', 'yticks': '12pt'})
lay = (p1 + p2 + p3 + p4).cols(1).opts(opts.Curve(**size_opts), opts.Scatter(**size_opts))
lay

lay dislays as:
image

Describe the solution you'd like

First I must say I really love the ‘frame_height’ and ‘frame_width’ arguments to holoviews bokeh elements as opposed to only having ‘height’ and ‘width’. These arguments provide ways to have the output much more as one expect it. I am wondering if it could be a similar concept for frame alignment (top, bottom, left, right) in elements input to holoviews layouts too. I am uncertain if this means development in the backend plotting library such as 'bokeh and do not really know the consequences in terms of effort.
I would want to say ‘frame_align’ = ‘left’, ‘x_dim_align’ = ‘True’ or similar. Not sure if it should be an option to layout or the elements though.

Describe alternatives you've considered

I raised a discussion in holoviz discourse on this and @MarcSkovMadsen showed a mitigating workaround using panel which is very useful, but also seems complicated if there was options to control alignment inside holoviews itself.

A workaround solution with panel aligment as proposed by @MarcSkovMadsen in holoviz discourse:

pn.Column(
    pn.panel(p1, align='end'),
    pn.panel(p2, align='end'),
    pn.panel(p3, align='end'),
    pn.panel(p4, align='end'),
)

displays as:
image
which is good, but the toolbar is repeated and the workflow a bit complicated as it needs panel to be involved.

Additional context

I would think frame alignment may have been discussed for holoviews before especially for situations when one dimension and it's range is shared across several elements so this may be input to some already ongoing discussion.

@hoxbro
Copy link
Member

hoxbro commented Apr 11, 2023

This will be done in the next Holoviews release (1.16.0) and using Bokeh 3.

Here is your example (note I have changed frame_width to width)
image

@hali-geoviz
Copy link
Author

hali-geoviz commented Apr 14, 2023

Thanks a lot, the display looks really great! 💯
To better understand: I was not sure if it is going to be required to use width instead of frame_width in 1.16 to achieve the alignment of axes(?)
I really, really like the ability to control the size of the frame explicitly. Will show one set of examples illustrating a workaround in 1.15.4 that seems to require 'frame_width' over 'width'. The idea is to put the yaxis='right' which facilitate that the frame align to the left (implicit left-alignment?).
First example using width:
image
Frame left-aligment is now ok, but the frame widths vary.

Second working example using frame_width:
image
Both frame left-alignment and frame widths are good. (Only constrain is the yaxis is bound to be on the right side.)

I guess what I am thinking of is: what if when yaxis='left' (default) then the 'frame alignment' was reversed(?) Maybe this is what you are planning for 1.16, but as mentioned I am uncertain if/why width is needed/used over frame_width.
Really great that you are looking into this and seem to have solved it :-)

@hali-geoviz
Copy link
Author

hali-geoviz commented Apr 14, 2023

Just came to thing of one possibly related topic: I am not sure what the future holds in terms of implementing something for the concept of multiple axes, e.g. twin axes #5355 and if this needs to be considered for the feature of axis alignment.

@hoxbro
Copy link
Member

hoxbro commented Apr 14, 2023

Multiple-axis support is already ongoing in #5621.

The changes to support this are done upstream in Bokeh, so I am unsure about all the details. The reason why I used width was because frame_width gave a weird output:

image

@hali-geoviz
Copy link
Author

Thanks for explanations! Really great to also see support for multiple axes is coming :-)

@hoxbro hoxbro closed this as completed May 8, 2023
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 23, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants