covid_bokeh_10.py

text/x-python
"""
Interactive State Explorer (Bokeh)

Bokeh server app for exploring COVID-19 data by state and time period.

Run with: bokeh serve covid_bokeh_10.py
"""

from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import (ColumnDataSource, Select, RangeSlider, 
                         HoverTool, Span)
from bokeh.plotting import figure
from datetime import timedelta

from covid_data_prep import prepare_full_dataset, TRANSITION_DATE


# Load all data once
print("Loading data for State Explorer...")
full_data = prepare_full_dataset()

# Extract metadata
min_date = full_data['date'].min()
max_date = full_data['date'].max()
total_days = (max_date - min_date).days

# Get initial data (National, full period)
initial_data = full_data[full_data['state'] == 'National'].copy()

# Create data source for display
display_source = ColumnDataSource(data={
    'date': initial_data['date'],
    'deaths_7day': initial_data['deaths_7day'],
    'cases_7day': initial_data['cases_7day'],
    'daily_deaths': initial_data['daily_deaths'],
    'daily_cases': initial_data['daily_cases']
})

# Create figure
p = figure(
    title='COVID-19 Deaths - National',
    x_axis_label='Date',
    y_axis_label='Daily Deaths (7-day average)',
    x_axis_type='datetime',
    width=900,
    height=450,
    tools='pan,wheel_zoom,box_zoom,reset,save'
)

# Add line
line = p.line('date', 'deaths_7day', source=display_source, 
              line_width=3, color='darkred', alpha=0.8)

# Add presidential transition line
transition_line = Span(
    location=TRANSITION_DATE, 
    dimension='height',
    line_color='blue', 
    line_dash='dashed', 
    line_width=2, 
    line_alpha=0.6
)
p.add_layout(transition_line)

# Add hover tool
hover = HoverTool(
    tooltips=[
        ('Date', '@date{%F}'),
        ('Deaths (7-day)', '@deaths_7day{0,0.0}'),
        ('Cases (7-day)', '@cases_7day{0,0.0}'),
    ],
    formatters={'@date': 'datetime'},
    mode='vline'
)
p.add_tools(hover)

# Style
p.title.text_font_size = '14pt'
p.title.align = 'center'
p.xgrid.grid_line_alpha = 0.3
p.ygrid.grid_line_alpha = 0.3

# Create widgets
# State selector
states_list = ['National'] + sorted(
    full_data[full_data['state'] != 'National']['state'].unique().tolist()
)
state_select = Select(
    title='Select State:',
    value='National',
    options=states_list,
    width=300
)

# Time period slider
date_slider = RangeSlider(
    title='Time Period (days since January 2020):',
    start=0,
    end=total_days,
    value=(0, total_days),
    step=1,
    width=600
)

# Python callback function
def update_plot():
    """Filter data and update the plot based on selected state and date range."""
    selected_state = state_select.value
    start_day, end_day = date_slider.value
    
    # Calculate actual dates
    start_date = min_date + timedelta(days=start_day)
    end_date = min_date + timedelta(days=end_day)
    
    # Filter the full dataset
    filtered = full_data[
        (full_data['state'] == selected_state) &
        (full_data['date'] >= start_date) &
        (full_data['date'] <= end_date)
    ].copy()
    
    # Update data source
    display_source.data = {
        'date': filtered['date'],
        'deaths_7day': filtered['deaths_7day'],
        'cases_7day': filtered['cases_7day'],
        'daily_deaths': filtered['daily_deaths'],
        'daily_cases': filtered['daily_cases']
    }
    
    # Update plot title
    p.title.text = f'COVID-19 Deaths - {selected_state}'

# Attach callbacks
state_select.on_change('value', lambda attr, old, new: update_plot())
date_slider.on_change('value', lambda attr, old, new: update_plot())

# Layout
layout = column(
    row(state_select),
    date_slider,
    p
)

# Add to document
curdoc().add_root(layout)
curdoc().title = "COVID-19 State Explorer"

print("State Explorer app ready!")
Maths.pm, par

pointcarre.app

Codes sources
Logo licence AGPLv3
Contenus
Logo licence Creative Commons