From ee856a6ee9e7ded7559d362b3f8f7b822f03f960 Mon Sep 17 00:00:00 2001 From: Tim Hopper Date: Tue, 4 Aug 2026 20:50:36 -0400 Subject: [PATCH 1/2] Add hvPlot (Bokeh) as a comparison package --- Examples.ipynb | 271 ++++++++++++++++++++++++++++++++++ INTRO.md | 2 +- pyproject.toml | 2 + render.py | 1 + tests/test_plots.py | 11 ++ uv.lock | 349 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 635 insertions(+), 1 deletion(-) diff --git a/Examples.ipynb b/Examples.ipynb index bc5e3e9..9533820 100644 --- a/Examples.ipynb +++ b/Examples.ipynb @@ -90,6 +90,72 @@ "\n", "get_ipython().display_formatter.formatters[\"image/png\"].for_type(\n", " lp.plot.core.PlotSpec, _lets_plot_png\n", + ")\n", + "\n", + "import atexit\n", + "import io\n", + "import logging\n", + "\n", + "import holoviews as hv\n", + "import hvplot.pandas # registers the .hvplot accessor on pandas objects\n", + "from bokeh.io.export import get_screenshot_as_png\n", + "from bokeh.models import Plot\n", + "\n", + "hv.extension(\"bokeh\")\n", + "logging.getLogger(\"bokeh.io.export\").setLevel(logging.ERROR)\n", + "hv.plotting.bokeh.ElementPlot.toolbar = None\n", + "hv.plotting.bokeh.ElementPlot.fontscale = 1.6\n", + "\n", + "_bokeh_driver = None\n", + "\n", + "\n", + "def _bokeh_webdriver():\n", + " \"\"\"Headless Chrome for Bokeh's PNG export.\n", + "\n", + " Bokeh screenshots plots through Selenium. Reuse the Chrome for Testing\n", + " build Kaleido already downloads; Selenium Manager supplies the matching\n", + " chromedriver.\n", + " \"\"\"\n", + " global _bokeh_driver\n", + " if _bokeh_driver is None:\n", + " from selenium.webdriver.chrome.options import Options\n", + " from selenium.webdriver.chrome.webdriver import WebDriver as Chrome\n", + "\n", + " options = Options()\n", + " options.add_argument(\"--headless=new\")\n", + " options.add_argument(\"--hide-scrollbars\")\n", + " options.add_argument(\"--force-device-scale-factor=1\")\n", + " options.add_argument(\"--force-color-profile=srgb\")\n", + " options.add_argument(\"--no-sandbox\")\n", + " try:\n", + " from choreographer.browsers.chromium import Chromium\n", + "\n", + " options.binary_location = str(Chromium.find_browser(skip_local=False))\n", + " except Exception: # fall back to whatever Chrome is on the system\n", + " pass\n", + " _bokeh_driver = Chrome(options=options)\n", + " atexit.register(_bokeh_driver.quit)\n", + " return _bokeh_driver\n", + "\n", + "\n", + "def _hvplot_png(obj):\n", + " \"\"\"Render an hvPlot/HoloViews Bokeh figure to PNG for the cell output.\"\"\"\n", + " figure = hv.render(obj, backend=\"bokeh\")\n", + " models = figure.references()\n", + " for model in models:\n", + " if \"toolbar_location\" in model.properties():\n", + " model.toolbar_location = None\n", + " panes = [m for m in models if isinstance(m, Plot)]\n", + " if len(panes) == 1: # leave faceted grids at their per-panel size\n", + " panes[0].width, panes[0].height = 750, 750\n", + " image = get_screenshot_as_png(figure, driver=_bokeh_webdriver())\n", + " buffer = io.BytesIO()\n", + " image.save(buffer, format=\"png\")\n", + " return buffer.getvalue()\n", + "\n", + "\n", + "get_ipython().display_formatter.formatters[\"image/png\"].for_type(\n", + " hv.core.dimension.Dimensioned, _hvplot_png\n", ")" ] }, @@ -397,6 +463,27 @@ " .label(title=\"Number of Cars by Make\"))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:bar-counts", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "\"\"\"hvPlot exposes Bokeh through a `.hvplot`\n", + "accessor that mirrors pandas' own `.plot`.\n", + "\"\"\"\n", + "(mpg[\"manufacturer\"]\n", + " .value_counts(sort=False)\n", + " .hvplot.barh(\n", + " title=\"Number of Cars by Make\"))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -545,6 +632,21 @@ " .add(so.Bars(), so.Hist(binwidth=2)))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:simple-histogram", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "mpg.hvplot.hist(\"cty\", bins=12)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -700,6 +802,26 @@ " title=\"Engine Displacement in Liters vs Highway MPG\"))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "mpg.hvplot.scatter(\n", + " x=\"displ\", y=\"hwy\",\n", + " xlabel=\"Engine Displacement in Liters\",\n", + " ylabel=\"Highway MPG\",\n", + " title=\"Engine Displacement in Liters \"\n", + " \"vs Highway MPG\")" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1043,6 +1165,22 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-colors", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "mpg.hvplot.scatter(\n", + " x=\"displ\", y=\"hwy\", by=\"class\")" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1199,6 +1337,23 @@ " .label(x=\"City MPG\", y=\"Highway MPG\"))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-size", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "mpg.hvplot.scatter(\n", + " x=\"cty\", y=\"hwy\",\n", + " s=\"cyl\", scale=4, alpha=0.5)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1338,6 +1493,25 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-facet", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "(mpg.hvplot.scatter(\n", + " x=\"displ\", y=\"hwy\", by=\"class\",\n", + " subplots=True, fontscale=0.65,\n", + " width=185, height=185)\n", + " .cols(4))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1482,6 +1656,27 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-facets", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "\"\"\"`row` and `col` build a HoloViews `GridSpace`,\n", + "which sorts its panels by the facet values.\n", + "\"\"\"\n", + "mpg.hvplot.scatter(\n", + " x=\"displ\", y=\"hwy\",\n", + " row=\"cyl\", col=\"drv\", subplots=True,\n", + " fontscale=0.8, width=230, height=180)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1778,6 +1973,26 @@ " .add(so.Bar(), so.Count(), so.Stack()))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:stacked-bar-chart", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "(diamonds\n", + " .groupby([\"cut\", \"clarity\"])\n", + " .size()\n", + " .unstack()\n", + " .hvplot.bar(stacked=True, rot=45,\n", + " legend=\"top_left\"))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1907,6 +2122,26 @@ " .add(so.Bar(), so.Count(), so.Dodge()))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:dodged-bar-chart", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "(diamonds\n", + " .groupby([\"cut\", \"clarity\"])\n", + " .size()\n", + " .unstack()\n", + " .hvplot.bar(stacked=False, rot=45,\n", + " legend=\"top_left\"))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -2072,6 +2307,27 @@ "Image(fig.to_image(format=\"png\", width=750, height=750))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:stacked-kde", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "\"\"\"`xlim` only clips the axis. Unlike ggplot2's\n", + "`xlim()` it doesn't drop rows before the\n", + "densities are estimated.\n", + "\"\"\"\n", + "diamonds.hvplot.kde(\n", + " y=\"depth\", by=\"cut\",\n", + " alpha=0.1, xlim=(55, 70))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -2186,6 +2442,21 @@ "(so.Plot(ts, x=\"date\", y=\"value\")\n", " .add(so.Line()))" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:timeseries", + "package:hvplot" + ] + }, + "outputs": [], + "source": [ + "ts.hvplot.line(x=\"date\", y=\"value\")" + ] } ], "metadata": { diff --git a/INTRO.md b/INTRO.md index 333bf38..8798f06 100644 --- a/INTRO.md +++ b/INTRO.md @@ -33,7 +33,7 @@ There are several tools that can make the kinds of plots described here. At pres "[Lets-Plot](https://lets-plot.org/ "Lets-Plot: an open-source plotting library for statistical data") is an open-source plotting library for statistical data," written by JetBrains and modeled on the grammar of graphics. Its Python API tracks ggplot2 closely enough that most of the examples below translate line for line. I provide Lets-Plot examples rendered as static images. -"[Bokeh](http://bokeh.pydata.org/en/latest/ "Python interactive visualization library") is a Python interactive visualization library that targets modern web browsers for presentation." +"[Bokeh](http://bokeh.pydata.org/en/latest/ "Python interactive visualization library") is a Python interactive visualization library that targets modern web browsers for presentation." The Bokeh examples below go through [hvPlot](https://hvplot.holoviz.org/), which adds an `.hvplot` accessor to data frames that deliberately echoes the pandas `.plot` API, so most of these plots are one call with a few keyword arguments. hvPlot has no regression line or loess smoother, so those two examples are missing. I provide hvPlot examples rendered as static images. "[bqplot](https://github.com/bloomberg/bqplot) is a Grammar of Graphics-based interactive plotting framework for the Jupyter notebook." diff --git a/pyproject.toml b/pyproject.toml index 089edbf..196fdb7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,10 +22,12 @@ dependencies = [ "plotly>=5.24", "altair>=5.0", "vl-convert-python>=1.0", # Required for Altair PNG export + "hvplot>=0.12", # Bokeh via the pandas .hvplot accessor # Image rendering "kaleido>=1.0", # v1+ requires Chrome "pillow>=10.0", + "selenium>=4.11", # Required for Bokeh PNG export # Statistical tools "statsmodels>=0.14", diff --git a/render.py b/render.py index a3164ff..8f2dc57 100644 --- a/render.py +++ b/render.py @@ -24,6 +24,7 @@ "plotnine": "plotnine", "lets-plot": "lets-plot", "plotly": "plotly", + "hvplot": "hvplot (Bokeh)", "altair": "Altair", "ggplot": "ggplot2 (R)", } diff --git a/tests/test_plots.py b/tests/test_plots.py index 3a4af40..745bfa4 100644 --- a/tests/test_plots.py +++ b/tests/test_plots.py @@ -4,12 +4,15 @@ defined_plots = { "bar-counts": [ "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "hvplot", ], "dodged-bar-chart": [ "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "hvplot", ], "scatter-plot": [ "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "hvplot", ], "scatter-plot-with-colors": [ "matplotlib", @@ -20,15 +23,19 @@ "ggplot", "plotly", "altair", + "hvplot", ], "scatter-plot-with-facet": [ "seaborn", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "hvplot", ], "scatter-plot-with-facets": [ "seaborn", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "hvplot", ], "scatter-plot-with-size": [ "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "hvplot", ], "scatter-with-regression": [ "seaborn", "plotnine", "lets-plot", "ggplot", "plotly", @@ -42,9 +49,11 @@ "ggplot", "plotly", "altair", + "hvplot", ], "stacked-bar-chart": [ "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "hvplot", ], "stacked-kde": [ "pandas", @@ -55,12 +64,14 @@ "ggplot", "plotly", "altair", + "hvplot", ], "stacked-smooth-line-and-scatter": [ "plotnine", "lets-plot", "ggplot", "plotly", "altair", ], "timeseries": [ "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "hvplot", ], } diff --git a/uv.lock b/uv.lock index 1ff7ce9..3a312d4 100644 --- a/uv.lock +++ b/uv.lock @@ -173,6 +173,27 @@ css = [ { name = "tinycss2" }, ] +[[package]] +name = "bokeh" +version = "3.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "jinja2" }, + { name = "narwhals" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyyaml" }, + { name = "tornado", marker = "sys_platform != 'emscripten'" }, + { name = "xyzservices" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/6f/262b649ad2fdd5ed23341452eebfa669a73b0e0c739a369204d02ee2e201/bokeh-3.9.2.tar.gz", hash = "sha256:5be30d105b329d7d2ab1cb92f51e6bd44e23f0fe21ab8d49594b220b2ab11632", size = 5751825, upload-time = "2026-07-25T16:57:56.207Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/0c/06a99ee998b1babf95074376cbf6ede5a86af726b12cbf47c4b84fa2803a/bokeh-3.9.2-py3-none-any.whl", hash = "sha256:448e07d5ee78231f5bdece3be020024bb98696c0d6b127e0e2df0b8ba8fa9765", size = 6409477, upload-time = "2026-07-25T16:57:54.62Z" }, +] + [[package]] name = "boto3" version = "1.43.63" @@ -405,6 +426,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "colorcet" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/af/b969f541242b84cbbacabdf20862e487689e352bf0f02f90df2795d29da5/colorcet-3.2.1.tar.gz", hash = "sha256:48d9a67e6e59dc5c0a965aa1b46fe5d59cdc95cc36a95949f29313f950ac59f7", size = 2202958, upload-time = "2026-04-28T16:25:37.43Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/24/e95471ae93c08d3606c9c7343cf65d490f154daa88b50581957a0aa780f4/colorcet-3.2.1-py3-none-any.whl", hash = "sha256:3f6fde13cef2169222dd5fe2a2bf847c02d644470fdf167ed566f6421df470f7", size = 262291, upload-time = "2026-04-28T16:25:35.365Z" }, +] + [[package]] name = "comm" version = "0.2.3" @@ -625,6 +655,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] +[[package]] +name = "holoviews" +version = "1.23.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bokeh" }, + { name = "colorcet" }, + { name = "narwhals" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pandas" }, + { name = "panel" }, + { name = "param" }, + { name = "python-dateutil" }, + { name = "pyviz-comms" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/1b/f5164c974faa6f629081425609af51613ded4b637e335cd8276a48ed94f9/holoviews-1.23.1.tar.gz", hash = "sha256:97877ee2fcf7bf38ce63f49b731dba75d9b2fcfaf093ca51bd229770a4273cc0", size = 5545288, upload-time = "2026-07-02T13:27:57.327Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/b4/3755e3b3f791eefad4131f661ae04eaf70aabd6fc877f41f533e28607a5d/holoviews-1.23.1-py3-none-any.whl", hash = "sha256:43960a6e945aff019816b109873e3088c45c7413f1b679c4dfd1821e2af1a64c", size = 5992415, upload-time = "2026-07-02T13:27:55.295Z" }, +] + [[package]] name = "httpcore" version = "1.0.9" @@ -653,6 +704,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "hvplot" +version = "0.12.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bokeh" }, + { name = "colorcet" }, + { name = "holoviews" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "panel" }, + { name = "param" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/b4/ce73a568fc263efe84cab8de04673949c8f6596f6aeb1531ca2b75b80903/hvplot-0.12.2.tar.gz", hash = "sha256:9e29bbe65f94937d4eeaeccf33566540df936c8f9aeadfa12ea9f306d5237938", size = 7084556, upload-time = "2025-12-18T11:15:37.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/cd/ec193e471780dfad60e44ded5526c123695c1354910edd537d7aa1d22094/hvplot-0.12.2-py3-none-any.whl", hash = "sha256:0687e2e4d2eeb035c437af0011922abff856054299c121914d903a02b1bb1b22", size = 180626, upload-time = "2025-12-18T11:15:35.091Z" }, +] + [[package]] name = "idna" version = "3.18" @@ -1239,6 +1310,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/21/f6fa7129af18153707ffe23aa7a89adc20225e7aa35976f0424e13c6940c/lets_plot-4.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:366410ddc5e063ca4c1685afd7c0b8f8dcd20946d3abc63d1024c3ef4f706992", size = 6599801, upload-time = "2026-06-30T13:22:40.752Z" }, ] +[[package]] +name = "linkify-it-py" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "uc-micro-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/c9/06ea13676ef354f0af6169587ae292d3e2406e212876a413bf9eece4eb23/linkify_it_py-2.1.0.tar.gz", hash = "sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b", size = 29158, upload-time = "2026-03-01T07:48:47.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/de/88b3be5c31b22333b3ca2f6ff1de4e863d8fe45aaea7485f591970ec1d3e/linkify_it_py-2.1.0-py3-none-any.whl", hash = "sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e", size = 19878, upload-time = "2026-03-01T07:48:46.098Z" }, +] + [[package]] name = "logistro" version = "2.0.1" @@ -1257,6 +1340,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/69/4a5af2bc115a9a33fefe51709749de8262be3f9ba063d1753a837cdbc49c/markdown-3.10.3-py3-none-any.whl", hash = "sha256:fa6c92a00a4a3c98b22728c64a935ae1928250ae65058a6ded814d2cc29a4cea", size = 110757, upload-time = "2026-07-30T19:05:27.883Z" }, ] +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + [[package]] name = "markupsafe" version = "3.0.3" @@ -1408,6 +1503,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534, upload-time = "2026-05-08T17:33:32.055Z" }, ] +[[package]] +name = "mdit-py-plugins" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/69/6da5581c6a7fede7dc261bf4e67d6adca4196f176b43288b55b3db395b6e/mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d", size = 66663, upload-time = "2026-05-13T09:03:37.76Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + [[package]] name = "mistune" version = "3.3.4" @@ -1507,6 +1623,40 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c5/3c/3179b85b0e1c3659f0369940200cd6d0fa900e6cefcc7ea0bc6dd0e29ffb/nest_asyncio2-1.7.2-py3-none-any.whl", hash = "sha256:f5dfa702f3f81f6a03857e9a19e2ba578c0946a4ad417b4c50a24d7ba641fe01", size = 7843, upload-time = "2026-02-13T00:34:02.691Z" }, ] +[[package]] +name = "nh3" +version = "0.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/1b/ef84624f14954d270f74060a19fc550dd4f06656399447569afb584d8c06/nh3-0.3.6.tar.gz", hash = "sha256:f3736c9dd3d1856f80cd031715b84ca75cda2bbb1ac802c3da26bfce590838d7", size = 24684, upload-time = "2026-06-22T00:47:02.008Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/3e/6506aa4f23dc7b7993a2d0a45dca3ce864ec48380adfe15a173e643c63e8/nh3-0.3.6-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2411e8c3cee81a1ddd62c2a5d50585c28aa5566d373ad1db92536b95ddb24ef2", size = 1421679, upload-time = "2026-06-22T00:46:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e1/e96e7864a7a53bd6b6fab7e9632467382a2a2c1f3fed951918ad131542fb/nh3-0.3.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e196fa70c2ff2eb4de7d3df3108f8f358c1d69dff20d45b11f20a5aa227ffb6d", size = 792570, upload-time = "2026-06-22T00:46:22.179Z" }, + { url = "https://files.pythonhosted.org/packages/59/62/5b6108bedaef2b2637fed04c87bdbcb5967b9961758b41f0e466ef22a022/nh3-0.3.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:34d2b0d934156b87ee114f599a3ba9b8b9e17b5d79652ba3a13fa50903de965e", size = 842243, upload-time = "2026-06-22T00:46:23.801Z" }, + { url = "https://files.pythonhosted.org/packages/4b/4a/526f199626bfcb496bc01a268051b44737962005553b158e985ed7e64865/nh3-0.3.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2f14b7ae1fca99c4a66c981aac3974e7fbc1ca30a12673d223ae1df76680917", size = 1001468, upload-time = "2026-06-22T00:46:25.481Z" }, + { url = "https://files.pythonhosted.org/packages/49/09/0d8e3101636d9ad88cdefb2914e764cb8e876ebdbb4286bfc251277d9c67/nh3-0.3.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:889932a97fb4abb6f95fef1914c0d269ebfb60011e67121c1163059b9449dbb4", size = 1082933, upload-time = "2026-06-22T00:46:27.15Z" }, + { url = "https://files.pythonhosted.org/packages/09/a1/ea83abe738a3fbaa203dfdb836ca7cbab0e7e9609faaee4fe1d4652599c0/nh3-0.3.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edb2b4a1a27523e6cc7c417f8d21ce3d005243548b93e56b762b66b0c7f589f9", size = 1043120, upload-time = "2026-06-22T00:46:28.89Z" }, + { url = "https://files.pythonhosted.org/packages/66/69/0654482b8635012fbae67826bd6c381abb05d841ac7388b9b4666300fdad/nh3-0.3.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:43bc1ed3fa0716295fabee29ba42b2667e4a51d140b0a68e092170a765474fa6", size = 1023824, upload-time = "2026-06-22T00:46:30.453Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a6/1f7285ffadc8307c4dbeb08d21b920536d5117785056d1079e998c4dfa44/nh3-0.3.6-cp314-cp314t-win32.whl", hash = "sha256:597a8e843bea00b2eb5520658dc24a9bb032e7fc9e7c2c0c4cd29420220c9796", size = 599253, upload-time = "2026-06-22T00:46:32.072Z" }, + { url = "https://files.pythonhosted.org/packages/36/ea/5542f3c45da4c00290d9d67a65e996702e23e613c4b627de3e09cb9fe357/nh3-0.3.6-cp314-cp314t-win_amd64.whl", hash = "sha256:4713502748f564fee0633b37b3403783ce0a3af3a3d148ad91025a5bdadb7bc6", size = 612553, upload-time = "2026-06-22T00:46:33.53Z" }, + { url = "https://files.pythonhosted.org/packages/66/35/26bd47e6af5915a628281dccdac354ddf4e32f7397047894270acd8c9870/nh3-0.3.6-cp314-cp314t-win_arm64.whl", hash = "sha256:69bbb92865a693d909db3a700d3c01537533844d0948c1e9323561ce06ecda41", size = 595151, upload-time = "2026-06-22T00:46:34.878Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ab/a7653bce9a3b204be6a6931767a9e23595807bb84790ce6685e4d7e5bd08/nh3-0.3.6-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a43ebd7543555c3ac1bc353023d0794e75cb76f6f18f19c32e95441496c0cc25", size = 1443564, upload-time = "2026-06-22T00:46:36.66Z" }, + { url = "https://files.pythonhosted.org/packages/41/21/e1084ab18eb589506335c7c7576f2d4643e9a0c0e33983ef0e549a256b96/nh3-0.3.6-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1b160831c9cdb06a6c79c2f9cdb11386602938f9af260d1c457a85add4f6f69", size = 838002, upload-time = "2026-06-22T00:46:38.101Z" }, + { url = "https://files.pythonhosted.org/packages/b0/94/f48d08e6f72a406300fa11d8acd929fea1a80d4bf750fa292cb10785f126/nh3-0.3.6-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d14bf7982e7a77c0c775634c29c07ce08b38a046df73e1c1f139b3e82f18a38e", size = 823045, upload-time = "2026-06-22T00:46:39.495Z" }, + { url = "https://files.pythonhosted.org/packages/25/bb/431615ba1d1d3eb63cde0f974f2114edf863a8a3f6049a12fed23fc241d3/nh3-0.3.6-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:44673b27010051ab5a5e438a86ec31bbda61d4a77d7e900af6b7be3037c1abae", size = 1093171, upload-time = "2026-06-22T00:46:41.21Z" }, + { url = "https://files.pythonhosted.org/packages/0e/24/a0d80182a18919665fefd19c1c06f1d1df1c9a6455d0252de40c034a0bc3/nh3-0.3.6-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6b7beece07525dc6e6b0fc2f104442de2ba328360ad00e50cbe2e1fd620447d", size = 1049217, upload-time = "2026-06-22T00:46:42.804Z" }, + { url = "https://files.pythonhosted.org/packages/0a/13/6f1e302ca674ac74362e150848ad56a1be5145391204f74facdb8e94df12/nh3-0.3.6-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:455469a29951edc92bc48b47ac2281c3f2609e6c4f6a047056449f8c2c23facf", size = 917372, upload-time = "2026-06-22T00:46:44.495Z" }, + { url = "https://files.pythonhosted.org/packages/5b/67/314f6151bad77a93d751978a344033e1fc890822f05f0416079338e34231/nh3-0.3.6-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:905f877dc66dd7aea4a76e54bcb26acb5ff8216f720c0017ccf63e0e6035698e", size = 806699, upload-time = "2026-06-22T00:46:45.99Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a6/bfaa00046e58603507dcfc266c4778e3ab7adf68a5dedd73b6274b8d9314/nh3-0.3.6-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:25c733bee928530556b1db0ea46c52cf5aa686146e38e60a6fc7cb801ef91cec", size = 835165, upload-time = "2026-06-22T00:46:47.617Z" }, + { url = "https://files.pythonhosted.org/packages/30/a8/fb2c38845efb703a9173bffdfc745fc64d2b0e55cfc73a3647d2f028250c/nh3-0.3.6-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f90d9a0cfdbee218994fdaaeeb5a0fde62d08f35e4eef0378ec1e2200172fd0", size = 858282, upload-time = "2026-06-22T00:46:49.276Z" }, + { url = "https://files.pythonhosted.org/packages/68/17/06e72a18ee9b572914447338237ca7eb164c0df901f141bc10d1282247a2/nh3-0.3.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:82ca5bf427ad1b216b65ede1a2e2d87dc49bec417ceba0f297213107d3cd9d78", size = 1014328, upload-time = "2026-06-22T00:46:51.026Z" }, + { url = "https://files.pythonhosted.org/packages/11/f9/3966c61455668c08853bf5e33b4bed93c421f3194ce4de896dc248d6f6ce/nh3-0.3.6-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f5ed5fe84aee7f39db95c214a7421bf0499fbf500fec6d86a4e29bfc37971438", size = 1098207, upload-time = "2026-06-22T00:46:52.674Z" }, + { url = "https://files.pythonhosted.org/packages/19/d3/479cb4ae440424825735d60525b53e3c77fd60fd6e6afc0e984f00eb0178/nh3-0.3.6-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:082675ff87b9385ec430ffe6d5847ba7456cc39b73720cd4add472f9f4cffd56", size = 1056961, upload-time = "2026-06-22T00:46:54.335Z" }, + { url = "https://files.pythonhosted.org/packages/17/0c/6cdb5ee1e127be50dc8391e54bddc1f64e87bf4bfad0c55633320e2e02db/nh3-0.3.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36d06341bd501240d320f5942481ed5e6846136b666e1ba4faf802b78ebc875f", size = 1033829, upload-time = "2026-06-22T00:46:56.258Z" }, + { url = "https://files.pythonhosted.org/packages/e9/55/9de666ad975d6ccd77d799ea0add55ee2347aa81286ce21b2a97c070746b/nh3-0.3.6-cp38-abi3-win32.whl", hash = "sha256:5276ef17bdba9ad8040575c74072008b13aae429436e9d0429e718bb5f90f4da", size = 609081, upload-time = "2026-06-22T00:46:57.665Z" }, + { url = "https://files.pythonhosted.org/packages/82/fa/2b5d684e3edf1e81bfd02d298c78c3e3da77ca1d8a2be3183a79544a7548/nh3-0.3.6-cp38-abi3-win_amd64.whl", hash = "sha256:f338ac7d594c067679f1e99b4f5ec3906842979560f9d8f15d6bdfa39a353b10", size = 624461, upload-time = "2026-06-22T00:46:59.163Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e5/7cafee2f0413ca4cb0ef3bd111e94d408a48810008b283ad8aee00dd1809/nh3-0.3.6-cp38-abi3-win_arm64.whl", hash = "sha256:69f365963f63a1e9bff53bdbb3c542c7c2efed3e163c9d5d83a772a2ac468c21", size = 603060, upload-time = "2026-06-22T00:47:00.596Z" }, +] + [[package]] name = "notebook" version = "7.6.1" @@ -1747,6 +1897,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/21/5a3f1e8913103b703a436a5664238e5b965ec392b555fe68943ea3691e6b/orjson-3.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9", size = 126687, upload-time = "2026-05-06T15:11:06.602Z" }, ] +[[package]] +name = "outcome" +version = "1.3.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/df/77698abfac98571e65ffeb0c1fba8ffd692ab8458d617a0eed7d9a8d38f2/outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8", size = 21060, upload-time = "2023-10-26T04:26:04.361Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/8b/5ab7257531a5d830fc8000c476e63c935488d74609b50f9384a643ec0a62/outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b", size = 10692, upload-time = "2023-10-26T04:26:02.532Z" }, +] + [[package]] name = "overrides" version = "7.7.0" @@ -1838,6 +2000,55 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663, upload-time = "2024-01-18T20:08:11.28Z" }, ] +[[package]] +name = "panel" +version = "1.9.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bokeh" }, + { name = "linkify-it-py" }, + { name = "markdown" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "narwhals" }, + { name = "nh3" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "panel-material-ui" }, + { name = "param" }, + { name = "pyviz-comms" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/89/65a7eb921a29a203dd0469ada43573108fbb95ad1c83ec947ba32bd3a0f9/panel-1.9.3.tar.gz", hash = "sha256:4d7de30561e31674451ade5f029cddbec6156fd30f3e4cfd7da82f6ecfcbb98f", size = 32215008, upload-time = "2026-06-01T16:02:03.823Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/0c/121f4a7d7be7ddbbc9923f3d5c687cb77e3958b2a2e812be69fd76461e4f/panel-1.9.3-py3-none-any.whl", hash = "sha256:46e8d82541b7ae47d2c2393dd746800b95fa7ad8eb545cc39ab5f366ba00a664", size = 30305471, upload-time = "2026-06-01T16:01:59.277Z" }, +] + +[[package]] +name = "panel-material-ui" +version = "0.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bokeh" }, + { name = "packaging" }, + { name = "panel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/24/cf/09318155fbfaf7236afeea41d62f76b1e993b10b0bb1141d9e3e0cb32f0d/panel_material_ui-0.14.1.tar.gz", hash = "sha256:709827337c39eb6ebc40fbd0297ebb7c8e8e104be11defac5c22e4323d4ccefa", size = 3988677, upload-time = "2026-07-29T12:13:15.157Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/0c/abb3ef5fb41dd87f1a5cb8fc380ecdc609614d97c9b28d5f2073342ebbf0/panel_material_ui-0.14.1-py3-none-any.whl", hash = "sha256:00c4c0956f401dcf0c7a468c8705a3e883e0c442061d13c015321cb82035801b", size = 1730907, upload-time = "2026-07-29T12:13:12.554Z" }, +] + +[[package]] +name = "param" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9d/33f32981c9cda89fe22abff60f7c71ef4af3e566b6af530392f8b938470d/param-2.4.1.tar.gz", hash = "sha256:364a33bd8a968b053d8a49d319af78dc31b3ccb9661ecb95c29a3ea509d7e443", size = 217776, upload-time = "2026-06-09T13:17:33.665Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/37/1351bbfabbacbe92cd38774f8779ff680fab48c36b5fed9bcfe0c160009c/param-2.4.1-py3-none-any.whl", hash = "sha256:6cdc0869b3ade232fc6d01691223cf86c2bdb53a6d5ba67a33dba838d8b8cf4c", size = 151874, upload-time = "2026-06-09T13:17:31.996Z" }, +] + [[package]] name = "parso" version = "0.8.7" @@ -2141,6 +2352,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/b9/3766cc361d93edb2ce81e2e1f87dd98f314d7d513877a342d31b30741680/pypng-0.20220715.0-py3-none-any.whl", hash = "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c", size = 58057, upload-time = "2022-07-15T14:11:03.713Z" }, ] +[[package]] +name = "pysocks" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, +] + [[package]] name = "pytest" version = "9.1.1" @@ -2185,6 +2405,7 @@ source = { virtual = "." } dependencies = [ { name = "altair" }, { name = "boto3" }, + { name = "hvplot" }, { name = "ipython" }, { name = "jinja2" }, { name = "jinja2-highlight" }, @@ -2204,6 +2425,7 @@ dependencies = [ { name = "rpy2" }, { name = "scikit-misc" }, { name = "seaborn" }, + { name = "selenium" }, { name = "simplegeneric" }, { name = "statsmodels", version = "0.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and sys_platform == 'emscripten'" }, { name = "statsmodels", version = "0.14.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or sys_platform != 'emscripten'" }, @@ -2215,6 +2437,7 @@ dependencies = [ requires-dist = [ { name = "altair", specifier = ">=5.0" }, { name = "boto3", specifier = ">=1.28" }, + { name = "hvplot", specifier = ">=0.12" }, { name = "ipython", specifier = ">=8.12" }, { name = "jinja2", specifier = ">=3.1" }, { name = "jinja2-highlight", specifier = ">=0.6" }, @@ -2233,6 +2456,7 @@ requires-dist = [ { name = "rpy2", specifier = ">=3.5" }, { name = "scikit-misc", specifier = ">=0.3" }, { name = "seaborn", specifier = ">=0.13" }, + { name = "selenium", specifier = ">=4.11" }, { name = "simplegeneric", specifier = ">=0.8" }, { name = "statsmodels", specifier = ">=0.14" }, { name = "tzlocal", specifier = ">=5.0" }, @@ -2242,6 +2466,18 @@ requires-dist = [ [package.metadata.requires-dev] dev = [] +[[package]] +name = "pyviz-comms" +version = "3.0.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "param" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/ee/2b5367b911bab506662abffe6f342101a9b3edacee91ff9afe62db5fe9a7/pyviz_comms-3.0.6.tar.gz", hash = "sha256:73d66b620390d97959b2c4d8a2c0778d41fe20581be4717f01e46b8fae8c5695", size = 197772, upload-time = "2025-06-20T16:50:30.97Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/5a/f8c0868199bbb231a02616286ce8a4ccb85f5387b9215510297dcfedd214/pyviz_comms-3.0.6-py3-none-any.whl", hash = "sha256:4eba6238cd4a7f4add2d11879ce55411785b7d38a7c5dba42c7a0826ca53e6c2", size = 84275, upload-time = "2025-06-20T16:50:28.826Z" }, +] + [[package]] name = "pywinpty" version = "3.0.5" @@ -2799,6 +3035,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914, upload-time = "2024-01-25T13:21:49.598Z" }, ] +[[package]] +name = "selenium" +version = "4.46.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "trio" }, + { name = "trio-websocket" }, + { name = "typing-extensions" }, + { name = "urllib3", extra = ["socks"] }, + { name = "websocket-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a0/53/053df98ef0c38535a74ea732217eb2dbfddc7ff5442a47a4b315ba577f85/selenium-4.46.0.tar.gz", hash = "sha256:54f7e1a4df5f7508ac8c38ce2ea584db1b27083dd79962b22524219219df5cbe", size = 1006000, upload-time = "2026-07-11T00:38:41.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/f4/365c723a37c7e9ba6a6a19bf0c9e407137703f56a72c6201d0e4cb1432cb/selenium-4.46.0-py3-none-any.whl", hash = "sha256:f03e864770a21ab32009961c9d015cb2c6275eba45c926c272e02d90af423aad", size = 9428587, upload-time = "2026-07-11T00:38:38.84Z" }, +] + [[package]] name = "send2trash" version = "2.1.0" @@ -2887,6 +3140,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, +] + [[package]] name = "soupsieve" version = "2.9.1" @@ -3020,6 +3291,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/71/2e/7b1c769803121b809112cf9a00681c472eae1d80e32d7ec0e0bd61d0d0e1/tornado-6.5.7-cp39-abi3-win_arm64.whl", hash = "sha256:ff934fce95643af5f11efdae618eaa73d469dc588641e5c8d19295a0c65c4796", size = 450506, upload-time = "2026-06-08T17:34:49.702Z" }, ] +[[package]] +name = "tqdm" +version = "4.70.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/3b/6c24bec5be5e743ffd99576daa5cc077722fc7d5bbc00bd133fa0c698dc6/tqdm-4.70.0.tar.gz", hash = "sha256:55b0b0dbd97462d06ebee91e4dac24ed4d4702be82b24f07e6c1d27e08cea220", size = 795438, upload-time = "2026-07-27T11:33:15.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/1c/01bfd571a64e7f270e6bab5e33777debe0edc56759233ce84f27dec92d14/tqdm-4.70.0-py3-none-any.whl", hash = "sha256:7f585706bfddbdebf89daac705b2dfcc16890130727d3197ca62c732b4310953", size = 80184, upload-time = "2026-07-27T11:33:13.167Z" }, +] + [[package]] name = "traitlets" version = "5.16.1" @@ -3029,6 +3312,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl", hash = "sha256:f775618166caa0396c8e337099240f2bd3e5e917d203b2e6fbe21a58d3cb1f6b", size = 86211, upload-time = "2026-08-03T08:32:34.48Z" }, ] +[[package]] +name = "trio" +version = "0.33.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "cffi", marker = "implementation_name != 'pypy' and os_name == 'nt'" }, + { name = "idna" }, + { name = "outcome" }, + { name = "sniffio" }, + { name = "sortedcontainers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/b6/c744031c6f89b18b3f5f4f7338603ab381d740a7f45938c4607b2302481f/trio-0.33.0.tar.gz", hash = "sha256:a29b92b73f09d4b48ed249acd91073281a7f1063f09caba5dc70465b5c7aa970", size = 605109, upload-time = "2026-02-14T18:40:55.386Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/93/dab25dc87ac48da0fe0f6419e07d0bfd98799bed4e05e7b9e0f85a1a4b4b/trio-0.33.0-py3-none-any.whl", hash = "sha256:3bd5d87f781d9b0192d592aef28691f8951d6c2e41b7e1da4c25cde6c180ae9b", size = 510294, upload-time = "2026-02-14T18:40:53.313Z" }, +] + +[[package]] +name = "trio-websocket" +version = "0.12.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "outcome" }, + { name = "trio" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/3c/8b4358e81f2f2cfe71b66a267f023a91db20a817b9425dd964873796980a/trio_websocket-0.12.2.tar.gz", hash = "sha256:22c72c436f3d1e264d0910a3951934798dcc5b00ae56fc4ee079d46c7cf20fae", size = 33549, upload-time = "2025-02-25T05:16:58.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/19/eb640a397bba49ba49ef9dbe2e7e5c04202ba045b6ce2ec36e9cadc51e04/trio_websocket-0.12.2-py3-none-any.whl", hash = "sha256:df605665f1db533f4a386c94525870851096a223adcb97f72a07e8b4beba45b6", size = 21221, upload-time = "2025-02-25T05:16:57.545Z" }, +] + [[package]] name = "typing-extensions" version = "4.16.0" @@ -3059,6 +3373,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/a4/017a7a6cbe387d961a688ec31364ae60a5c4e22c96ae9921b79a947c855d/tzlocal-5.4.4-py3-none-any.whl", hash = "sha256:aae09f0126a8a86fa736be266eb4a471380d26a0de3bc14844e7821fee3e2a15", size = 18115, upload-time = "2026-06-29T08:03:38.666Z" }, ] +[[package]] +name = "uc-micro-py" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/78/67/9a363818028526e2d4579334460df777115bdec1bb77c08f9db88f6389f2/uc_micro_py-2.0.0.tar.gz", hash = "sha256:c53691e495c8db60e16ffc4861a35469b0ba0821fe409a8a7a0a71864d33a811", size = 6611, upload-time = "2026-03-01T06:31:27.526Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/73/d21edf5b204d1467e06500080a50f79d49ef2b997c79123a536d4a17d97c/uc_micro_py-2.0.0-py3-none-any.whl", hash = "sha256:3603a3859af53e5a39bc7677713c78ea6589ff188d70f4fee165db88e22b242c", size = 6383, upload-time = "2026-03-01T06:31:26.257Z" }, +] + [[package]] name = "uri-template" version = "1.3.0" @@ -3077,6 +3400,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, ] +[package.optional-dependencies] +socks = [ + { name = "pysocks" }, +] + [[package]] name = "vl-convert-python" version = "1.9.0.post1" @@ -3134,3 +3462,24 @@ sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b wheels = [ { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, ] + +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, +] + +[[package]] +name = "xyzservices" +version = "2026.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/08/3cb9f67a8d48021aca2a02292cc26eecd71d949ae70ad66420a8730cc302/xyzservices-2026.3.0.tar.gz", hash = "sha256:d226866a5d8e9fef337034d8da37a8298f0a1d9d1489b4018e69579eb321fea4", size = 1135736, upload-time = "2026-03-30T14:42:25.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a9/d23012099dc88ec69a29c6407b41d89681cb674c2043cd5b467c7e299c08/xyzservices-2026.3.0-py3-none-any.whl", hash = "sha256:503183d4b322bfebc3c50cdd21192aa3e81e36c5efbf9133d54ae82143e0576b", size = 94101, upload-time = "2026-03-30T14:42:24.608Z" }, +] From b5168a8e68023e0972ae59411b42affd5c4e1cd9 Mon Sep 17 00:00:00 2001 From: Tim Hopper Date: Tue, 4 Aug 2026 20:56:16 -0400 Subject: [PATCH 2/2] Tune hvPlot dodged bars and harden the Bokeh webdriver lookup --- CLAUDE.md | 1 + Examples.ipynb | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 490136a..d725ba4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -95,6 +95,7 @@ All plots are rendered to static PNG images inside the executed notebook: - **Plotly**: `pio.renderers.default = "png"` with Kaleido (needs Chrome; `uv run kaleido_get_chrome`) - **Altair**: `alt.renderers.enable("png")` with vl-convert-python (set in the notebook's first cell — do not remove) - **R/ggplot2**: rpy2 against the system R installation (`%%R` cell magic) +- **Lets-Plot** and **hvPlot/Bokeh**: an `image/png` IPython formatter registered in the notebook's first cell. Bokeh has no headless renderer, so hvPlot figures are screenshotted through Selenium driving the same Chrome for Testing build Kaleido downloads; Selenium Manager fetches the matching chromedriver on first use (needs network). ## Adding New Plots diff --git a/Examples.ipynb b/Examples.ipynb index 9533820..ee51a95 100644 --- a/Examples.ipynb +++ b/Examples.ipynb @@ -131,9 +131,10 @@ " from choreographer.browsers.chromium import Chromium\n", "\n", " options.binary_location = str(Chromium.find_browser(skip_local=False))\n", + " _bokeh_driver = Chrome(options=options)\n", " except Exception: # fall back to whatever Chrome is on the system\n", - " pass\n", - " _bokeh_driver = Chrome(options=options)\n", + " options.binary_location = \"\"\n", + " _bokeh_driver = Chrome(options=options)\n", " atexit.register(_bokeh_driver.quit)\n", " return _bokeh_driver\n", "\n", @@ -2138,8 +2139,8 @@ " .groupby([\"cut\", \"clarity\"])\n", " .size()\n", " .unstack()\n", - " .hvplot.bar(stacked=False, rot=45,\n", - " legend=\"top_left\"))" + " .hvplot.bar(stacked=False, rot=90,\n", + " fontscale=0.9))" ] }, {