diff --git a/bstim-covid19-dash.ipynb b/bstim-covid19-dash.ipynb index a84ced66141ba9c66a1b9c1c62205bb516642b00..5efdea48c8d5b16c796908f3dd26e7c36839f2fe 100644 --- a/bstim-covid19-dash.ipynb +++ b/bstim-covid19-dash.ipynb @@ -35,7 +35,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 168, "metadata": {}, "outputs": [], "source": [ @@ -47,6 +47,7 @@ "import dash_player\n", "from flask_caching import Cache\n", "\n", + "import os\n", "import pandas as pd" ] }, @@ -62,7 +63,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -87,7 +88,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 349, "metadata": {}, "outputs": [], "source": [ @@ -125,25 +126,29 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 350, "metadata": {}, "outputs": [], "source": [ "from datetime import datetime as dt, timedelta\n", "\n", - "#asset_url=app.get_asset_url('assets') # = $JUPYTERHUB_SERVICE_PREFIX/assets/\n", + "asset_url=app.get_asset_url('assets') # = $JUPYTERHUB_SERVICE_PREFIX/assets/\n", "# example: \"https://jupyter-jsc.fz-juelich.de/user/<user>@fz-juelich.de/<machine>/proxy/8050/assets/\"\n", - "asset_url=\"https://jupyter-jsc.fz-juelich.de/user/j.goebbert@fz-juelich.de/jureca_login/proxy/8050/assets/\"\n", + "#asset_url=\"https://jupyter-jsc.fz-juelich.de/user/j.goebbert@fz-juelich.de/jureca_login/proxy/8050/assets/\"\n", "\n", "metadata = pd.read_csv(\"assets/metadata.csv\")\n", "#for index, row in metadata.iterrows():\n", "# print(row['countyId'])\n", "\n", + "deltadays = 25\n", + "\n", "min_date=dt(2020, 1, 29).date()\n", - "max_date=dt(2020, 6, 16).date() # dt.today().date()\n", + "max_date=dt(2020, 10, 1).date() # dt.today().date()\n", + "\n", "init_date=dt(2020, 6, 16).date() # dt.today().date()\n", + "init_assets_dir = (init_date -timedelta(days=deltadays)).strftime('%Y_%m_%d') + \"/\"\n", + "\n", "init_countyid=11001\n", - "deltadays = 25\n", "\n", "def get_assets_dir(date):\n", " date = dt.strptime(date.split(' ')[0], '%Y-%m-%d')\n", @@ -160,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 351, "metadata": {}, "outputs": [], "source": [ @@ -194,32 +199,39 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 352, "metadata": {}, "outputs": [], "source": [ "## load dynamic data\n", "# => dataframe 'inf'\n", "import plotly.graph_objects as go\n", + "from numpy import nan\n", "\n", "def create_map(mapcsv_path):\n", "\n", - " # read number of infections from csv file\n", - " mapcsv = pd.read_csv(mapcsv_path)\n", - " #print(mapcsv.loc[mapcsv['countyID'] == 3159]) # test with Freiburg\n", - "\n", - " # create (correctly sorted) dataframe from no.infections\n", " infArr=[]\n", - " # loop over all counties and read no.infections from mapcsv\n", - " for feat in counties_geojson['features']: # same loop as for df (important)\n", - " cca_str = feat['properties'].get('RS')\n", - " if cca_str is not None:\n", - " # read value for this county from mapcsv\n", - " cca_valuedf = mapcsv.loc[mapcsv['countyID']==int(cca_str), 'newInf100k']\n", - " cca_value = next(iter(cca_valuedf), 0.0)\n", - " infArr.append(cca_value)\n", - " else:\n", - " infArr.append(0.0)\n", + " try:\n", + " # read number of infections from csv file \n", + " mapcsv = pd.read_csv(mapcsv_path)\n", + " #print(mapcsv.loc[mapcsv['countyID'] == 3159]) # test with Freiburg\n", + "\n", + " # create (correctly sorted) dataframe from no.infections\n", + " # loop over all counties and read no.infections from mapcsv\n", + " for feat in counties_geojson['features']: # same loop as for df (important)\n", + " cca_str = feat['properties'].get('RS')\n", + " if cca_str is not None:\n", + " # read value for this county from mapcsv\n", + " cca_valuedf = mapcsv.loc[mapcsv['countyID']==int(cca_str), 'newInf100k']\n", + " cca_value = next(iter(cca_valuedf), 0.0)\n", + " infArr.append(cca_value)\n", + " else:\n", + " infArr.append(0.0)\n", + " except: #IOError as e:\n", + " print(\"File not found: \" + mapcsv_path)\n", + " for feat in counties_geojson['features']:\n", + " infArr.append(nan)\n", + "\n", " counties_infdf = pd.DataFrame(data={'infections': infArr})\n", "\n", " # test\n", @@ -233,7 +245,7 @@ " z=counties_infdf.infections,\n", " text=counties_metadf.names,\n", " colorscale=\"Jet\",\n", - " colorbar=dict(thickness=20, ticklen=3, title=\"Neuinfektionen pro 100.000 Einwohner\", titleside=\"right\"),\n", + " colorbar=dict(thickness=20, ticklen=3, title=\"Neuinfektionen pro 100.000 Einwohner und Tag\", titleside=\"right\"),\n", " zmin=0, zmax=10,\n", " marker_opacity=0.5, marker_line_width=0,\n", " hovertemplate=\n", @@ -263,7 +275,7 @@ " margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", " return mapfig\n", "\n", - "#mapfig = create_map(\"assets/figures/2020_06_12/map.csv\")\n", + "init_mapfig = create_map(\"assets/figures/\" + init_assets_dir + \"map.csv\")\n", "#mapfig.show(config={\"displayModeBar\": False, \"showTips\": False, \"modeBarButtonsToRemove\": ['toImage']}) # \"staticPlot\": True})" ] }, @@ -276,7 +288,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 353, "metadata": {}, "outputs": [], "source": [ @@ -356,7 +368,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 354, "metadata": {}, "outputs": [], "source": [ @@ -457,7 +469,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 355, "metadata": {}, "outputs": [], "source": [ @@ -768,7 +780,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 356, "metadata": {}, "outputs": [], "source": [ @@ -814,7 +826,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 357, "metadata": {}, "outputs": [], "source": [ @@ -961,7 +973,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 358, "metadata": {}, "outputs": [], "source": [ @@ -981,7 +993,7 @@ " dcc.Loading(\n", " id = \"left_date_tab1_loading_graph\", \n", " children=[html.Div(children=[\n", - " dcc.Graph(id='left_date_tab1_graph', figure={}),\n", + " dcc.Graph(id='left_date_tab1_graph', figure=init_mapfig),\n", " ])],\n", " type=\"circle\", # 'graph', 'cube', 'circle', 'dot', 'default'\n", " color=\"#343A40\",\n", @@ -1138,10 +1150,6 @@ " type=\"circle\", # 'graph', 'cube', 'circle', 'dot', 'default'\n", " color=\"#343A40\",\n", " ), \n", - " html.Div(\n", - " id=\"left_date_tab3_txt\",\n", - " children=[\"left_date_tab3_txt\"],\n", - " ),\n", " dbc.Tooltip(\n", " \"Die Infektionszahlen (Nowcast) pro Tag pro Landkreis und gewähltem Zeitfenster. \"\n", " \"Der angezeigte Wert entspricht dem Nowcast, also der Schätzung der Anzahl der tatsächlich Neuinfizierten. \"\n", @@ -1151,6 +1159,10 @@ " style={\"width\": \"200%\"},\n", " placement=\"left\",\n", " ),\n", + " #html.Div(\n", + " # id=\"left_date_tab3_txt\",\n", + " # children=[\"left_date_tab3_txt\"],\n", + " #),\n", " ]),\n", " ]),\n", " ])\n", @@ -1169,7 +1181,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 359, "metadata": {}, "outputs": [], "source": [ @@ -1212,7 +1224,9 @@ "def update_left_date_picker(date):\n", " if date is not None:\n", " return get_assets_dir(date)\n", - " \n", + " else:\n", + " return init_assets_dir\n", + "\n", "# Interactive Map\n", "@app.callback(\n", " Output(component_id='left_date_tab1_graph', component_property='figure'),\n", @@ -1221,8 +1235,10 @@ "def update_left_date_tab1_map(date):\n", " if date is not None:\n", " assets_dir = get_assets_dir(date)\n", - " mapfig = create_map(\"assets/figures/\" + assets_dir + \"/map.csv\") \n", - " return mapfig\n", + " mapfig = create_map(\"assets/figures/\" + assets_dir + \"/map.csv\")\n", + " else:\n", + " mapfig = create_map(\"assets/placeholders/map_empty.csv\")\n", + " return mapfig\n", "\n", "# Interaction Kernel\n", "@app.callback(\n", @@ -1233,8 +1249,11 @@ "def update_left_date_tab2_img(date):\n", " if date is not None:\n", " assets_dir = get_assets_dir(date)\n", - " imgUrl = asset_url + \"figures/\" + assets_dir + \"/interaction_kernel.png\"\n", - " return imgUrl, imgUrl\n", + " imgUrl = \"figures/\" + assets_dir + \"interaction_kernel.png\"\n", + " if imgUrl and not os.path.isfile(\"assets/\" + imgUrl): \n", + " imgUrl = \"placeholders/plot_not_found.png\"\n", + " imgUrl = asset_url + imgUrl\n", + " return imgUrl, imgUrl\n", "\n", "# Static Kernel\n", "@app.callback(\n", @@ -1244,13 +1263,16 @@ "def update_left_date_tab3_img(date):\n", " if date is not None:\n", " assets_dir = get_assets_dir(date)\n", - " imgUrl = asset_url + \"figures/\" + assets_dir + \"/map.png\"\n", - " return imgUrl" + " imgUrl = \"figures/\" + assets_dir + \"map.png\"\n", + " if imgUrl and not os.path.isfile(\"assets/\" + imgUrl): \n", + " imgUrl = \"placeholders/plot_not_found.png\"\n", + " imgUrl = asset_url + imgUrl\n", + " return imgUrl" ] }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 360, "metadata": {}, "outputs": [], "source": [ @@ -1270,7 +1292,7 @@ " dcc.Loading(\n", " id = \"right_date_tab1_loading_graph\", \n", " children=[html.Div(children=[\n", - " dcc.Graph(id='right_date_tab1_graph', figure={}),\n", + " dcc.Graph(id='right_date_tab1_graph', figure=init_mapfig),\n", " ])],\n", " type=\"circle\", # 'graph', 'cube', 'circle', 'dot', 'default'\n", " color=\"#343A40\",\n", @@ -1436,10 +1458,10 @@ " style={\"width\": \"200%\"},\n", " placement=\"right\",\n", " ),\n", - " html.Div(\n", - " id=\"right_date_tab3_txt\",\n", - " children=[\"right_date_tab3_txt\"],\n", - " ),\n", + " #html.Div(\n", + " # id=\"right_date_tab3_txt\",\n", + " # children=[\"right_date_tab3_txt\"],\n", + " #),\n", " ]),\n", " ]),\n", " ])\n", @@ -1458,7 +1480,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 361, "metadata": {}, "outputs": [], "source": [ @@ -1501,7 +1523,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 362, "metadata": {}, "outputs": [], "source": [ @@ -1544,6 +1566,8 @@ "def update_right_date_picker(date):\n", " if date is not None:\n", " return get_assets_dir(date)\n", + " else:\n", + " return init_assets_dir\n", "\n", "# Interactive Map\n", "@app.callback(\n", @@ -1554,7 +1578,9 @@ " if date is not None:\n", " assets_dir = get_assets_dir(date)\n", " mapfig = create_map(\"assets/figures/\" + assets_dir + \"/map.csv\") \n", - " return mapfig\n", + " else:\n", + " mapfig = create_map(\"assets/placeholders/map_empty.csv\")\n", + " return mapfig\n", " \n", "# Interaction Kernel\n", "@app.callback(\n", @@ -1565,9 +1591,12 @@ "def update_right_date_tab2_img(date):\n", " if date is not None:\n", " assets_dir = get_assets_dir(date)\n", - " imgUrl = asset_url + \"figures/\" + assets_dir + \"/interaction_kernel.png\"\n", - " return imgUrl, imgUrl\n", - " \n", + " imgUrl = \"figures/\" + assets_dir + \"interaction_kernel.png\"\n", + " if imgUrl and not os.path.isfile(\"assets/\" + imgUrl): \n", + " imgUrl = \"placeholders/plot_not_found.png\"\n", + " imgUrl = asset_url + imgUrl\n", + " return imgUrl, imgUrl\n", + "\n", "# Static Kernel\n", "@app.callback(\n", " Output(component_id='right_date_tab3_img', component_property='src'),\n", @@ -1576,13 +1605,16 @@ "def update_right_date_tab3_img(date):\n", " if date is not None:\n", " assets_dir = get_assets_dir(date)\n", - " imgUrl = asset_url + \"figures/\" + assets_dir + \"/map.png\"\n", - " return imgUrl" + " imgUrl = \"figures/\" + assets_dir + \"map.png\"\n", + " if imgUrl and not os.path.isfile(\"assets/\" + imgUrl): \n", + " imgUrl = \"placeholders/plot_not_found.png\"\n", + " imgUrl = asset_url + imgUrl\n", + " return imgUrl" ] }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 363, "metadata": {}, "outputs": [], "source": [ @@ -1636,8 +1668,10 @@ " color=\"#343A40\",\n", " ), \n", " html.Div(\n", - " id=\"left_pos_tab1_txt\",\n", - " children=[\"left_pos_tab1_txt\"],\n", + " dcc.Markdown(\n", + " id=\"left_pos_tab1_txt\",\n", + " children=[\"\"],\n", + " )\n", " ),\n", " dbc.Tooltip(\n", " \"Analyse und Vorhersage der Infektionszahlen für den ausgewählten Landkreis. \"\n", @@ -1713,8 +1747,10 @@ " color=\"#343A40\",\n", " ), \n", " html.Div(\n", - " id=\"left_pos_tab2_txt\",\n", - " children=[\"left_pos_tab2_txt\"],\n", + " dcc.Markdown(\n", + " id=\"left_pos_tab2_txt\",\n", + " children=[\"\"],\n", + " )\n", " ),\n", " dbc.Tooltip(\n", " \"Analyse und Vorhersage der Infektionszahlen für den ausgewählten Landkreis. \"\n", @@ -1744,7 +1780,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 364, "metadata": {}, "outputs": [], "source": [ @@ -1801,8 +1837,11 @@ "@cache.memoize(timeout=cache_timeout)\n", "def update_left_pos_tab1_img(value, assets_dir):\n", " if value is not None:\n", - " imgUrl = asset_url + \"figures/\" + assets_dir + \"curve_trend_{0:05d}.png\".format(value)\n", - " return imgUrl, imgUrl\n", + " imgUrl = \"figures/\" + assets_dir + \"curve_trend_{0:05d}.png\".format(value)\n", + " if imgUrl and not os.path.isfile(\"assets/\" + imgUrl): \n", + " imgUrl = \"placeholders/plot_not_found.png\"\n", + " imgUrl = asset_url + imgUrl\n", + " return imgUrl, imgUrl\n", "\n", "# ungeglättet\n", "@app.callback(\n", @@ -1813,8 +1852,11 @@ "@cache.memoize(timeout=cache_timeout)\n", "def update_left_pos_tab2_img(value, assets_dir):\n", " if value is not None:\n", - " imgUrl = asset_url + \"figures/\" + assets_dir + \"curve_{0:05d}.png\".format(value)\n", - " return imgUrl, imgUrl\n", + " imgUrl = \"figures/\" + assets_dir + \"curve_{0:05d}.png\".format(value)\n", + " if imgUrl and not os.path.isfile(\"assets/\" + imgUrl): \n", + " imgUrl = \"placeholders/plot_not_found.png\"\n", + " imgUrl = asset_url + imgUrl\n", + " return imgUrl, imgUrl\n", "\n", "# print meta-information\n", "@app.callback(\n", @@ -1825,14 +1867,28 @@ "@cache.memoize(timeout=cache_timeout)\n", "def update_left_pos_txt(value, assets_dir):\n", " if value is not None:\n", - " mdat = pd.read_csv(\"./assets/figures/\" + assets_dir + \"/metadata.csv\")\n", - " msg = mdat.loc[mdat['countyID'] == value]['probText'].to_string(index=False)\n", - " return msg, msg" + " try:\n", + " mdat = pd.read_csv(\"./assets/figures/\" + assets_dir + \"/metadata.csv\")\n", + " msg = mdat.loc[mdat['countyID'] == value]['probText'].to_string(index=False)\n", + " try:\n", + " val = float(msg)\n", + " if val<0.0:\n", + " msg = 'Die Infektioneszahlen werden mit einer Wahrscheinlichkeit von **{:.1f}%** fallen.'.format(val)\n", + " else:\n", + " msg = 'Die Neuinfektionen werden mit einer Wahrscheinlichkeit von **{:.1f}%** steigen.'.format(val)\n", + " except:\n", + " print(\"Exception in update_right_pos_txt\")\n", + " pass\n", + " except:\n", + " msg = \" \"\n", + " else:\n", + " msg = \" \"\n", + " return msg, msg" ] }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 365, "metadata": {}, "outputs": [], "source": [ @@ -1886,8 +1942,10 @@ " color=\"#343A40\",\n", " ), \n", " html.Div(\n", - " id=\"right_pos_tab1_txt\",\n", - " children=[\"right_pos_tab1_txt\"],\n", + " dcc.Markdown(\n", + " id=\"right_pos_tab1_txt\",\n", + " children=[\"\"],\n", + " )\n", " ),\n", " dbc.Tooltip(\n", " \"Analyse und Vorhersage der Infektionszahlen für den ausgewählten Landkreis. \"\n", @@ -1963,8 +2021,10 @@ " color=\"#343A40\",\n", " ), \n", " html.Div(\n", - " id=\"right_pos_tab2_txt\",\n", - " children=[\"right_pos_tab2_txt\"],\n", + " dcc.Markdown(\n", + " id=\"right_pos_tab2_txt\",\n", + " children=[\"\"],\n", + " )\n", " ),\n", " dbc.Tooltip(\n", " \"Analyse und Vorhersage der Infektionszahlen für den ausgewählten Landkreis. \"\n", @@ -1994,7 +2054,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 366, "metadata": {}, "outputs": [], "source": [ @@ -2051,8 +2111,11 @@ "@cache.memoize(timeout=cache_timeout)\n", "def update_right_pos_tab1_img(value, assets_dir):\n", " if value is not None:\n", - " imgUrl = asset_url + \"figures/\" + assets_dir + \"curve_trend_{0:05d}.png\".format(value)\n", - " return imgUrl, imgUrl\n", + " imgUrl = \"figures/\" + assets_dir + \"curve_trend_{0:05d}.png\".format(value)\n", + " if imgUrl and not os.path.isfile(\"assets/\" + imgUrl): \n", + " imgUrl = \"placeholders/plot_not_found.png\"\n", + " imgUrl = asset_url + imgUrl\n", + " return imgUrl, imgUrl\n", " \n", "# ungeglättet\n", "@app.callback(\n", @@ -2063,8 +2126,11 @@ "@cache.memoize(timeout=cache_timeout)\n", "def update_right_pos_tab2_img(value, assets_dir):\n", " if value is not None:\n", - " imgUrl = asset_url + \"figures/\" + assets_dir + \"curve_{0:05d}.png\".format(value)\n", - " return imgUrl, imgUrl\n", + " imgUrl = \"figures/\" + assets_dir + \"curve_{0:05d}.png\".format(value)\n", + " if imgUrl and not os.path.isfile(\"assets/\" + imgUrl): \n", + " imgUrl = \"placeholders/plot_not_found.png\"\n", + " imgUrl = asset_url + imgUrl\n", + " return imgUrl, imgUrl\n", "\n", "# print meta-information\n", "@app.callback(\n", @@ -2075,9 +2141,23 @@ "@cache.memoize(timeout=cache_timeout)\n", "def update_right_pos_txt(value, assets_dir):\n", " if value is not None:\n", - " mdat = pd.read_csv(\"./assets/figures/\" + assets_dir + \"/metadata.csv\")\n", - " msg = mdat.loc[mdat['countyID'] == value]['probText'].to_string(index=False)\n", - " return msg, msg" + " try:\n", + " mdat = pd.read_csv(\"./assets/figures/\" + assets_dir + \"/metadata.csv\")\n", + " msg = mdat.loc[mdat['countyID'] == value]['probText'].to_string(index=False)\n", + " try:\n", + " val = float(msg)\n", + " if val<0.0:\n", + " msg = 'Die Infektioneszahlen werden mit einer Wahrscheinlichkeit von **{:.1f}% fallen**.'.format(val)\n", + " else:\n", + " msg = 'Die Neuinfektionen werden mit einer Wahrscheinlichkeit von **{:.1f}% steigen**.'.format(val)\n", + " except:\n", + " print(\"Exception in update_right_pos_txt\")\n", + " pass\n", + " except:\n", + " msg = \" \"\n", + " else:\n", + " msg = \" \"\n", + " return msg, msg" ] }, { @@ -2092,7 +2172,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 367, "metadata": {}, "outputs": [], "source": [ @@ -2264,7 +2344,7 @@ " # --- Ortsangabe (left) ---\n", " dbc.Card(\n", " style={\n", - " 'margin': '0% 0% 0% 0%', # top, right, bottom, left\n", + " 'margin': '0% 0% 0% 0%', # top, right, bottom, leftleft\n", " 'padding': '0',\n", " }, \n", " children=[\n", @@ -2278,8 +2358,8 @@ " id=\"left_pos-card-tabs\",\n", " active_tab=\"tab-0\", \n", " children=[\n", - " dbc.Tab(left_pos_tab1, label=\"geglättet\", style={'padding': '0', 'height': '300px'}),\n", - " dbc.Tab(left_pos_tab2, label=\"ungeglättet\", style={'padding': '0', 'height': '300px'}),\n", + " dbc.Tab(left_pos_tab1, label=\"geglättet\", style={'padding': '0', 'height': '320px'}),\n", + " dbc.Tab(left_pos_tab2, label=\"ungeglättet\", style={'padding': '0', 'height': '320px'}),\n", " ]),\n", "\n", " html.P(\n", @@ -2341,8 +2421,8 @@ " id=\"right_pos-card-tabs\",\n", " active_tab=\"tab-0\", \n", " children=[\n", - " dbc.Tab(right_pos_tab1, label=\"geglättet\", style={'padding': '0', 'height': '300px'}),\n", - " dbc.Tab(right_pos_tab2, label=\"ungeglättet\", style={'padding': '0', 'height': '300px'}),\n", + " dbc.Tab(right_pos_tab1, label=\"geglättet\", style={'padding': '0', 'height': '320px'}),\n", + " dbc.Tab(right_pos_tab2, label=\"ungeglättet\", style={'padding': '0', 'height': '320px'}),\n", " ]),\n", "\n", " html.P(\n", @@ -2369,11 +2449,11 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 368, "metadata": {}, "outputs": [], "source": [ - "app.run_server(mode=\"jupyterlab\") #,port=8052,debug=True)\n", + "app.run_server(mode=\"external\") #,port=8052,debug=True)\n", "# mode=\"jupyterlab\" -> will open the app in a tab in JupyterLab\n", "# mode=\"inline\" -> will open the app below this cell\n", "# mode=\"external\" -> will displays a URL that you can click on to open the app in a browser tab"