diff --git a/README.md b/README.md index 38bccb10d..30c4f0cb6 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ The API is based on the familiar Pandas `.plot` API and the innovative `.interac hvPlot -- supports a wide range of data sources including [Pandas](http://pandas.pydata.org), [Polars](https://docs.pola.rs/), [XArray](http://xarray.pydata.org), [Dask](http://dask.pydata.org), [Streamz](http://streamz.readthedocs.io), [Intake](http://github.com/ContinuumIO/intake), [GeoPandas](http://geopandas.org) and [NetworkX](https://networkx.github.io/documentation/stable/). +- supports a wide range of data sources including [Pandas](http://pandas.pydata.org), [Polars](https://docs.pola.rs/), [XArray](http://xarray.pydata.org), [Dask](http://dask.pydata.org), [Intake](http://github.com/ContinuumIO/intake), [GeoPandas](http://geopandas.org) and [NetworkX](https://networkx.github.io/documentation/stable/). - supports the plotting backends [Bokeh](https://docs.bokeh.org/en/latest/), [Matplotlib](https://matplotlib.org/) and [Plotly](https://plotly.com/python/). - exposes the powerful tools from the [HoloViz](https://holoviz.org/) ecosystem in a familiar and convenient API. diff --git a/binder/environment.yml b/binder/environment.yml index 9764eaf62..44ee3ef2a 100644 --- a/binder/environment.yml +++ b/binder/environment.yml @@ -8,7 +8,6 @@ dependencies: - geopandas>=0.9.0 - xarray>=0.18.2 - networkx>=2.6.3 - - streamz>=0.3.0 - intake>=0.6.5 - intake-parquet>=0.2.3 - intake-xarray>=0.5.0 diff --git a/doc/conftest.py b/doc/conftest.py index b2123a8bc..55fdfc714 100644 --- a/doc/conftest.py +++ b/doc/conftest.py @@ -1,5 +1,4 @@ import os -import sys from importlib.util import find_spec @@ -16,6 +15,7 @@ collect_ignore_glob = [ 'user_guide/Streaming.ipynb', + 'ref/plotting_options/streaming.ipynb', ] # Slow examples are excluded by default. @@ -52,11 +52,6 @@ 'user_guide/Plotting_with_Plotly.ipynb', ] -if not find_spec('streamz') or sys.version_info[:2] >= (3, 14): - collect_ignore_glob += [ - 'ref/plotting_options/streaming.ipynb', - ] - if _PD_GE_3_0_0: # Because of fugue collect_ignore_glob += ['ref/data_libraries.ipynb'] diff --git a/doc/index.md b/doc/index.md index a9763c788..b41ec362f 100644 --- a/doc/index.md +++ b/doc/index.md @@ -185,21 +185,6 @@ align: center --- ::: -:::{tab-item} Streamz -```python -import hvplot.streamz -from streamz.dataframe import Random - -df_streamz = Random(interval='200ms', freq='50ms') -df_streamz.hvplot() -``` -```{image} ./assets/streamz_demo.gif ---- -alt: Works with Streamz -align: center ---- -::: - :::: `.hvplot()` can generate plots with [Bokeh](https://bokeh.org/) (default), [Matplotlib](https://matplotlib.org/) or [Plotly](https://plotly.com/). diff --git a/doc/ref/api/index.md b/doc/ref/api/index.md index 2d4e91ee5..736562d42 100644 --- a/doc/ref/api/index.md +++ b/doc/ref/api/index.md @@ -36,7 +36,7 @@ df.hvplot(kind='scatter') Under the hood, these special imports like `import hvplot.pandas` register an accessor that returns an instance of an `hvPlotBase` class. Tabular-like data sources rely on the `hvPlotTabular` class and gridded-like sources on `hvPlot` (subclass of `hvPlotTabular` extended with methods like {meth}`image `): -- `hvPlotTabular`: cuDF, Dask, Fugue, Ibis, Pandas, Streamz +- `hvPlotTabular`: cuDF, Dask, Fugue, Ibis, Pandas - `hvPlotTabularDuckDB`: DuckDB - `hvPlotTabularPolars`: Polars - `hvPlot`: Xarray diff --git a/doc/ref/data_libraries.ipynb b/doc/ref/data_libraries.ipynb index e8ce66656..8f5b7f33e 100644 --- a/doc/ref/data_libraries.ipynb +++ b/doc/ref/data_libraries.ipynb @@ -42,20 +42,20 @@ "id": "5f0d3dcc", "metadata": {}, "source": [ - "| Source | Module | Type | HoloViews interface | Comment |\n", - "| --------------------------------- | ---------------- | ----------------- | ------------------- | ----------------------------- |\n", - "| [Pandas](#libraries-pandas) | `hvplot.pandas` | Tabular | ✅ | |\n", - "| [Dask](#libraries-dask) | `hvplot.dask` | Tabular | ✅ | |\n", - "| [Geopandas](#libraries-geopandas) | `hvplot.pandas` | Tabular | ✅ | |\n", - "| [Ibis](#libraries-ibis) | `hvplot.ibis` | Tabular | ✅ | |\n", - "| [Polars](#libraries-polars) | `hvplot.polars` | Tabular | ❌ | To Pandas |\n", - "| [DuckDB](#libraries-duckdb) | `hvplot.duckdb` | Tabular | ❌ | To Pandas |\n", - "| [RAPIDS cuDF](#libraries-cudf) | `hvplot.cudf` | Tabular | ✅ | GPU |\n", - "| [Fugue](#libraries-fugue) | `hvplot.fugue` | Tabular | ❌ | Experimental |\n", + "| Source | Module | Type | HoloViews interface | Comment |\n", + "| --------------------------------- | ---------------- | ---------------- | ------------------- | ----------------------------- |\n", + "| [Pandas](#libraries-pandas) | `hvplot.pandas` | Tabular | ✅ | |\n", + "| [Dask](#libraries-dask) | `hvplot.dask` | Tabular | ✅ | |\n", + "| [Geopandas](#libraries-geopandas) | `hvplot.pandas` | Tabular | ✅ | |\n", + "| [Ibis](#libraries-ibis) | `hvplot.ibis` | Tabular | ✅ | |\n", + "| [Polars](#libraries-polars) | `hvplot.polars` | Tabular | ❌ | To Pandas |\n", + "| [DuckDB](#libraries-duckdb) | `hvplot.duckdb` | Tabular | ❌ | To Pandas |\n", + "| [RAPIDS cuDF](#libraries-cudf) | `hvplot.cudf` | Tabular | ✅ | GPU |\n", + "| [Fugue](#libraries-fugue) | `hvplot.fugue` | Tabular | ❌ | Experimental |\n", "| [Xarray](#libraries-xarray) | `hvplot.xarray` | Multidimensional | ✅ | |\n", - "| [Intake](#libraries-intake) | `hvplot.intake` | Catalog | ❌ | |\n", - "| [Streamz](#libraries-streamz) | `hvplot.streamz` | Streaming | ✅ | |\n", - "| [NetworkX](#libraries-networkx) | - | Graph | - | [Different API](api-networkx) |\n" + "| [Intake](#libraries-intake) | `hvplot.intake` | Catalog | ❌ | |\n", + "| [Streamz](#libraries-streamz) | `hvplot.streamz` | Streaming | ✅ | **Deprecated** |\n", + "| [NetworkX](#libraries-networkx) | - | Graph | - | [Different API](api-networkx) |\n" ] }, { @@ -630,7 +630,12 @@ "metadata": {}, "source": [ "(libraries-streamz)=\n", - "### Streamz" + "### Streamz\n", + "\n", + ":::{warning}\n", + "\n", + "Streamz support has been deprecated and will be removed in a future version.\n", + ":::" ] }, { diff --git a/doc/ref/plotting_options/streaming.ipynb b/doc/ref/plotting_options/streaming.ipynb index 76eca2c9b..52371bae9 100644 --- a/doc/ref/plotting_options/streaming.ipynb +++ b/doc/ref/plotting_options/streaming.ipynb @@ -7,6 +7,10 @@ "source": [ "# Streaming Options\n", "\n", + ":::{warning}\n", + "Streamz support has been deprecated and will be removed in a future version.\n", + ":::\n", + "\n", ":::{note}\n", "All the examples below require a live python process to experience the full interactivity of the plots.\n", ":::\n", diff --git a/doc/tutorials/getting_started.ipynb b/doc/tutorials/getting_started.ipynb index a33657fe3..cc6af29c0 100644 --- a/doc/tutorials/getting_started.ipynb +++ b/doc/tutorials/getting_started.ipynb @@ -66,7 +66,7 @@ "To run the guides in this site locally, create an environment with the required dependencies:\n", "\n", "```bash\n", - "conda create -n hvplot-env -c conda-forge --override-channels hvplot geoviews datashader xarray pandas geopandas dask streamz networkx intake intake-xarray intake-parquet s3fs scipy spatialpandas pooch rasterio fiona plotly matplotlib hvsampledata jupyterlab\n", + "conda create -n hvplot-env -c conda-forge --override-channels hvplot geoviews datashader xarray pandas geopandas dask networkx intake intake-xarray intake-parquet s3fs scipy spatialpandas pooch rasterio fiona plotly matplotlib hvsampledata jupyterlab\n", "```" ] }, diff --git a/doc/user_guide/Introduction.ipynb b/doc/user_guide/Introduction.ipynb index a7d793214..a6f6dfd95 100644 --- a/doc/user_guide/Introduction.ipynb +++ b/doc/user_guide/Introduction.ipynb @@ -19,7 +19,6 @@ "* [Polars](https://www.pola.rs/): Polars is a fast DataFrame library/in-memory query engine (columnar/tabular data)\n", "* [Dask](https://www.dask.org): DataFrame, Series (distributed/out of core arrays and columnar data)\n", "* [XArray](https://xarray.pydata.org): Dataset, DataArray (labelled multidimensional arrays)\n", - "* [Streamz](https://streamz.readthedocs.io): DataFrame(s), Series(s) (streaming columnar data)\n", "* [Intake](https://github.com/ContinuumIO/intake): DataSource (data catalogues)\n", "* [GeoPandas](https://geopandas.org): GeoDataFrame (geometry data)\n", "* [NetworkX](https://networkx.github.io/documentation/stable/): Graph (network graphs)\n", diff --git a/doc/user_guide/Streaming.ipynb b/doc/user_guide/Streaming.ipynb deleted file mode 100644 index 4cc21ee8c..000000000 --- a/doc/user_guide/Streaming.ipynb +++ /dev/null @@ -1,252 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Streaming" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "hvPlot supports [streamz](https://github.com/mrocklin/streamz) DataFrame and Series objects, automatically generating streaming plots in a Jupyter notebook or deployed as a [Bokeh Server app](https://bokeh.pydata.org/en/latest/docs/user_guide/server.html). \n", - "\n", - "All hvPlot methods on streamz objects return HoloViews `DynamicMap` objects that update the plot whenever `streamz` triggers an event. For more information on `DynamicMap` and HoloViews dynamic plotting support, see the [HoloViews User Guide](https://holoviews.org/user_guide); here we will focus on using the simple, high-level hvPlot API rather than on the details of how events and data flow behind the scenes.\n", - "\n", - "**All plots generated by the streamz plotting interface dynamically stream data from Python into the web browser. The web version for this page includes *screen captures* of the streaming visualizations, not live streaming data.**\n", - "\n", - "As for any of the data backends, we start by patching the streamz library with the plotting API:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import hvplot.streamz # noqa" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Basic plotting\n", - "\n", - "Throughout this section we will be using the ``Random`` object from streamz, which provides an easy way of generating a DataFrame of random streaming data but which could be substituted with any streamz ``DataFrame`` or ``Series`` driven by a live, external data source instead. To stop the ``Random`` stream you can call ``df.stop()`` at any point." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from streamz.dataframe import Random\n", - "df = Random(interval='200ms', freq='50ms')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The plot method on Series and DataFrame is a simple wrapper around a\n", - "line plot, which will plot all columns:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df.hvplot()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The plot method can also be called on a Series, plotting a specific column:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df.z.cumsum().hvplot()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "Actually, *all* the functionality described in the [Plotting](Plotting.ipynb) user guide should work just the same as it does for a regular (non-streaming) dask or pandas DataFrame or Series, but will now update as new data appears.\n", - "\n", - "## Controlling the backlog\n", - "\n", - "The main difference when using a streaming DataFrame is that a only a certain amount of data will be buffered and displayed. The number of rows of data that will be buffered can be controlled by the ``backlog`` parameter. Here, let's buffer and display 10 rows of our streaming DataFrame as a table:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df.hvplot.table(width=400, backlog=10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This of course only has an effect when we are directly streaming data point by point. When we have an aggregate DataFrame, the plot will continuously accumulate updates:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df.groupby('y').sum().hvplot.bar(x='y')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Composing Plots\n", - "\n", - "hvPlot is a convenient API for generating HoloViews objects. One of the core strengths of HoloViews objects is the ease with which they can be composed, which works with streaming plots just as with static ones. Individual plots can be composed using the ``*`` and ``+`` operators, which overlay and compose plots into layouts respectively. For more information on composing objects see the HoloViews [User Guide](https://holoviews.org/user_guide/Composing_Elements.html).\n", - "\n", - "By using these operators we can combine multiple plots into composite Overlay and Layout objects, and lay them out in two columns using the ``Layout.cols`` method:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "(df.hvplot.line(width=400, backlog=100) * df.hvplot.scatter(width=400, backlog=100) +\n", - " df.groupby('y').sum().hvplot.bar('y', 'x', width=400) +\n", - " df.hvplot.box(width=400) + df.x.hvplot.kde(width=400, shared_axes=False)).cols(2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Deployment as Bokeh apps\n", - "\n", - "HoloViews objects automatically render themselves in Jupyter notebook cells, but when deploying a bokeh app the plot has to be rendered explicitly. Deploying as a Bokeh Server app allows you to share live, dynamically updated visualizations like those for streaming data, backed by a running Python process.\n", - "\n", - "The following example describes how to set up a streaming DataFrame, declare some plots, compose them, set up a callback to update the plot and finally convert the composite plot to a bokeh Document, which can be served from a script using ``bokeh serve`` on the commandline.\n", - "\n", - "```python\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import hvplot.streamz\n", - "import holoviews as hv\n", - "\n", - "from streamz import Stream\n", - "from streamz.dataframe import DataFrame\n", - "\n", - "renderer = hv.renderer('bokeh')\n", - "\n", - "# Set up streaming DataFrame\n", - "stream = Stream()\n", - "index = pd.DatetimeIndex([])\n", - "example = pd.DataFrame({'x': [], 'y': [], 'z': []},\n", - " columns=['x', 'y', 'z'], index=[])\n", - "df = DataFrame(stream, example=example)\n", - "cumulative = df.cumsum()[['x', 'z']]\n", - "\n", - "# Declare plots\n", - "line = cumulative.hvplot.line(width=400)\n", - "scatter = cumulative.hvplot.scatter(width=400)\n", - "bars = df.groupby('y').sum().hvplot.bar(width=400)\n", - "box = df.hvplot.box(width=400)\n", - "kde = df.x.hvplot.kde(width=400)\n", - "\n", - "# Compose plots\n", - "layout = (line * scatter + bars + box + kde).cols(2)\n", - "\n", - "# Set up callback with streaming data\n", - "def emit():\n", - " now = pd.datetime.now()\n", - " delta = np.timedelta64(500, 'ms')\n", - " index = pd.date_range(np.datetime64(now)-delta, now, freq='100ms')\n", - " df = pd.DataFrame({'x': np.random.randn(len(index)),\n", - " 'y': np.random.randint(0, 10, len(index)),\n", - " 'z': np.random.randn(len(index))},\n", - " columns=['x', 'y', 'z'], index=index)\n", - " stream.emit(df)\n", - "\n", - "# Render layout to bokeh server Document and attach callback\n", - "doc = renderer.server_doc(layout)\n", - "doc.title = 'Streamz HoloViews based Plotting API Bokeh App Demo'\n", - "doc.add_periodic_callback(emit, 500)\n", - "```\n", - "\n", - "For more details on deploying Bokeh apps see the HoloViews [User Guide](https://holoviews.org/user_guide/Deploying_Bokeh_Apps.html).\n", - "\n", - "## Using HoloViews directly\n", - "\n", - "HoloViews itself includes first class support for streamz DataFrame and Series; for more details see the [Streaming Data section](https://holoviews.org/user_guide/Streaming_Data.html) in the HoloViews documentation." - ] - } - ], - "metadata": { - "language_info": { - "name": "python", - "pygments_lexer": "ipython3" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/doc/user_guide/index.md b/doc/user_guide/index.md index 0a12009b2..6d84b26f9 100644 --- a/doc/user_guide/index.md +++ b/doc/user_guide/index.md @@ -50,8 +50,6 @@ pandas.plotting module. Displaying and saving plots in the notebook, at the command prompt, or in scripts. - [Subplots](Subplots) How to generate subplots and grids. -- [Streaming](Streaming) - How to use hvPlot for streaming plots with the streamz library. - [Gridded Data](Gridded_Data) How to use hvPlot for plotting Xarray-based gridded data. - [Network Graphs](NetworkX) diff --git a/hvplot/converter.py b/hvplot/converter.py index 35a5260fb..763ae31b7 100644 --- a/hvplot/converter.py +++ b/hvplot/converter.py @@ -83,6 +83,7 @@ import_geoviews, is_mpl_cmap, _find_stack_level, + _HV_VERSION, _is_within_latlon_bounds, _convert_latlon_to_mercator, _convert_limit_to_mercator, @@ -1451,6 +1452,15 @@ def _process_data( datatype = 'ibis' self.data = data elif is_streamz(data): + if _HV_VERSION >= (1, 23, 0): + raise RuntimeError( + 'streamz support has been removed. HoloViews >= 1.23.0 no longer supports streamz.' + ) + warnings.warn( + 'streamz support has been deprecated and will be removed in a future version.', + DeprecationWarning, + stacklevel=_find_stack_level(), + ) datatype = 'streamz' self.data = data.example if isinstance(self.data, pd.DataFrame): diff --git a/hvplot/streamz.py b/hvplot/streamz.py index 311fb9616..fb7ffe213 100644 --- a/hvplot/streamz.py +++ b/hvplot/streamz.py @@ -1,12 +1,29 @@ +import warnings + +from .util import _find_stack_level, _HV_VERSION + + def patch(name='hvplot', extension='bokeh', logo=False): from . import hvPlotTabular, post_patch, _module_extensions + if _HV_VERSION >= (1, 23, 0): + raise RuntimeError( + 'streamz support has been removed. HoloViews >= 1.23.0 no longer supports streamz.' + ) + try: import streamz.dataframe as sdf except ImportError: raise ImportError( 'Could not patch plotting API onto streamz. Streamz could not be imported.' ) + + warnings.warn( + 'streamz support has been deprecated and will be removed in a future version.', + DeprecationWarning, + stacklevel=_find_stack_level(), + ) + if 'hvplot.streamz' not in _module_extensions: _patch_plot = lambda self: hvPlotTabular(self) # noqa: E731 _patch_plot.__doc__ = hvPlotTabular.__call__.__doc__ diff --git a/hvplot/tests/testdeprecations.py b/hvplot/tests/testdeprecations.py index 98e32afee..be04c555a 100644 --- a/hvplot/tests/testdeprecations.py +++ b/hvplot/tests/testdeprecations.py @@ -8,6 +8,7 @@ from hvplot.converter import HoloViewsConverter from hvplot.plotting import plot from hvplot.tests.util import makeDataFrame +from hvplot.util import _HV_VERSION def test_converter_argument_debug(disable_param_warnings_as_exceptions): @@ -28,3 +29,16 @@ def test_converter_argument_hover_formatters(): df = pd.DataFrame({'x': [0, 1], 'y': [0, 1]}) with pytest.warns(DeprecationWarning): HoloViewsConverter(df, 'x', 'y', hover_formatters={'@{y}': 'printf'}) + + +def test_streamz_patch(): + pytest.importorskip('streamz') + if _HV_VERSION >= (1, 23, 0): + pytest.skip('streamz support has been removed in HoloViews >= 1.23.0') + with pytest.warns( + DeprecationWarning, + match='streamz support has been deprecated', + ): + from hvplot.streamz import patch + + patch() diff --git a/hvplot/tests/testpatch.py b/hvplot/tests/testpatch.py index ea7982856..e5aa21064 100644 --- a/hvplot/tests/testpatch.py +++ b/hvplot/tests/testpatch.py @@ -10,6 +10,7 @@ import pandas as pd from hvplot.plotting import hvPlotTabular, hvPlot +from hvplot.util import _HV_VERSION class TestPatchPandas(TestCase): @@ -74,8 +75,8 @@ def test_xarray_dataset_patched(self): class TestPatchStreamz(TestCase): def setUp(self): - if sys.version_info[:2] >= (3, 14): - raise SkipTest('streamz not compatible') + if _HV_VERSION >= (1, 23, 0): + raise SkipTest('streamz support has been removed in HoloViews >= 1.23.0') try: import streamz # noqa except ImportError: diff --git a/pyproject.toml b/pyproject.toml index 228e63176..ac0bf52c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -132,7 +132,6 @@ examples = [ "scikit-learn >=1.4.0", "scipy >=1.5.3", "selenium >=3.141.0", - "streamz >=0.3.0", "xarray >=0.18.2", "xyzservices >=2022.9.0", "geodatasets >=2023.12.0",