{ "cells": [ { "cell_type": "markdown", "id": "158bb196-9e72-45f0-ae7d-585e66922abe", "metadata": {}, "source": [ "# hvplot" ] }, { "cell_type": "markdown", "id": "055d7cc3", "metadata": {}, "source": [ "The core functionality provided by hvPlot is a **simple and high-level plotting interface** (API), modeled on [Pandas](https://pandas.pydata.org)'s `.plot` API and extended in various ways leveraging capabilities offered by the packages of the [HoloViz](https://holoviz.org) ecosystem, most notably [HoloViews](https://holoviews.org/). hvPlot can generate interactive plots with either [Bokeh](https://www.bokeh.org) (default) or [Plotly](https://plotly.com/python/), or static plots with [Matplotlib](https://matplotlib.org). hvPlot supports many data libraries of the Python ecosystem:\n", "\n", "* [Pandas](https://pandas.pydata.org): DataFrame, Series (columnar/tabular data)\n", "* [XArray](https://xarray.pydata.org): Dataset, DataArray (labelled multidimensional arrays)\n", "* [GeoPandas](https://geopandas.org): GeoDataFrame (geometry data)\n", "* [Rapids cuDF](https://docs.rapids.ai/api/cudf/stable/): GPU DataFrame, Series (columnar/tabular data)\n", "* [Polars](https://www.pola.rs/): DataFrame, LazyFrame, Series (columnar/tabular data)\n", "* [Dask](https://www.dask.org): DataFrame, Series (distributed/out of core arrays and columnar data)\n", "* [Streamz](https://streamz.readthedocs.io): DataFrame(s), Series(s) (streaming columnar data)\n", "* [Intake](https://github.com/ContinuumIO/intake): DataSource (data catalogues)\n", "* [Ibis](https://ibis-project.org/): DataFrame interface for many backends (DuckDB, SQLite, SnowFlake, etc.)\n", "* [NetworkX](https://networkx.github.io/documentation/stable/): Graph (network graphs)\n" ] }, { "cell_type": "markdown", "id": "05e0d700-a058-425c-bdce-a6e65220564a", "metadata": {}, "source": [ "## Register `.hvplot`" ] }, { "cell_type": "markdown", "id": "28f0a720-35ef-43f3-bd45-1d0facb06199", "metadata": {}, "source": [ "Let's create a simple Pandas DataFrame we'll plot later." ] }, { "cell_type": "code", "execution_count": null, "id": "841fe838-888d-4bab-8d10-a7030e37b5bc", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "np.random.seed(1)\n", "\n", "idx = pd.date_range('1/1/2000', periods=1000)\n", "df = pd.DataFrame(np.random.randn(1000, 4), index=idx, columns=list('ABCD')).cumsum()\n", "df.head(2)" ] }, { "cell_type": "markdown", "id": "e296fd3a-a4e7-40bb-a088-ff2f2b3f5346", "metadata": {}, "source": [ "The most convenient way to use hvPlot is to register the `.hvplot` *accessor* on the data type you are working with. This is done with a special import of the form `import hvplot.`." ] }, { "cell_type": "code", "execution_count": null, "id": "e7ba9819-0fde-40b0-ba99-189d4a942fbd", "metadata": {}, "outputs": [], "source": [ "import hvplot.pandas # noqa" ] }, { "cell_type": "markdown", "id": "368c4d14-4226-4d04-8bc8-ca47bafe7dde", "metadata": {}, "source": [ "In addition to registering the `.hvplot` accessor on Pandas objects (`DataFrame` and `Series`), the import above sets the Bokeh plotting library as the default one and loads its corresponding extension.\n", "\n", ":::{note}\n", "In a notebook, loading the extension means that there's actually some front-end code that is injected in the cell output of the import, this code being required for HoloViews plots to behave correctly. So make sure not to remove this cell!\n", ":::" ] }, { "cell_type": "markdown", "id": "09afd413", "metadata": {}, "source": [ "Now simply call `.hvplot()` on the DataFrame as you would call Pandas' `.plot()`." ] }, { "cell_type": "code", "execution_count": null, "id": "a029c2cb", "metadata": {}, "outputs": [], "source": [ "first_plot = df.hvplot()\n", "first_plot" ] }, { "cell_type": "markdown", "id": "e0c0785a-43de-4f22-9dca-c5caa9c799e9", "metadata": {}, "source": [ "The same process can be applied to other libraries, we'll just show another example with Xarray." ] }, { "cell_type": "code", "execution_count": null, "id": "e16be263-ccb3-4ed0-b0ef-15089f8d001a", "metadata": {}, "outputs": [], "source": [ "import hvplot.xarray # noqa\n", "import xarray as xr\n", "\n", "air_ds = xr.tutorial.open_dataset('air_temperature').load()\n", "air_ds['air'].isel(time=slice(0, 1000, 60)).hvplot.image(dynamic=False)" ] }, { "cell_type": "markdown", "id": "341e811c-057e-4c40-b254-25d74f8f8949", "metadata": {}, "source": [ "## Bokeh plots" ] }, { "cell_type": "markdown", "id": "c59ce73e-900a-4062-9166-2a66fcdefebf", "metadata": {}, "source": [ "As you can see, the default plots hvPlot generate are Bokeh plots. These plots are interactive and support panning, zooming, hovering, and clickable/selectable legends. It's worth spending some time getting used to interact with this kind of plot, try for instance zooming in on an axis and see what happens!" ] }, { "cell_type": "code", "execution_count": null, "id": "d261dcb7-d81f-4974-9bdc-09255736ce57", "metadata": {}, "outputs": [], "source": [ "first_plot" ] }, { "cell_type": "markdown", "id": "73502b90-80d6-4f70-b374-e44236fb20bb", "metadata": {}, "source": [ "## `hvplot` namespace" ] }, { "cell_type": "markdown", "id": "eb68d0b3-fb9b-4844-8162-c3ec29ca194f", "metadata": {}, "source": [ "The `.hvplot` namespace holds the range of supported plot methods (e.g. `line`, `scatter`, `hist`, etc.). Use tab completion to explore the available plot types.\n", "\n", "```python\n", "df.hvplot.\n", "```" ] }, { "cell_type": "markdown", "id": "26352b8f-e157-43b0-9919-cd57ffe760cb", "metadata": {}, "source": [ "Similarly to Panda's API, every plot method accepts a wide range of parameters. You can explore them by calling `hvplot.help('line')` or using tab completion:\n", "\n", "```python\n", "df.hvplot.line(\n", "```" ] }, { "cell_type": "markdown", "id": "a1857839-ce65-40b6-a757-0d4eb51feacb", "metadata": {}, "source": [ "## Compose plots " ] }, { "cell_type": "markdown", "id": "c0d288f1-aca0-4448-8d9d-99aafb621a9c", "metadata": {}, "source": [ "The object returned by a `.hvplot.()` call is a `HoloViews` object. " ] }, { "cell_type": "code", "execution_count": null, "id": "5b7e6818-9bf7-4884-9b64-5eacd5564820", "metadata": {}, "outputs": [], "source": [ "plot1 = df['A'].hvplot.area(alpha=0.2, color='red', width=300)\n", "plot2 = df['B'].hvplot.line(width=300)\n", "print(plot2)" ] }, { "cell_type": "markdown", "id": "2f99a75d-8c3b-462e-9500-9ab46f5ec1e3", "metadata": {}, "source": [ "`HoloViews` objects can easily composed using the `+` and `*` operators:\n", "\n", "- ` + ` lays the plots out in a *row* container\n", "- `( + ).cols(1)` lays the plots out in a *column* container\n", "- ` * ` overlays the plots" ] }, { "cell_type": "code", "execution_count": null, "id": "2896b706-518d-4bc9-8513-80d2cc2cbc4e", "metadata": {}, "outputs": [], "source": [ "plot1 + plot2" ] }, { "cell_type": "code", "execution_count": null, "id": "813c4e2b-b6ef-425f-8742-98a8c1b983da", "metadata": {}, "outputs": [], "source": [ "plot1 * plot2" ] }, { "cell_type": "markdown", "id": "7afab2c4-e9f2-456e-bc3b-e26c998e2236", "metadata": {}, "source": [ "## Widgets-based exploration" ] }, { "cell_type": "code", "execution_count": null, "id": "347883ca-4ae1-4ec1-8429-ff66f488df64", "metadata": {}, "outputs": [], "source": [ "from bokeh.sampledata.penguins import data as df_penguins\n", "df_penguins.head(2)" ] }, { "cell_type": "markdown", "id": "4ec16eba-8520-43f4-b0ff-ad6680f038fc", "metadata": {}, "source": [ "The `groupby` parameter allows to explore a dimension of the data using widgets." ] }, { "cell_type": "code", "execution_count": null, "id": "873280fc-9c44-4db6-a520-826d4e693218", "metadata": {}, "outputs": [], "source": [ "df_penguins.hvplot.scatter(x='bill_length_mm', y='bill_depth_mm', groupby=['island', 'sex'], dynamic=False)" ] }, { "cell_type": "markdown", "id": "eb285877-d8fa-4b6e-81ab-79dd5bb90326", "metadata": {}, "source": [ "## Display large data" ] }, { "cell_type": "code", "execution_count": null, "id": "846decf2-0b4c-496e-9a1c-ebcd9eb03559", "metadata": {}, "outputs": [], "source": [ "NUM = 1_000_000\n", "dists = [\n", " pd.DataFrame(dict(x=np.random.normal(x, s, NUM), y=np.random.normal(y, s, NUM)))\n", " for x, y, s in [\n", " ( 5, 2, 0.20), \n", " ( 2, -4, 0.10), \n", " (-2, -3, 0.50), \n", " (-5, 2, 1.00), \n", " ( 0, 0, 3.00)]\n", "]\n", "df_large_data = pd.concat(dists, ignore_index=True)\n", "len(df_large_data)" ] }, { "cell_type": "markdown", "id": "e5fc9abf-0447-4b5c-a4e4-1bd830f5099e", "metadata": {}, "source": [ "hvPlot provides multiple ways to display arbitrarily large datasets. The most versatile option depends on [Datashader](https://datashader.org/) (optional dependency) and simply consists of setting `rasterize` to `True`. The plot returned is an image, each pixel of that image being colorized based on the number of points it contains; which means that **all** the points contribute to the image. This plot is also dynamic, zooming in and out and panning leads to a recomputation of the image. Luckily, this all happens really fast! The plot below is generated with no less than 5 million points." ] }, { "cell_type": "code", "execution_count": null, "id": "79a99e9f-8d16-4b21-8041-0966b7efc250", "metadata": {}, "outputs": [], "source": [ "df_large_data.hvplot.points('x', 'y', rasterize=True, cnorm='eq_hist', aspect=1, colorbar=False)" ] }, { "cell_type": "markdown", "id": "5dc56ae5-a5bf-4427-8e80-bf67e28676e5", "metadata": {}, "source": [ "## Geographic plots" ] }, { "cell_type": "markdown", "id": "f759ee37-5446-4499-bbf0-37c1911f32c2", "metadata": {}, "source": [ "hvPlot can generate geographic plots and handle geographic data (e.g. GeoPandas DataFrame) thank to [GeoViews](https://geoviews.org/) (optional dependency)." ] }, { "cell_type": "code", "execution_count": null, "id": "cde31b00-1f83-4d6d-8acf-df08ba1d42ee", "metadata": {}, "outputs": [], "source": [ "import cartopy.crs as crs\n", "\n", "proj = crs.Orthographic(-90, 30)\n", "air_ds.air.sel(time='2013-06-01 12:00').hvplot.quadmesh(\n", " 'lon', 'lat', projection=proj, project=True,\n", " global_extent=True, cmap='viridis', coastline=True\n", ")" ] }, { "cell_type": "markdown", "id": "25b389c1", "metadata": {}, "source": [ "## Matplotlib or Plotly plots" ] }, { "cell_type": "markdown", "id": "8d8ad549", "metadata": {}, "source": [ "hvPlot offers the possibility to create [Matplotlib](https://matplotlib.org/) and [Plotly](https://plotly.com/) plots. Load the chosen plotting library with the `extension` function." ] }, { "cell_type": "code", "execution_count": null, "id": "096532ab", "metadata": {}, "outputs": [], "source": [ "hvplot.extension('matplotlib')" ] }, { "cell_type": "code", "execution_count": null, "id": "f0f9846c", "metadata": {}, "outputs": [], "source": [ "air_ds.air.isel(time=slice(0, 9, 3)).hvplot.quadmesh(\n", " 'lon', 'lat', projection=proj, project=True, global_extent=True, \n", " cmap='viridis', rasterize=True, dynamic=False, coastline=True,\n", " xaxis=None, yaxis=None, width=370\n", ")" ] }, { "cell_type": "markdown", "id": "3adf5093-8797-4c9f-9125-aa916fea02aa", "metadata": {}, "source": [ "## Conclusion" ] }, { "cell_type": "markdown", "id": "cad50096", "metadata": {}, "source": [ "We have briefly explored some of the main features of `.hvplot()`. There's a lot more to it! For more information on using `.hvplot()` take a look at the [Bokeh Plotting Guide](../user_guide/Plotting.ipynb) and the various plotting guides available." ] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 5 }