Eli Turasky
Eli Turasky

Reputation: 1061

Adding simple dropdown to Bokeh plot using pandas data

I am very new to Bokeh and cannot seem to find any really good examples of a simple dropdown plot with data from a pandas dataframe. I am working with a dictionary that has 4 different keys, where each key contains a dataframe.

df_vals.keys()
dict_keys(['corn', 'soybeans', 'winterwheat', 'springwheat'])

df_vals['corn'].head()
time    2m_temp_prod    2m_temp_area    total_precip_prod   total_precip_area
0   2020-09-16 00:00:00 299.346777  299.799234  0.000000    0.000000
1   2020-09-16 06:00:00 294.039512  294.443352  0.191070    0.286952
2   2020-09-16 12:00:00 292.959274  293.182931  0.155765    0.216606
3   2020-09-16 18:00:00 301.318046  301.767516  0.421768    0.485691
4   2020-09-17 00:00:00 300.623567  300.979650  0.363572    0.501164

Next, I can add this data to a source.

source=ColumnDataSource(data=df_vals['corn'])

Plotting from here is simple.

p1=figure(x_axis_type='datetime')
p1.line(x='time', y='2m_temp_prod',source=source)
show(p1)

This does exactly what I want. It plots a line plot with datetime as the x-axis. However, now I want to add a dropdown widget to switch between 2 columns in df_vals['corn'] (2m_temp_prod and total_precip_prod). I have tried this code below but it is not working and I am not sure if that's even the right way to go about it.

def update_plot(attr, old, new):
    if new == 'total_precip_prod': 
        source.data = {
            'x' : df_vals['corn']['time'],
            'y' : df_vals['corn']['total_precip_prod'].cumsum()
        }
select = Select(title="hi", options=['2m_temp_area', 'total_precip_prod'], value='2m_temp_area')
select.on_change('value', update_plot)
 
# Create layout and add to current document
layout = row(select, p1)
curdoc().add_root(layout)

Ideally, the button would have two options: temps and precip. How would I go about do this?

Upvotes: 0

Views: 2961

Answers (1)

gherka
gherka

Reputation: 1446

You can use Bokeh CustomJS callbacks to toggle visibility of the plots according to what's selected in a dropdown. A basic example would look like this:

from bokeh.models import ColumnDataSource, CustomJS, Range1d, Select
from bokeh.plotting import figure, output_notebook, show
from bokeh.layouts import column

import pandas as pd
import numpy as np

# you can also output to an HTML file
output_notebook() 

# toy dataframe with two y columns you can switch between
df = pd.DataFrame(data={
    "x"     : range(0,100),
    "y1"     : np.random.randint(10, 20, 100),
    "y2"     :  np.random.randint(40, 50, 100)
})

# fix the y_range to be the same for two lines
p = figure(y_range=Range1d(0, 60), plot_width=600, plot_height=400)

# shared datasource
source = ColumnDataSource(df)

plot_1 = p.line(x="x", y="y1", color="teal", source=source, line_width=1)
plot_2 = p.line(x="x", y="y2", color="firebrick", source=source, line_width=1)

# initialise the plot with only y1 visible - to match the dropdown default
plot_2.visible = False

# dropdown widget + Javascript code for interactivity
select = Select(title="Plot to show:", value="Line 1", options=["Line 1", "Line 2"])
select.js_on_change("value", CustomJS(args=dict(line_1=plot_1, line_2=plot_2), code="""

line_1.visible = true
line_2.visible = true

if (this.value === "Line 1") {
    line_2.visible = false 
} else {
    line_1.visible = false
}
    
"""))

layout = column(select, p)
show(layout)

enter image description here

Upvotes: 2

Related Questions