{ "cells": [ { "cell_type": "markdown", "id": "b9b61e4f-b075-4d00-b6a1-c1d09526f132", "metadata": {}, "source": [ "# IPywidgets + Voila Tutorial\n", "\n", "This tutorial demonstrates how to create interactive Jupyter notebooks using ipywidgets and deploy them as web applications using Voila.\n", "\n", "## What are IPywidgets?\n", "\n", "IPywidgets are interactive HTML widgets for Jupyter notebooks and the IPython kernel. They allow you to build GUIs for your functions and make your data analysis more interactive and engaging." ] }, { "cell_type": "code", "execution_count": 1, "id": "490d84af-823e-4d3b-94f0-efadb0cf0f96", "metadata": {}, "outputs": [], "source": [ "import ipywidgets as widgets\n", "from IPython.display import display\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "\n", "# Enable inline plotting\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "id": "8a7efd22-b1a7-44b2-a168-b0b035734700", "metadata": {}, "source": [ "## Basic Widget Examples\n", "\n", "Let's start with some basic widget examples to understand how ipywidgets work." ] }, { "cell_type": "markdown", "id": "0689046b-e327-4c73-965a-4c63af8af2e1", "metadata": { "panel-layout": { "height": 44.08203125, "visible": true, "width": 100 } }, "source": [ "### Components" ] }, { "cell_type": "code", "execution_count": 2, "id": "cb1ae51c-9118-439c-9e41-361e8b7bc970", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "5f108958279d44829d00ac6422630c9a", "version_major": 2, "version_minor": 0 }, "text/plain": [ "IntSlider(value=50)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# 1. Slider Widget\n", "slider = widgets.IntSlider(\n", " value=50,\n", " min=0,\n", " max=100\n", ")\n", "\n", "display(slider)" ] }, { "cell_type": "code", "execution_count": 4, "id": "ed95958a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "23" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "slider.value" ] }, { "cell_type": "code", "execution_count": null, "id": "c18228a3", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 5, "id": "tovg4rv7xci", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "0662b22f19444674a306487f60a880f9", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Dropdown(description='Choose:', options=('Option 1', 'Option 2', 'Option 3'), value='Option 1')" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# 2. Dropdown Widget\n", "dropdown = widgets.Dropdown(\n", " options=['Option 1', 'Option 2', 'Option 3'],\n", " value='Option 1',\n", " description='Choose:',\n", " disabled=False,\n", ")\n", "\n", "display(dropdown)" ] }, { "cell_type": "code", "execution_count": 8, "id": "swt7hrtwvdp", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "a70c4bff66eb47019b1f95a63508a1dc", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Button(button_style='success', description='Click me!', icon='check', style=ButtonStyle(), tooltip='Click to t…" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "2e9835abbbc343fd9760faf6387ebf41", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Output()" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# 3. Button Widget\n", "button = widgets.Button(\n", " description='Click me!',\n", " disabled=False,\n", " button_style='success', # 'success', 'info', 'warning', 'danger' or ''\n", " tooltip='Click to trigger an action',\n", " icon='check'\n", ")\n", "\n", "output = widgets.Output()\n", "\n", "def on_button_clicked(b):\n", " with output:\n", " print(\"Button clicked!\")\n", "\n", "button.on_click(on_button_clicked)\n", "\n", "display(button, output)" ] }, { "cell_type": "code", "execution_count": 9, "id": "n8x63mge818", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "203f0650ee8b4c40a6b66d3a3ff0aa93", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Text(value='Hello World', description='Name:', placeholder='Type something')" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# 4. Text Input Widget\n", "text_input = widgets.Text(\n", " value='Hello World',\n", " placeholder='Type something',\n", " description='Name:',\n", " disabled=False\n", ")\n", "\n", "display(text_input)" ] }, { "cell_type": "code", "execution_count": 11, "id": "5c89ac95-4536-4035-b789-6cdaa0bbbcd0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'asgasdfg'" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "text_input.value" ] }, { "cell_type": "markdown", "id": "0vkhmg4lm7l", "metadata": {}, "source": [ "## Interactive Data Visualization\n", "\n", "Now let's connect widgets to data visualization to create interactive plots." ] }, { "cell_type": "code", "execution_count": 12, "id": "apvimw3hlfu", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "0dcbb352432141a69a28d78ed1b5be38", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(FloatSlider(value=1.0, description='Frequency:', max=5.0, min=0.1), FloatSlider(value=1.…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Interactive sine wave plot\n", "def plot_sine_wave(frequency=1, amplitude=1, phase=0):\n", " x = np.linspace(0, 4*np.pi, 1000)\n", " y = amplitude * np.sin(frequency * x + phase)\n", " \n", " plt.figure(figsize=(10, 6))\n", " plt.plot(x, y, 'b-', linewidth=2)\n", " plt.title(f'Sine Wave: A={amplitude}, f={frequency}, φ={phase}')\n", " plt.xlabel('x')\n", " plt.ylabel('y')\n", " plt.grid(True, alpha=0.3)\n", " plt.ylim(-3, 3)\n", " plt.show()\n", "\n", "# Create interactive widgets\n", "freq_slider = widgets.FloatSlider(value=1, min=0.1, max=5, step=0.1, description='Frequency:')\n", "amp_slider = widgets.FloatSlider(value=1, min=0.1, max=3, step=0.1, description='Amplitude:')\n", "phase_slider = widgets.FloatSlider(value=0, min=0, max=2*np.pi, step=0.1, description='Phase:')\n", "\n", "# Use interact to connect widgets to function\n", "widgets.interact(plot_sine_wave, frequency=freq_slider, amplitude=amp_slider, phase=phase_slider)" ] }, { "cell_type": "code", "execution_count": 13, "id": "bzaxoj0nvfn", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "9fdf5e65484d4e23a2170e806d0dbf04", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(Dropdown(description='Category:', options=('All', 'A', 'B', 'C'), value='All'), IntSlide…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Interactive scatter plot with dataset\n", "np.random.seed(42)\n", "data = pd.DataFrame({\n", " 'x': np.random.randn(200),\n", " 'y': np.random.randn(200),\n", " 'category': np.random.choice(['A', 'B', 'C'], 200)\n", "})\n", "\n", "def plot_scatter(category='All', point_size=50, alpha=0.7):\n", " plt.figure(figsize=(10, 6))\n", " \n", " if category == 'All':\n", " for cat in data['category'].unique():\n", " mask = data['category'] == cat\n", " plt.scatter(data[mask]['x'], data[mask]['y'], \n", " s=point_size, alpha=alpha, label=f'Category {cat}')\n", " else:\n", " mask = data['category'] == category\n", " plt.scatter(data[mask]['x'], data[mask]['y'], \n", " s=point_size, alpha=alpha, label=f'Category {category}')\n", " \n", " plt.xlabel('X values')\n", " plt.ylabel('Y values')\n", " plt.title(f'Interactive Scatter Plot - {category}')\n", " plt.legend()\n", " plt.grid(True, alpha=0.3)\n", " plt.show()\n", "\n", "# Interactive widgets for scatter plot\n", "category_dropdown = widgets.Dropdown(\n", " options=['All', 'A', 'B', 'C'],\n", " value='All',\n", " description='Category:'\n", ")\n", "\n", "size_slider = widgets.IntSlider(\n", " value=50,\n", " min=10,\n", " max=200,\n", " step=10,\n", " description='Point Size:'\n", ")\n", "\n", "alpha_slider = widgets.FloatSlider(\n", " value=0.7,\n", " min=0.1,\n", " max=1.0,\n", " step=0.1,\n", " description='Transparency:'\n", ")\n", "\n", "widgets.interact(plot_scatter, category=category_dropdown, point_size=size_slider, alpha=alpha_slider);" ] }, { "cell_type": "markdown", "id": "c11grjphsr", "metadata": {}, "source": [ "## Try It Out!\n", "\n", "### Check out\n", "\n", "- Explore more widget types: `widgets.DatePicker`, `widgets.ColorPicker`, `widgets.FileUpload`\n", "- Learn about widget layouts: `widgets.HBox`, `widgets.VBox`, `widgets.Tab`\n", "- Try advanced features: custom styling, widget linking, and event handling\n", "- Build a complete dashboard combining multiple visualizations\n", "\n", "### Resources\n", "\n", "- [IPywidgets Documentation](https://ipywidgets.readthedocs.io/)\n", "- [Voila Documentation](https://voila.readthedocs.io/)\n", "- [Widget List](https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20List.html)\n", "- [Voila Gallery](https://voila-gallery.org/)" ] }, { "cell_type": "code", "execution_count": null, "id": "56e2def9-52db-4613-80b4-df96706ea0e8", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "c576a674-6d0b-4964-8011-624b76e23aeb", "metadata": {}, "source": [ "### Exercise!" ] }, { "cell_type": "markdown", "id": "0a98ef0a-eb9f-47c5-9b96-2f7462a24aad", "metadata": {}, "source": [ "#### Penguins dataset" ] }, { "cell_type": "markdown", "id": "647e0254-00bb-4891-8c66-207eb6c8158a", "metadata": {}, "source": [ "- For the species bar chart:\n", "\"Add a dropdown menu to let users choose between showing counts of species, islands, or sex. This helps explore different categorical breakdowns of the dataset.\"\n", "\n", "- For the body weight histogram:\n", "\"Add a slider to adjust the number of bins in the histogram. Try values from 10 to 50 bins and see how it changes what patterns you can see in the weight distribution.\"\n", "\n", "- For the bill length vs depth scatter plot:\n", "\"Add checkboxes to let users show/hide different species on the plot. This makes it easier to focus on comparing just two species at a time.\"" ] }, { "cell_type": "code", "execution_count": null, "id": "86d86b74-aeda-4d62-98dd-807a8ed93730", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "10d223eb-0661-4a4c-ac36-0d1dbd5baac7", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "b20b019e-27fa-4707-8a88-5d1755f8cf99", "metadata": {}, "source": [ "#### Car crashes dataset" ] }, { "cell_type": "markdown", "id": "49da027d-bdfe-4415-80f9-f2625f600f70", "metadata": {}, "source": [ "- For the total crashes by state bar chart:\n", "\"Add a dropdown menu to let users choose between displaying total crashes, speeding crashes, alcohol crashes, or crashes involving distracted drivers. This helps compare different types of crashes across states.\"\n", " \n", "- For the insurance premiums histogram:\n", "\"Add a slider to adjust the number of bins in the histogram. Try values from 5 to 30 bins to see how different bin sizes reveal different patterns in the premium distribution.\"\n", " \n", "- For the total vs speeding crashes scatter plot:\n", "\"Add checkboxes to let users show/hide state labels and add a dropdown to color points by different crash types (alcohol, distracted, etc.). This makes it easier to identify patterns and outlier states.\"" ] }, { "cell_type": "code", "execution_count": null, "id": "0c0c6b0b-416b-4ef8-b2d2-f766c7cbc519", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "c9a50974-7826-487d-bfc8-16c1db82cf65", "metadata": {}, "source": [ "#### Chlorophyll dataset" ] }, { "cell_type": "markdown", "id": "222599cf-33ae-4445-8f9f-16e3be1e94f9", "metadata": {}, "source": [ "- For the chlorophyll concentration histogram:\n", "\"Add a slider to adjust the number of bins and a dropdown to filter by different date ranges (by year or season). This helps you see how concentration patterns change over time periods.\"\n", "\n", "- For the geographic distribution scatter plot:\n", "\"Add a slider to filter data by chlorophyll concentration levels (e.g., show only high concentration areas) and checkboxes to show/hide different water body types. This helps identify geographic hotspots of algae activity.\"\n", "\n", "- For the chlorophyll over time line plot:\n", "\"Add checkboxes to select specific water bodies to display and a dropdown to choose different time aggregations (daily, monthly, yearly averages). This lets you compare trends between different lakes and reservoirs.\"" ] }, { "cell_type": "code", "execution_count": null, "id": "a3c52223-afbe-4a52-a52a-5966598dff65", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "showyourwork", "language": "python", "name": "showyourwork" }, "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.13.3" }, "panel-cell-order": [ "7f0f1391-a1e1-494e-9911-495990d09d71", "07adf396-ba3a-417f-9cf7-912498d8803d", "0689046b-e327-4c73-965a-4c63af8af2e1" ] }, "nbformat": 4, "nbformat_minor": 5 }