diff --git a/Dockerfile b/Dockerfile index 2f3cb9d7a9f17d998496e5ac5d23511e2c208ed8..eb711f8747382e1fcb6f57f9239f4ee0d5d696c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,12 @@ RUN conda env update -q -f /tmp/environment.yml && \ conda clean -y --all && \ conda env export -n "root" && \ jupyter lab build + +RUN /opt/conda/bin/pip install ipywidgets voila + +RUN jupyter labextension install @jupyter-voila/jupyterlab-preview && \ + jupyter labextension install @jupyter-widgets/jupyterlab-manager + USER ${NB_USER} # install the R dependencies diff --git a/README.md b/README.md index 084e10a6e78372d80ec6fc8b76d79faff0fee151..7dd999bfff930471bb3ea92cbe0e3dd5d5691134 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,8 @@ The environment image allows you to work in Python or R in JupyterLab or RStudio <td><a href="https://github.com/pcm-dpc/COVID-19">Covid-19 data for Italy</a></td> <td><a href="https://renkulab.io/projects/covid-19/covid-19-public-data/datasets/286c58b1-dbbc-4caa-a23a-fcb001d5ac51/">covid-19-italy</a></td> <td><code>data/covid-19-italy</code></td> -<td>N/A</td> +<td><a href="https://renkulab.io/projects/covid-19/covid-19-public-data/files/blob/notebooks/examples/italy-examples/italy-notebook-example.ipynb">notebook</a>, + <a href="https://renkulab.io/projects/covid-19/covid-19-public-data/files/blob/notebooks/examples/italy-examples/italy-dashboard-example.ipynb">dashboard</a></td> </tr> <tr> <td><a href="https://github.com/echen102/COVID-19-TweetIDs">Covid-19 tweet IDs</a></td> diff --git a/notebooks/examples/italy-examples/italy-dashboard-example.ipynb b/notebooks/examples/italy-examples/italy-dashboard-example.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..4dfaa3dff9d3cbfb84bfef4f38d689b2551b2111 --- /dev/null +++ b/notebooks/examples/italy-examples/italy-dashboard-example.ipynb @@ -0,0 +1,174 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "from italy_utils import * " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data_folder = \"../../../data/covid-19-italy/\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df_provinces = prepare_dataframe(\n", + " data_folder, \n", + " \"dpc-covid19-ita-province.csv\", \n", + " \"dati-province-description.json\",\n", + " use_time_index=True\n", + ")\n", + "\n", + "province_dict = get_province_structure(df_provinces)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_growth_factor_series(province, df, N_min=1000):\n", + " ts = df.loc[\n", + " (df['province'] == province) & \\\n", + " (df['total_cases'] >= N_min)\n", + " ] \\\n", + " ['total_cases'] \\\n", + " .rolling('3d') \\\n", + " .mean() \\\n", + " .pct_change() \\\n", + " .add(1.0)\n", + " return ts.iloc[1:]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_growth_factors(provinces, df, N_min=1000):\n", + " if len(provinces) == 0:\n", + " return \n", + " plt.figure(figsize=(9, 6))\n", + " datemin = datemax = df.index[-1]\n", + " for province in provinces:\n", + " data = get_growth_factor_series(province, df, N_min=N_min)\n", + " if len(data) >= 1:\n", + " data.plot(label=province)\n", + " datemin = min(data.index[0], datemin)\n", + "\n", + " ax = plt.gca()\n", + " plt.legend(loc='upper left', bbox_to_anchor=(1.05, 1.0), frameon=False)\n", + " plt.plot([datemin, datemax], [1,1], color='gray', alpha=0.2)\n", + " plt.ylim(0.95, ax.get_ylim()[1])\n", + " plt.title('Daily growth rate of total cases per province')\n", + " plt.xlabel('');\n", + " \n", + "def plot_total_cases(provinces, df, N_min=500):\n", + " if len(provinces) == 0:\n", + " return \n", + " plt.figure(figsize=(9, 6))\n", + " for province in provinces:\n", + " data = df.loc[\n", + " (df['province'] == province) & \\\n", + " (df['total_cases'] >= N_min)\n", + " ] \\\n", + " ['total_cases'] \\\n", + " .rolling('1d') \\\n", + " .mean() \\\n", + " .add(1.0)\n", + " if len(data) >= 1:\n", + " data.plot(label=province, logy=True)\n", + "\n", + " ax = plt.gca()\n", + " plt.legend(loc='upper left', bbox_to_anchor=(1.05, 1.0), frameon=False)\n", + " plt.title('Total cases per province')\n", + " plt.xlabel('');\n", + " \n", + "def make_plots(provinces, df):\n", + " plot_growth_factors(provinces, df)\n", + " plot_total_cases(provinces, df)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_province_selector(region):\n", + " return widgets.SelectMultiple(\n", + " options=province_dict[region],\n", + " value=[],\n", + " description='Provinces:',\n", + " disabled=region_selector.value is None\n", + " )\n", + "def get_interactive_widgets(region):\n", + " widgets.interact(lambda prov: make_plots(list(prov), df_provinces), prov=get_province_selector(region_selector.value));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "region_selector = widgets.Dropdown(\n", + " options=[key for key in province_dict],\n", + " value='Lombardia',\n", + " description='Region:',\n", + " disabled=False,\n", + ")\n", + "widgets.interact(lambda reg: get_interactive_widgets(reg), reg=region_selector);" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/notebooks/examples/italy-examples/italy-notebook-example.ipynb b/notebooks/examples/italy-examples/italy-notebook-example.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..95d52a1a79db4dc8c0d92e227900c1e1636bbf81 --- /dev/null +++ b/notebooks/examples/italy-examples/italy-notebook-example.ipynb @@ -0,0 +1,571 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The autoreload extension is already loaded. To reload it, use:\n", + " %reload_ext autoreload\n" + ] + } + ], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "from italy_utils import * " + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "data_folder = \"../../../data/covid-19-italy/\"" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>country_code</th>\n", + " <th>hospitalised_with_symptoms</th>\n", + " <th>intensive_care</th>\n", + " <th>total_hospitalised</th>\n", + " <th>home_confinement</th>\n", + " <th>total_current_positive</th>\n", + " <th>new_current_positive</th>\n", + " <th>recovered</th>\n", + " <th>dead</th>\n", + " <th>total_cases</th>\n", + " <th>tests</th>\n", + " </tr>\n", + " <tr>\n", + " <th>date</th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>2020-03-18</th>\n", + " <td>ITA</td>\n", + " <td>14363</td>\n", + " <td>2257</td>\n", + " <td>16620</td>\n", + " <td>12090</td>\n", + " <td>28710</td>\n", + " <td>2648</td>\n", + " <td>4025</td>\n", + " <td>2978</td>\n", + " <td>35713</td>\n", + " <td>165541</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-03-19</th>\n", + " <td>ITA</td>\n", + " <td>15757</td>\n", + " <td>2498</td>\n", + " <td>18255</td>\n", + " <td>14935</td>\n", + " <td>33190</td>\n", + " <td>4480</td>\n", + " <td>4440</td>\n", + " <td>3405</td>\n", + " <td>41035</td>\n", + " <td>182777</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-03-20</th>\n", + " <td>ITA</td>\n", + " <td>16020</td>\n", + " <td>2655</td>\n", + " <td>18675</td>\n", + " <td>19185</td>\n", + " <td>37860</td>\n", + " <td>4670</td>\n", + " <td>5129</td>\n", + " <td>4032</td>\n", + " <td>47021</td>\n", + " <td>206886</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-03-21</th>\n", + " <td>ITA</td>\n", + " <td>17708</td>\n", + " <td>2857</td>\n", + " <td>20565</td>\n", + " <td>22116</td>\n", + " <td>42681</td>\n", + " <td>4821</td>\n", + " <td>6072</td>\n", + " <td>4825</td>\n", + " <td>53578</td>\n", + " <td>233222</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-03-22</th>\n", + " <td>ITA</td>\n", + " <td>19846</td>\n", + " <td>3009</td>\n", + " <td>22855</td>\n", + " <td>23783</td>\n", + " <td>46638</td>\n", + " <td>3957</td>\n", + " <td>7024</td>\n", + " <td>5476</td>\n", + " <td>59138</td>\n", + " <td>258402</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " country_code hospitalised_with_symptoms intensive_care \\\n", + "date \n", + "2020-03-18 ITA 14363 2257 \n", + "2020-03-19 ITA 15757 2498 \n", + "2020-03-20 ITA 16020 2655 \n", + "2020-03-21 ITA 17708 2857 \n", + "2020-03-22 ITA 19846 3009 \n", + "\n", + " total_hospitalised home_confinement total_current_positive \\\n", + "date \n", + "2020-03-18 16620 12090 28710 \n", + "2020-03-19 18255 14935 33190 \n", + "2020-03-20 18675 19185 37860 \n", + "2020-03-21 20565 22116 42681 \n", + "2020-03-22 22855 23783 46638 \n", + "\n", + " new_current_positive recovered dead total_cases tests \n", + "date \n", + "2020-03-18 2648 4025 2978 35713 165541 \n", + "2020-03-19 4480 4440 3405 41035 182777 \n", + "2020-03-20 4670 5129 4032 47021 206886 \n", + "2020-03-21 4821 6072 4825 53578 233222 \n", + "2020-03-22 3957 7024 5476 59138 258402 " + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_national_trend = prepare_dataframe(\n", + " data_folder, \n", + " \"dpc-covid19-ita-andamento-nazionale.csv\", \n", + " \"dati-andamento-nazionale-description.json\",\n", + " use_time_index=True\n", + ")\n", + "df_national_trend.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "df_national_trend[\"New cases per day\"] = df_national_trend[\"total_cases\"].diff().rolling('3d').mean()\n", + "df_national_trend.plot(y=\"New cases per day\", kind=\"bar\", logy=True, legend=False);\n", + "plt.title('New cases per day in Italy, 3 day backwards mean');" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>country_code</th>\n", + " <th>region_code</th>\n", + " <th>region</th>\n", + " <th>province_code</th>\n", + " <th>province</th>\n", + " <th>province_short</th>\n", + " <th>latitude</th>\n", + " <th>longitude</th>\n", + " <th>total_cases</th>\n", + " </tr>\n", + " <tr>\n", + " <th>date</th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>2020-02-24</th>\n", + " <td>ITA</td>\n", + " <td>13</td>\n", + " <td>Abruzzo</td>\n", + " <td>69</td>\n", + " <td>Chieti</td>\n", + " <td>CH</td>\n", + " <td>42.351032</td>\n", + " <td>14.167546</td>\n", + " <td>0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-02-24</th>\n", + " <td>ITA</td>\n", + " <td>13</td>\n", + " <td>Abruzzo</td>\n", + " <td>66</td>\n", + " <td>L'Aquila</td>\n", + " <td>AQ</td>\n", + " <td>42.351222</td>\n", + " <td>13.398438</td>\n", + " <td>0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-02-24</th>\n", + " <td>ITA</td>\n", + " <td>13</td>\n", + " <td>Abruzzo</td>\n", + " <td>68</td>\n", + " <td>Pescara</td>\n", + " <td>PE</td>\n", + " <td>42.464584</td>\n", + " <td>14.213648</td>\n", + " <td>0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-02-24</th>\n", + " <td>ITA</td>\n", + " <td>13</td>\n", + " <td>Abruzzo</td>\n", + " <td>67</td>\n", + " <td>Teramo</td>\n", + " <td>TE</td>\n", + " <td>42.658918</td>\n", + " <td>13.704400</td>\n", + " <td>0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-02-24</th>\n", + " <td>ITA</td>\n", + " <td>13</td>\n", + " <td>Abruzzo</td>\n", + " <td>979</td>\n", + " <td>In fase di definizione/aggiornamento</td>\n", + " <td>NaN</td>\n", + " <td>0.000000</td>\n", + " <td>0.000000</td>\n", + " <td>0</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " country_code region_code region province_code \\\n", + "date \n", + "2020-02-24 ITA 13 Abruzzo 69 \n", + "2020-02-24 ITA 13 Abruzzo 66 \n", + "2020-02-24 ITA 13 Abruzzo 68 \n", + "2020-02-24 ITA 13 Abruzzo 67 \n", + "2020-02-24 ITA 13 Abruzzo 979 \n", + "\n", + " province province_short latitude \\\n", + "date \n", + "2020-02-24 Chieti CH 42.351032 \n", + "2020-02-24 L'Aquila AQ 42.351222 \n", + "2020-02-24 Pescara PE 42.464584 \n", + "2020-02-24 Teramo TE 42.658918 \n", + "2020-02-24 In fase di definizione/aggiornamento NaN 0.000000 \n", + "\n", + " longitude total_cases \n", + "date \n", + "2020-02-24 14.167546 0 \n", + "2020-02-24 13.398438 0 \n", + "2020-02-24 14.213648 0 \n", + "2020-02-24 13.704400 0 \n", + "2020-02-24 0.000000 0 " + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_provinces = prepare_dataframe(\n", + " data_folder, \n", + " \"dpc-covid19-ita-province.csv\", \n", + " \"dati-province-description.json\",\n", + " use_time_index=True\n", + ")\n", + "\n", + "df_provinces.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>country_code</th>\n", + " <th>region_code</th>\n", + " <th>region</th>\n", + " <th>province_code</th>\n", + " <th>province</th>\n", + " <th>province_short</th>\n", + " <th>latitude</th>\n", + " <th>longitude</th>\n", + " <th>total_cases</th>\n", + " </tr>\n", + " <tr>\n", + " <th>date</th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " <th></th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>2020-02-24</th>\n", + " <td>ITA</td>\n", + " <td>13</td>\n", + " <td>Abruzzo</td>\n", + " <td>69</td>\n", + " <td>Chieti</td>\n", + " <td>CH</td>\n", + " <td>42.351032</td>\n", + " <td>14.167546</td>\n", + " <td>0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-02-24</th>\n", + " <td>ITA</td>\n", + " <td>13</td>\n", + " <td>Abruzzo</td>\n", + " <td>66</td>\n", + " <td>L'Aquila</td>\n", + " <td>AQ</td>\n", + " <td>42.351222</td>\n", + " <td>13.398438</td>\n", + " <td>0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-02-24</th>\n", + " <td>ITA</td>\n", + " <td>13</td>\n", + " <td>Abruzzo</td>\n", + " <td>68</td>\n", + " <td>Pescara</td>\n", + " <td>PE</td>\n", + " <td>42.464584</td>\n", + " <td>14.213648</td>\n", + " <td>0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-02-24</th>\n", + " <td>ITA</td>\n", + " <td>13</td>\n", + " <td>Abruzzo</td>\n", + " <td>67</td>\n", + " <td>Teramo</td>\n", + " <td>TE</td>\n", + " <td>42.658918</td>\n", + " <td>13.704400</td>\n", + " <td>0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2020-02-24</th>\n", + " <td>ITA</td>\n", + " <td>13</td>\n", + " <td>Abruzzo</td>\n", + " <td>979</td>\n", + " <td>In fase di definizione/aggiornamento</td>\n", + " <td>NaN</td>\n", + " <td>0.000000</td>\n", + " <td>0.000000</td>\n", + " <td>0</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " country_code region_code region province_code \\\n", + "date \n", + "2020-02-24 ITA 13 Abruzzo 69 \n", + "2020-02-24 ITA 13 Abruzzo 66 \n", + "2020-02-24 ITA 13 Abruzzo 68 \n", + "2020-02-24 ITA 13 Abruzzo 67 \n", + "2020-02-24 ITA 13 Abruzzo 979 \n", + "\n", + " province province_short latitude \\\n", + "date \n", + "2020-02-24 Chieti CH 42.351032 \n", + "2020-02-24 L'Aquila AQ 42.351222 \n", + "2020-02-24 Pescara PE 42.464584 \n", + "2020-02-24 Teramo TE 42.658918 \n", + "2020-02-24 In fase di definizione/aggiornamento NaN 0.000000 \n", + "\n", + " longitude total_cases \n", + "date \n", + "2020-02-24 14.167546 0 \n", + "2020-02-24 13.398438 0 \n", + "2020-02-24 14.213648 0 \n", + "2020-02-24 13.704400 0 \n", + "2020-02-24 0.000000 0 " + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_regions = prepare_dataframe(\n", + " data_folder, \n", + " \"dpc-covid19-ita-regioni.csv\", \n", + " \"dati-regioni-description.json\",\n", + " use_time_index=True\n", + ")\n", + "\n", + "df_provinces.head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/notebooks/examples/italy-examples/italy_utils.py b/notebooks/examples/italy-examples/italy_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..0cf0b7272bdcf39f1f39b168e17d44bcaaaea60b --- /dev/null +++ b/notebooks/examples/italy-examples/italy_utils.py @@ -0,0 +1,46 @@ +import json +import os +import pandas as pd + + +def translate_columns(data_folder, df, description_filename): + description_file_path = os.path.join(data_folder, description_filename) + + with open(description_file_path, 'r') as description_file: + decoded_data = description_file.read().encode().decode('utf-8-sig') + descriptions = json.loads(decoded_data) + descriptions = { column_dict['Nome campo']: column_dict for column_dict in descriptions} + + df.rename(columns=lambda col: descriptions[col]['Field name'], inplace=True) + return df + +def set_time_index(df, drop_hour=True): + if drop_hour: + lambda_func = lambda x: x.split(' ')[0] + else: + labda_func = lambda x: x + + timestamp = pd.DatetimeIndex(df['date'].apply(lambda_func)) + df.set_index(timestamp, inplace=True) + del df['date'] + return df + +def prepare_dataframe(data_folder, df_filename, description_filename, use_time_index=False): + data_file_path = os.path.join(data_folder, df_filename) + df = pd.read_csv(data_file_path) + df = translate_columns(data_folder, df, description_filename) + if use_time_index: + df = set_time_index(df) + return df + +def get_province_structure(df_provinces): + """Extract the province/region structure from the province dataframe.""" + + def get_province_list(region): + """Get list of provinces for a given region.""" + provinces = set(df_provinces.loc[df_provinces['region']==region]['province']) + provinces.discard('In fase di definizione/aggiornamento') + return list(provinces) + + regions = df_provinces['region'].unique() + return {region: get_province_list(region) for region in regions} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index d05293f9bb3bbe4cccce6fece1282633e991d91a..2bc49958b6cd110a8a5284d2e4f6826a7e404c13 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,11 @@ +aiohttp==3.6.2 alembic==1.4.0 altair==4.0.1 ansiwrap==0.8.4 +appnope==0.1.0 argcomplete==1.11.1 async-generator==1.10 +async-timeout==3.0.1 attrs==19.3.0 backcall==0.1.0 bleach==3.1.1 @@ -15,6 +18,7 @@ Click==7.0 colorama==0.4.3 -e src/covid-19/covid_19_dashboard cryptography==2.8 +cycler==0.10.0 decorator==4.4.1 defusedxml==0.6.0 distro==1.4.0 @@ -34,14 +38,18 @@ jsonschema==3.2.0 jupyter-client==6.0.0 jupyter-core==4.6.3 jupyter-rsession-proxy==1.1 +jupyter-server-proxy==1.2.0 jupyter-telemetry==0.0.5 jupyterhub==0.9.6 jupyterlab==1.2.5 jupyterlab-git==0.9.0 jupyterlab-server==1.0.6 +kiwisolver==1.1.0 Mako==1.1.0 MarkupSafe==1.1.1 +matplotlib==3.2.1 mistune==0.8.4 +multidict==4.7.5 nbconvert==5.6.1 nbdime==1.1.0 nbformat==5.0.4 @@ -66,6 +74,7 @@ pycurl==7.43.0.5 Pygments==2.5.2 PyJWT==1.7.1 pyOpenSSL==19.1.0 +pyparsing==2.4.6 pyrsistent==0.15.7 PySocks==1.7.1 python-dateutil==2.8.1 @@ -76,9 +85,10 @@ pytz==2019.3 PyYAML==5.3 pyzmq==19.0.0 requests==2.23.0 -ruamel-yaml==0.15.80 +ruamel.yaml==0.15.80 ruamel.yaml.clib==0.2.0 Send2Trash==1.5.0 +simpervisor==0.3 six==1.14.0 smmap==3.0.1 SQLAlchemy==1.3.13 @@ -95,4 +105,5 @@ userpath==1.3.0 vega-datasets==0.8.0 wcwidth==0.1.8 webencodings==0.5.1 +yarl==1.4.2 zipp==3.0.0