The Panel library from PyViz lets you make widget-controlled apps and dashboards from a wide variety of plotting libraries and data types. Here let's set up four different plotting libraries controlled by a couple of widgets, for Hans Rosling's gapminder example.
import param
import numpy as np
import pandas as pd
import panel as pn
import altair as alt
import plotly.graph_objs as go
import matplotlib.pyplot as plt
import hvplot.pandas
pn.extension('vega', 'plotly')
First, we'll get the data into a Pandas dataframe:
url = 'https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv'
dataset = pd.read_csv(url)
dataset.sample(10)
Now let's define a couple of user-settable parameters (the year and whether to show a legend), then write methods to plot this data with Matplotlib, Plotly, Altair (using Vega), and hvPlot (using HoloViews and Bokeh):
class Gapminder(param.Parameterized):
year = param.ObjectSelector(default=1952, objects=list(dataset.year.unique()))
show_legend = param.Boolean(default=True)
title = 'Life expectancy vs. GDP, %s'
xlabel = 'GDP per capita (2000 dollars)'
ylabel = 'Life expectancy (years)'
ylim = (20, 90)
def get_data(self):
df = dataset[(dataset.year==self.year) & (dataset.gdpPercap < 10000)].copy()
df['size'] = np.sqrt(df['pop']*2.666051223553066e-05)
self.xlim = (df['gdpPercap'].min()-100,df['gdpPercap'].max()+1000)
return df
def mpl_view(self):
data = self.get_data()
title = "Matplotlib: " + (self.title % self.year)
plot = plt.figure(figsize=(7, 6))
ax = plot.add_subplot(111)
ax.set_xscale("log")
ax.set_title(title)
ax.set_xlabel(self.xlabel)
ax.set_ylabel(self.ylabel)
ax.set_ylim(self.ylim)
ax.set_xlim(self.xlim)
for continent, df in data.groupby('continent'):
ax.scatter(df.gdpPercap, y=df.lifeExp, s=df['size']*5,
edgecolor='black', label=continent)
if self.show_legend:
ax.legend(loc=4)
plt.close(plot)
return plot
def plotly_view(self):
data = self.get_data()
title = 'Plotly: ' + (self.title % self.year)
traces = []
for continent, df in data.groupby('continent'):
marker=dict(symbol='circle', sizemode='area', sizeref=0.1, size=df['size'], line=dict(width=2))
traces.append(go.Scatter(x=df.gdpPercap, y=df.lifeExp, mode='markers', marker=marker, name=continent, text=df.country))
axis_opts = dict(gridcolor='rgb(255, 255, 255)', zerolinewidth=1, ticklen=5, gridwidth=2)
layout = go.Layout(title=title, showlegend=self.show_legend, width=550,
xaxis=dict(title=self.xlabel, type='log', **axis_opts),
yaxis=dict( title=self.ylabel, **axis_opts))
return go.Figure(data=traces, layout=layout)
def altair_view(self):
data = self.get_data()
title = "Altair/Vega: " + (self.title % self.year)
legend= ({} if self.show_legend else {'legend': None})
plot = alt.Chart(data).mark_circle().encode(
alt.X('gdpPercap:Q', scale=alt.Scale(type='log'), axis=alt.Axis(title=self.xlabel)),
alt.Y('lifeExp:Q', scale=alt.Scale(zero=False, domain=self.ylim), axis=alt.Axis(title=self.ylabel)),
size=alt.Size('pop:Q', scale=alt.Scale(type="log"), legend=None),
color=alt.Color('continent', scale=alt.Scale(scheme="category10"), **legend),
tooltip=['continent','country'])\
.properties(title=title).configure_axis(grid=False)
return plot.interactive()
def hvplot_view(self):
data = self.get_data()
title = "hvPlot/Bokeh: " + (self.title % self.year)
plot = data.hvplot.scatter('gdpPercap', 'lifeExp', by='continent', s='size',
logx=True, title=title, width=500, height=400, legend=self.show_legend, hover_cols=['country'])
plot = plot.options(legend_position='bottom_right', xticks=[500, 1000, 2000, 5000, 10000])
plot = plot.redim.label(gdpPercap=self.xlabel, lifeExp=self.ylabel)
plot = plot.redim.range(lifeExp=self.ylim, gdpPercap=(200, 12000))
return plot
gm = Gapminder(name='')
We can now make a panel showing the parameters of this gm
object along with the result of calling four different _view()
methods:
pn.Column(gm.param,
pn.Row(gm.hvplot_view, gm.altair_view),
pn.Row(gm.mpl_view, gm.plotly_view))
Here the Matplotlib output is being rendered as PNG, but the rest are interactive, with hovering, zooming, and similar features (if allowed by the notebook viewer being used). Note that due to interactions between the various JS libraries used here, the Plotly output can get mixed up when resizing or reloading the page, but it should be reset if you adjust the year slider once you are done.
We can also make these plots available as a standalone server, if we add .servable()
to the panel object and call this notebook as bokeh serve --show Panel_Gapminders.ipynb
. Before doing that, let's make another panel where we can add a logo and a title, so that it makes a nicer page layout when served separately. We'll also add an R ggplot2-based plot to fill out the page:
logo = """<a href="http://panel.pyviz.org">
<img src="https://panel.pyviz.org/_static/logo_stacked.png"
width=150 height=127 align="left" margin=20px>"""
title = '<h2>Plotting library comparison</h2>'
desc = pn.pane.HTML("""
The <a href="http://panel.pyviz.org">Panel</a> library from <a href="http://pyviz.org">PyViz</a>
lets you make widget-controlled apps and dashboards from a wide variety of
plotting libraries and data types. Here you can try out five different plotting libraries
controlled by a couple of widgets, for Hans Rosling's
<a href="https://demo.bokehplots.com/apps/gapminder">gapminder</a> example.""", width=450)
pn.Row(pn.Column(logo, title, desc, pn.panel(gm.param, widgets={'year': pn.widgets.DiscreteSlider})),
pn.Column(pn.Row(gm.hvplot_view, gm.altair_view),
pn.Row(gm.mpl_view, gm.plotly_view))).servable()
Once you run Bokeh Server on this notebook, you should get a panel like the following in your web browser that you can explore or share with other users of your machine: