diff --git a/doc/homepage.ipynb b/doc/homepage.ipynb index 71ff638b5..5d38ced63 100644 --- a/doc/homepage.ipynb +++ b/doc/homepage.ipynb @@ -25,7 +25,7 @@ "source": [ "## Usage\n", "\n", - "HoloPlot replaces the static PNG-based plotting that comes with [Pandas](http://pandas.pydata.org) and other data libraries with interactive [Bokeh](http://bokeh.pydata.org)-based plotting that supports panning, zooming, hovering, and clickable/selectable legends:" + "HoloPlot provides an alternative for the static plotting API provided by [Pandas](http://pandas.pydata.org) and other libraries, with an interactive [Bokeh](http://bokeh.pydata.org)-based plotting API that supports panning, zooming, hovering, and clickable/selectable legends:" ] }, { @@ -39,7 +39,7 @@ "df = pd.DataFrame(np.random.randn(1000, 4), index=idx, columns=list('ABCD')).cumsum()\n", "\n", "import holoplot.pandas\n", - "df.plot()" + "df.holoplot()" ] }, { @@ -99,8 +99,8 @@ "\n", "streaming_df = Random(freq='5ms') \n", "\n", - "streaming_df.plot(backlog=100, height=400, width=500) +\\\n", - "streaming_df.plot.hexbin(x='x', y='z', backlog=2000, height=400, width=500);" + "streaming_df.holoplot(backlog=100, height=400, width=500) +\\\n", + "streaming_df.holoplot.hexbin(x='x', y='z', backlog=2000, height=400, width=500);" ] }, { @@ -128,7 +128,7 @@ "\n", "air_temp = load_dataset('air_temperature').air\n", "\n", - "air_temp.isel(time=slice(0, 3)).plot(groupby='time', cmap='viridis', dynamic=False, width=500, height=300)" + "air_temp.isel(time=slice(0, 3)).holoplot(groupby='time', cmap='viridis', dynamic=False, width=500, height=300)" ] }, { diff --git a/examples/user_guide/Gridded_Data.ipynb b/examples/user_guide/Gridded_Data.ipynb index 64d7e69de..6fedb8247 100644 --- a/examples/user_guide/Gridded_Data.ipynb +++ b/examples/user_guide/Gridded_Data.ipynb @@ -45,7 +45,7 @@ "outputs": [], "source": [ "air1d = air.isel(lat=10, lon=10)\n", - "air1d.plot()" + "air1d.holoplot()" ] }, { @@ -62,7 +62,7 @@ "outputs": [], "source": [ "air1d_sel = air1d.isel(time=slice(0, 200))\n", - "air1d_sel.plot(color='purple') * air1d_sel.plot.scatter(marker='o', color='blue', size=15)" + "air1d_sel.holoplot(color='purple') * air1d_sel.holoplot.scatter(marker='o', color='blue', size=15)" ] }, { @@ -80,7 +80,7 @@ "metadata": {}, "outputs": [], "source": [ - "air.isel(lon=10, lat=[19, 21, 22]).plot.line()" + "air.isel(lon=10, lat=[19, 21, 22]).holoplot.line()" ] }, { @@ -96,7 +96,7 @@ "metadata": {}, "outputs": [], "source": [ - "air.isel(time=10, lon=[10, 11]).plot.line(y='lat', by='lon')" + "air.isel(time=10, lon=[10, 11]).holoplot.line(y='lat', by='lon')" ] }, { @@ -110,7 +110,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "By default the ``DataArray.plot()`` method generates an image if the data is two-dimensional." + "By default the ``DataArray.holoplot()`` method generates an image if the data is two-dimensional." ] }, { @@ -120,7 +120,7 @@ "outputs": [], "source": [ "air2d = air.isel(time=500)\n", - "air2d.plot(width=400)" + "air2d.holoplot(width=400)" ] }, { @@ -136,7 +136,7 @@ "metadata": {}, "outputs": [], "source": [ - "air2d.plot.contour(width=400, levels=20) + air2d.plot.contourf(width=400, levels=4)" + "air2d.holoplot.contour(width=400, levels=20) + air2d.holoplot.contourf(width=400, levels=8)" ] }, { @@ -159,7 +159,7 @@ "metadata": {}, "outputs": [], "source": [ - "air.plot()" + "air.holoplot()" ] }, { @@ -175,7 +175,7 @@ "metadata": {}, "outputs": [], "source": [ - "air.plot.image(groupby='time', width=500)" + "air.holoplot.image(groupby='time', width=500)" ] }, { @@ -191,7 +191,7 @@ "metadata": {}, "outputs": [], "source": [ - "air.plot.line()" + "air.holoplot.line()" ] }, { @@ -214,7 +214,7 @@ "metadata": {}, "outputs": [], "source": [ - "air.sel(lat=[25, 50, 75], method='nearest').plot.kde('air', by='lat', alpha=0.5)" + "air.sel(lat=[25, 50, 75], method='nearest').holoplot.kde('air', by='lat', alpha=0.5)" ] }, { @@ -230,7 +230,7 @@ "metadata": {}, "outputs": [], "source": [ - "air.plot.violin('air', by='lat')" + "air.holoplot.violin('air', by='lat')" ] }, { @@ -250,8 +250,8 @@ "metadata": {}, "outputs": [], "source": [ - "air.plot.scatter('time', groupby=[], datashade=True, use_dask=True) *\\\n", - "air.plot.line('time', groupby=[], use_dask=True, color='indianred').aggregate(function=np.mean)" + "air.holoplot.scatter('time', groupby=[], datashade=True, use_dask=True) *\\\n", + "air.groupby('time').mean().holoplot.line('time', use_dask=True, color='indianred')" ] }, { diff --git a/examples/user_guide/Introduction.ipynb b/examples/user_guide/Introduction.ipynb index c9db6cfdb..14f640190 100644 --- a/examples/user_guide/Introduction.ipynb +++ b/examples/user_guide/Introduction.ipynb @@ -60,9 +60,9 @@ "source": [ "The result is a PNG image that displays easily, but is otherwise static.\n", "\n", - "## HoloPlot .plot()\n", + "## .holoplot()\n", "\n", - "If we instead change `%matplotlib inline` to `import holoplot.pandas`, the same call will now show an interactively explorable [Bokeh](http://bokeh.pydata.org) plot with panning, zooming, hovering, and clickable/selectable legends:" + "If we instead change `%matplotlib inline` to `import holoplot.pandas` and use the ``df.holoplot`` method, it will now display an interactively explorable [Bokeh](http://bokeh.pydata.org) plot with panning, zooming, hovering, and clickable/selectable legends:" ] }, { @@ -73,18 +73,18 @@ "source": [ "import holoplot.pandas\n", "\n", - "df.plot()" + "df.holoplot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "This interactive plot makes it much easier to explore the properties of the data, without having to write code to select ranges, columns, or data values manually.\n", + "This interactive plot makes it much easier to explore the properties of the data, without having to write code to select ranges, columns, or data values manually. Note that while pandas, dask and xarray all use the ``.holoplot`` method, ``intake`` uses HoloPlot as its main plotting API, which means that is available using ``.plot()``. \n", "\n", "## HoloPlot native API\n", "\n", - "For the plot above, HoloPlot dynamically patched the Pandas `.plot()` method so that you can use the same `.plot()` syntax as with the Pandas default plotting. If you prefer to be more explicit, you can instead work directly with HoloPlot objects:" + "For the plot above, HoloPlot dynamically added the Pandas `.holoplot()` method, so that you can use the same syntax as with the Pandas default plotting. If you prefer to be more explicit, you can instead work directly with ``HoloPlot`` objects:" ] }, { @@ -93,11 +93,11 @@ "metadata": {}, "outputs": [], "source": [ - "import holoplot as hp\n", + "from holoplot import HoloPlot\n", "import holoviews as hv\n", "hv.extension('bokeh')\n", "\n", - "plot = hp.HoloPlot(df)\n", + "plot = HoloPlot(df)\n", "plot(y='A')" ] }, diff --git a/examples/user_guide/Plotting.ipynb b/examples/user_guide/Plotting.ipynb index 0e5cda46e..0538a4e5f 100644 --- a/examples/user_guide/Plotting.ipynb +++ b/examples/user_guide/Plotting.ipynb @@ -23,16 +23,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Intake's ``DataSource`` objects will now support the HoloPlot plot API. The HoloPlot API closely mirrors the [Pandas plotting API](https://pandas.pydata.org/pandas-docs/stable/visualization.html), but instead of generating static images when used in a notebook, it uses HoloViews to generate either static or dynamically streaming Bokeh plots. Static plots can be used in any context, while streaming plots require a live [Jupyter notebook](http://jupyter.org) or a deployed [Bokeh Server app](https://bokeh.pydata.org/en/latest/docs/user_guide/server.html).\n", + "As we learned The HoloPlot API closely mirrors the [Pandas plotting API](https://pandas.pydata.org/pandas-docs/stable/visualization.html), but instead of generating static images when used in a notebook, it uses HoloViews to generate either static or dynamically streaming Bokeh plots. Static plots can be used in any context, while streaming plots require a live [Jupyter notebook](http://jupyter.org) or a deployed [Bokeh Server app](https://bokeh.pydata.org/en/latest/docs/user_guide/server.html).\n", "\n", - "HoloViews provides an extensive very rich set of objects and operations on them, as you can find out in the [HoloViews User Guide](http://holoviews.org/user_guide/index.html). But here we will focus on the most essential mechanisms needed to make your data visualizable, without having to worry about the mechanics going on behind the scenes.\n", + "HoloViews provides an extensive, very rich set of objects along with a powerful set of operations to apply, as you can find out in the [HoloViews User Guide](http://holoviews.org/user_guide/index.html). But here we will focus on the most essential mechanisms needed to make your data visualizable, without having to worry about the mechanics going on behind the scenes.\n", "\n", "We will be focusing on two different datasets:\n", "\n", "- A small CSV file of US crime data, broken down by state\n", - "- A larger Parquet-format file of airline data\n", + "- A larger Parquet-format file of airline flight data\n", "\n", - "Here we used the `intake` package as a convenient way to get data into Python, but the same plotting commands below will work regardless of whether you use an Intake object or supplied a Pandas or Dask dataframe directly." + "Here we used the `intake` data catalogue to load datasets we have installed as conda packages but of course the data can be loaded directly using pandas or dask. First let's look at the two datasets, ``intake`` lets us load the data as a ``pandas.DataFrame``:" ] }, { @@ -46,6 +46,13 @@ "crime.head()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using intake it is also possible to load the data into a ``dask.DataFrame``, however for the purposes of this user guide we will stick with a regular DataFrame:" + ] + }, { "cell_type": "code", "execution_count": null, @@ -53,7 +60,7 @@ "outputs": [], "source": [ "flights = intake.cat.airline_flights.get().read()\n", - "print(type(flights))\n", + "# With dask: intake.cat.airline_flights.get().to_dask().persist()\n", "flights.head()" ] }, @@ -68,7 +75,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The ``dask.dataframe.DataFrame.plot``, ``pandas.DataFrame.plot`` and ````intake.DataSource.plot`` interfaces (and Series equivalents) from HoloPlot provides a powerful high-level API to generate complex plots. The ``.plot`` API can be called directly or used as a namespace to generate specific plot types." + "The ``dask.dataframe.DataFrame.holoplot``, ``pandas.DataFrame.holoplot`` and ``intake.DataSource.plot`` interfaces (and Series equivalents) from HoloPlot provide a powerful high-level API to generate complex plots. The ``.holoplot`` API can be called directly or used as a namespace to generate specific plot types." ] }, { @@ -91,7 +98,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot(x='Year', y='Violent Crime rate')" + "crime.holoplot.line(x='Year', y='Violent Crime rate')" ] }, { @@ -107,7 +114,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot(x='Year', y='Violent Crime rate', kind='scatter')" + "crime.holoplot(x='Year', y='Violent Crime rate', kind='scatter')" ] }, { @@ -123,7 +130,8 @@ "metadata": {}, "outputs": [], "source": [ - "flights[flights.carrier.isin([b'OH', b'F9'])].plot(x='distance', y='depdelay', by='carrier', kind='scatter', alpha=0.2)" + "flight_subset = flights[flights.carrier.isin([b'OH', b'F9'])]\n", + "flight_subset.holoplot(x='distance', y='depdelay', by='carrier', kind='scatter', alpha=0.2, persist=True)" ] }, { @@ -141,22 +149,22 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot(x='Year', y=['Violent Crime rate', 'Robbery rate', 'Burglary rate'],\n", - " value_label='Rate (per 100k people)')" + "crime.holoplot(x='Year', y=['Violent Crime rate', 'Robbery rate', 'Burglary rate'],\n", + " value_label='Rate (per 100k people)')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### The plot namespace" + "### The holoplot namespace" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Instead of using the ``kind`` argument to the plot call, we can use the ``plot`` namespace, which lets us easily discover the range of plot types that are supported. Plot types available include:\n", + "Instead of using the ``kind`` argument to the plot call, we can use the ``holoplot`` namespace, which lets us easily discover the range of plot types that are supported. Plot types available include:\n", "\n", "* ``.area()``: Plots a area chart similar to a line chart except for filling the area under the curve and optionally stacking \n", "* ``.bar()``: Plots a bar chart that can be stacked or grouped\n", @@ -187,7 +195,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot.area(x='Year', y=['Robbery', 'Aggravated assault'], stacked=True)" + "crime.holoplot.area(x='Year', y=['Robbery', 'Aggravated assault'], stacked=True)" ] }, { @@ -203,7 +211,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot.area(x='Year', y=['Robbery', 'Aggravated assault'], stacked=False, alpha=0.6)" + "crime.holoplot.area(x='Year', y=['Robbery', 'Aggravated assault'], stacked=False, alpha=0.4)" ] }, { @@ -221,7 +229,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot.bar('Year', 'Violent Crime rate', rot=90)" + "crime.holoplot.bar('Year', 'Violent Crime rate', rot=90)" ] }, { @@ -237,8 +245,8 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot.bar('Year', ['Violent crime total', 'Property crime total'],\n", - " stacked=True, rot=90, width=800)" + "crime.holoplot.bar('Year', ['Violent crime total', 'Property crime total'],\n", + " stacked=True, rot=90, width=800)" ] }, { @@ -256,7 +264,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot.scatter('Violent Crime rate', 'Burglary rate', c='Year', cmap='viridis', size=6, colorbar=True)" + "crime.holoplot.scatter('Violent Crime rate', 'Burglary rate', c='Year', cmap='viridis', size=12, colorbar=True)" ] }, { @@ -274,7 +282,7 @@ "metadata": {}, "outputs": [], "source": [ - "flights.plot.hexbin(x='airtime', y='arrdelay', width=600, height=500)" + "flights.holoplot.hexbin(x='airtime', y='arrdelay', width=600, height=500)" ] }, { @@ -292,7 +300,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot.bivariate('Violent Crime rate', 'Burglary rate', colorbar=True, width=600, height=500)" + "crime.holoplot.bivariate('Violent Crime rate', 'Burglary rate', colorbar=True, width=600, height=500)" ] }, { @@ -310,8 +318,7 @@ "metadata": {}, "outputs": [], "source": [ - "flights.plot.heatmap(x='day', y='carrier', C='depdelay', reduce_function=np.mean,\n", - " colorbar=True)" + "flights.holoplot.heatmap(x='day', y='carrier', C='depdelay', reduce_function=np.mean, colorbar=True)" ] }, { @@ -329,7 +336,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot.table(columns=['Year', 'Population', 'Violent Crime rate'], width=400)" + "crime.holoplot.table(columns=['Year', 'Population', 'Violent Crime rate'], width=400)" ] }, { @@ -355,7 +362,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot.hist('Violent Crime rate')" + "crime.holoplot.hist('Violent Crime rate')" ] }, { @@ -372,7 +379,7 @@ "outputs": [], "source": [ "columns = ['Violent Crime rate', 'Property crime rate', 'Burglary rate']\n", - "crime.plot.hist(y=columns, bins=20, alpha=0.5)" + "crime.holoplot.hist(y=columns, bins=20, alpha=0.5)" ] }, { @@ -388,7 +395,7 @@ "metadata": {}, "outputs": [], "source": [ - "flights[flights.carrier.isin([b'AA', b'US', b'OH'])].plot.hist('depdelay', by='carrier', bins=20, bin_range=(-20, 100), alpha=0.3)" + "flights[flights.carrier.isin([b'AA', b'US', b'OH'])].holoplot.hist('depdelay', by='carrier', bins=20, bin_range=(-20, 100), alpha=0.3)" ] }, { @@ -397,7 +404,7 @@ "source": [ "#### KDE\n", "\n", - "You can also create density plots using ``DataSource.plot.kde()`` method:" + "You can also create density plots using ``holoplot.kde()`` method:" ] }, { @@ -406,7 +413,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot.kde('Violent Crime rate')" + "crime.holoplot.kde('Violent Crime rate')" ] }, { @@ -423,7 +430,7 @@ "outputs": [], "source": [ "columns=['Violent Crime rate', 'Property crime rate', 'Burglary rate']\n", - "crime.plot.kde(y=columns, alpha=0.5, value_label='Rate')" + "crime.holoplot.kde(y=columns, alpha=0.5, value_label='Rate')" ] }, { @@ -439,7 +446,7 @@ "metadata": {}, "outputs": [], "source": [ - "flights[flights.carrier.isin([b'AA', b'US', b'OH'])].plot.kde('depdelay', by='carrier', alpha=0.3, xlim=(-20, 70))" + "flights[flights.carrier.isin([b'AA', b'US', b'OH'])].holoplot.kde('depdelay', by='carrier', alpha=0.3, xlim=(-20, 70))" ] }, { @@ -457,7 +464,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot.box('Violent Crime rate')" + "crime.holoplot.box('Violent Crime rate')" ] }, { @@ -475,7 +482,7 @@ "source": [ "columns=['Burglary rate', 'Larceny-theft rate', 'Motor vehicle theft rate',\n", " 'Property crime rate', 'Violent Crime rate']\n", - "crime.plot.box(y=columns, group_label='Crime', legend=False, value_label='Rate (per 100k)', invert=True)" + "crime.holoplot.box(y=columns, group_label='Crime', legend=False, value_label='Rate (per 100k)', invert=True)" ] }, { @@ -491,7 +498,7 @@ "metadata": {}, "outputs": [], "source": [ - "flights[flights.carrier.isin([b'AA', b'US', b'OH'])].plot.box('depdelay', by='carrier', ylim=(-10, 70))" + "flights[flights.carrier.isin([b'AA', b'US', b'OH'])].holoplot.box('depdelay', by='carrier', ylim=(-10, 70))" ] }, { @@ -515,7 +522,7 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot('Year', 'Violent Crime rate') * crime.plot.scatter('Year', 'Violent Crime rate', size=30)" + "crime.holoplot('Year', 'Violent Crime rate') * crime.holoplot.scatter('Year', 'Violent Crime rate', size=30)" ] }, { @@ -531,8 +538,8 @@ "metadata": {}, "outputs": [], "source": [ - "(crime.plot.bar('Year', 'Violent Crime rate', rot=90, width=550) +\n", - " crime.plot.table(['Year', 'Population', 'Violent Crime rate'], width=420))" + "(crime.holoplot.bar('Year', 'Violent Crime rate', rot=90, width=550) +\n", + " crime.holoplot.table(['Year', 'Population', 'Violent Crime rate'], width=420))" ] }, { @@ -550,7 +557,7 @@ "metadata": {}, "outputs": [], "source": [ - "flights.plot.scatter('distance', 'airtime', datashade=True)" + "flights.holoplot.scatter('distance', 'airtime', datashade=True)" ] }, { @@ -568,7 +575,7 @@ "metadata": {}, "outputs": [], "source": [ - "flights.plot.violin('depdelay', by='carrier', groupby='dayofweek', ylim=(-20, 60), height=500, dynamic=False)" + "flights.holoplot.violin('depdelay', by='carrier', groupby='dayofweek', ylim=(-20, 60), height=500, dynamic=False)" ] }, { diff --git a/examples/user_guide/Statistical_Plots.ipynb b/examples/user_guide/Statistical_Plots.ipynb index 4f8242c48..8de05a699 100644 --- a/examples/user_guide/Statistical_Plots.ipynb +++ b/examples/user_guide/Statistical_Plots.ipynb @@ -118,7 +118,7 @@ "metadata": {}, "outputs": [], "source": [ - "stock_df.plot.line()" + "stock_df.holoplot.line()" ] }, { diff --git a/examples/user_guide/Streaming.ipynb b/examples/user_guide/Streaming.ipynb index 6ff2681df..66490aa40 100644 --- a/examples/user_guide/Streaming.ipynb +++ b/examples/user_guide/Streaming.ipynb @@ -62,7 +62,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.plot()" + "df.holoplot()" ] }, { @@ -85,7 +85,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.z.cumsum().plot()" + "df.z.cumsum().holoplot()" ] }, { @@ -107,7 +107,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.plot.table(width=400, backlog=10)" + "df.holoplot.table(width=400, backlog=10)" ] }, { @@ -130,7 +130,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.groupby('y').sum().plot.bar(x='y')" + "df.groupby('y').sum().holoplot.bar(x='y')" ] }, { @@ -157,9 +157,9 @@ "metadata": {}, "outputs": [], "source": [ - "(df.plot.line(width=400, backlog=50) * df.plot.scatter(width=400, backlog=50) +\n", - " df.groupby('y').sum().plot.bar('y', 'x', width=400) +\n", - " df.plot.box(width=400) + df.x.plot.kde(width=400, shared_axes=False)).cols(2)" + "(df.holoplot.line(width=400, backlog=50) * df.holoplot.scatter(width=400, backlog=50) +\n", + " df.groupby('y').sum().holoplot.bar('y', 'x', width=400) +\n", + " df.holoplot.box(width=400) + df.x.holoplot.kde(width=400, shared_axes=False)).cols(2)" ] }, { @@ -198,11 +198,11 @@ "cumulative = df.cumsum()[['x', 'z']]\n", "\n", "# Declare plots\n", - "line = cumulative.plot.line(width=400)\n", - "scatter = cumulative.plot.scatter(width=400)\n", - "bars = df.groupby('y').sum().plot.bar(width=400)\n", - "box = df.plot.box(width=400)\n", - "kde = df.x.plot.kde(width=400)\n", + "line = cumulative.holoplot.line(width=400)\n", + "scatter = cumulative.holoplot.scatter(width=400)\n", + "bars = df.groupby('y').sum().holoplot.bar(width=400)\n", + "box = df.holoplot.box(width=400)\n", + "kde = df.x.holoplot.kde(width=400)\n", "\n", "# Compose plots\n", "layout = (line * scatter + bars + box + kde).cols(2)\n", diff --git a/examples/user_guide/Subplots.ipynb b/examples/user_guide/Subplots.ipynb index 4f23cbf92..6f9b9b74e 100644 --- a/examples/user_guide/Subplots.ipynb +++ b/examples/user_guide/Subplots.ipynb @@ -23,7 +23,7 @@ "\n", "crime = intake.cat.us_crime.get().read()\n", "\n", - "crime.plot(x='Year', y=['Burglary rate', 'Violent Crime rate', 'Robbery rate'], value_label='Rate')" + "crime.holoplot(x='Year', y=['Burglary rate', 'Violent Crime rate', 'Robbery rate'], value_label='Rate')" ] }, { @@ -39,8 +39,8 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot(x='Year', y=['Burglary rate', 'Violent Crime rate', 'Robbery rate'], \n", - " value_label='Rate', subplots=True, width=250, height=200)" + "crime.holoplot(x='Year', y=['Burglary rate', 'Violent Crime rate', 'Robbery rate'], \n", + " value_label='Rate', subplots=True, width=300, height=200)" ] }, { @@ -58,8 +58,8 @@ "metadata": {}, "outputs": [], "source": [ - "crime.plot(x='Year', y=['Robbery', 'Robbery rate', 'Burglary', 'Burglary rate'], \n", - " width=350, height=300, subplots=True, shared_axes=False).cols(2)" + "crime.holoplot(x='Year', y=['Robbery', 'Robbery rate', 'Burglary', 'Burglary rate'], \n", + " width=350, height=300, subplots=True, shared_axes=False).cols(2)" ] }, { @@ -80,8 +80,8 @@ "flights = intake.cat.airline_flights.get().read()\n", "subset = flights[flights.carrier.isin([b'OH', b'F9', b'US'])].sample(2000)\n", "\n", - "subset.plot.scatter(x='arrdelay', y='depdelay', by='carrier', \n", - " subplots=True, width=250, height=250, alpha=0.1)" + "subset.holoplot.scatter(x='arrdelay', y='depdelay', by='carrier',\n", + " subplots=True, width=250, height=250, alpha=0.1)" ] }, { @@ -100,7 +100,7 @@ "metadata": {}, "outputs": [], "source": [ - "subset.sort_values('dayofweek').plot.scatter(x='arrdelay', y='depdelay', \n", + "subset.sort_values('dayofweek').holoplot.scatter(x='arrdelay', y='depdelay', \n", " row='dayofweek', col='carrier', width=100, height=100, alpha=0.2)" ] }, @@ -120,8 +120,9 @@ "outputs": [], "source": [ "air_ds = xr.tutorial.load_dataset('air_temperature')\n", - "air_ds.air.isel(time=slice(0, 5)).plot(colorbar=False, col='time', \n", - " width=200, height=150, xaxis=False, yaxis=False)" + "air_ds.air.isel(time=slice(0, 5)).holoplot(\n", + " colorbar=False, col='time', width=200, height=150, xaxis=False, yaxis=False\n", + ")" ] }, { diff --git a/examples/user_guide/Viewing.ipynb b/examples/user_guide/Viewing.ipynb index 799fff52d..71bc1b0c0 100644 --- a/examples/user_guide/Viewing.ipynb +++ b/examples/user_guide/Viewing.ipynb @@ -26,7 +26,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now the objects returned by the `.plot()` API can be composed and will display themselves when the object is on the last line of a cell:" + "Unlike other libraries like pandas and xarray, ``intake`` integrates ``HoloPlot`` directly, which means that ``.plot`` rather than ``.hv.plot`` may be used to display object. Therefore objects returned by the `.plot()` API can be composed and will display themselves when the object is on the last line of a cell:" ] }, { diff --git a/holoplot/__init__.py b/holoplot/__init__.py index 7e65fd114..222a9087c 100644 --- a/holoplot/__init__.py +++ b/holoplot/__init__.py @@ -18,52 +18,38 @@ def _patch_plot(self): return HoloPlot(self) -def patch(library, extension=None, logo=False): +def patch(library, name='holoplot', extension=None, logo=False): """ Patch library to support HoloViews based plotting API. """ if not isinstance(library, list): library = [library] + patch_property = property(_patch_plot) if 'streamz' in library: try: import streamz.dataframe as sdf except ImportError: raise ImportError('Could not patch plotting API onto streamz. ' 'Streamz could not be imported.') - sdf.DataFrame.plot = property(_patch_plot) - sdf.DataFrames.plot = property(_patch_plot) - sdf.Series.plot = property(_patch_plot) - sdf.Seriess.plot = property(_patch_plot) + setattr(sdf.DataFrame, name, patch_property) + setattr(sdf.DataFrames, name, patch_property) + setattr(sdf.Series, name, patch_property) + setattr(sdf.Seriess, name, patch_property) if 'pandas' in library: try: import pandas as pd except: raise ImportError('Could not patch plotting API onto pandas. ' 'Pandas could not be imported.') - pd.DataFrame.plot = property(_patch_plot) - pd.Series.plot = property(_patch_plot) + setattr(pd.DataFrame, name, patch_property) + setattr(pd.Series, name, patch_property) if 'dask' in library: try: import dask.dataframe as dd except: raise ImportError('Could not patch plotting API onto dask. ' 'Dask could not be imported.') - dd.DataFrame.plot = property(_patch_plot) - dd.Series.plot = property(_patch_plot) - if 'intake' in library: - try: - from intake.source.base import DataSource - except ImportError: - raise ImportError('Could not patch plotting API onto intake. ' - 'Intake could not be imported.') - DataSource.plot = property(_patch_plot) - if 'xarray' in library: - try: - from xarray import DataArray, Dataset - except ImportError: - raise ImportError('Could not patch plotting API onto xarray. ' - 'Xarray could not be imported.') - DataArray.plot = property(_patch_plot) - Dataset.plot = property(_patch_plot) + setattr(dd.DataFrame, name, patch_property) + setattr(dd.Series, name, patch_property) if extension and not _hv.extension._loaded: _hv.extension(extension, logo=logo) diff --git a/holoplot/dask.py b/holoplot/dask.py index 666777eae..93fd88d96 100644 --- a/holoplot/dask.py +++ b/holoplot/dask.py @@ -1,3 +1,3 @@ from . import patch -patch('dask', 'bokeh') +patch('dask', extension='bokeh') diff --git a/holoplot/intake.py b/holoplot/intake.py index e93f56c3e..e9ee8c494 100644 --- a/holoplot/intake.py +++ b/holoplot/intake.py @@ -2,6 +2,7 @@ try: import intake.plotting # noqa - patch('intake', 'bokeh') + patch('intake', extension='bokeh') except: - _hv.extension('bokeh', logo=False) + if not _hv.extension._loaded: + _hv.extension('bokeh', logo=False) diff --git a/holoplot/pandas.py b/holoplot/pandas.py index fb15497ec..fb7e1ecb3 100644 --- a/holoplot/pandas.py +++ b/holoplot/pandas.py @@ -1,4 +1,3 @@ from . import patch -patch('pandas', 'bokeh') - +patch('pandas', extension='bokeh') diff --git a/holoplot/streamz.py b/holoplot/streamz.py index b6a49780d..57a370214 100644 --- a/holoplot/streamz.py +++ b/holoplot/streamz.py @@ -1,3 +1,3 @@ from . import patch -patch('streamz', 'bokeh') +patch('streamz', extension='bokeh') diff --git a/holoplot/tests/testcharts.py b/holoplot/tests/testcharts.py index 57fd8c09e..1c15bd1ef 100644 --- a/holoplot/tests/testcharts.py +++ b/holoplot/tests/testcharts.py @@ -19,31 +19,31 @@ def setUp(self): @parameterized.expand([('line', Curve), ('area', Area), ('scatter', Scatter)]) def test_wide_chart(self, kind, element): - plot = self.df.plot(kind=kind) + plot = self.df.holoplot(kind=kind) obj = NdOverlay({'x': element(self.df, 'index', 'x').redim(x='value'), 'y': element(self.df, 'index', 'y').redim(y='value')}, 'Variable') self.assertEqual(plot, obj) @parameterized.expand([('line', Curve), ('area', Area), ('scatter', Scatter)]) def test_wide_chart_labels(self, kind, element): - plot = self.df.plot(kind=kind, value_label='Test', group_label='Category') + plot = self.df.holoplot(kind=kind, value_label='Test', group_label='Category') obj = NdOverlay({'x': element(self.df, 'index', 'x').redim(x='Test'), 'y': element(self.df, 'index', 'y').redim(y='Test')}, 'Category') self.assertEqual(plot, obj) @parameterized.expand([('line', Curve), ('area', Area), ('scatter', Scatter)]) def test_tidy_chart(self, kind, element): - plot = self.df.plot(x='x', y='y', kind=kind) + plot = self.df.holoplot(x='x', y='y', kind=kind) self.assertEqual(plot, element(self.df, 'x', 'y')) @parameterized.expand([('line', Curve), ('area', Area), ('scatter', Scatter)]) def test_tidy_chart_index(self, kind, element): - plot = self.df.plot(x='index', y='y', kind=kind) + plot = self.df.holoplot(x='index', y='y', kind=kind) self.assertEqual(plot, element(self.df, 'index', 'y')) @parameterized.expand([('line', Curve), ('area', Area), ('scatter', Scatter)]) def test_tidy_chart_index_by(self, kind, element): - plot = self.df.plot(x='index', y='y', by='x', kind=kind) + plot = self.df.holoplot(x='index', y='y', by='x', kind=kind) obj = NdOverlay({1: element(self.df[self.df.x==1], 'index', 'y'), 3: element(self.df[self.df.x==3], 'index', 'y'), 5: element(self.df[self.df.x==5], 'index', 'y')}, 'x') @@ -52,22 +52,22 @@ def test_tidy_chart_index_by(self, kind, element): @parameterized.expand([('line', Curve), ('area', Area), ('scatter', Scatter)]) def test_use_index_disabled(self, kind, element): with self.assertRaises(ValueError): - self.df.plot(use_index=False, kind=kind) + self.df.holoplot(use_index=False, kind=kind) @parameterized.expand([('line', Curve), ('area', Area), ('scatter', Scatter)]) def test_tidy_chart_ranges(self, kind, element): - plot = self.df.plot(x='x', y='y', kind=kind, xlim=(0, 3), ylim=(5, 10)) + plot = self.df.holoplot(x='x', y='y', kind=kind, xlim=(0, 3), ylim=(5, 10)) self.assertEqual(plot.kdims[0].range, (0, 3)) self.assertEqual(plot.vdims[0].range, (5, 10)) @parameterized.expand([('line', Curve), ('area', Area), ('scatter', Scatter)]) def test_wide_chart_ranges(self, kind, element): - plot = self.df.plot(kind=kind, xlim=(0, 3), ylim=(5, 10)) + plot = self.df.holoplot(kind=kind, xlim=(0, 3), ylim=(5, 10)) self.assertEqual(plot.last.kdims[0].range, (0, 3)) self.assertEqual(plot.last.vdims[0].range, (5, 10)) def test_area_stacked(self): - plot = self.df.plot.area(stacked=True) + plot = self.df.holoplot.area(stacked=True) obj = NdOverlay({'x': Area(self.df, 'index', 'x').redim(x='value'), 'y': Area(self.df, 'index', 'y').redim(y='value')}, 'Variable') self.assertEqual(plot, Area.stack(obj)) diff --git a/holoplot/tests/testdefaults.py b/holoplot/tests/testdefaults.py new file mode 100644 index 000000000..c10ab36e2 --- /dev/null +++ b/holoplot/tests/testdefaults.py @@ -0,0 +1,44 @@ +import pandas as pd +from holoplot import HoloPlot, patch +from holoviews import Store, Scatter +from holoviews.element.comparison import ComparisonTestCase + + +class TestDefaults(ComparisonTestCase): + + def setUp(self): + patch('pandas') + self.df = pd.DataFrame([[1, 2], [3, 4], [5, 6]], columns=['x', 'y']) + + def test_define_default_options(self): + holoplot = HoloPlot(self.df, width=42, height=42) + curve = holoplot(y='y') + opts = Store.lookup_options('bokeh', curve, 'plot') + self.assertEqual(opts.options.get('width'), 42) + self.assertEqual(opts.options.get('height'), 42) + + def test_define_custom_method(self): + holoplot = HoloPlot(self.df, {'custom_scatter': {'width': 42, 'height': 42}}) + custom_scatter = holoplot.custom_scatter(y='y') + scatter = holoplot.scatter(y='y') + custom_opts = Store.lookup_options('bokeh', custom_scatter, 'plot') + opts = Store.lookup_options('bokeh', scatter, 'plot') + self.assertEqual(custom_opts.options.get('width'), 42) + self.assertEqual(custom_opts.options.get('height'), 42) + self.assertNotEqual(opts.options.get('width'), 42) + self.assertNotEqual(opts.options.get('height'), 42) + + def test_define_customize_method(self): + holoplot = HoloPlot(self.df, {'scatter': {'width': 42, 'height': 42}}) + custom_scatter = holoplot.scatter(y='y') + curve = holoplot.line(y='y') + custom_opts = Store.lookup_options('bokeh', custom_scatter, 'plot') + opts = Store.lookup_options('bokeh', curve, 'plot') + self.assertEqual(custom_opts.options.get('width'), 42) + self.assertEqual(custom_opts.options.get('height'), 42) + self.assertNotEqual(opts.options.get('width'), 42) + self.assertNotEqual(opts.options.get('height'), 42) + + def test_attempt_to_override_kind_on_method(self): + holoplot = HoloPlot(self.df, {'scatter': {'kind': 'line'}}) + self.assertIsInstance(holoplot.scatter(y='y'), Scatter) diff --git a/holoplot/tests/testpatch.py b/holoplot/tests/testpatch.py index 189653c31..de3a790e4 100644 --- a/holoplot/tests/testpatch.py +++ b/holoplot/tests/testpatch.py @@ -12,21 +12,17 @@ class TestPatchPandas(TestCase): def setUp(self): - try: - import pandas as pd # noqa - except: - raise SkipTest('Pandas not available') patch('pandas') def test_pandas_series_patched(self): import pandas as pd series = pd.Series([0, 1, 2]) - self.assertIsInstance(series.plot, HoloPlot) + self.assertIsInstance(series.holoplot, HoloPlot) def test_pandas_dataframe_patched(self): import pandas as pd df = pd.DataFrame([[1, 2], [3, 4], [5, 6]], columns=['x', 'y']) - self.assertIsInstance(df.plot, HoloPlot) + self.assertIsInstance(df.holoplot, HoloPlot) class TestPatchDask(TestCase): @@ -43,14 +39,14 @@ def test_dask_series_patched(self): import dask.dataframe as dd series = pd.Series([0, 1, 2]) dseries = dd.from_pandas(series, 2) - self.assertIsInstance(dseries.plot, HoloPlot) + self.assertIsInstance(dseries.holoplot, HoloPlot) def test_dask_dataframe_patched(self): import pandas as pd import dask.dataframe as dd df = pd.DataFrame([[1, 2], [3, 4], [5, 6]], columns=['x', 'y']) ddf = dd.from_pandas(df, 2) - self.assertIsInstance(ddf.plot, HoloPlot) + self.assertIsInstance(ddf.holoplot, HoloPlot) class TestPatchXArray(TestCase): @@ -60,14 +56,21 @@ def setUp(self): import xarray as xr # noqa except: raise SkipTest('XArray not available') - patch('xarray') + import holoplot.xarray # noqa def test_xarray_dataarray_patched(self): import xarray as xr array = np.random.rand(100, 100) xr_array = xr.DataArray(array, coords={'x': range(100), 'y': range(100)}, dims=('y', 'x')) - self.assertIsInstance(xr_array.plot, HoloPlot) + self.assertIsInstance(xr_array.holoplot, HoloPlot) + def test_xarray_dataset_patched(self): + import xarray as xr + array = np.random.rand(100, 100) + xr_array = xr.DataArray(array, coords={'x': range(100), 'y': range(100)}, dims=('y', 'x')) + xr_ds = xr.Dataset({'z': xr_array}) + self.assertIsInstance(xr_ds.holoplot, HoloPlot) + class TestPatchStreamz(TestCase): @@ -81,19 +84,19 @@ def setUp(self): def test_streamz_dataframe_patched(self): from streamz.dataframe import Random random_df = Random() - self.assertIsInstance(random_df.plot, HoloPlot) + self.assertIsInstance(random_df.holoplot, HoloPlot) def test_streamz_series_patched(self): from streamz.dataframe import Random random_df = Random() - self.assertIsInstance(random_df.x.plot, HoloPlot) + self.assertIsInstance(random_df.x.holoplot, HoloPlot) def test_streamz_dataframes_patched(self): from streamz.dataframe import Random random_df = Random() - self.assertIsInstance(random_df.groupby('x').sum().plot, HoloPlot) + self.assertIsInstance(random_df.groupby('x').sum().holoplot, HoloPlot) def test_streamz_seriess_patched(self): from streamz.dataframe import Random random_df = Random() - self.assertIsInstance(random_df.groupby('x').sum().y.plot, HoloPlot) + self.assertIsInstance(random_df.groupby('x').sum().y.holoplot, HoloPlot) diff --git a/holoplot/xarray.py b/holoplot/xarray.py index df4ea62d9..6f2706035 100644 --- a/holoplot/xarray.py +++ b/holoplot/xarray.py @@ -1,3 +1,13 @@ -from . import patch +import xarray as xr -patch('xarray', 'bokeh') +from . import HoloPlot, _hv + +if not _hv.extension._loaded: + _hv.extension('bokeh', logo=False) + +@xr.register_dataset_accessor('holoplot') +@xr.register_dataarray_accessor('holoplot') +class XArrayHoloPlot(HoloPlot): + """ + HoloPlot implementation for xarray + """