{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Fileinput Exploration with Pandas and Plotly\n", "\n", "In Panel the FileDownload widget allows downloading a file generated on the server the app is running on while the `FileInput` widget allows uploading a file. In this example we demonstrate a pipeline of two little apps one which allows generating a sample data CSV file and one which allows uploading this file and displays it as a Plotly plot. \n", "\n", "For more details on how to use these components see [`FileInput`](../../reference/widgets/FileInput.ipynb) and [`FileDownload`](../../reference/widgets/FileDownload.ipynb) reference guides." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import io\n", "import param\n", "import panel as pn\n", "import pandas as pd\n", "import random\n", "\n", "from datetime import datetime, timedelta\n", "\n", "import plotly.express as px\n", "\n", "pn.extension('plotly', 'tabulator', sizing_mode=\"stretch_width\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lets start out by creating some sample data by defining some parameters which declare bounds on the values to generate along with a [`FileDownload`](../../reference/widgets/FileDownload.ipynb) widget which will allow the user to download the data onto their machine." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class SampleDataApp(param.Parameterized):\n", " \n", " samples = param.Integer(default=40, bounds=(0,100), doc=\"\"\"\n", " Number of data samples to generate.\"\"\")\n", " \n", " voltage_bounds=param.Range(default=(0,100), bounds=(0,1000), doc=\"\"\"\n", " The bounds of the voltage values to generate.\"\"\")\n", "\n", " time_bounds=param.CalendarDateRange(\n", " default=(datetime(2020, 2, 1), datetime(2020, 2, 26)),\n", " bounds=(datetime(2020, 1, 1), datetime(2020, 3, 26)),\n", " doc=\"The bounds of the time values to generate.\")\n", " \n", " fub_ids = param.ListSelector(\n", " default=[\"a1\", \"b1\", \"b2\"], objects=[\"a1\", \"b1\", \"b2\"], doc=\"\"\"\n", " The IDS to generate.\"\"\")\n", " \n", " sample_df = param.DataFrame(doc=\"\"\"\n", " The current dataframe of samples.\"\"\")\n", " \n", " generate_sample_df = param.Action(lambda self: self.update_sample_df(), label=\"Generate Data\", doc=\"\"\"\n", " An action callback which will update the sample_df.\"\"\")\n", " \n", " file_name = param.String(default=\"data.csv\", doc=\"\"\"\n", " The filename to save to.\"\"\")\n", " \n", " def __init__(self, **params):\n", " super().__init__(**params)\n", " self.update_sample_df()\n", " self.download = pn.widgets.FileDownload(\n", " name=\"Download Data\", filename=self.file_name,\n", " callback=self._download_callback, button_type=\"primary\"\n", " )\n", " self.table = pn.widgets.Tabulator(\n", " self.sample_df.head(10), layout='fit_data_stretch', theme='site', height=360\n", " )\n", "\n", " @pn.depends('file_name', watch=True)\n", " def _update_filename(self):\n", " self.download.filename = self.file_name\n", "\n", " def _download_callback(self):\n", " \"\"\"\n", " A FileDownload callback will return a file-like object which can be serialized\n", " and sent to the client.\n", " \"\"\"\n", " self.download.filename = self.file_name\n", " sio = io.StringIO()\n", " self.sample_df.to_csv(sio, index=False)\n", " sio.seek(0)\n", " return sio\n", " \n", " def update_sample_df(self, event=None):\n", " start = self.time_bounds[0]\n", " end = self.time_bounds[1]\n", " days = (end-start).days\n", " \n", " sample_data = {\n", " \"Time\": [start+timedelta(days=random.uniform(0,days)) for _ in range(0,self.samples)],\n", " \"Voltage\": [random.uniform(*self.voltage_bounds) for _ in range(0,self.samples)],\n", " \"FubId\": [random.choice(self.fub_ids) for _ in range(0,self.samples)],\n", " }\n", " self.sample_df = pd.DataFrame(sample_data) \n", " \n", " @pn.depends(\"sample_df\", watch=True)\n", " def _update_table(self):\n", " if hasattr(self, \"table\"):\n", " self.table.value = self.sample_df.head(10)\n", " \n", " def save_sample_data(self, event=None):\n", " if not self.sample_df is None:\n", " self.sample_df\n", " \n", " def view(self):\n", " return pn.Column(\n", " \"## Generate and Download Data\",\n", " pn.Row(\n", " pn.Param(self, parameters=['samples', 'voltage_bounds', 'time_bounds', 'generate_sample_df'], show_name=False, widgets={\"generate_sample_df\": {\"button_type\": \"primary\"}}),\n", " pn.Column(self.param.file_name, self.download, align='end', margin=(10,5,5,5)),\n", " ),\n", " \"**Sample data (10 Rows)**\",\n", " self.table,\n", " )\n", "\n", "sample_data_app = SampleDataApp()\n", "sample_data_app_view = sample_data_app.view()\n", "sample_data_app_view" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Click the `Save sample df` button**\n", "\n", "This should save the dataframe to your default download folder. Now let us define the `VoltageApp` which will display the data we just generated." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class VoltageApp(param.Parameterized):\n", " data = param.DataFrame()\n", " \n", " file_input = param.Parameter()\n", " \n", " def __init__(self, **params):\n", " super().__init__(file_input=pn.widgets.FileInput(), **params)\n", " self.plotly_pane = pn.pane.Plotly(height=400, sizing_mode=\"stretch_width\")\n", "\n", " @pn.depends(\"file_input.value\", watch=True)\n", " def _parse_file_input(self):\n", " value = self.file_input.value\n", " if value:\n", " string_io = io.StringIO(value.decode(\"utf8\"))\n", " self.data = pd.read_csv(string_io, parse_dates=[\"Time\"])\n", " else:\n", " print(\"error\")\n", "\n", " @pn.depends('data', watch=True)\n", " def get_plot(self):\n", " df = self.data\n", " if df is None:\n", " return\n", " assert (\"Voltage\" in df.columns) and (\"Time\" in df.columns), \"no columns voltage and time\"\n", " df = (df.loc[df['Voltage'] != 'Invalid/Calib']).copy(deep=True)\n", " df['Voltage'] = df['Voltage'].astype(float)\n", " if \"FubId\" in df.columns:\n", " p = px.scatter(df, x=\"Time\", y=\"Voltage\", color=\"FubId\")\n", " else:\n", " p = px.scatter(df, x=\"Time\", y=\"Voltage\")\n", " self.plotly_pane.object = p\n", " \n", " def view(self):\n", " return pn.Column(\n", " \"## Upload and Plot Data\",\n", " self.file_input,\n", " self.plotly_pane,\n", " )\n", " \n", "voltage_app = VoltageApp()\n", "\n", "voltage_app_view = voltage_app.view()\n", "voltage_app_view" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let us put these two components together into a servable app:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "description = \"\"\"\n", "This application demonstrates the ability to **download** a file using the `FileDownload` widget \n", "and **upload** a file using the `FileInput` widget.\n", "

\n", "Try filtering the data, download the file by clicking on the Download button\n", "and then plot it on the right by uploading that same file.\n", "\"\"\"\n", "\n", "component = pn.Column(\n", " description,\n", " sample_data_app_view,\n", " voltage_app_view,\n", " sizing_mode='stretch_both'\n", ")\n", "component" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## App\n", "\n", "Lets wrap it into nice template that can be served via `panel serve download_upload_csv.ipynb`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pn.template.FastListTemplate(site=\"Panel\", title=\"Download and Upload CSV File\", main=[ description, sample_data_app_view, voltage_app_view,]).servable();" ] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 4 }