diff --git a/doc/assets/combined_penguin.png b/doc/assets/combined_penguin.png new file mode 100644 index 000000000..8de14131b Binary files /dev/null and b/doc/assets/combined_penguin.png differ diff --git a/doc/assets/vscode_penguin.png b/doc/assets/vscode_penguin.png new file mode 100644 index 000000000..07b65b55b Binary files /dev/null and b/doc/assets/vscode_penguin.png differ diff --git a/doc/explanation/index.md b/doc/explanation/index.md new file mode 100644 index 000000000..5af3e7348 --- /dev/null +++ b/doc/explanation/index.md @@ -0,0 +1,10 @@ +# Explanation + +Explanation guides provide in-depth understanding of key concepts in hvPlot. These guides help you understand the reasoning behind design decisions and when to use different approaches. + +```{toctree} +:titlesonly: +:hidden: +:maxdepth: 2 + +``` diff --git a/doc/how_to/display_plots.ipynb b/doc/how_to/display_plots.ipynb new file mode 100644 index 000000000..c81ff63a4 --- /dev/null +++ b/doc/how_to/display_plots.ipynb @@ -0,0 +1,499 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f75f8cba", + "metadata": {}, + "source": [ + "# How to Display Plots in Different Environments\n", + "\n", + "This guide shows you how to display hvPlot visualizations in various environments: Jupyter notebooks, Python scripts, and interactive command prompts." + ] + }, + { + "cell_type": "markdown", + "id": "a6b83489", + "metadata": {}, + "source": [ + "## Jupyter Notebook Environment\n", + "\n", + "In Jupyter notebooks, hvPlot displays plots automatically when you create them. The easiest way to get started is to import the plotting extensions:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "718036cd", + "metadata": {}, + "outputs": [], + "source": [ + "import hvplot.pandas # noqa\n", + "\n", + "hvplot.extension('bokeh', 'matplotlib')" + ] + }, + { + "cell_type": "markdown", + "id": "f177e50e", + "metadata": {}, + "source": [ + "Now let's load some sample datasets and create plots that will display automatically:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc508d1d", + "metadata": {}, + "outputs": [], + "source": [ + "penguins = hvplot.sampledata.penguins(\"pandas\").dropna()\n", + "large_data = hvplot.sampledata.synthetic_clusters(\"pandas\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "316188d5", + "metadata": {}, + "outputs": [], + "source": [ + "# Create individual plots\n", + "bill_length_plot = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " by='species',\n", + " width=400,\n", + " height=300,\n", + " title=\"Bill Length vs Depth by Species\",\n", + " alpha=0.7\n", + ")\n", + "\n", + "body_mass_plot = penguins.hvplot.hist(\n", + " 'body_mass_g',\n", + " by='species',\n", + " width=400,\n", + " height=300,\n", + " title=\"Body Mass Distribution by Species\",\n", + " alpha=0.7\n", + ")\n", + "\n", + "# Display individual plots\n", + "bill_length_plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9dc8ec42", + "metadata": {}, + "outputs": [], + "source": [ + "body_mass_plot" + ] + }, + { + "cell_type": "markdown", + "id": "8223d3f7", + "metadata": {}, + "source": [ + "### Creating Layouts in Notebooks\n", + "\n", + "You can combine plots into layouts using the `+` (side-by-side) and `*` (overlay) operators:" + ] + }, + { + "cell_type": "markdown", + "id": "1cf593db-acff-4f1e-b21a-66fcdc3d5b45", + "metadata": {}, + "source": [ + "### Side-by-side layout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fd73cd88-963d-4259-9d7b-1a940e1f4453", + "metadata": {}, + "outputs": [], + "source": [ + "side_by_side = bill_length_plot + body_mass_plot\n", + "side_by_side" + ] + }, + { + "cell_type": "markdown", + "id": "97d2955c-6ee4-427b-8ea7-cd815c5ca86d", + "metadata": {}, + "source": [ + "### Overlays" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc51a68c-9f55-47b1-9810-5d561f478db9", + "metadata": {}, + "outputs": [], + "source": [ + "stocks = hvplot.sampledata.stocks(\"pandas\")\n", + "\n", + "tech_stocks_1 = stocks.hvplot.line(\n", + " x='date',\n", + " y=['Apple', 'Google'],\n", + " title=\"Tech Stock Comparison (Normalized Prices)\",\n", + " width=600,\n", + " height=400,\n", + " alpha=0.8\n", + ")\n", + "\n", + "tech_stocks_2 = stocks.hvplot.line(\n", + " x='date',\n", + " y=['Microsoft', 'Amazon'],\n", + " width=600,\n", + " height=400,\n", + " alpha=0.8\n", + ")\n", + "\n", + "overlay = tech_stocks_1 * tech_stocks_2\n", + "\n", + "overlay" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b9c6ad0", + "metadata": {}, + "outputs": [], + "source": [ + "cluster_scatter = large_data.hvplot.scatter(\n", + " x='x',\n", + " y='y',\n", + " by='cat',\n", + " datashade=True,\n", + " alpha=0.6,\n", + " width=400,\n", + " height=300,\n", + " title=\"Synthetic Clusters\"\n", + ")\n", + "\n", + "cluster_hist = large_data.hvplot.hist(\n", + " 'x',\n", + " bins=30,\n", + " width=400,\n", + " height=300,\n", + " title=\"X Distribution in Clusters\"\n", + ")\n", + "\n", + "# Create a 2x2 layout\n", + "layout = (bill_length_plot +\n", + " body_mass_plot +\n", + " cluster_scatter +\n", + " cluster_hist).opts(shared_axes=False).cols(2)\n", + "layout" + ] + }, + { + "cell_type": "markdown", + "id": "da43b41f", + "metadata": {}, + "source": [ + "## Python Scripts and Command Line\n", + "\n", + "When working outside Jupyter notebooks, plots won't display automatically. You need to use the `hvplot.show()` function to open plots in a browser window." + ] + }, + { + "cell_type": "markdown", + "id": "8870874c", + "metadata": {}, + "source": [ + "### Interactive Python Command Prompt\n", + "\n", + "When working in an interactive Python session (IPython, Python REPL), using `hvplot.show()` in the ipython environment automatically opens a bokeh server, displaying with plot with all the interactive features enabled:\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "id": "6a7cfd6a", + "metadata": {}, + "source": [ + "### Scripts\n", + "\n", + "When using python scripts like in vscode or other IDEs, the display behavior is also identical, as it also opens up a bokeh server:\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "id": "e1cb758a", + "metadata": {}, + "source": [ + "## Environment-Specific Tips\n", + "\n", + "### Jupyter Lab vs Jupyter Notebook\n", + "\n", + "hvPlot works seamlessly in both Jupyter Lab and Jupyter Notebook. The display behavior is identical:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73048fd3", + "metadata": {}, + "outputs": [], + "source": [ + "penguin_plot = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " by='species',\n", + " title=\"Penguin Bill Dimensions\",\n", + " alpha=0.7\n", + ")\n", + "\n", + "penguin_plot" + ] + }, + { + "cell_type": "markdown", + "id": "9f3282ca", + "metadata": {}, + "source": [ + "### VS Code with Jupyter Extension\n", + "\n", + "When using VS Code with the Jupyter extension, hvPlot plots display inline just like in traditional Jupyter environments:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49258fb5", + "metadata": {}, + "outputs": [], + "source": [ + "vscode_plot = penguins.hvplot.violin(\n", + " y='body_mass_g',\n", + " by='species',\n", + " title=\"Penguin Body Mass Distribution by Species\",\n", + ")\n", + "\n", + "vscode_plot" + ] + }, + { + "cell_type": "markdown", + "id": "7e0fc4a3", + "metadata": {}, + "source": [ + "### Google Colab\n", + "\n", + "In Google Colab, you may need to enable the Bokeh extension explicitly:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "deabc63b", + "metadata": {}, + "outputs": [], + "source": [ + "# hvplot.extension('bokeh') # Explicitly enable Bokeh\n", + "\n", + "colab_plot = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " by='species',\n", + " title=\"Penguin Measurements (Colab Compatible)\",\n", + " alpha=0.7\n", + ")\n", + "\n", + "colab_plot" + ] + }, + { + "cell_type": "markdown", + "id": "686792dd", + "metadata": {}, + "source": [ + "### Terminal/SSH Environments\n", + "\n", + "When working on remote servers or in terminal-only environments, you have several options:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "823e3f5d-2196-4eec-9f4f-ad3ba5572247", + "metadata": {}, + "outputs": [], + "source": [ + "# Option 1: Save plots to files instead of displaying\n", + "terminal_plot = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " by='species',\n", + " title=\"Penguin Bill Dimensions (for Terminal Environment)\",\n", + " alpha=0.7\n", + ")\n", + "\n", + "hvplot.save(terminal_plot, 'terminal_plot.html')\n", + "print(\"Plot saved as 'terminal_plot.html'\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0aad103-32df-4856-ac57-281c5db385d2", + "metadata": {}, + "outputs": [], + "source": [ + "# Option 2: Use matplotlib backend for direct PNG export\n", + "hvplot.output(backend='matplotlib')\n", + "terminal_plot_mpl = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " by='species',\n", + " title=\"Penguin Bill Dimensions (Matplotlib)\",\n", + ")\n", + "\n", + "hvplot.save(terminal_plot_mpl, 'terminal_plot.png')\n", + "print(\"Plot saved as 'terminal_plot.png'\")" + ] + }, + { + "cell_type": "markdown", + "id": "1d8dea3d", + "metadata": {}, + "source": [ + "## Troubleshooting Display Issues\n", + "\n", + "### Plots Not Displaying in Notebooks\n", + "\n", + "If plots aren't showing in your notebook, try these solutions:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb8e1d53", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution 1: Explicitly enable the extension\n", + "hvplot.extension('bokeh')\n", + "\n", + "# Solution 2: Check if the import worked\n", + "import hvplot.pandas # noqa\n", + "print(\"hvplot.pandas imported successfully\")\n", + "\n", + "# Solution 3: Create a test plot\n", + "test_plot = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " color='species',\n", + " title=\"Test Plot\"\n", + ")\n", + "\n", + "test_plot" + ] + }, + { + "cell_type": "markdown", + "id": "a34f3ed2", + "metadata": {}, + "source": [ + "### Browser Issues with hvplot.show()\n", + "\n", + "If `hvplot.show()` isn't opening your browser, you can save the plot as a html file and open it manually:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "672d941a-0857-4ae5-9f11-41c883b4b8f3", + "metadata": {}, + "outputs": [], + "source": [ + "backup_plot = penguins.hvplot.scatter(x='bill_length_mm', y='flipper_length_mm', by='species')\n", + "hvplot.save(backup_plot, 'backup_plot.html')\n", + "print(\"Manually open 'backup_plot.html'\")" + ] + }, + { + "cell_type": "markdown", + "id": "cd040662", + "metadata": {}, + "source": [ + "## Cleanup\n", + "\n", + "Let's clean up the files we created:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4fa37ee2", + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "files_to_clean = [\n", + " 'terminal_plot.html',\n", + " 'terminal_plot.png',\n", + " 'backup_plot.html'\n", + "]\n", + "\n", + "for filename in files_to_clean:\n", + " file_path = Path(filename)\n", + " if file_path.exists():\n", + " file_path.unlink()\n", + " print(f\"Removed {filename}\")\n", + " else:\n", + " print(f\"{filename} not found\")" + ] + }, + { + "cell_type": "markdown", + "id": "005c4d00", + "metadata": {}, + "source": [ + ":::{admonition} Summary\n", + "\n", + "Here's a quick reference for displaying plots in different environments:\n", + "\n", + "| Environment | How to Display | Notes |\n", + "|-------------|----------------|-------|\n", + "| **Jupyter Notebook/Lab** | Automatic (just create the plot) | Import `hvplot.pandas` or similar |\n", + "| **VS Code + Jupyter** | Automatic (just create the plot) | Same as Jupyter |\n", + "| **Python Scripts** | `hvplot.show(plot)` | Opens in browser |\n", + "| **Interactive Python** | `hvplot.show(plot)` | Opens in browser |\n", + "| **Google Colab** | Automatic (may need `hvplot.extension('bokeh')`) | Import hvplot first |\n", + "| **Terminal/SSH** | Save to file: `hvplot.save(plot, 'file.html')` | Transfer file to view locally |\n", + "| **Offline/Airgapped** | Use matplotlib backend or save with `INLINE` resources | No internet required |\n", + ":::\n", + "\n", + ":::{admonition} Next Steps\n", + ":class: seealso\n", + "\n", + "- Learn how to:\n", + " - [Save Bokeh plots](save_bokeh_plots.ipynb)\n", + " - [Save Matplotlib plots](save_matplotlib_plots.ipynb)\n", + "- Explore [using Panel for advanced layouts and interactivity](use_panel_for_display.ipynb)\n", + ":::" + ] + } + ], + "metadata": { + "language_info": { + "name": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/doc/how_to/index.md b/doc/how_to/index.md new file mode 100644 index 000000000..658c49de4 --- /dev/null +++ b/doc/how_to/index.md @@ -0,0 +1,14 @@ +# How-To Guides + +How-to guides are practical, problem-oriented instructions that help you accomplish specific tasks with hvPlot. These guides assume you're already familiar with the basics and want to solve particular problems or achieve specific goals. + +```{toctree} +:titlesonly: +:hidden: +:maxdepth: 2 + +display_plots +save_bokeh_plots +save_matplotlib_plots +use_panel_for_display +``` diff --git a/doc/how_to/save_bokeh_plots.ipynb b/doc/how_to/save_bokeh_plots.ipynb new file mode 100644 index 000000000..5507dd447 --- /dev/null +++ b/doc/how_to/save_bokeh_plots.ipynb @@ -0,0 +1,438 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d3667843", + "metadata": {}, + "source": [ + "# How to Save Interactive Bokeh Plots\n", + "\n", + "This guide shows you how to export hvPlot visualizations using the Bokeh backend to various file formats, with a focus on interactive HTML exports and static PNG images." + ] + }, + { + "cell_type": "markdown", + "id": "5cd5a9f8", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "First, let's set up hvPlot with the default Bokeh backend and create a sample plot:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9979426d", + "metadata": {}, + "outputs": [], + "source": [ + "import hvplot.pandas # noqa\n", + "\n", + "penguins = hvplot.sampledata.penguins(\"pandas\").dropna()\n", + "\n", + "plot = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " color='species',\n", + " title=\"Penguin Bill Dimensions\"\n", + ")\n", + "\n", + "plot" + ] + }, + { + "cell_type": "markdown", + "id": "6cc859c2", + "metadata": {}, + "source": [ + "## HTML Export (Recommended)\n", + "\n", + "The primary strength of Bokeh plots is their interactivity. HTML export preserves all interactive features like zooming, panning, and hover tooltips:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ebd7e190", + "metadata": {}, + "outputs": [], + "source": [ + "hvplot.save(plot, 'penguins_bokeh.html')\n", + "print(\"Interactive HTML plot saved as 'penguins_bokeh.html'\")" + ] + }, + { + "cell_type": "markdown", + "id": "e41cd8e0", + "metadata": {}, + "source": [ + "## HTML with Inline Resources\n", + "\n", + "By default, HTML files depend on loading JavaScript from online CDN repositories. For offline or airgapped environments, use inline resources:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e50cecd", + "metadata": {}, + "outputs": [], + "source": [ + "hvplot.save(plot, 'penguins_offline.html', resources='inline')\n", + "print(\"Self-contained HTML plot saved as 'penguins_offline.html'\")" + ] + }, + { + "cell_type": "markdown", + "id": "03308dda", + "metadata": {}, + "source": [ + "## PNG Export\n", + "\n", + "For static images, Bokeh can export to PNG, but this requires additional browser driver dependencies." + ] + }, + { + "cell_type": "markdown", + "id": "1e9d4450", + "metadata": {}, + "source": [ + "### Installing PNG Export Dependencies\n", + "\n", + "Choose one of these options:\n", + "\n", + "**Option 1: Chrome/Chromium (Recommended)**\n", + "\n", + "Install Chrome or Chromium browser first, then install the Python package.\n", + "\n", + "```bash\n", + "pip install selenium chromedriver-autoinstaller\n", + "```\n", + "\n", + "**Option 2: Firefox**\n", + "\n", + "Install Firefox browser first, then install geckodriver.\n", + "```bash\n", + "pip install selenium\n", + "```\n", + "Download geckodriver from the official [GitHub releases page](https://github.com/mozilla/geckodriver), then add geckodriver to your PATH\n", + "\n", + "**Option 3: Using conda**\n", + "```bash\n", + "conda install selenium\n", + "```\n", + "\n", + "\n", + ":::{tip}\n", + "For help adding geckodriver to your PATH:\n", + "- **Windows:**\n", + " - Place `geckodriver.exe` in a folder (e.g., `C:\\\\tools\\\\geckodriver`)\n", + " - Add that folder to your System PATH:\n", + " - _System Properties_ > _Environment Variables_.\n", + " - Edit the Path variable and add the folder path.\n", + "- **macOS/Linux:**\n", + " - Move the `geckodriver` binary to `/usr/local/bin` or another directory already in your PATH.\n", + " - Alternatively, update your PATH manually in your shell config (e.g., `export PATH=\"$PATH:/path/to/geckodriver\"`)\n", + "\n", + "If unsure, consult your operating system documentation for instructions on modifying the PATH environment variable.\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "id": "5b1c82dc", + "metadata": {}, + "source": [ + "### Saving PNG Files\n", + "\n", + "Once dependencies are installed:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d9a4667", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " hvplot.save(plot, 'penguins_bokeh.png')\n", + " print(\"PNG saved successfully as 'penguins_bokeh.png'\")\n", + "except Exception as e:\n", + " print(f\"PNG export failed: {e}\")\n", + " print(\"Make sure you have selenium and a browser driver installed.\")" + ] + }, + { + "cell_type": "markdown", + "id": "e869e235", + "metadata": {}, + "source": [ + ":::{note}\n", + "This will only work if you have selenium and a browser driver installed\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "id": "e36e5455", + "metadata": {}, + "source": [ + "### High-Resolution PNG Export\n", + "\n", + "For presentations or print, you may want higher resolution images:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82961f10", + "metadata": {}, + "outputs": [], + "source": [ + "high_res_plot = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " width=1200,\n", + " height=800,\n", + " color='species',\n", + " title=\"High-Resolution Penguin Plot\"\n", + ")\n", + "\n", + "high_res_plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5162834", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " hvplot.save(high_res_plot, 'penguins_high_res.png')\n", + " print(\"High-resolution PNG saved\")\n", + "except Exception as e:\n", + " print(f\"PNG export failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "86012fc5", + "metadata": {}, + "source": [ + "## Batch Saving Multiple Plots\n", + "\n", + "You can efficiently save multiple interactive plots:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88b92170", + "metadata": {}, + "outputs": [], + "source": [ + "plots = {\n", + " 'scatter': penguins.hvplot.scatter(x='bill_length_mm', y='bill_depth_mm', color='species', width=400),\n", + " 'histogram': penguins.hvplot.hist('body_mass_g', by='species', alpha=0.7, width=400),\n", + " 'box': penguins.hvplot.box(y='flipper_length_mm', by='species', width=400),\n", + "}\n", + "\n", + "# Display the plots\n", + "(plots['scatter'] + plots['histogram'] + plots['box']).cols(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb0f717b", + "metadata": {}, + "outputs": [], + "source": [ + "for name, plot in plots.items():\n", + " # Save interactive HTML\n", + " hvplot.save(plot, f'{name}_bokeh.html')\n", + " print(f\"Saved {name}_bokeh.html\")\n", + "\n", + " # Try PNG export\n", + " try:\n", + " hvplot.save(plot, f'{name}_bokeh.png')\n", + " print(f\"Saved {name}_bokeh.png\")\n", + " except Exception as e:\n", + " print(f\"PNG export failed for {name}: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "60f34a01", + "metadata": {}, + "source": [ + "## Testing PNG Export Setup\n", + "\n", + "Here's how to test if your PNG export setup is working:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2d327f6", + "metadata": {}, + "outputs": [], + "source": [ + "# Test if selenium can find your browser\n", + "try:\n", + " from selenium import webdriver\n", + " from selenium.webdriver.chrome.options import Options\n", + "\n", + " # Test Chrome\n", + " chrome_options = Options()\n", + " chrome_options.add_argument(\"--headless\") # Run in background\n", + " driver = webdriver.Chrome(options=chrome_options)\n", + " driver.quit()\n", + " print(\"✓ Chrome driver is working\")\n", + "\n", + "except Exception as e:\n", + " print(f\"✗ Chrome driver issue: {e}\")\n", + "\n", + " # Try Firefox as fallback\n", + " try:\n", + " from selenium.webdriver.firefox.options import Options as FirefoxOptions\n", + " firefox_options = FirefoxOptions()\n", + " firefox_options.add_argument(\"--headless\")\n", + " driver = webdriver.Firefox(options=firefox_options)\n", + " driver.quit()\n", + " print(\"✓ Firefox driver is working\")\n", + " except Exception as e2:\n", + " print(f\"✗ Firefox driver issue: {e2}\")\n", + " print(\"Consider installing browser drivers or using matplotlib backend for PNG export\")" + ] + }, + { + "cell_type": "markdown", + "id": "44dbf404", + "metadata": {}, + "source": [ + "## Working with Large Datasets\n", + "\n", + "For very large datasets, consider using datashader for efficient rendering:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6aca1b0", + "metadata": {}, + "outputs": [], + "source": [ + "large_data = hvplot.sampledata.synthetic_clusters(\"pandas\")\n", + "print(f\"Dataset size: {len(large_data):,} points\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14276034", + "metadata": {}, + "outputs": [], + "source": [ + "datashaded_plot = large_data.hvplot.scatter(\n", + " x='x',\n", + " y='y',\n", + " by='cat',\n", + " datashade=True,\n", + " width=400,\n", + " height=400,\n", + " title=\"Large Dataset with Datashader\"\n", + ")\n", + "\n", + "datashaded_plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fcad710e", + "metadata": {}, + "outputs": [], + "source": [ + "hvplot.save(datashaded_plot, 'large_dataset_bokeh.html')\n", + "print(\"Large dataset plot saved successfully\")" + ] + }, + { + "cell_type": "markdown", + "id": "71a6cd15", + "metadata": {}, + "source": [ + "## Cleanup\n", + "\n", + "Let's clean up the files we created during this demonstration:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b66bfd6", + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "files_to_clean = [\n", + " 'penguins_bokeh.html', 'penguins_offline.html', 'penguins_bokeh.png',\n", + " 'penguins_high_res.png',\n", + " 'scatter_bokeh.html', 'histogram_bokeh.html', 'box_bokeh.html',\n", + " 'scatter_bokeh.png', 'histogram_bokeh.png', 'box_bokeh.png',\n", + " 'large_dataset_bokeh.html'\n", + "]\n", + "\n", + "for filename in files_to_clean:\n", + " file_path = Path(filename)\n", + " if file_path.exists():\n", + " file_path.unlink()\n", + " print(f\"Removed {filename}\")\n", + " else:\n", + " print(f\"{filename} not found\")" + ] + }, + { + "cell_type": "markdown", + "id": "25125417", + "metadata": {}, + "source": [ + "## Best Practices for Bokeh Plots\n", + "\n", + "1. **Prioritize HTML export**: Bokeh's strength is interactivity, so HTML should be your primary export format\n", + "\n", + "2. **Use inline resources for offline sharing**: Include `resources=INLINE` when sharing files that won't have internet access\n", + "\n", + "3. **Test PNG export setup**: Always verify your selenium and browser driver installation\n", + "\n", + "4. **Consider file sizes**: Interactive HTML files can be large for complex plots, but preserve all functionality\n", + "\n", + "5. **Handle large datasets**: Use `datashade=True` or `rasterize=True` for datasets with many points\n", + "\n", + "6. **Optimize for your use case**:\n", + " - **Web embedding**: HTML with CDN resources (default)\n", + " - **Offline sharing**: HTML with inline resources\n", + " - **Presentations/documents**: PNG (requires browser driver)\n", + " - **Email attachments**: PNG (smaller, universally viewable)\n", + "\n", + ":::{seealso}\n", + "- Learn about [saving matplotlib plots](save_matplotlib_plots.ipynb) for publication-quality static images\n", + "- Explore [using Panel for advanced layouts and interactivity](use_panel_for_display.ipynb)\n", + ":::" + ] + } + ], + "metadata": { + "language_info": { + "name": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/doc/how_to/save_matplotlib_plots.ipynb b/doc/how_to/save_matplotlib_plots.ipynb new file mode 100644 index 000000000..0c866f10f --- /dev/null +++ b/doc/how_to/save_matplotlib_plots.ipynb @@ -0,0 +1,369 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "72979cef", + "metadata": {}, + "source": [ + "# How to Save Publication-Quality Matplotlib Plots\n", + "\n", + "This guide shows you how to export hvPlot visualizations using the matplotlib backend to create high-quality static images perfect for publications, presentations, and print media." + ] + }, + { + "cell_type": "markdown", + "id": "7238e264", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "First, let's set up hvPlot with the matplotlib backend:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c33ba493", + "metadata": {}, + "outputs": [], + "source": [ + "import hvplot.pandas # noqa\n", + "hvplot.extension('matplotlib')\n", + "\n", + "penguins = hvplot.sampledata.penguins(\"pandas\").dropna()\n", + "\n", + "plot = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " color='species',\n", + " title=\"Penguin Bill Dimensions (Matplotlib)\",\n", + ")\n", + "\n", + "plot" + ] + }, + { + "cell_type": "markdown", + "id": "c5c5fcb4", + "metadata": {}, + "source": [ + "## PNG Export (No Browser Driver Required!)\n", + "\n", + "The main advantage of matplotlib is that it generates PNG files natively without requiring browser drivers:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c29a135", + "metadata": {}, + "outputs": [], + "source": [ + "hvplot.save(plot, 'mpl_plot.png')\n", + "print(\"Matplotlib plot saved as PNG\")" + ] + }, + { + "cell_type": "markdown", + "id": "63253c78", + "metadata": {}, + "source": [ + "## High-Quality Publication Figures\n", + "\n", + "For publications, you can control the DPI and other matplotlib settings:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55f3db15", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib\n", + "matplotlib.rcParams['figure.dpi'] = 150 # High DPI for better quality\n", + "\n", + "publication_plot = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " color='species',\n", + " width=1200,\n", + " height=800,\n", + " title=\"Penguin Bill Dimensions\",\n", + " xlabel=\"Bill Length (mm)\",\n", + " ylabel=\"Bill Depth (mm)\"\n", + ")\n", + "\n", + "publication_plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3053915c", + "metadata": {}, + "outputs": [], + "source": [ + "hvplot.save(publication_plot, 'publication_figure.png')\n", + "print(\"Publication-quality figure saved as 'publication_figure.png'\")" + ] + }, + { + "cell_type": "markdown", + "id": "3b9b5e2d", + "metadata": {}, + "source": [ + "## Customizing Output Quality\n", + "\n", + "You can fine-tune various matplotlib parameters for optimal output:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4727d75e", + "metadata": {}, + "outputs": [], + "source": [ + "# Configure matplotlib for high-quality output\n", + "matplotlib.rcParams.update({\n", + " 'figure.dpi': 300, # High DPI for print\n", + " 'savefig.dpi': 300, # DPI for saved figures\n", + " 'font.size': 12, # Base font size\n", + " 'axes.labelsize': 14, # Axis label font size\n", + " 'axes.titlesize': 16, # Title font size\n", + " 'legend.fontsize': 11, # Legend font size\n", + " 'figure.figsize': (10, 6) # Default figure size\n", + "})\n", + "\n", + "print(\"Matplotlib configured for high-quality output\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77f11648", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a professional-looking plot\n", + "professional_plot = penguins.hvplot.scatter(\n", + " x='bill_length_mm',\n", + " y='bill_depth_mm',\n", + " color='species',\n", + " title=\"Bill Length vs Bill Depth by Species\",\n", + " xlabel=\"Bill Length (mm)\",\n", + " ylabel=\"Bill Depth (mm)\",\n", + " legend='top_right'\n", + ")\n", + "\n", + "professional_plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8d9afde", + "metadata": {}, + "outputs": [], + "source": [ + "hvplot.save(professional_plot, 'professional_plot.png')\n", + "print(\"Professional plot saved as 'professional_plot.png'\")" + ] + }, + { + "cell_type": "markdown", + "id": "b12a439d", + "metadata": {}, + "source": [ + "## Multiple Plot Types\n", + "\n", + "Matplotlib also works well with all hvPlot chart types:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54cc8595", + "metadata": {}, + "outputs": [], + "source": [ + "plots = {\n", + " 'scatter': penguins.hvplot.scatter(x='bill_length_mm', y='bill_depth_mm', color='species', width=400),\n", + " 'histogram': penguins.hvplot.hist('body_mass_g', by='species', alpha=0.7, width=400),\n", + " 'box': penguins.hvplot.box(y='flipper_length_mm', by='species', width=400),\n", + " 'violin': penguins.hvplot.violin(y='bill_length_mm', by='species', width=400),\n", + "}\n", + "\n", + "layout = (plots['scatter'] + plots['histogram'] + plots['box'] + plots['violin']).cols(2)\n", + "layout" + ] + }, + { + "cell_type": "markdown", + "id": "6a77790e", + "metadata": {}, + "source": [ + "## Batch Saving Multiple Static Plots\n", + "\n", + "Save multiple matplotlib plots efficiently:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d65c7475", + "metadata": {}, + "outputs": [], + "source": [ + "for name, plot in plots.items():\n", + " filename = f'{name}_matplotlib.png'\n", + " hvplot.save(plot, filename)\n", + " print(f\"Saved {filename}\")" + ] + }, + { + "cell_type": "markdown", + "id": "915440bc-e652-48e5-a2cf-f9a87b88c84e", + "metadata": {}, + "source": [ + "## Saving as Multi-panels for Publications\n", + "\n", + "For scientific publications, you might need to create multi-panel figures:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72367442-63bf-496a-a875-4313d4cb1cf8", + "metadata": {}, + "outputs": [], + "source": [ + "hvplot.save(layout, 'figure_panels.png')\n", + "print(\"Multi-panel figure saved as 'figure_panels.png'\")" + ] + }, + { + "cell_type": "markdown", + "id": "0a9c5ad0", + "metadata": {}, + "source": [ + "## Different File Formats\n", + "\n", + "Matplotlib supports various file formats beyond PNG:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95f8b9ae", + "metadata": {}, + "outputs": [], + "source": [ + "# Save in different formats\n", + "formats = ['png', 'pdf', 'svg', 'gif']\n", + "\n", + "for fmt in formats:\n", + " try:\n", + " filename = f'penguin_plot.{fmt}'\n", + " hvplot.save(plot, filename)\n", + " print(f\"Saved {filename}\")\n", + " except Exception as e:\n", + " print(f\"Failed to save {fmt}: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "e8ade29b", + "metadata": {}, + "source": [ + "## Cleanup\n", + "\n", + "Let's clean up the files we created during this demonstration:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5074ea76", + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "files_to_clean = [\n", + " 'mpl_plot.png', 'publication_figure.png', 'professional_plot.png',\n", + " 'scatter_matplotlib.png', 'histogram_matplotlib.png', 'box_matplotlib.png', 'violin_matplotlib.png',\n", + " 'penguin_plot.png', 'penguin_plot.pdf', 'penguin_plot.svg', 'penguin_plot.gif',\n", + " 'figure_panels.png',\n", + "]\n", + "\n", + "for filename in files_to_clean:\n", + " file_path = Path(filename)\n", + " if file_path.exists():\n", + " file_path.unlink()\n", + " print(f\"Removed {filename}\")\n", + " else:\n", + " print(f\"{filename} not found\")" + ] + }, + { + "cell_type": "markdown", + "id": "934eac39-1983-492b-a7d8-3a0766b4f7e4", + "metadata": {}, + "source": [ + "## Advantages of Matplotlib for Static Export\n", + "\n", + "- **No browser driver required**: Matplotlib generates PNG files natively\n", + "- **Better text rendering**: Superior font handling for publications\n", + "- **Smaller file sizes**: More efficient PNG compression\n", + "- **Scientific publishing**: Better suited for academic papers\n", + "- **Consistent output**: Reproducible across different systems" + ] + }, + { + "cell_type": "markdown", + "id": "97e8d3f3", + "metadata": {}, + "source": [ + "## Best Practices for Matplotlib Plots\n", + "\n", + "1. **No browser dependencies**: Matplotlib generates images natively, making it ideal for server environments\n", + "\n", + "2. **Configure DPI appropriately**:\n", + " - **Screen display**: 72-96 DPI\n", + " - **Web use**: 96-150 DPI\n", + " - **Print/publications**: 300+ DPI\n", + "\n", + "3. **Choose the right format**:\n", + " - **PNG**: Best for complex plots with many colors\n", + " - **PDF**: Vector format, ideal for publications\n", + " - **SVG**: Vector format, good for web embedding\n", + "\n", + "4. **Optimize for your use case**:\n", + " - **Scientific papers**: High DPI PNG or PDF\n", + " - **Presentations**: Medium DPI PNG\n", + " - **Web display**: Lower DPI PNG\n", + " - **Print materials**: 300+ DPI PDF or PNG\n", + "\n", + "5. **Font and sizing**:\n", + " - Use appropriate font sizes for your target medium\n", + " - Test readability at the intended viewing size\n", + " - Consider colorblind-friendly palettes\n", + "\n", + ":::{seealso}\n", + "- Learn about [saving interactive Bokeh plots](save_bokeh_plots.ipynb) for web sharing and interactive exploration\n", + "- Explore [using Panel for advanced layouts and interactivity](use_panel_for_display.ipynb)\n", + ":::" + ] + } + ], + "metadata": { + "language_info": { + "name": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/doc/how_to/use_panel_for_display.ipynb b/doc/how_to/use_panel_for_display.ipynb new file mode 100644 index 000000000..4d00925a2 --- /dev/null +++ b/doc/how_to/use_panel_for_display.ipynb @@ -0,0 +1,566 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "290fd872", + "metadata": {}, + "source": [ + "# How to Use Panel for Interactive Display and Layouts\n", + "\n", + "This guide shows you how to use [Panel](https://panel.holoviz.org) to create interactive displays, update plots dynamically, and build sophisticated layouts with hvPlot visualizations." + ] + }, + { + "cell_type": "markdown", + "id": "223bea4a", + "metadata": {}, + "source": [ + "## Why Use Panel with hvPlot?\n", + "\n", + "Panel extends hvPlot's capabilities by providing:\n", + "\n", + "- **Dynamic updates**: Change plot content without recreating the entire visualization\n", + "- **Advanced layouts**: More sophisticated arrangements than simple `+` and `*` operators\n", + "- **Interactive widgets**: Add controls like sliders, dropdowns, and buttons\n", + "- **Dashboard creation**: Build full applications and dashboards\n", + "- **Server deployment**: Easily serve interactive applications\n", + "\n", + ":::{note}\n", + "Some of the dynamic functionality in this notebook require a live python process to work.\n", + ":::\n", + "\n", + "Let's start with the basics:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8527e864", + "metadata": {}, + "outputs": [], + "source": [ + "import panel as pn\n", + "import hvplot.pandas # noqa\n", + "\n", + "# Enable Panel extension for notebooks\n", + "pn.extension('tabulator')\n", + "\n", + "# Load sample data from hvsampledata\n", + "earthquakes = hvplot.sampledata.earthquakes(\"pandas\")\n", + "penguins = hvplot.sampledata.penguins(\"pandas\").dropna()\n", + "stocks = hvplot.sampledata.stocks(\"pandas\")" + ] + }, + { + "cell_type": "markdown", + "id": "8908e40a", + "metadata": {}, + "source": [ + "## Basic Panel Usage\n", + "\n", + "### Creating a Panel Pane\n", + "\n", + "The fundamental concept in Panel is a \"pane\" - a container that can hold and display objects:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2905dcca", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a simple plot\n", + "earthquake_magnitude_plot = earthquakes.hvplot.line(\n", + " x='time',\n", + " y='mag',\n", + " title=\"Earthquake Magnitude Over Time\",\n", + " color='red',\n", + ")\n", + "\n", + "# Wrap it in a Panel pane\n", + "pane = pn.panel(earthquake_magnitude_plot)\n", + "pane" + ] + }, + { + "cell_type": "markdown", + "id": "58f9f1f1", + "metadata": {}, + "source": [ + "### Dynamic Plot Updates\n", + "\n", + "One of Panel's key features is the ability to update plot content dynamically:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "795b1c98", + "metadata": {}, + "outputs": [], + "source": [ + "earthquake_depth_plot = earthquakes.hvplot.line(\n", + " x='time',\n", + " y='depth',\n", + " title=\"Earthquake Depth Over Time\",\n", + ")\n", + "\n", + "pane.object = earthquake_depth_plot\n", + "print(\"Pane updated with earthquake depth data - check the plot above!\")" + ] + }, + { + "cell_type": "markdown", + "id": "c7db4dc6", + "metadata": {}, + "source": [ + "### In-Place Updates with Overlays\n", + "\n", + "You can also update the plot content in-place using overlay operations:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a3ded04", + "metadata": {}, + "outputs": [], + "source": [ + "# Add the earthquake magnitude plot as an overlay\n", + "pane.object *= earthquake_magnitude_plot\n", + "print(\"Added earthquake magnitude as overlay - now showing both trends!\")" + ] + }, + { + "cell_type": "markdown", + "id": "0259a1d6", + "metadata": {}, + "source": [ + "## Advanced Layouts with Panel\n", + "\n", + "### Column and Row Layouts\n", + "\n", + "Panel provides more sophisticated layout options than hvPlot's basic `+` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9031c551", + "metadata": {}, + "outputs": [], + "source": [ + "# Create multiple plots\n", + "\n", + "size_opts = {\"width\": 350, \"height\": 250}\n", + "\n", + "plot1 = earthquakes.hvplot(\n", + " x='time', y='mag',\n", + " title=\"Magnitude\", **size_opts,\n", + ")\n", + "\n", + "plot2 = earthquakes.hvplot(\n", + " x='time', y='depth',\n", + " title=\"Depth\", **size_opts,\n", + ")\n", + "\n", + "plot3 = earthquakes.hvplot.scatter(\n", + " x='lon', y='lat',\n", + " title=\"Geographic Distribution\", **size_opts,\n", + ")\n", + "\n", + "plot4 = earthquakes.hvplot.hist(\n", + " 'mag', bins=20,\n", + " title=\"Magnitude Distribution\", **size_opts,\n", + ")\n", + "\n", + "# Create a row layout\n", + "row_layout = pn.Row(plot1, plot2, plot3)\n", + "row_layout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37fc5176", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a column layout\n", + "column_layout = pn.Column(plot1, plot2, plot3)\n", + "column_layout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "461cc037", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a nested layout (2x2 grid)\n", + "grid_layout = pn.Column(\n", + " pn.Row(plot1, plot2),\n", + " pn.Row(plot3, plot4)\n", + ")\n", + "grid_layout" + ] + }, + { + "cell_type": "markdown", + "id": "c355d2c5", + "metadata": {}, + "source": [ + "### Tabs for Organized Content\n", + "\n", + "Tabs are perfect for organizing multiple related visualizations:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "83685ed5", + "metadata": {}, + "outputs": [], + "source": [ + "# Create different types of plots for the penguin data\n", + "size_opts = {\"width\": 600, \"height\": 400}\n", + "\n", + "penguin_scatter = penguins.hvplot.scatter(\n", + " x='bill_length_mm', y='bill_depth_mm',\n", + " by='species', title=\"Bill Dimensions\",\n", + " **size_opts,\n", + ")\n", + "\n", + "penguin_hist = penguins.hvplot.hist(\n", + " 'body_mass_g', by='species',\n", + " title=\"Body Mass Distribution\",\n", + " alpha=0.7, **size_opts,\n", + ")\n", + "\n", + "penguin_box = penguins.hvplot.box(\n", + " y='flipper_length_mm', by='species',\n", + " title=\"Flipper Length by Species\",\n", + " **size_opts,\n", + ")\n", + "\n", + "# Create tabbed interface\n", + "tabs = pn.Tabs(\n", + " (\"Scatter Plot\", penguin_scatter),\n", + " (\"Histogram\", penguin_hist),\n", + " (\"Box Plot\", penguin_box)\n", + ")\n", + "\n", + "tabs" + ] + }, + { + "cell_type": "markdown", + "id": "31c1b0f3", + "metadata": {}, + "source": [ + "## Interactive Widgets and Controls\n", + "\n", + "### Simple Widget Example\n", + "\n", + "Let's create a dropdown to switch between different earthquake metrics:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f02a00c6", + "metadata": {}, + "outputs": [], + "source": [ + "# Get available earthquake numeric columns\n", + "earthquake_columns = ['mag', 'depth']\n", + "\n", + "# Create a dropdown widget\n", + "earthquake_selector = pn.widgets.Select(\n", + " name='Earthquake Metric',\n", + " value=earthquake_columns[0],\n", + " options=earthquake_columns,\n", + " width=200\n", + ")\n", + "\n", + "# Function to create plot based on selection\n", + "def create_earthquake_plot(metric):\n", + " return earthquakes.hvplot(\n", + " x='time',\n", + " y=metric,\n", + " title=f\"{metric.title()} Over Time\",\n", + " line_width=3\n", + " )\n", + "\n", + "# Create reactive plot that updates when widget changes\n", + "interactive_plot = pn.bind(create_earthquake_plot, earthquake_selector)\n", + "\n", + "# Combine widget and plot\n", + "widget_layout = pn.Column(\n", + " \"## Interactive Earthquake Data Explorer\",\n", + " earthquake_selector,\n", + " interactive_plot\n", + ")\n", + "\n", + "widget_layout" + ] + }, + { + "cell_type": "markdown", + "id": "a88f06dd", + "metadata": {}, + "source": [ + "### Multiple Widgets Example\n", + "\n", + "Let's create a more complex example with multiple controls for the penguin data:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3349a60f", + "metadata": {}, + "outputs": [], + "source": [ + "# Create multiple widgets\n", + "x_axis = pn.widgets.Select(\n", + " name='X Axis',\n", + " value='bill_length_mm',\n", + " options=['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g']\n", + ")\n", + "\n", + "y_axis = pn.widgets.Select(\n", + " name='Y Axis',\n", + " value='bill_depth_mm',\n", + " options=['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g']\n", + ")\n", + "\n", + "plot_type = pn.widgets.Select(\n", + " name='Plot Type',\n", + " value='scatter',\n", + " options=['scatter', 'line', 'bar']\n", + ")\n", + "\n", + "color_by = pn.widgets.Select(\n", + " name='Color By',\n", + " value='species',\n", + " options=['species', 'island', 'sex', None]\n", + ")\n", + "\n", + "alpha_slider = pn.widgets.FloatSlider(\n", + " name='Transparency',\n", + " start=0.1,\n", + " end=1.0,\n", + " value=0.7,\n", + " step=0.1\n", + ")\n", + "\n", + "# Function to create plot based on all widgets\n", + "def create_penguin_plot(x, y, plot_kind, color, alpha):\n", + " plot_method = getattr(penguins.hvplot, plot_kind)\n", + "\n", + " kwargs = {\n", + " 'x': x,\n", + " 'y': y,\n", + " 'title': f\"{plot_kind.title()} Plot: {y} vs {x}\",\n", + " 'width': 600,\n", + " 'height': 400,\n", + " 'alpha': alpha\n", + " }\n", + "\n", + " if color:\n", + " kwargs['by'] = color\n", + "\n", + " return plot_method(**kwargs)\n", + "\n", + "# Bind the function to all widgets\n", + "complex_interactive_plot = pn.bind(\n", + " create_penguin_plot,\n", + " x_axis, y_axis,\n", + " plot_type, color_by, alpha_slider\n", + ")\n", + "\n", + "# Create control panel\n", + "controls = pn.Card(\n", + " x_axis, y_axis, plot_type, color_by, alpha_slider,\n", + " title=\"Plot Controls\",\n", + ")\n", + "\n", + "# Combine controls and plot side by side\n", + "complex_layout = pn.Row(\n", + " controls,\n", + " pn.Column(\n", + " \"## Dynamic Penguin Data Explorer\",\n", + " complex_interactive_plot\n", + " )\n", + ")\n", + "\n", + "complex_layout" + ] + }, + { + "cell_type": "markdown", + "id": "35019415", + "metadata": {}, + "source": [ + "## Dashboard Creation\n", + "\n", + "### Multi-Dataset Dashboard\n", + "\n", + "Let's create a comprehensive dashboard combining multiple datasets:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c34bd450", + "metadata": {}, + "outputs": [], + "source": [ + "# Create summary statistics\n", + "earthquake_summary = pn.widgets.Tabulator(\n", + " earthquakes.describe().round(2),\n", + " width=400,\n", + " height=200\n", + ")\n", + "\n", + "penguin_summary = pn.widgets.Tabulator(\n", + " penguins.describe().round(2),\n", + " width=400,\n", + " height=200\n", + ")\n", + "\n", + "# Create overview plots\n", + "earthquake_overview = earthquakes.hvplot.line(\n", + " x='time', y=['mag', 'depth'],\n", + " title=\"Earthquake Trends Overview\",\n", + " width=500, height=300,\n", + ")\n", + "\n", + "penguin_overview = penguins.hvplot.scatter(\n", + " x='bill_length_mm', y='body_mass_g',\n", + " by='species', title=\"Penguin Overview\",\n", + " width=500, height=300,\n", + ")\n", + "\n", + "summary = pn.pane.Markdown(\"\"\"\n", + " ## Multi-Dataset Analytics Dashboard\n", + "\n", + " This dashboard demonstrates:\n", + " - Multiple dataset integration\n", + " - Tables with summary statistics\n", + " - Responsive layout\n", + " - Mixed content types (plots, tables, text)\n", + " \"\"\")\n", + "\n", + "tables = pn.Row(\n", + " earthquake_summary,\n", + " penguin_summary\n", + ")\n", + "\n", + "plots = pn.Row(\n", + " earthquake_overview,\n", + " penguin_overview\n", + " )\n", + "\n", + "layout = pn.Column(summary, tables, plots)\n", + "\n", + "layout" + ] + }, + { + "cell_type": "markdown", + "id": "20cc245a", + "metadata": {}, + "source": [ + "## Panel Templates and Styling\n", + "\n", + "### Using Built-in Templates\n", + "\n", + "Panel provides several built-in templates for professional-looking applications:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5775a7ce", + "metadata": {}, + "outputs": [], + "source": [ + "# Material Design Template\n", + "material_template = pn.template.MaterialTemplate(\n", + " title=\"Material Design Dashboard\",\n", + " sidebar=[\n", + " \"## Controls\",\n", + " earthquake_selector,\n", + " pn.widgets.Button(name=\"Refresh\", button_type='primary')\n", + " ],\n", + " main=[interactive_plot],\n", + " header_background='#3f51b5'\n", + ")\n", + "\n", + "material_template.servable();" + ] + }, + { + "cell_type": "markdown", + "id": "063b9fb6-5010-43db-ae1e-3d4c9e4fbd79", + "metadata": {}, + "source": [ + ":::{tip}\n", + "You can use `.show()` to display the dashboard in a new tab.\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "id": "d6e6af9b", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "Panel extends hvPlot's capabilities in several key ways:\n", + "\n", + "### Key Features:\n", + "- **Dynamic Updates**: Change plot content without page reloads\n", + "- **Advanced Layouts**: Rows, columns, tabs, and templates\n", + "- **Interactive Widgets**: Dropdowns, sliders, buttons, and more\n", + "- **Dashboard Creation**: Professional applications with minimal code\n", + "\n", + "### When to Use Panel:\n", + "- Building interactive dashboards\n", + "- Creating applications for non-technical users\n", + "- Need for real-time data updates\n", + "- Complex layouts beyond simple plot combinations\n", + "- Deploying web applications\n", + "\n", + "### Quick Reference:\n", + "\n", + "| Task | Panel Code |\n", + "|------|------------|\n", + "| Basic pane | `pn.panel(plot)` |\n", + "| Update plot | `pane.object = new_plot` |\n", + "| Row layout | `pn.Row(plot1, plot2)` |\n", + "| Column layout | `pn.Column(plot1, plot2)` |\n", + "| Tabs | `pn.Tabs((\"Tab1\", plot1), (\"Tab2\", plot2))` |\n", + "| Interactive | `pn.bind(function, widget)` |\n", + "\n", + ":::{seealso}\n", + "- Check out other [Panel templates](https://panel.holoviz.org/tutorials/basic/templates.html) here\n", + "\n", + "- Check out the full [Panel documentation](https://panel.holoviz.org) for advanced features!\n", + ":::" + ] + } + ], + "metadata": { + "language_info": { + "name": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/doc/index.md b/doc/index.md index e868f5cec..5340961fd 100644 --- a/doc/index.md +++ b/doc/index.md @@ -434,8 +434,10 @@ align: center Tutorials User Guide +How-To Guides Gallery Reference +Explanation Developer Guide Releases Roadmap