pythonbokeh

Twin Axis with linear and logarithmic scale using bokeh plot Python


I have a linear Bokeh plot under Bokeh version 3.3.0. I would like to add a (scatter) plot on an additional x-axis with logarithmic scale. This is my code:

import pandas as pd
from bokeh.plotting import figure, show, ColumnDataSource
from bokeh.layouts import gridplot 
from bokeh.models import LogAxis, LinearAxis, Label, LabelSet, Title, Range1d 

# A simple dataframe with time, UTCs, altitude and two columns of imaginary data
df = pd.DataFrame([['2024-06-21 06:22:38', 605.968389, 0.994548, 3],
                   ['2024-06-21 06:22:39', 616.009398, 0.983443, 4],
                   ['2024-06-21 06:22:40', 624.630573, 0.973647, 1],
                   ['2024-06-21 06:22:41', 633.476367, 1.017651, 2],
                   ['2024-06-21 06:22:42', 642.322161, 5.017651, 2],
                   ['2024-06-21 06:22:43', 650.268389, 30.726555, 4],
                   ['2024-06-21 06:22:44', 659.559398, 89.2359, 5],
                   ['2024-06-21 06:22:45', 665.630573, 6.92018, 3],
                   ['2024-06-21 06:22:47', 673.476367, 0.69398, 2],
                   ['2024-06-21 06:22:48', 685.322161, 0.770802, 1],
                   ['2024-06-21 06:22:49', 697.155939, 0.856488, 0],
                   ['2024-06-21 06:22:51', 716.763057, 0.934408, 3],
                   ['2024-06-21 06:22:52', 722.012345, 7.865522, 2],
                   ], 
                  columns=['time', 'Altitude', 'log_data', 'linear_data'])

#Linear Plot with second x-axis in logarithmic scale
p = figure(title="Combined linear / log x-axes", 
           x_axis_label="Linear Data",
           y_axis_label="Altitude / km", 
           x_axis_type='linear',
           frame_height = 900,
           frame_width = 650,
           )
p.xaxis.axis_label_text_color = "red"

# linear data
p1 = p.line('linear_data', 
            'Altitude',
            line_color='red', 
            line_width=2, 
            source = df)

# log data
p.add_layout(LinearAxis(x_range_name = "x2", 
                        axis_label="Log data",
                        axis_label_text_color='blue',),
            'below' )

p2 = p.scatter('log_data', 
               'Altitude', 
               line_color='blue', 
               line_width=2,
               x_range_name = 'x2', 
               source = df)

# Range of additional x-axis:
p.extra_x_ranges = {"x2": Range1d(start = 0, 
                                  end = 115),
                   }

# Graphical output:
layout_p =  gridplot([p], ncols=1, width=700, height=900)
show(layout_p)

I changed LinearAxis into LogAxis

# log data
p.add_layout(LogAxis(x_range_name = "x2", 
                        axis_label="Log data",
                        axis_label_text_color='blue',),
            'below' )

but this caused the second axis to be displayed without any ticks and the scatter plot was still shown on a linear base. Any hint for me?


Solution

  • After another search, I finally found a solution. Once I had found it, it seemed logical that something was still missing:

    In the header section, the following must also be imported: from bokeh.models import LogScale

    And then the additional axis must be defined as a logarithmic scale: p.extra_x_scales['x2'] = LogScale()

    I changed the code a bit to make the result easier to see:

    And here is the code:

    import pandas as pd
    from bokeh.plotting import figure, show, ColumnDataSource
    from bokeh.layouts import gridplot 
    from bokeh.models import LogAxis, LogScale, LinearAxis, Label, LabelSet, Title, Range1d 
    
    # A simple dataframe with time, UTCs, altitude and two columns of imaginary data
    df = pd.DataFrame([['2024-06-21 06:22:38', 605.968389, 0.994548, 3],
                       ['2024-06-21 06:22:39', 616.009398, 0.983443, 4],
                       ['2024-06-21 06:22:40', 624.630573, 0.973647, 1],
                       ['2024-06-21 06:22:41', 633.476367, 1.017651, 2],
                       ['2024-06-21 06:22:42', 642.322161, 5.017651, 2],
                       ['2024-06-21 06:22:43', 650.268389, 30.726555, 4],
                       ['2024-06-21 06:22:44', 659.559398, 89.2359, 5],
                       ['2024-06-21 06:22:45', 665.630573, 6.92018, 3],
                       ['2024-06-21 06:22:47', 673.476367, 0.69398, 2],
                       ['2024-06-21 06:22:48', 685.322161, 0.770802, 1],
                       ['2024-06-21 06:22:49', 697.155939, 0.856488, 0],
                       ['2024-06-21 06:22:51', 716.763057, 0.934408, 3],
                       ['2024-06-21 06:22:52', 722.012345, 7.865522, 2],
                       ], 
                      columns=['time', 'Altitude', 'log_data', 'linear_data'])
    
    `#Linear Plot with second x-axis in logarithmic scale
    p = figure(title="Combined linear / log x-axes", 
               x_axis_label="Linear Data",
               y_axis_label="Altitude / km", 
               x_axis_type='linear',
               x_range = (0,10),
               frame_height = 600,
               frame_width = 450,
               )
    p.xaxis.axis_label_text_color = "red"
    
    # linear data
    p1 = p.line('linear_data', 
                'Altitude',
                #legend_label="Temperature", 
                        line_color='red', 
                        line_width=2, 
                        source = df )
    
    # log data
    p.add_layout(LogAxis(x_range_name = "x2", 
                           axis_label="Log data",
                           axis_label_text_color='blue',
                           ),
                         #axis_line_color=f2), 
                     'below' )
    
    p2 = p.line('log_data', 
                   'Altitude', 
                       #legend_label="Relative Humidity", 
                        line_color='blue', 
                        line_width=2,
                        x_range_name = 'x2', 
                        source = df)
    
    # Range of additional x-axis:
    p.extra_x_ranges = {"x2": Range1d(start = 0.1, 
                                      end = 115),
                       }
    p.extra_x_scales['x2'] = LogScale()
                       
    layout_p =  gridplot([p], ncols=1, width=400, height=500)
    show(layout_p)
    

    This is the resulting picture:

    enter image description here