Bokeh Plot with equal axes
Asked Answered
P

2

20

Bokeh Plot with equal axes

I created a Plot with the Python library Bokeh (see code).

from bokeh.plotting import *

figure()
hold()
rect([1,3], [1,1], [1,0.5], [1,0.5])
patch([0,0,4,4], [2,0,0,2], line_color="black", fill_color=None)
show()

How can I represent the squares (rectangle with the same width and height) with equal axes as in matplotlib with the command axis('equal')?

http://matplotlib.org/examples/pylab_examples/axis_equal_demo.html

I see the option to change the width and height of the plot or define the axis range to solve this problem but I think, there should be a smarter alternative.

NOTE: I'm using Python v.2.7.8 and Bokeh v.0.6.1.

Proconsul answered 31/10, 2014 at 12:31 Comment(3)
This is an open issue: github.com/bokeh/bokeh/issues/474Fur
Thanks for the link. I am looking forward to use a solution.Proconsul
Please note new answer by @DuCorey below regarding match_aspect in 0.12.7Fur
H
23

As of Bokeh 0.12.7, this feature has been implemented. Plots can now accept two new properties. match_aspect which, when set to true, will match the aspect of the data space to the pixel space of the plot. For example, squares drawn in data units will now be perfect squares in pixel units as well.

p = figure(match_aspect=True)
p.circle([-1, +1, +1, -1], [-1, -1, +1, +1])

Bokeh match aspect = True

aspect_scale allows you to further control the aspect ratio by specifying a multiplier on top of the aspect correction made by match_aspect.

p = figure(aspect_scale=2)
p.circle([-1, +1, +1, -1], [-1, -1, +1, +1])

Bokeh aspect scale = 2

p = figure(aspect_scale=0.5)
p.circle([-1, +1, +1, -1], [-1, -1, +1, +1])

Bokeh aspect scale = 0.5

Hulking answered 30/8, 2017 at 13:33 Comment(2)
It'd be instructive if you add the code used to generate these figures.Thermoelectricity
@Thermoelectricity Thanks for the comment. I've added the code that was used to generate the figures.Hulking
T
4

Sadly it seems that two years later this feature is still missing. As a workaround, I have written a function that sets the x_range and y_range properties of a figure appropriately to display your data with a given aspect ratio. This works fine as long as you don't allow any tools like box zoom that let the user modify the aspect ratio.

__all__ = ['set_aspect']

from bokeh.models import Range1d

def set_aspect(fig, x, y, aspect=1, margin=0.1):
    """Set the plot ranges to achieve a given aspect ratio.

    Args:
      fig (bokeh Figure): The figure object to modify.
      x (iterable): The x-coordinates of the displayed data.
      y (iterable): The y-coordinates of the displayed data.
      aspect (float, optional): The desired aspect ratio. Defaults to 1.
        Values larger than 1 mean the plot is squeezed horizontally.
      margin (float, optional): The margin to add for glyphs (as a fraction
        of the total plot range). Defaults to 0.1
    """
    xmin = min(xi for xi in x)
    xmax = max(xi for xi in x)
    ymin = min(yi for yi in y)
    ymax = max(yi for yi in y)
    width = (xmax - xmin)*(1+2*margin)
    if width <= 0:
        width = 1.0
    height = (ymax - ymin)*(1+2*margin)
    if height <= 0:
        height = 1.0
    xcenter = 0.5*(xmax + xmin)
    ycenter = 0.5*(ymax + ymin)
    r = aspect*(fig.plot_width/fig.plot_height)
    if width < r*height:
        width = r*height
    else:
        height = width/r
    fig.x_range = Range1d(xcenter-0.5*width, xcenter+0.5*width)
    fig.y_range = Range1d(ycenter-0.5*height, ycenter+0.5*height)

if __name__ == '__main__':
    from bokeh.plotting import figure, output_file, show

    x = [-1, +1, +1, -1]
    y = [-1, -1, +1, +1]
    output_file("bokeh_aspect.html")
    p = figure(plot_width=400, plot_height=300, tools='pan,wheel_zoom',
               title="Aspect Demo")
    set_aspect(p, x, y, aspect=2)
    p.circle(x, y, size=10)
    show(p)
Traveled answered 23/4, 2016 at 10:59 Comment(4)
FYI box zoom can now (as of 0.12 dev builds) be configured to respect the existing aspect of the plot.Fur
Regarding the time frame, for a library that generates static images, aspect control would be a trivial feature. Unfortunately, Bokeh embeds plots in a larger browser layout context, and trying to reconcile fixed aspect with "web responsiveness" (also a continuously requested feature), it becomes a very thorny problem. We are working on it, as best and as fast as our limited resources permit.Fur
This solution is great. However when implementing it to run on a 0.12.5 server, I had to instead modify the range start and end properties directly for it to work. fig.x_range.start = xcenter - 0.5 * width fig.x_range.end = xcenter + 0.5 * width fig.y_range.start = ycenter - 0.5 * height fig.y_range.end = ycenter + 0.5 * heightHulking
Please note new answer by @Hulking below about match_aspect in 0.12.7Fur

© 2022 - 2024 — McMap. All rights reserved.