279 lines
9.6 KiB
Plaintext
279 lines
9.6 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 78,
|
|
"id": "1aa3ee04-09e1-4278-94de-b047a73b6c44",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from IPython.display import clear_output\n",
|
|
"import ipywidgets as wdg\n",
|
|
"import pandas as pd\n",
|
|
"import numpy as np\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"import json\n",
|
|
"from uk_covid19 import Cov19API"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 79,
|
|
"id": "253fb2f6-d4b0-4dbf-8ef6-4f378e4dae4a",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"%matplotlib inline\n",
|
|
"# make figures larger\n",
|
|
"plt.rcParams['figure.dpi'] = 100"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 80,
|
|
"id": "fa161876-b8fc-44ce-a60d-f6779542a5b0",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"with open(\"mydata.json\", \"rt\") as INFILE:\n",
|
|
" data=json.load(INFILE)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 81,
|
|
"id": "ad30dde5-07f5-4a3d-b5eb-d2b7ed8f9ed5",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def access_api():\n",
|
|
" \"\"\" Accesses the PHE API. Returns raw data in the same format as data loaded from the \"canned\" JSON file. \"\"\"\n",
|
|
" filters = [\n",
|
|
" 'areaType=nation',\n",
|
|
" 'areaName=England'\n",
|
|
" ]\n",
|
|
" deaths = {\n",
|
|
" \"date\":\"date\",\n",
|
|
" \"newDailyNsoDeathsByDeathDate\":\"newDailyNsoDeathsByDeathDate\",\n",
|
|
" \"newDeaths28DaysByDeathDateChange\":\"newDeaths28DaysByDeathDateChange\",\n",
|
|
" \"cumPeopleVaccinatedCompleteByVaccinationDate\":\"cumPeopleVaccinatedCompleteByVaccinationDate\"\n",
|
|
" }\n",
|
|
" api = Cov19API(filters=filters, structure=deaths)\n",
|
|
" mydata=api.get_json()\n",
|
|
" # print(mydata)\n",
|
|
" #data.to_pickle(\"mydata.pkl\")\n",
|
|
" #convert(mydata)\n",
|
|
" return mydata # return data read from the API\n",
|
|
"\n",
|
|
"def convert(whatever):\n",
|
|
" with open(\"mydata.json\", \"wt\") as OUTF:\n",
|
|
" json.dump(whatever, OUTF)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 82,
|
|
"id": "611ee042-1d4d-485d-ae8c-d81438a3219d",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def wrangle_data(rawdata):\n",
|
|
" datalist=data['data']\n",
|
|
"\n",
|
|
" dates=[dictionary['date'] for dictionary in datalist ]\n",
|
|
" dates.sort()\n",
|
|
"\n",
|
|
" def parse_date(datestring):\n",
|
|
" \"\"\" Convert a date string into a pandas datetime object \"\"\"\n",
|
|
" return pd.to_datetime(datestring, format=\"%Y-%m-%d\")\n",
|
|
"\n",
|
|
"\n",
|
|
" startdate=parse_date(dates[0])\n",
|
|
" enddate=parse_date(dates[-1])\n",
|
|
"\n",
|
|
"\n",
|
|
" index=pd.date_range(startdate, enddate, freq='D')\n",
|
|
" patdata=pd.DataFrame(index=index, columns=['newDailyNsoDeathsByDeathDate', 'newDeaths28DaysByDeathDateChange', 'cumPeopleVaccinatedCompleteByVaccinationDate'])\n",
|
|
"\n",
|
|
" for entry in datalist: # each entry is a dictionary with date, cases, hospital and deaths\n",
|
|
" date=parse_date(entry['date'])\n",
|
|
" for column in ['newDailyNsoDeathsByDeathDate', 'newDeaths28DaysByDeathDateChange', 'cumPeopleVaccinatedCompleteByVaccinationDate']:\n",
|
|
" # check that nothing is there yet - just in case some dates are duplicated,\n",
|
|
" # maybe with data for different columns in each entry\n",
|
|
" if pd.isna(patdata.loc[date, column]): \n",
|
|
" # replace None with 0 in our data \n",
|
|
" value= float(entry[column]) if entry[column]!=None else 0.0\n",
|
|
" # this is the way you access a specific location in the dataframe - use .loc\n",
|
|
" # and put index,column in a single set of [ ]\n",
|
|
" patdata.loc[date, column]=value\n",
|
|
" \n",
|
|
" patdata.fillna(0.0, inplace=True)\n",
|
|
" return patdata\n",
|
|
"\n",
|
|
"\n",
|
|
"# putting the wrangling code into a function allows you to call it again after refreshing the data through \n",
|
|
"# the API. You should call the function directly on the JSON data when the dashboard starts, by including \n",
|
|
"# the call in the cell as below:\n",
|
|
"patdata=wrangle_data(data) # df is the dataframe for plotting"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 83,
|
|
"id": "2302aa9e-cf6a-4c9b-86a3-527caa1af49b",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/vnd.jupyter.widget-view+json": {
|
|
"model_id": "996d7680ae8042c8a62aa30b9dc8ceff",
|
|
"version_major": 2,
|
|
"version_minor": 0
|
|
},
|
|
"text/plain": [
|
|
"Button(button_style='info', description='Refresh data', icon='download', style=ButtonStyle(), tooltip='Keep ca…"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"# Printout from this function will be lost in Voila unless captured in an\n",
|
|
"# output widget - therefore, we give feedback to the user by changing the \n",
|
|
"# appearance of the button\n",
|
|
"def api_button_callback(button):\n",
|
|
" \"\"\" Button callback - it must take the button as its parameter (unused in this case).\n",
|
|
" Accesses API, wrangles data, updates global variable df used for plotting. \"\"\"\n",
|
|
" apidata=access_api()\n",
|
|
" global df\n",
|
|
" df=wrangle_data(apidata)\n",
|
|
" refresh_graph()\n",
|
|
" apibutton.icon=\"check\"\n",
|
|
"\n",
|
|
" \n",
|
|
"apibutton=wdg.Button(\n",
|
|
" description='Refresh data',\n",
|
|
" disabled=False,\n",
|
|
" button_style='info',\n",
|
|
" tooltip=\"Keep calm and carry on\",\n",
|
|
" icon='download'\n",
|
|
")\n",
|
|
"\n",
|
|
"# remember to register your button callback function with the button\n",
|
|
"apibutton.on_click(api_button_callback) # the name of your function inside these brackets\n",
|
|
"\n",
|
|
"display(apibutton)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 84,
|
|
"id": "27f4771b-c987-4c45-971d-d746fa2cc69f",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/vnd.jupyter.widget-view+json": {
|
|
"model_id": "583376c6524247659ff90a98cee173ca",
|
|
"version_major": 2,
|
|
"version_minor": 0
|
|
},
|
|
"text/plain": [
|
|
"SelectMultiple(description='Test stats:', index=(2,), layout=Layout(width='max-content'), options=('newDailyNs…"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"application/vnd.jupyter.widget-view+json": {
|
|
"model_id": "4a3bf98280f641a0b4641b9dbe896551",
|
|
"version_major": 2,
|
|
"version_minor": 0
|
|
},
|
|
"text/plain": [
|
|
"Output()"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"def plot(data):\n",
|
|
" patdata=data\n",
|
|
" patdata.plot( logy=True)\n",
|
|
" patdata.plot( logx=True, logy=True, kind='scatter', y=['newDailyNsoDeathsByDeathDate'], x=['cumPeopleVaccinatedCompleteByVaccinationDate'])\n",
|
|
"\n",
|
|
" agecols=wdg.SelectMultiple(\n",
|
|
" options=['newDailyNsoDeathsByDeathDate', 'newDeaths28DaysByDeathDateChange', 'cumPeopleVaccinatedCompleteByVaccinationDate'], # options available\n",
|
|
" value=['cumPeopleVaccinatedCompleteByVaccinationDate'], # initial value\n",
|
|
" rows=3, # rows of the selection box\n",
|
|
" description='Test stats:',\n",
|
|
" disabled=False,\n",
|
|
" layout={'width': 'max-content'}\n",
|
|
")\n",
|
|
"\n",
|
|
"def patdata_graph(graphcolumns):\n",
|
|
" # our callback function.\n",
|
|
" ncols=len(graphcolumns)\n",
|
|
" if ncols>0:\n",
|
|
" patdata.plot(logy=True, y=list(graphcolumns)) # graphcolumns is a tuple - we need a list\n",
|
|
" plt.show() # important - graphs won't update properly if this is missing\n",
|
|
" else:\n",
|
|
" # if the user has not selected any column, print a message instead\n",
|
|
" print(\"Click to select data for graph\")\n",
|
|
" print(\"(CTRL-Click to select more than one category)\")\n",
|
|
" \n",
|
|
"# keep calling age_graph(graphcolumns=value_of_agecols); capture output in widget output \n",
|
|
"output=wdg.interactive_output(patdata_graph, {'graphcolumns': agecols})\n",
|
|
"\n",
|
|
"def refresh_graph():\n",
|
|
" \"\"\" We change the value of the widget in order to force a redraw of the graph;\n",
|
|
" this is useful when the data have been updated. This is a bit of a gimmick; it\n",
|
|
" needs to be customised for one of your widgets. \"\"\"\n",
|
|
" current=agecols.value\n",
|
|
" if current==agecols.options[2]:\n",
|
|
" other=agecols.options[1]\n",
|
|
" elif current==agecols.options[1]:\n",
|
|
" other=agecols.options[0]\n",
|
|
" else:\n",
|
|
" other=agecols.options[2]\n",
|
|
" agecols.value=other # forces the redraw\n",
|
|
" agecols.value=current # now we can change it back\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"graph=wdg.interactive_output(plot, {'daata': agecols})\n",
|
|
"\n",
|
|
"display(agecols, output)\n",
|
|
"\n",
|
|
"patdata.to_pickle(\"patdata.pkl\")"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3 (ipykernel)",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.10.6"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|