Skip to content

Commit

Permalink
Merge branch 'main' into joss-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
layik committed Oct 17, 2024
2 parents 06dca2e + 7dc0cc7 commit 2ac584a
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 21 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

Setvis is a python library for visualising set membership and patterns of missingness in data.

It can be used both programmatically and interactively in a Jupyter notebook (powered by [Bokeh](https://docs.bokeh.org/en/latest/index.html) widgets). It operates on data using a memory efficient architecture, and supports loading data from flat files, Pandas dataframes, and directly from a Postgres database.
The plotting and interactive workflow of Setvis is designed for use within a Jupyter notebook (although it is possible to run outside Jupyter). The other components of Setvis can be used interactively or programmatically. The interactive plots are powered by [Bokeh](https://docs.bokeh.org/en/latest/index.html) widgets.

It operates on data using a memory efficient architecture, and supports loading data from flat files, Pandas dataframes, and directly from a Postgres database.

## Documentation

Expand Down
6 changes: 4 additions & 2 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ data <https://en.wikipedia.org/wiki/Missing_data>`_ in a dataset).
It can also be used to visualize set membership of which data
missingness is a special case.

It is designed to work particularly well when used interactively from
a notebook, but can also be used non-interactively.
The plotting and interactive workflow of Setvis (see :ref:`plots`) is
designed for use within a Jupyter notebook (although it is possible
to run outside Jupyter, see :ref:`plot_outside_notebook`). The other
components of Setvis can be used interactively or programmatically.

At the moment, setvis can load data from `pandas
<https://pandas.pydata.org/>`_ dataframes, csv files, and also
Expand Down
62 changes: 62 additions & 0 deletions docs/source/plots.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,68 @@
.. _plots:

Plotting and interactivity
==========================

The `setvis.plots` module
-------------------------

.. automodule:: setvis.plots
:members:


.. _plot_outside_notebook:
Plotting outside of a notebook
------------------------------

.. note::

This is 'advanced' use of Setvis, and is not as well tested as the notebook
based workflow.


After creating a :class:`PlotSession` object, interactive plots can be
created with the :meth:`PlotSession.add_plot` method. The usual,
Jupyter-notebook-based workflow, creates these inline in the notebook.

When calling :meth:`PlotSession.add_plot` with ``notebook=False``, a
Bokeh server is started and returned, enabling SetVis to be used outside of
a Jupyter notebook.

Note that this usage is still *interactive*, but the plots are no longer
confined to a notebook. One use-case for this is to show the plots on a
large external display.

The code below creates and starts the Bokeh server.

.. code-block:: python
import pandas as pd
from setvis.plots import PlotSession
## This csv file can be found in the Setvis git repository
df = pd.read_csv("examples/datasets/Synthetic_APC_DIAG_Fields.csv")
session = PlotSession(df)
## Create a plot
bokeh_plot_server = session.add_plot(name="Plot 3", notebook=False, html_display_link=False)
## Display the URL of the plot that was just created
print(f"Connect to http://localhost:{bokeh_plot_server.port}/ to see the plot")
## Start and enter the event loop (this command blocks)
## Not required if running inside Jupyter
bokeh_plot_server.run_until_shutdown()
The last command (``run_until_shutdown()``) is not required if
creating the plot from inside Jupyter (it will be attached to
Jupyter's event loop). See the Bokeh documentation for more
information on using a Bokeh server, including how it can be embedded
in other applications.


.. seealso::

- The example notebook in the Setvis repository `Example - plotting outside the notebook.ipynb <https://github.com/alan-turing-institute/setvis/blob/main/notebooks/Example%20-%20plotting%20outside%20the%20notebook.ipynb>`_ (GitHub link)
- The Bokeh documentation on `Bokeh server APIs <https://docs.bokeh.org/en/latest/docs/user_guide/server/library.html>`_
72 changes: 60 additions & 12 deletions notebooks/Example - plotting outside the notebook.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"id": "1ed3b2ee",
"metadata": {},
"source": [
"# Example - plotting outside the notebook and additional options"
"# Example - additional plot options and plotting outside the notebook"
]
},
{
Expand All @@ -31,12 +31,22 @@
"metadata": {},
"outputs": [],
"source": [
"from setvis.plots import *\n",
"import pandas as pd\n",
"from setvis.plots import PlotSession\n",
"\n",
"df = pd.read_csv(\"../examples/datasets/Synthetic_APC_DIAG_Fields.csv\")\n",
"\n",
"session = PlotSession(df)"
]
},
{
"cell_type": "markdown",
"id": "6c95e844-45f7-46ee-96b2-ee4170f19984",
"metadata": {},
"source": [
"## Passing additional arguments to configure the plot"
]
},
{
"cell_type": "markdown",
"id": "eda7ddc0",
Expand All @@ -51,40 +61,46 @@
"cell_type": "code",
"execution_count": null,
"id": "09c59044",
"metadata": {
"scrolled": false
},
"metadata": {},
"outputs": [],
"source": [
"session.add_plot(name=\"Plot 1\", height=480, output_backend=\"svg\")"
]
},
{
"cell_type": "markdown",
"id": "3a0803d3-4795-4f71-a411-1eefe0f361f1",
"metadata": {},
"source": [
"## Showing plots outside of the notebook (Bokeh server)"
]
},
{
"cell_type": "markdown",
"id": "a7d5631c",
"metadata": {},
"source": [
"The cell below produces a new plot that does not show inline. Instead, it displays a link to a Bokeh server which can be opened in another tab or window. This can be done to make use of larger displays."
"The cell below produces a new plot that does not show inline. Instead, it displays a link to a Bokeh server which can be opened in another tab or window. This can be done to make use of larger displays, or to use SetVis outside of Jupyter.\n",
"\n",
"Notice how the `based_on` argument behaves in the same way as it does when the plot is rendered inline in the notebook, with the resulting plot (Plot 2) showing the data that was selected in Plot 1 above. Updates to the selection in Plot 1 aren't propagated until the cell below is run again, which will create a new Bokeh server."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9732b0c4",
"metadata": {
"scrolled": false
},
"metadata": {},
"outputs": [],
"source": [
"session.add_plot(name=\"Plot 2\", based_on=\"Plot 1\", notebook=False)"
]
},
{
"cell_type": "markdown",
"id": "1991c84c",
"id": "51cdf2ca-90d3-46df-88ed-eb3d495ea4f4",
"metadata": {},
"source": [
"Making a selection with the plot shown in the newly-opened browser tab will affect the result of the following cell (which simply counts the number of records selected in the plot). Re-run the cell below after changing the selection."
"Making a selection with the plot shown in the newly-opened browser tab will affect the result of the following cell (which simply counts the number of records selected in the plot). Try re-running the cell below after changing the selection in Plot 2."
]
},
{
Expand All @@ -96,6 +112,38 @@
"source": [
"sum(session.selected_records(\"Plot 2\"))"
]
},
{
"cell_type": "markdown",
"id": "05cfae41-93c2-4845-8edf-6044a967737f",
"metadata": {},
"source": [
"## Creating Setvis plots from outside of a Jupyter notebook (advanced)"
]
},
{
"cell_type": "markdown",
"id": "529c7ba9-f506-4b03-93c8-f0135ea75eda",
"metadata": {},
"source": [
"When run from a Jupyter notebook, as in the example above, the Bokeh server runs immediately in the background (it is attached to the asyncio event loop that Jupyter creates).\n",
"\n",
"When run from outside Jupyter, for example from a Python script or from the Python interactive shell, the event loop needs to be created. A simple way to do this is shown in the cell below (which is not run here). The Bokeh server object is returned from `add_plot`.\n",
"\n",
"Try running the contents of this cell from outside of a notebook."
]
},
{
"cell_type": "raw",
"id": "9f68ff5d-73dd-48ba-897c-2f577a71144d",
"metadata": {},
"source": [
"# Create a plot\n",
"bokeh_plot_server = session.add_plot(name=\"Plot 3\", notebook=False, html_display_link=False)\n",
"\n",
"# Start and enter the event loop (this command blocks)\n",
"bokeh_plot_server.run_until_shutdown()"
]
}
],
"metadata": {
Expand All @@ -114,7 +162,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.2"
"version": "3.12.1"
}
},
"nbformat": 4,
Expand Down
12 changes: 6 additions & 6 deletions setvis/plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from bokeh.models import TabPanel, Tabs
from bokeh.events import SelectionGeometry
import bokeh.io
import bokeh.server
import bokeh.server.server
import pandas as pd
import numpy as np
import logging
Expand Down Expand Up @@ -147,8 +147,6 @@ def __init__(
self.tools = [
"xbox_select",
"tap",
"box_zoom",
"pan",
"reset",
"save",
HelpTool(
Expand Down Expand Up @@ -613,7 +611,7 @@ def plot(self, **kwargs) -> bokeh.plotting.figure:
source=self.source,
line_color=self.linecolor,
)
p.xaxis.ticker = [x - 0.5 for x in range(self._bins)]
p.xaxis.ticker = [x for x in range(self._bins)]
p.xaxis.major_label_overrides = self._get_xtick_labels()
p.xaxis.axis_label = self.xlabel
p.yaxis.axis_label = self.ylabel
Expand Down Expand Up @@ -746,7 +744,7 @@ def plot(self, **kwargs) -> bokeh.plotting.figure:
source=self.source,
line_color=self.linecolor,
)
p.xaxis.ticker = [x - 0.5 for x in range(self._bins)]
p.xaxis.ticker = [x for x in range(self._bins)]
p.xaxis.major_label_overrides = self._get_xtick_labels()
p.xaxis.axis_label = self.xlabel
p.yaxis.axis_label = self.ylabel
Expand Down Expand Up @@ -1289,9 +1287,11 @@ def active_tab_callback(attr, old, new):
else:
server = bokeh.server.server.Server({"/": plot_app}, port=0)
server.start()
from IPython.core.display import display, HTML

logger.info(f"Bokeh server for plot {name} started at http://localhost:{server.port}/")

if html_display_link:
from IPython.display import display, HTML
display(
HTML(
f"<a href='http://localhost:{server.port}' target='_blank' rel='noopener noreferrer'>"
Expand Down

0 comments on commit 2ac584a

Please sign in to comment.