How to update x_axis_type of a figure when Select Widget is used and x_axis is updated

I am adding a Select widget that updates the x-axis of the plot. One of the options has a 'datetime' x_axis_type which is set for an initial value for column ‘day’. How do i remove the x_axis_type for another value among the options to have an int64 dtype for that value.

cases_summary = requests.get('https://api.rootnet.in/covid19-in/stats/history')
json_data = cases_summary.json()
cases_summary=pd.json_normalize(json_data['data'], record_path='regional', meta='day')

cases_summary['day']=pd.to_datetime(cases_summary['day'])

#Cumulative Days count since 1st reported case for each state
cases_summary['day_count']=(cases_summary['day'].groupby(cases_summary['loc']).cumcount())+1

#Cumulative Days count since 1st reported case
cases_summary['day'] = pd.to_datetime(cases_summary['day'])

cases_summary['cum_day_count']=(cases_summary['day']-cases_summary['day'].min())+timedelta(days=1)

cases_summary['cum_day_count']= cases_summary['cum_day_count'].astype('str').str.replace(' days 00:00:00.000000000', '')

cases_summary['cum_day_count'] = pd.to_numeric(cases_summary['cum_day_count'])

# Initializing the first default plot
source = ColumnDataSource(data=cases_summary)

legend_it=[]
p = figure(plot_width=1200, plot_height=600,  sizing_mode="scale_both", x_axis_type='datetime')
p.title.text='Statewise Cases over Time'
p.title.align='center'
p.title.text_font_size='17px'
p.yaxis.axis_label = 'Number of Cases'

for i, color in zip(range(len(cases_summary['loc'].unique())), itertools.cycle(Dark2_8)):
    view = CDSView(source=source,
                   filters=[GroupFilter(column_name='loc', group=cases_summary['loc'].sort_values(ascending=True).unique()[i])])

    renderer_yhat = p.step('day',
                           'totalConfirmed', line_width=2, alpha=1,
                           muted_alpha=0.1, source=source, view=view, color=color)

    renderer_yhat.visible = False

    legend_it.append((cases_summary['loc'].sort_values(ascending=True).unique()[i], [renderer_yhat]))

legend1 = Legend(items=legend_it[0:16], location=(10, 21), click_policy='hide',
                 title="Click on States to Switch ON/OFF", title_text_font_style="bold")
legend2 = Legend(items=legend_it[16:34], location=(10, 0), click_policy='hide',
                 title="Click on States to Switch ON/OFF", title_text_font_style="bold")

p.add_layout(legend1, 'right')
p.add_layout(legend2, 'right')

hover = HoverTool(line_policy='next')
hover.tooltips = [('Date', '@day'),
                  ('Cases', '@totalConfirmed{0000}'),
                  ('Day since 1st Case in the state', '@day_count')
]
hover.formatters = {'@day': 'datetime'}
p.add_tools(hover)

select=Select(title="Select:", value="day", options=['day', 'cum_day_count'], align='end', width=250, margin=(20,0,0,20))

x_map = {
    "day": cases_summary['day'],
    "cum_day_count": cases_summary['cum_day_count']
}

def update_plot(attr, old, new):

    x = x_map[select.value]
    p.xaxis.axis_label = x
    new_data = {
        'x': x,
        'y': cases_summary['totalConfirmed'],
        'cum_day':cases_summary['cum_day_count'],
        'loc': cases_summary['loc'],
        'day_count': cases_summary['day_count'],
        'day':cases_summary['day']
    }

    # Assign new_data to source.data
    source.data = new_data

    p.x_range.start = min(x)
    p.x_range.end = max(x)

select.on_change('value', update_plot)

layout=row(p,select)

curdoc().add_root(layout) 

The error I am getting is as follows:

 error handling message
 message: Message 'PATCH-DOC' content: {'events': [{'kind': 'ModelChanged', 'model': {'id': '1312'}, 'attr': 'value', 'new': 'cum_day_count'}], 'references': []} 
 error: ValueError('expected a value of type str, got 0        1\n1        1\n2        1\n3        1\n4        1\n        ..\n1808    65\n1809    65\n1810    65\n1811    65\n1812    65\nName: cum_day_count, Length: 1813, dtype: object of type Series')

You are trying to set the value of the axis label to be a Pandas series:

x = x_map[select.value]  # x is Pandas DataFrame column
p.xaxis.axis_label = x   # but this label value should be a string

Perhaps you mean to do:

p.xaxis.axis_label = select.value

instead?

Tho I also notice that you want to switch from datetime to non-datetime axes? To be honest in that case I would suggest making two separate plots and using the widget to toggle visibility between the two. And the reason is that “datetime axis” is actually just shorthand for an entire specific collection of different objects like tickers and tick formatters, and changing to “non-datetime” means swapping out all those with a new set. Simpler just to make separate plots.

Can you elaborate on that. How can I add two plots and toggle the visibility using the select options? I am not sure how to add both the plots to the layout(). :thinking:

Isn’t there any way to update the x_axis_type of the figure() too like other figure properties upon the selection of a particular option?

Hi @akashagl92

See the trivial example below. Five figures are added to a columnar layout, and a select button chooses which of them is visible. Note that only the visible one is shown and the invisible ones basically take up no real-estate on the page.

from bokeh.plotting import figure, curdoc
from bokeh.models import Select
from bokeh.layouts import column

n = 5

p = []
for i in range(n):
  _p = figure(width=500,height=500, title='Figure '+str(i), name='fig_'+str(i))
  if i:
      _p.visible = False

  p += [_p]


def sel_cb(attr, old, new):
    p[int(old)].visible = False
    p[int(new)].visible = True
    
sel = Select(title='Plot', value='0', options=[str(i) for i in range(n)])
sel.on_change('value', sel_cb)

curdoc().add_root(column([sel]+p))