0

I am working on COVID19 analysis and am using a JSON data source. I have converted the json to dataframe. I am working on plotting a daily case, daily death and daily recovered bar chart over a datetime x-axis for each state and the state can be selected using a Select widget. I don't know Javascript so, I am trying to avoid using Javascript callbacks but have been using a function to update the select.value. I am not sure why is the plot not getting updated even when i am running the code on Bokeh server and there are no exceptions raised by the interpreter.

Can someone provide me with any direction or help with what might be causing the issue as I am new to Python and any help is appreciated? Or if there's any other alternative. This code is a derivation from a similar plot on [bokeh discourse][1]

#Creating DataFrame
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'])
cases_summary['daily deaths']=cases_summary['deaths'].groupby(cases_summary['loc']).diff(1)
cases_summary['daily confirmed']=cases_summary['totalConfirmed'].groupby(cases_summary['loc']).diff(1)
cases_summary['daily discharged']=cases_summary['discharged'].groupby(cases_summary['loc']).diff(1)

#Initializing the first default plot
cases=cases_summary[cases_summary['loc']=='Delhi']


source=ColumnDataSource(data=cases)

a = figure(plot_width=1200, plot_height=700, sizing_mode="scale_both", x_axis_type='datetime')


def make_plot(cases_val):
    a.vbar('day', top='daily confirmed', width=timedelta(days=0.5),
                   legend_label='Daily Confirmed', color='#5e4fa2', source=cases_val)
    a.vbar('day', bottom='daily discharged', width=timedelta(days=0.5),
                        legend_label='Daily Recovered', color='#66c2a5', source=cases_val)
    a.vbar('day', bottom='daily deaths', width=timedelta(days=0.5),
                      legend_label='Daily Deaths', color='#3288bd', source=cases_val)
   return a

def update_plot(attr,old,new):
    location=select.value
    data_loc = cases_summary[cases_summary['loc'] == location]
    source = ColumnDataSource(data=dict()).from_df(data_loc)
    layout.children[0]=make_plot(source)

select = Select(title="Select State:", value="Delhi", options=cases_summary['loc'].unique().tolist())

plot = make_plot(cases)

controls = column(select)
layout = row(a, controls)
select.on_change('value', update_plot)

curdoc().add_root(layout)


  [1]: https://discourse.bokeh.org/t/how-to-update-the-bar-chart-that-has-dataframe-as-source-with-bokeh-select-widget/2031/8

1 Answers1

0

This can be done more simply using a view and a filter. Here is an alternative approach:

import requests
import pandas as pd
from bokeh.plotting import figure
from bokeh.layouts import column, row
from bokeh.io import curdoc
from bokeh.models import *
from datetime import timedelta

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"])
cases_summary["daily deaths"] = (
    cases_summary["deaths"].groupby(cases_summary["loc"]).diff(1)
)
cases_summary["daily confirmed"] = (
    cases_summary["totalConfirmed"].groupby(cases_summary["loc"]).diff(1)
)
cases_summary["daily discharged"] = (
    cases_summary["discharged"].groupby(cases_summary["loc"]).diff(1)
)

source = ColumnDataSource(cases_summary)
filter = GroupFilter(column_name='loc',group='Delhi')
view = CDSView(source=source, filters = [filter])

a = figure(
    plot_width=1200, plot_height=700, sizing_mode="scale_both", x_axis_type="datetime"
)

a.vbar(
    "day",
    top="daily confirmed",
    width=timedelta(days=0.5),
    legend_label="Daily Confirmed",
    color="#5e4fa2",
    source=source,
    view = view
)

a.vbar(
    "day",
    bottom="daily discharged",
    width=timedelta(days=0.5),
    legend_label="Daily Recovered",
    color="#66c2a5",
    source=source,
    view = view
)

a.vbar(
    "day",
    bottom="daily deaths",
    width=timedelta(days=0.5),
    legend_label="Daily Deaths",
    color="#3288bd",
    source=source,
    view = view
)


def update_plot(attr, old, new):
    view.filters = [GroupFilter(column_name='loc',group=select.value)]

select = Select(
    title="Select State:", value="Delhi", options=cases_summary["loc"].unique().tolist()
)

controls = column(select)
layout = row(a, controls)
select.on_change("value", update_plot)

curdoc().add_root(layout)
  • Thank you for a very simplified approach to solving the problem. This gave me a chance to learn more on GroupFilter and CDSView. Although, shouldn't we pass any argument to make_plot? When I run the source code on bokeh serve , the plot is blank. Upon selecting the options as well, the plot doesn't get updated. Little more help along with an explanation might help. Thank you! – Akash Agrawal May 04 '20 at 20:34
  • The code works for me with `bokeh serve app.py` running bokeh version 2.0.2 and python 3.7.7. In fact, the make_plot function is not necessary. In my original post I omitted the last few lines of the code since they were the same as yours, but now I've edited my code to include the full app. Perhaps that will help? – Jeremy Teitelbaum May 05 '20 at 13:35
  • Thank you @Jeremy . This is helpful. – Akash Agrawal May 05 '20 at 15:31