Team Ai
Apppublic

Estudio24516/Narrative_Visualization

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
app.py690 linesDownload Raw Back to root
1# Import necessary packages2import pandas as pd3import numpy as np4import altair as alt5import datetime as dt6import panel as pn7import vega_datasets8from vega_datasets import data9import requests10import pycountry11 12# Enable panel extensions13pn.extension(design='bootstrap')14pn.extension('vega')15 16# Create a basic template17template = pn.template.BootstrapTemplate(18    title='COVID-19: Policy Responses and Google Mobility Trends',19)20 21# Set up the main column to hold key content22maincol = pn.Column()23 24# Headline25maincol.append("# How has the COVID-19 pandemic changed the movement of people around the world?")26 27# Load the datasets and data processing28## define a function to get the numeric code from the alpha3 code in the original dataset29def alpha3_to_numeric(alpha3):30    try:31        # return the numeric code for the country32        return str(int(pycountry.countries.get(alpha_3=alpha3).numeric))33    except:34        # returning None if the country code is not found35        return None36## load data for Google Mobility Trends37data = pd.read_excel('Mobility Data.xlsx')38data['ID'] = data['Code'].apply(alpha3_to_numeric)39## convert the 'Day' column to datetime40data['Day'] = pd.to_datetime(data['Day'])41## reshape the data42worldwide_data = data.melt(id_vars=['Entity', 'Code', 'Day', 'ID'], 43                  value_vars=['Retail & Recreation', 'Grocery & Pharmacy', 'Residential', 44                              'Transit Stations', 'Parks', 'Workplaces'],45                  var_name='Category', 46                  value_name='Percentage')47## load data for Publict Transportation Policy48public_transportation_data = pd.read_excel('Close Public Transport Data.xlsx')49public_transportation_data['ID'] = public_transportation_data['Code'].apply(alpha3_to_numeric)50## convert the 'Day' column to datetime51public_transportation_data['Day'] = pd.to_datetime(public_transportation_data['Day'])52## load data for Stay Home Requirements Policy53stay_home_data = pd.read_excel('Stay Home Requirements Data.xlsx')54stay_home_data['ID'] = stay_home_data['Code'].apply(alpha3_to_numeric)55## convert the 'Day' column to datetime56stay_home_data['Day'] = pd.to_datetime(stay_home_data['Day'])57## load data for Work Place Closure Policy58work_place_data = pd.read_excel('Workplace Closure Data.xlsx')59work_place_data['ID'] = work_place_data['Code'].apply(alpha3_to_numeric)60## convert the 'Day' column to datetime61work_place_data['Day'] = pd.to_datetime(work_place_data['Day'])62## load data for Gathering Restrictions Policy63gathering_data = pd.read_excel('Restriction Gatherings Data.xlsx')64gathering_data['ID'] = gathering_data['Code'].apply(alpha3_to_numeric)65## convert the 'Day' column to datetime66gathering_data['Day'] = pd.to_datetime(gathering_data['Day'])67alt.data_transformers.disable_max_rows()68 69# Introduction70maincol.append("The coronavirus which causes the [COVID-19 pandemic](https://www.cdc.gov/coronavirus/2019-nCoV/index.html) not only resulted in a global health crisis, but also greatly affected global economic and social environment. In response to the pandemic, nations worldwide have adopted various strict measures to slow the spread of the virus, including **imposing restrictions on public transportation**, **enforcing ‘stay-at-home’ lockdowns**, and **halting social gatherings**. These interventions aim to minimize close contact among individuals, thereby reducing the transmission.")71maincol.append("These measures undeniably have a profound impact on daily life. This raises important questions: **How have these measures affected human mobility?** **What impact have these policy restrictions had on the everyday lives of people worldwide?** Moreover, **why these restrictions are helpful in terms of disease control?**")72maincol.append("To explore the first two questions, we can refer to the [COVID-19 Community Mobility Reports]( https://www.google.com/covid19/mobility/) provided by Google. These reports utilize anonymized location data from applications like Google Maps to generate a consistently updated overview of movement trends during the pandemic. Specifically, it tracks daily visits to places like grocery stores, parks, and transit stations, comparing these figures to a pre-pandemic baseline established from the median activity between *January 3, 2020 and February 6, 2020*. In addition, comparisons are made relative to the baseline with typical daily fluctuations taken into account, which eliminates the influence of people’s different routines on weekdays and weekends.")73maincol.append("The reports are originally presented in PDF form for each country/region, which summarizes key insights in a neat and straightforward form for readers’ reference. However, it sacrifices offering readers an opportunity to explore the data based on their own interests. Therefore, I have transformed this data into interactive visualizations below, which simplify tracking trends of mobility change over time and demonstrating the possible impact of specific restriction policies on human mobility. It is to be noted that **Google advises against making cross-regional comparisons** due to potential discrepancies in location categories that could skew interpretations. Meanwhile, it's important to note that the changes are compared to the early 2020 baseline, disregarding seasonal variations which might, for example, increase the frequency of outdoor activities during warmer months. Thus, some of the shifts may reflect more about seasonal patterns rather than changes induced by COVID-19 policies.")74maincol.append("Throughout this article, some countries/regions or specific mobility categories are picked as typical examples to discuss some interesting insights. However, please feel free to explore based on your own interests by utilizing the selector in interactive visualizations which supports the switch interaction.")75maincol.append(pn.layout.Divider())76 77# Visualization 178maincol.append("## Change in mobility by category")79maincol.append("This interactive chart shows how human mobility in categorized places has changed compared to the baseline. The *Residential* category shows a change in **duration of time** spent at home, while the other categories measure a change in **total number of visits**.")80maincol.append("""81Some tips on how to use this interactive visualization:821. Click on the dropdown **Country/Region** to switch to another country/region.832. Click on the dropdown **Category** to highlight a category you would like to focus on.843. Change the date range by clicking and moving the **date bar** to take a closer look at a specific period.854. Hover over on the line to see **tooltips** including accurate date and value for percentage change from baseline.86""")87## define a function to create the plot88def create_plot(subgroup='United States', date_range=None, selected_category='All'):89    ### filter dataset based on the selected subgroup90    df_filtered = worldwide_data[worldwide_data['Entity'] == subgroup]91    ### apply date range filter if specified92    if date_range is not None:93        start_date, end_date = date_range94        start_date, end_date = pd.to_datetime(start_date), pd.to_datetime(end_date)95        df_filtered = df_filtered[(df_filtered['Day'] >= start_date) & (df_filtered['Day'] <= end_date)]   96    ### create opacity condition related to selection97    opacity_condition = alt.condition(98        alt.datum.Category == selected_category,99        alt.value(1),100        alt.value(0.2) if selected_category != 'All' else alt.value(1)101    )102    ### create line chart103    line_chart = alt.Chart(df_filtered).mark_line(size=2).encode(104        x=alt.X('Day:T',axis=alt.Axis(grid=False)),105        y=alt.Y('Percentage:Q',scale=alt.Scale(domain=[-200, 200]),axis=alt.Axis(values=[-200, -150, -100, -50, 0, 50, 100, 150, 200],grid=True,gridColor='lightgrey',title='Change from baseline (%)')),106        color='Category:N',107        opacity=opacity_condition,108        tooltip=[109            alt.Tooltip('Entity:N', title='Country'),110            alt.Tooltip('Category:N', title='Category'),111            alt.Tooltip('Day:T', title='Date'),112            alt.Tooltip('Percentage:Q', title='Change from baseline(%)', format=".2f")113        ]114    ).properties(115        width=900,116        height=500117    )118    ### return the line chart119    return line_chart120## create widgets for interaction121subgroup_select = pn.widgets.Select(name='Country/Region', options=worldwide_data['Entity'].unique().tolist(), value='United States')122date_range_slider = pn.widgets.DateRangeSlider(name='Date Range', start=worldwide_data['Day'].min(), end=worldwide_data['Day'].max())123category_select = pn.widgets.Select(name='Category', options=['All'] + worldwide_data['Category'].unique().tolist(), value='All')124## define a function to update the plot based on widgets125def update_plot(event):126    plot = create_plot(subgroup=subgroup_select.value, 127                       date_range=date_range_slider.value,128                       selected_category=category_select.value)129    plot_pane.object = plot130## bind the widgets to the create_plot function131subgroup_select.param.watch(update_plot, 'value')132date_range_slider.param.watch(update_plot, 'value')133category_select.param.watch(update_plot, 'value')134## initial plot135initial_plot = create_plot()136plot_pane = pn.pane.Vega(initial_plot, width=1000, height=500)137maincol.append(pn.Row(subgroup_select,category_select))138maincol.append(date_range_slider)139maincol.append(plot_pane)140maincol.append(" ")141maincol.append(" ")142maincol.append(" ")143maincol.append(" ")144maincol.append(" ")145maincol.append("""146From the visualization, it is easy for us to perceive a general decrease of visits to places such as workplaces, transit stations as well as retail and recreation places at the beginning of the global pandemic. Meanwhile, we also notice an increase of time spent at home. Take **United States** as an example, we see a sharp decrease in number of visits to all categorized places except residential at around March 15, 2020 to March 29, 2020. This period corresponds to the onset of widespread COVID-19 lockdown measures across various states, which were implemented to curb the spread of the virus:147* *March 15, 2020*: The Centers for Disease Control and Prevention (CDC) recommended no gatherings of 50 people or more for the next eight weeks.148* *March 16, 2020*: Many states including Ohio, Maryland, Washington, and California started closing bars, restaurants, and public gatherings to slow the spread of the virus.149* *March 19, 2020*: California issued a statewide stay-at-home order, the first of its kind in the nation.150* *March 21, 2020*: New York State followed with its own stay-at-home order, mandating non-essential workers to stay home.151* *March 23, 2020*: Washington State and Massachusetts issued stay-at-home orders.152* *March 24 to 29, 2020*: Several other states, including Michigan, Indiana, Wisconsin, and West Virginia, issued formal stay-at-home orders.153""")154maincol.append("In addition, throughout the time period, visits to workplaces and transit stations in the U.S. are consistently below the baseline level, suggesting a substantial change in people's patterns of working and commuting. Moreover, the visits to parks in the U.S. show a strong seasonal pattern, indicating that changes might not be induced by COVID-19 policies. However, situations in different countries can be extremely different. For example, in **Mexico**, after a sharp decrease at around *March 15, 2020* to *April 15, 2020*, we see visits to all categories show a trend of increase and end up with all beyond baseline level in *October, 2020*.")155maincol.append(pn.layout.Divider())156maincol.append("## Policy Restrictions and Change in mobility")157maincol.append("Next, we are going to see possible influence of different policy restrictions on human mobility respectively. Data for specific policy restrictions are retrieved from [Oxford COVID-19 Government Response Tracker](https://www.bsg.ox.ac.uk/research/research-projects/oxford-covid-19-government-response-tracker). If policies vary at the subnational level, the index is shown as **the response level of the strictest sub-region**.")158maincol.append("""159Some tips on how to use the following interactive dashboards:1601. Upper left is a line chart showing **the selected country/region’s policy requirements level across time**. Lower left is a line chart showing **the selected country/region’s mobility change from baseline in a specified category across time**. The reference lines in line charts denote **current selected date**.1612. Upper right is a choropleth map showing **the policy requirements level around the globe on the selected date**. Lower right is a choropleth map for **mobility change from baseline in a specified category around the globe on the selected date**. The selected country/region in these maps are highlighted with <span style='color: red;'>**red boundary**</span>.1623. Click on the dropdown **Country/Region** to switch to another country/region.1634. Click on the dropdown **Category** to switch to another category.1645. Move the **date button** along the date bar to select a specific date.1656. **Zoom in and out** based on x-axis with mouse for line charts to delve into details of a period and get back.1667. Hover over on the line or a country/region in the map to see **tooltips** including detailed data.167""")168# Visualization 2169maincol.append("### Policy on public transport")170maincol.append("This interactive dashboard allows us to explore government policies on public transport closures and its influence on human mobility. For the public transportation closure level, the larger the number, the stricter the restrictions are. Particularly, **0** represents <span style='color: #8ca08c;'>**no measures**</span>, **1** represents <span style='color: #fceb8c;'>**recommended closing or reduce volume**</span>, and *2* represents <span style='color: #f4c7c3;'>**required closing or prohibit most using it**</span>. ")171## load TopoJSON for world countries172world_json_url = "https://vega.github.io/vega-datasets/data/world-110m.json"173## create widgets for interaction174category_select_transportation = pn.widgets.Select(name='Category', options=worldwide_data['Category'].unique().tolist(), value=worldwide_data['Category'].unique()[0])175date_slider_transportation = pn.widgets.DateSlider(name='Day', start=worldwide_data['Day'].min(), end=worldwide_data['Day'].max(), value=worldwide_data['Day'].min())176subgroup_select_transportation = pn.widgets.Select(name='Country/Region', options=worldwide_data['Entity'].unique().tolist(), value='United States')177## define the function to create dashboard178def create_transportation_dashboard(date, category, subgroup):179    ### date to datetime180    date = pd.to_datetime(date)181    ### zoom condition for line charts182    zoom = alt.selection_interval(bind='scales', encodings=['x'], name="zoom")183    ### color dictionary184    category_colors = {185        'Retail & Recreation': '#17becf',186        'Grocery & Pharmacy': '#1f77b4',187        'Parks': '#ff7f0e',188        'Transit Stations': '#2ca02c',189        'Workplaces': '#bcbd22',190        'Residential': '#d62728'191    }192    choro_colors = {193        'Retail & Recreation': 'blueorange',194        'Grocery & Pharmacy': 'brownbluegreen',195        'Parks': 'purplegreen',196        'Transit Stations': 'pinkyellowgreen',197        'Workplaces': 'purpleorange',198        'Residential': 'redblue'199    }200    current_category_color = category_colors.get(category, 'black')201    current_choro_color = choro_colors.get(category, 'redyellowgreen')202    ###--------------------category choro part--------------------203    ### filter the data204    filtered_data_category_choro = worldwide_data[(worldwide_data['Day'] == date) & (worldwide_data['Category'] == category)]205    ### create the base for category choro206    base_category = alt.Chart(alt.topo_feature(world_json_url, 'countries')).mark_geoshape(207        stroke='black', strokeWidth=0.5208    ).properties(209        width=600,210        height=300211    ).project('naturalEarth1')212    ### create the category choro213    data_layer = base_category.transform_lookup(214        lookup='id',215        from_=alt.LookupData(filtered_data_category_choro, 'ID', ['Entity', 'Category', 'Percentage']),216    ).encode(217        color=alt.Color('Percentage:Q', 218        scale=alt.Scale(domain=[-100, 100], scheme=current_choro_color),  # Using the stepped_colors for the scale219        legend=alt.Legend(title='Change from baseline', orient='right')220        ),221        tooltip=[222            alt.Tooltip('Entity:N', title='Country'),223            alt.Tooltip('Category:N', title='Category'),224            alt.Tooltip('Day:T', title='Date'),225            alt.Tooltip('Percentage:Q', title='Change from baseline(%)', format=".2f")226        ],227        stroke=alt.condition(alt.datum.Entity == subgroup, alt.value('red'),alt.value('black'))228    )229    ### create another background layer230    background = alt.Chart(alt.topo_feature(world_json_url, 'countries')).mark_geoshape(231        fill='lightgray', stroke='black', strokeWidth=0.5232    ).properties(233        width=600,234        height=300235    ).project(236        'naturalEarth1'237    )238    ### combine the choro and the background layer239    category_choro = alt.layer(background, data_layer).resolve_scale(color='independent')240    ###--------------------transportation choro part--------------------241    ### filter the data242    filtered_data_transportation_choro = public_transportation_data[(public_transportation_data['Day'] == date)]243    ### create the base for transportation choro244    base_transportation = alt.Chart(alt.topo_feature(world_json_url, 'countries')).mark_geoshape(245        stroke='black', strokeWidth=0.5246    ).properties(247        width=600,248        height=300249    ).project('naturalEarth1')250    ### create the transportation choro251    transportation_choro = base_transportation.transform_lookup(252        lookup='id',253        from_=alt.LookupData(filtered_data_transportation_choro, 'ID', ['Entity','Close Public Transport']),254        default='No Data'255    ).encode(256        color=alt.Color('Close Public Transport:N', scale=alt.Scale(domain=[0, 1, 2, 'No Data'],257                        range=["#8ca08c", "#fceb8c", "#f4c7c3", "#d3d3d3"]),258                        legend=alt.Legend(title='Public transport closure level', orient='right', offset=20)),259        tooltip=[alt.Tooltip('Entity:N', title='Country'), alt.Tooltip('Day:T', title='Date'), alt.Tooltip('Close Public Transport:N', title='Closure Level')],260        stroke=alt.condition(alt.datum.Entity == subgroup, alt.value('red'),alt.value('black'))261    )262    ###--------------------category line part--------------------263    filtered_data_category_line = worldwide_data[(worldwide_data['Entity'] == subgroup) & (worldwide_data['Category'] == category)]264    line_chart_category = alt.Chart(filtered_data_category_line).mark_area(color=current_category_color).encode(265        x=alt.X('Day:T',axis=alt.Axis(grid=False)),266        y=alt.Y('Percentage:Q',scale=alt.Scale(domain=[-200, 200]),axis=alt.Axis(values=[-200, -150, -100, -50, 0, 50, 100, 150, 200],grid=True,gridColor='lightgrey',title='Change from baseline (%)')),267        tooltip=[268            alt.Tooltip('Entity:N', title='Country'),269            alt.Tooltip('Category:N', title='Category'),270            alt.Tooltip('Day:T', title='Date'),271            alt.Tooltip('Percentage:Q', title='Change from baseline(%)', format=".2f")272        ]273    ).properties(274        width=400,275        height=300276    ).add_params(277        zoom278    )279    line_chart_category += alt.Chart(pd.DataFrame({'Day': [date]})).mark_rule(color='#e377c2').encode(280        x='Day:T'281    )282    ###--------------------transportation line part--------------------283    filtered_data_transportation_line = public_transportation_data[(public_transportation_data['Entity'] == subgroup)]284    line_chart_transportation = alt.Chart(filtered_data_transportation_line).mark_line(size=2,color='#8ca08c').encode(285        x=alt.X('Day:T',axis=alt.Axis(grid=False)),286        y=alt.Y('Close Public Transport:Q',scale=alt.Scale(domain=[0, 3]),axis=alt.Axis(values=[0,1,2,3], grid=True,gridColor='lightgrey',title='Public Transportation Closure Level')),287        tooltip=[288            alt.Tooltip('Entity:N', title='Country/Region'),289            alt.Tooltip('Day:T', title='Date'),290            alt.Tooltip('Close Public Transport:N', title='Closure Level')291        ]292    ).properties(293    width=400,294    height=300295    ).add_params(296        zoom297    )298    line_chart_transportation += alt.Chart(pd.DataFrame({'Day': [date]})).mark_rule(color='#e377c2').encode(299        x='Day:T'300    )301    ### combine two choros302    final_chart = alt.vconcat(303        alt.hconcat(line_chart_transportation,transportation_choro),304        alt.hconcat(line_chart_category,category_choro)305    )306    return final_chart307## initial map creation308initial_transportation_dashboard = create_transportation_dashboard(date_slider_transportation.value, category_select_transportation.value, subgroup_select_transportation.value)309transportation_dashboard_pane = pn.pane.Vega(initial_transportation_dashboard, width=1000, height=700)310## define a function to update dashboard based on widgets311def update_transportation_dashboard(event):312    new_transportation_dashboard = create_transportation_dashboard(date_slider_transportation.value, category_select_transportation.value, subgroup_select_transportation.value)313    transportation_dashboard_pane.object = new_transportation_dashboard314## bind the widgets to the create_plot function315date_slider_transportation.param.watch(update_transportation_dashboard, 'value')316category_select_transportation.param.watch(update_transportation_dashboard, 'value')317subgroup_select_transportation.param.watch(update_transportation_dashboard, 'value')318## arrange widgets and map pane in a layout319dashboard_transportation = pn.Column(320    pn.Row(category_select_transportation,subgroup_select_transportation),321    date_slider_transportation, 322    transportation_dashboard_pane323)324maincol.append(dashboard_transportation)325maincol.append(" ")326maincol.append(" ")327maincol.append(" ")328maincol.append(" ")329maincol.append(" ")330maincol.append("From this visualization, we can develop more insights in the relationship between policies on public transportation and human mobility. We see at the beginning of the global pandemic, many countries/regions have policies such as recommend or forcing closing public transportation systems. Intuitively, we can imagine this will greatly affect people’s activity to transit stations. After taking a detailed look at several different countries (i.e., **United States**, **Spain**, **Brasil**, and **India**), we find out that in most cases a significant decline in mobility for transit stations will occur when the country/region strengthen their public transportation restriction policy. Conversely, if the restrictions are loosened, mobility levels generally return to baseline or even surpass it.")331 332# Visualization 3333maincol.append("### Policy on stay-at-home lockdown")334maincol.append("This interactive dashboard allows us to explore government policies on stay-at-home requirements and its influence on human mobility. For the stay-at-home requirements level, the larger the number, the stricter the requirements are. Particularly, **0** represents <span style='color: #366388;'>**no measures**</span>, **1** represents <span style='color: #fceb8c;'>**recommended not to leave the house**</span>, **2** represents <span style='color: #f07f59;'>**required to not leave the house with exceptions for daily exercise, grocery shopping, and ‘essential’ trips**</span>, and **3** represents <span style='color: #d26e66;'>**Required to not leave the house with minimal exceptions (e.g. allowed to leave only once every few days, or only one person can leave at a time, etc.)**</span>. ")335## create widgets for interaction336category_select_stay_home = pn.widgets.Select(name='Category', options=worldwide_data['Category'].unique().tolist(), value=worldwide_data['Category'].unique()[0])337date_slider_stay_home = pn.widgets.DateSlider(name='Day', start=worldwide_data['Day'].min(), end=worldwide_data['Day'].max(), value=worldwide_data['Day'].min())338subgroup_select_stay_home = pn.widgets.Select(name='Country/Region', options=worldwide_data['Entity'].unique().tolist(), value='United States')339## define the function to create dashboard340def create_stay_home_dashboard(date, category, subgroup):341    ### date to datetime342    date = pd.to_datetime(date)343    ### zoom condition for line charts344    zoom = alt.selection_interval(bind='scales', encodings=['x'], name="zoom")345    ### color dictionary346    category_colors = {347        'Retail & Recreation': '#17becf',348        'Grocery & Pharmacy': '#1f77b4',349        'Parks': '#ff7f0e',350        'Transit Stations': '#2ca02c',351        'Workplaces': '#bcbd22',352        'Residential': '#d62728'353    }354    choro_colors = {355        'Retail & Recreation': 'blueorange',356        'Grocery & Pharmacy': 'brownbluegreen',357        'Parks': 'purplegreen',358        'Transit Stations': 'pinkyellowgreen',359        'Workplaces': 'purpleorange',360        'Residential': 'redblue'361    }362    current_category_color = category_colors.get(category, 'black')363    current_choro_color = choro_colors.get(category, 'redyellowgreen')364    ###--------------------category choro part--------------------365    ### filter the data366    filtered_data_category_choro = worldwide_data[(worldwide_data['Day'] == date) & (worldwide_data['Category'] == category)]367    ### create the base for category choro368    base_category = alt.Chart(alt.topo_feature(world_json_url, 'countries')).mark_geoshape(369        stroke='black', strokeWidth=0.5370    ).properties(371        width=600,372        height=300373    ).project('naturalEarth1')374    ### create the category choro375    data_layer = base_category.transform_lookup(376        lookup='id',377        from_=alt.LookupData(filtered_data_category_choro, 'ID', ['Entity', 'Category', 'Percentage']),378    ).encode(379        color=alt.Color('Percentage:Q', 380        scale=alt.Scale(domain=[-100, 100], scheme=current_choro_color, reverse=True),381        legend=alt.Legend(title='Change from baseline', orient='right')382        ),383        tooltip=[384            alt.Tooltip('Entity:N', title='Country'),385            alt.Tooltip('Category:N', title='Category'),386            alt.Tooltip('Day:T', title='Date'),387            alt.Tooltip('Percentage:Q', title='Change from baseline(%)', format=".2f")388        ],389        stroke=alt.condition(alt.datum.Entity == subgroup, alt.value('red'),alt.value('black'))390    )391    ### create another background layer392    background = alt.Chart(alt.topo_feature(world_json_url, 'countries')).mark_geoshape(393        fill='lightgray', stroke='black', strokeWidth=0.5394    ).properties(395        width=600,396        height=300397    ).project(398        'naturalEarth1'399    )400    ### combine the choro and the background layer401    category_choro = alt.layer(background, data_layer).resolve_scale(color='independent')402    ###--------------------stay home choro part--------------------403    ### filter the data404    filtered_data_stay_home_choro = stay_home_data[(stay_home_data['Day'] == date)]405    ### create the base for stay home choro406    base_stay_home = alt.Chart(alt.topo_feature(world_json_url, 'countries')).mark_geoshape(407        stroke='black', strokeWidth=0.5408    ).properties(409        width=600,410        height=300411    ).project('naturalEarth1')412    ### create the stay home choro413    stay_home_choro = base_stay_home.transform_lookup(414        lookup='id',415        from_=alt.LookupData(filtered_data_stay_home_choro, 'ID', ['Entity','Stay Home Requirements']),416        default='No Data'417    ).encode(418        color=alt.Color('Stay Home Requirements:N', scale=alt.Scale(domain=[0, 1, 2, 3, 'No Data'],419                        range=["#366388", "#fceb8c", "#f07f59", "#d26e66", "#d3d3d3"]),420                        legend=alt.Legend(title='Stay-at-home requirements level', orient='right', offset=20)),421        tooltip=[alt.Tooltip('Entity:N', title='Country/Region'), alt.Tooltip('Day:T', title='Date'), alt.Tooltip('Stay Home Requirements:N', title='Stay-at-home requirements level')],422        stroke=alt.condition(alt.datum.Entity == subgroup, alt.value('red'),alt.value('black'))423    )424    ###--------------------category line part--------------------425    filtered_data_category_line = worldwide_data[(worldwide_data['Entity'] == subgroup) & (worldwide_data['Category'] == category)]426    line_chart_category = alt.Chart(filtered_data_category_line).mark_area(color=current_category_color).encode(427        x=alt.X('Day:T',axis=alt.Axis(grid=False)),428        y=alt.Y('Percentage:Q',scale=alt.Scale(domain=[-200, 200]),axis=alt.Axis(values=[-200, -150, -100, -50, 0, 50, 100, 150, 200],grid=True,gridColor='lightgrey',title='Change from baseline (%)')),429        tooltip=[430            alt.Tooltip('Entity:N', title='Country'),431            alt.Tooltip('Category:N', title='Category'),432            alt.Tooltip('Day:T', title='Date'),433            alt.Tooltip('Percentage:Q', title='Change from baseline(%)', format=".2f")434        ]435    ).properties(436        width=400,437        height=300438    ).add_params(439        zoom440    )441    line_chart_category += alt.Chart(pd.DataFrame({'Day': [date]})).mark_rule(color='#e377c2').encode(442        x='Day:T'443    )444    ###--------------------stay home line part--------------------445    filtered_data_stay_home_line = stay_home_data[(stay_home_data['Entity'] == subgroup)]446    line_chart_stay_home = alt.Chart(filtered_data_stay_home_line).mark_line(size=2,color='#366388').encode(447        x=alt.X('Day:T',axis=alt.Axis(grid=False)),448        y=alt.Y('Stay Home Requirements:Q',scale=alt.Scale(domain=[0, 4]),axis=alt.Axis(values=[0,1,2,3,4], grid=True,gridColor='lightgrey',title='Stay-at-home Requirements Level')),449        tooltip=[450            alt.Tooltip('Entity:N', title='Country/Region'),451            alt.Tooltip('Day:T', title='Date'),452            alt.Tooltip('Stay Home Requirements:N', title='Stay-at-home Requirements Level')453        ]454    ).properties(455    width=400,456    height=300457    ).add_params(458        zoom459    )460    line_chart_stay_home += alt.Chart(pd.DataFrame({'Day': [date]})).mark_rule(color='#e377c2').encode(461        x='Day:T'462    )463    ### combine two choros464    final_chart = alt.vconcat(465        alt.hconcat(line_chart_stay_home,stay_home_choro),466        alt.hconcat(line_chart_category,category_choro)467    )468    return final_chart469## initial dashboard creation470initial_stay_home_dashboard = create_stay_home_dashboard(date_slider_stay_home.value, category_select_stay_home.value, subgroup_select_stay_home.value)471stay_home_dashboard_pane = pn.pane.Vega(initial_stay_home_dashboard, width=1000, height=700)472## define a function to update dashboard based on widgets473def update_stay_home_dashboard(event):474    new_stay_home_dashboard = create_stay_home_dashboard(date_slider_stay_home.value, category_select_stay_home.value, subgroup_select_stay_home.value)475    stay_home_dashboard_pane.object = new_stay_home_dashboard476## bind the widgets to the create_plot function477date_slider_stay_home.param.watch(update_stay_home_dashboard, 'value')478category_select_stay_home.param.watch(update_stay_home_dashboard, 'value')479subgroup_select_stay_home.param.watch(update_stay_home_dashboard, 'value')480## arrange widgets and dashboard pane in a layout481dashboard_stay_home = pn.Column(482    pn.Row(category_select_stay_home,subgroup_select_stay_home),483    date_slider_stay_home,484    stay_home_dashboard_pane485)486maincol.append(dashboard_stay_home)487maincol.append(" ")488maincol.append(" ")489maincol.append(" ")490maincol.append(" ")491maincol.append(" ")492maincol.append("Generally, stay-at-home related policies are considered as *most influencial* since they might have an impact on any category of human mobility. In terms of global trend, we see more and more countries/regions take measures related to stay-home policies after in *early April, 2020*, when global pandemic situation becomes worsen and worsen. Many countries have imposed the highest level requirement at some points, such as **Russia**, **China**, and **India**. The stay-at-home restriction level in most countries/regions reduces to no measures or recommended at around *late March, 2022*. Let's take **United States** as an example to investigate the possible inflence of its stay-at-home restrictions on human mobility. We see that the **required not to leave except for essential activities** began on **March 15, 2020**. After the level reduced to **recommended not to leave**, there is a slight gradually decrease in time spent at home, which dropped from around **10%** to around **4%**. A similar increasing trend can be seen in visits to grocery and pharmacy, retail and recreation, and transit stations. However, such trend is not seen in visits to workplaces, suggesting that people might still prefer working from home at this time. For visits to park, since the increase after the change of stay-home restriction level appears similar season pattern as in 2021, we suppose the change might not caused by policy changes.")493 494# Visualization 4495maincol.append("### Policy on social gatherings")496maincol.append("This interactive dashboard allows us to explore government policies on social gatherings and its influence on human mobility. For the gathering restriction level, the larger the number, the stricter the requirements are. Particularly, **0** represents <span style='color: #e7af6c;'>**no restrictions**</span>, **1** represents <span style='color: #fceb8c;'>**restrictions on very large gatherings (the limit is above 1000 people)**</span>, **2** represents <span style='color: #a1dab4;'>**restrictions on gatherings between 100 to 1000 people**</span>, **3** represents <span style='color: #41b6c4;'>**restrictions on gatherings between 10 to 100 people**</span>, and **4** represents <span style='color: #253494;'>**restrictions on gatherings of less than 10 people**</span>.")497## create widgets for interaction498category_select_gathering = pn.widgets.Select(name='Category', options=worldwide_data['Category'].unique().tolist(), value=worldwide_data['Category'].unique()[0])499date_slider_gathering = pn.widgets.DateSlider(name='Day', start=worldwide_data['Day'].min(), end=worldwide_data['Day'].max(), value=worldwide_data['Day'].min())500subgroup_select_gathering = pn.widgets.Select(name='Country/Region', options=worldwide_data['Entity'].unique().tolist(), value='United States')501## define the function to create dashboard502def create_gathering_dashboard(date, category, subgroup):503    ### date to datetime504    date = pd.to_datetime(date)505    ### zoom condition for line charts506    zoom = alt.selection_interval(bind='scales', encodings=['x'], name="zoom")507    ### color dictionary508    category_colors = {509        'Retail & Recreation': '#17becf',510        'Grocery & Pharmacy': '#1f77b4',511        'Parks': '#ff7f0e',512        'Transit Stations': '#2ca02c',513        'Workplaces': '#bcbd22',514        'Residential': '#d62728'515    }516    choro_colors = {517        'Retail & Recreation': 'blueorange',518        'Grocery & Pharmacy': 'brownbluegreen',519        'Parks': 'purplegreen',520        'Transit Stations': 'pinkyellowgreen',521        'Workplaces': 'purpleorange',522        'Residential': 'redblue'523    }524    current_category_color = category_colors.get(category, 'black')525    current_choro_color = choro_colors.get(category, 'redyellowgreen')526    ###--------------------category choro part--------------------527    ### filter the data528    filtered_data_category_choro = worldwide_data[(worldwide_data['Day'] == date) & (worldwide_data['Category'] == category)]529    ### create the base for category choro530    base_category = alt.Chart(alt.topo_feature(world_json_url, 'countries')).mark_geoshape(531        stroke='black', strokeWidth=0.5532    ).properties(533        width=600,534        height=300535    ).project('naturalEarth1')536    ### create the category choro537    data_layer = base_category.transform_lookup(538        lookup='id',539        from_=alt.LookupData(filtered_data_category_choro, 'ID', ['Entity', 'Category', 'Percentage']),540    ).encode(541        color=alt.Color('Percentage:Q', 542        scale=alt.Scale(domain=[-100, 100], scheme=current_choro_color, reverse=True),543        legend=alt.Legend(title='Change from baseline', orient='right')544        ),545        tooltip=[546            alt.Tooltip('Entity:N', title='Country'),547            alt.Tooltip('Category:N', title='Category'),548            alt.Tooltip('Day:T', title='Date'),549            alt.Tooltip('Percentage:Q', title='Change from baseline(%)', format=".2f")550        ],551        stroke=alt.condition(alt.datum.Entity == subgroup, alt.value('red'),alt.value('black'))552    )553    ### create another background layer554    background = alt.Chart(alt.topo_feature(world_json_url, 'countries')).mark_geoshape(555        fill='lightgray', stroke='black', strokeWidth=0.5556    ).properties(557        width=600,558        height=300559    ).project(560        'naturalEarth1'561    )562    ### combine the choro and the background layer563    category_choro = alt.layer(background, data_layer).resolve_scale(color='independent')564    ###--------------------gathering choro part--------------------565    ### filter the data566    filtered_data_gathering_choro = gathering_data[(gathering_data['Day'] == date)]567    ### create the base for gathering choro568    base_gathering = alt.Chart(alt.topo_feature(world_json_url, 'countries')).mark_geoshape(569        stroke='black', strokeWidth=0.5570    ).properties(571        width=600,572        height=300573    ).project('naturalEarth1')574    ### create the gathering choro575    gathering_choro = base_gathering.transform_lookup(576        lookup='id',577        from_=alt.LookupData(filtered_data_gathering_choro, 'ID', ['Entity','Restriction Gatherings']),578        default='No Data'579    ).encode(580        color=alt.Color('Restriction Gatherings:N', scale=alt.Scale(domain=[0, 1, 2, 3, 4, 'No Data'],581                        range=["#e7af6c", "#fceb8c", "#a1dab4", "#41b6c4", "#253494", "#d3d3d3"]),582                        legend=alt.Legend(title='Gatherings restriction level')),583        tooltip=[alt.Tooltip('Entity:N', title='Country'), alt.Tooltip('Day:T', title='Date'), alt.Tooltip('Restriction Gatherings:N', title='Level')],584        stroke=alt.condition(alt.datum.Entity == subgroup, alt.value('red'),alt.value('black'))585    )586    ###--------------------category line part--------------------587    filtered_data_category_line = worldwide_data[(worldwide_data['Entity'] == subgroup) & (worldwide_data['Category'] == category)]588    line_chart_category = alt.Chart(filtered_data_category_line).mark_area(color=current_category_color).encode(589        x=alt.X('Day:T',axis=alt.Axis(grid=False)),590        y=alt.Y('Percentage:Q',scale=alt.Scale(domain=[-200, 200]),axis=alt.Axis(values=[-200, -150, -100, -50, 0, 50, 100, 150, 200],grid=True,gridColor='lightgrey',title='Change from baseline (%)')),591        tooltip=[592            alt.Tooltip('Entity:N', title='Country'),593            alt.Tooltip('Category:N', title='Category'),594            alt.Tooltip('Day:T', title='Date'),595            alt.Tooltip('Percentage:Q', title='Change from baseline(%)', format=".2f")596        ]597    ).properties(598        width=400,599        height=300600    ).add_params(601        zoom602    )603    line_chart_category += alt.Chart(pd.DataFrame({'Day': [date]})).mark_rule(color='#e377c2').encode(604        x='Day:T'605    )606    ###--------------------gathering line part--------------------607    filtered_data_gathering_line = gathering_data[(gathering_data['Entity'] == subgroup)]608    line_chart_gathering = alt.Chart(filtered_data_gathering_line).mark_line(size=2,color='#8ca08c').encode(609        x=alt.X('Day:T',axis=alt.Axis(grid=False)),610        y=alt.Y('Restriction Gatherings:Q',scale=alt.Scale(domain=[0, 5]),axis=alt.Axis(values=[0,1,2,3,4,5], grid=True,gridColor='lightgrey',title='Gathering Restrictions Level')),611        tooltip=[612            alt.Tooltip('Entity:N', title='Country/Region'),613            alt.Tooltip('Day:T', title='Date'),614            alt.Tooltip('Restriction Gatherings:N', title='Gathering Restrictions Level')615        ]616    ).properties(617    width=400,618    height=300619    ).add_params(620        zoom621    )622    line_chart_gathering += alt.Chart(pd.DataFrame({'Day': [date]})).mark_rule(color='#e377c2').encode(623        x='Day:T'624    )625    ### combine two choros626    final_chart = alt.vconcat(627        alt.hconcat(line_chart_gathering,gathering_choro),628        alt.hconcat(line_chart_category,category_choro)629    )630    return final_chart631## initial dashboard creation632initial_gathering_dashboard = create_gathering_dashboard(date_slider_gathering.value, category_select_gathering.value, subgroup_select_gathering.value)633gathering_dashboard_pane = pn.pane.Vega(initial_gathering_dashboard, width=1000, height=700)634## define a function to update dashboard based on widgets635def update_gathering_dashboard(event):636    new_gathering_dashboard = create_gathering_dashboard(date_slider_gathering.value, category_select_gathering.value, subgroup_select_gathering.value)637    gathering_dashboard_pane.object = new_gathering_dashboard638## bind the widgets to the create_plot function639date_slider_gathering.param.watch(update_gathering_dashboard, 'value')640category_select_gathering.param.watch(update_gathering_dashboard, 'value')641subgroup_select_gathering.param.watch(update_gathering_dashboard, 'value')642## Arrange widgets and dashboard pane in a layout643dashboard_gathering = pn.Column(644    pn.Row(category_select_gathering,subgroup_select_gathering),645    date_slider_gathering,646    gathering_dashboard_pane647)648maincol.append(dashboard_gathering)649maincol.append(" ")650maincol.append(" ")651maincol.append(" ")652maincol.append(" ")653maincol.append(" ")654maincol.append("The social gathering policy is the type of restrictions that most countries/regions have imposed on a highest level among all kinds of policies we are looking at. It's typical influence on mobility in *Retail & Recreation* are found across many countries. For instance, both **United States** and **Switzerland** strenghthened their restrictions on social gathering from level 3 to 4 **near the end of 2021**, leading to a sharp decrease in visits to retail and recreation places in these two countries, respectively. While for **Turkey**, which experienced a restriction relaxation from level 3 to no measures in *July 2021*, we see a clear trend of increase in visits to retail and recreation places. Similar pattern can also be found in **Canada** after its relaxation of social gathering policy in *April, 2022*.")655maincol.append(pn.layout.Divider())656 657# Simulation658maincol.append("## Understanding the effect of policy restrictions")659## local path for video files660normal_video_path = 'simulation_normal.mp4'661lockdown_video_path = 'simulation_lockdown.mp4'662## Create video panes663normal_video_pane = pn.pane.Video(normal_video_path, width=800, loop=True)664lockdown_video_pane = pn.pane.Video(lockdown_video_path, width=800, loop=True)665maincol.append("After exploring all these interactive dashboards, we are more informed of what influence might various policies have on people’s mobility. However, there is an additional question pending: **why these restrictions are helpful in terms of disease control?**")666maincol.append("To fully understand the mechanism requires rich knowledge in COVID-19 transmission and public health. However, it is definitely not difficult to get an intuition. Therefore, let’s take a look at some simple simulations to better understand why these policies can be helpful.")667maincol.append("""668Imagine a small town with **2000** residents. We assume the following conditions to simulate the spead of a hypothetical disease in this town:669* Population’s age follows a **gaussian distribution** with a mean **55**, stand deviation of **1/3 the mean**, and max of **105**.670* There is a **3%** chance of becoming infected when being around an infected person.671* The disease might lead to mortality with a baseline rate **2%**. Mortality chances start increasing at age 55, going up exponentially up to **10%** at age 75 and above.672* The healthcare capacity is **300 beds** in this town.673* When in medical treatment, mortality chance is **halved**. Otherwise, mortality chance **increases threefold**.674""")675## Simulation for no measures676maincol.append("### Simulation for no measures")677maincol.append("We first take a look at what will happen if no measures are taken at all, which means people will act as usual. Notice how the slope of the red curve, which represents the number of infected people, rises rapidly as the disease spreads. In addition, the healthcare system becomes completely overwhelmed, leading to **60 fatalities** (**3%** of the population). ")678maincol.append(normal_video_pane)679maincol.append("### Simulation for lock-down")680maincol.append("What if all conditions are the same, but to simulate people staying at home whenever possible and only going out when they have to? ")681maincol.append("Let's simulate a lock-down once **5%** of the population get infected. To simulate this, we will make **90% of the residents stop moving when the lock-down begins**, while **the remaining 10% move with substantially reduced speed** to simulate them being more cautious. This 10% represents the professions that are considered critical to society: these people will still be on the move and in contact with other people even under a lock-down policy. Another part of this 10% comes from people being people, meaning no lock-down will be perfect as there will always be those breaking quarantine. Notice that once the policy begins, the number of infections still increases for some time. This happens because of some of the healthy people will be locked into the same household with infected people, and thus become infected relatively quickly as well. If one of the moving population members (perhaps someone delivering groceries or packages) infects one of a cluster of people locked down together, the disease might spread. However, the general situation is much better than the situation when no actions are taken. The total infected people does not exceed the healthcare system's capacity, and the fatalites reduces to **8**, which is only **0.4%** of the population.")682maincol.append(lockdown_video_pane)683maincol.append(pn.layout.Divider())684maincol.append("## Conclusion")685maincol.append("In conclusion, through interactive visualizations above, we observe significant trends, such as increased time spent at residential locations and decreases in visits to other mobility categories during the global pandemic. We also make comparative analysis on possible influence of policy changes on certain type of human mobility. Except for the examples mentioned above, you are highly encouraged to use the above visual tools to explore the data by adjusting selection conditions and viewing the outcomes.")686maincol.append("However, these visualizations have their limitations. We do not employ rigorous statistical tests to confirm the observed correlations between policy implementations and changes in human mobility, meaning the findings should be interpreted with caution. Furthermore, the simulations presented to illustrate potential disease spread and control under a lock-down policy are based on a hypothetical setting, which simplifies many complex factors encountered in real-world situations. These models do not account for all demographic, social, and geographical variables that can influence disease transmission and the effectiveness of policy measures.")687maincol.append("Understanding these impacts and the dynamics of mobility during the pandemic is crucial for better preparation and response in future health crises. For those looking to explore this topic further, we refer you to additional resources include the [Johns Hopkins Coronavirus Resource Center](https://coronavirus.jhu.edu) and the [World Health Organization's COVID-19 dashboard](https://data.who.int/dashboards/covid19) for more comprehensive data and in-depth analysis. Academic journals and publications related to epidemiology and public health policy also offer more detailed studies and findings of the pandemic's broader socio-economic effects and how policy measures contribute to controlling disease spread.")688# Finish the application689template.main.append(maincol)690template.servable(title="COVID-19: Policy Responses and Google Mobility Trends")