Search code examples
python-3.xplotbokehaxes

How to link axes of all plots in a Bokeh layout?


I am designing a Bokeh layout using the Bokeh server. I am defining two main columns (see attached image), and I am attempting to link the x-axis of all plots on the right column. The problems are that:

  • I am trying to make this app as dynamic as possible, which mean that depending on the case-study, not all the plots will be available, and each individual plot is set from a separate function

  • Each plot object is stored in a list, and I don't know how to access their properties

  • The reference plot is not known a priori so I don't see how I can implement the example in the Bokeh doc - in other words, I need to first plot all the subplots to then get the relevant x_range

So I was wondering if it is possible to set the linking behaviour a posteriori once all plots in the column are defined (i.e. the output of plotDataset below). My intuition is to loop through the objects, get the children and set the x_range to the first plot, but I don't know how to do that.

Below is a simplified version of what I am trying to achieve. Ideally, I would get the x_range of the first plot of fCol and apply it to all other plots just before return column(fCol)

Any idea is greatly appreciated! And also, I am fairly beginner with Python so please shout if you see anything else horrible!

Thank you

def plotTS(data, col):
    tTmp = []

    # A loop that defines each tab of the plot
    for i in range(len(col)):
        fTmp = figure()

        fTmp.circle(data[:]['time'], data[:][col[i]], color=color)

        # Append tab
        tTmp.append(Panel(child=fTmp))

    # Return the tabs
    return Tabs(tabs=tTmp)    

def plotDataset(data):
    col = ['NDVI', 'EVI'] # Name of the tabs

    fCol = []
    fCol.append(plotTS(data, col))

    # NOTE: I use an append approach because in reality plotTS is called more than once

    return column(fCol)

# General layout - I did not include the code for the left column
layout = row(leftColumn, plotDataset(data))

Link to image


Solution

  • See code below (Bokeh v1.1.0).

    from bokeh.models import Panel, Tabs, Column, Row
    from bokeh.plotting import figure
    from tornado.ioloop import IOLoop
    from bokeh.server.server import Server
    from bokeh.application import Application
    from bokeh.application.handlers.function import FunctionHandler
    
    def modify_doc(doc):
    
        leftColumn = Column(figure())
    
        def plotTS(data, col):
            tTmp = []
    
            for i in col:
                fTmp = figure()
                fTmp.circle(data['x'], data['y'], color='black')
                tTmp.append(Panel(child=fTmp, title = i))
    
            return Tabs(tabs=tTmp)    
    
        def plotDataset(data):
            col = ['NDVI', 'EVI'] 
            fCol = plotTS(data, col)
    
            shared_range = None
            for panel in fCol.tabs:
                fig = panel.child
                if shared_range is None:
                    shared_range = fig.x_range
                else:
                    fig.x_range = shared_range
    
            return Column(fCol)
    
        layout = Row(leftColumn, plotDataset(data = dict(x = [1, 2, 3], y = [1, 2, 3])))
        doc.add_root(layout)
    
    
    io_loop = IOLoop.current()
    server = Server(applications = {'/app': Application(FunctionHandler(modify_doc))}, io_loop = io_loop, port = 5002)
    server.start()
    server.show('/app')
    io_loop.start()