{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import param\n", "import numpy as np \n", "import pandas as pd\n", "import panel as pn\n", "\n", "import altair as alt\n", "import plotly.graph_objs as go\n", "import plotly.io as pio\n", "import matplotlib.pyplot as plt\n", "\n", "pn.extension('vega', 'plotly', defer_load=True, template='fast')\n", "import hvplot.pandas" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Configuration\n", "\n", "Let us start by configuring some high-level variables and configure the template:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "XLABEL = 'GDP per capita (2000 dollars)'\n", "YLABEL = 'Life expectancy (years)'\n", "YLIM = (20, 90)\n", "ACCENT = \"#00A170\"\n", "\n", "PERIOD = 1000 # milliseconds\n", "\n", "pn.state.template.param.update(\n", " site_url=\"https://panel.holoviz.org\",\n", " title=\"Hans Rosling's Gapminder\",\n", " header_background=ACCENT,\n", " accent_base_color=ACCENT,\n", " favicon=\"static/extensions/panel/images/favicon.ico\",\n", " theme_toggle=False\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Extract the dataset\n", "\n", "First, we'll get the data into a Pandas dataframe. We use the [built in `cache`](https://panel.holoviz.org/how_to/caching/memoization.html) to speed up the app." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@pn.cache\n", "def get_dataset():\n", " url = 'https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv'\n", " return pd.read_csv(url)\n", "\n", "dataset = get_dataset()\n", "\n", "YEARS = [int(year) for year in dataset.year.unique()]\n", "\n", "dataset.sample(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Set up widgets and description\n", "\n", "Next we will set up a periodic callback to allow cycling through the years, set up the widgets to control the application and write an introduction:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def play():\n", " if year.value == YEARS[-1]:\n", " year.value = YEARS[0]\n", " return\n", "\n", " index = YEARS.index(year.value)\n", " year.value = YEARS[index+1] \n", "\n", "year = pn.widgets.DiscreteSlider(\n", " value=YEARS[-1], options=YEARS, name=\"Year\", width=280\n", ")\n", "show_legend = pn.widgets.Checkbox(value=True, name=\"Show Legend\")\n", "\n", "periodic_callback = pn.state.add_periodic_callback(play, start=False, period=PERIOD)\n", "player = pn.widgets.Checkbox.from_param(periodic_callback.param.running, name=\"Autoplay\")\n", "\n", "widgets = pn.Column(year, player, show_legend, margin=(0,15))\n", "\n", "desc = \"\"\"## 🎓 Info\n", "\n", "The [Panel](http://panel.holoviz.org) library from [HoloViz](http://holoviz.org)\n", "lets you make widget-controlled apps and dashboards from a wide variety of \n", "plotting libraries and data types. Here you can try out four different plotting libraries\n", "controlled by a couple of widgets, for Hans Rosling's \n", "[gapminder](https://demo.bokeh.org/gapminder) example.\n", "\n", "Source: [pyviz-topics - gapminder](https://github.com/pyviz-topics/examples/blob/master/gapminders/gapminders.ipynb)\n", "\"\"\"\n", "\n", "settings = pn.Column(\n", " \"## ⚙️ Settings\", widgets, desc,\n", " sizing_mode='stretch_width'\n", ").servable(area='sidebar')\n", "\n", "settings" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Define plotting functions\n", "\n", "Now let's define helper functions and functions to plot this dataset with Matplotlib, Plotly, Altair, and hvPlot (using HoloViews and Bokeh)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@pn.cache\n", "def get_data(year):\n", " df = dataset[(dataset.year==year) & (dataset.gdpPercap < 10000)].copy()\n", " df['size'] = np.sqrt(df['pop']*2.666051223553066e-05)\n", " df['size_hvplot'] = df['size']*6\n", " return df\n", "\n", "def get_title(library, year):\n", " return f\"{library}: Life expectancy vs. GDP, {year}\"\n", "\n", "def get_xlim(data):\n", " return (data['gdpPercap'].min()-100,data['gdpPercap'].max()+1000)\n", "\n", "@pn.cache\n", "def mpl_view(year=1952, show_legend=True):\n", " data = get_data(year)\n", " title = get_title(\"Matplotlib\", year)\n", " xlim = get_xlim(data)\n", "\n", " plot = plt.figure(figsize=(10, 6), facecolor=(0, 0, 0, 0))\n", " ax = plot.add_subplot(111)\n", " ax.set_xscale(\"log\")\n", " ax.set_title(title)\n", " ax.set_xlabel(XLABEL)\n", " ax.set_ylabel(YLABEL)\n", " ax.set_ylim(YLIM)\n", " ax.set_xlim(xlim)\n", "\n", " for continent, df in data.groupby('continent'):\n", " ax.scatter(df.gdpPercap, y=df.lifeExp, s=df['size']*5,\n", " edgecolor='black', label=continent)\n", "\n", " if show_legend:\n", " ax.legend(loc=4)\n", "\n", " plt.close(plot)\n", " return plot\n", "\n", "pio.templates.default = None\n", "\n", "@pn.cache\n", "def plotly_view(year=1952, show_legend=True):\n", " data = get_data(year)\n", " title = get_title(\"Plotly\", year)\n", " xlim = get_xlim(data)\n", "\n", " traces = []\n", " for continent, df in data.groupby('continent'):\n", " marker=dict(symbol='circle', sizemode='area', sizeref=0.1, size=df['size'], line=dict(width=2))\n", " traces.append(go.Scatter(x=df.gdpPercap, y=df.lifeExp, mode='markers', marker=marker, name=continent, text=df.country))\n", "\n", " axis_opts = dict(gridcolor='rgb(255, 255, 255)', zerolinewidth=1, ticklen=5, gridwidth=2)\n", " layout = go.Layout(\n", " title=title, showlegend=show_legend,\n", " xaxis=dict(title=XLABEL, type='log', **axis_opts),\n", " yaxis=dict(title=YLABEL, **axis_opts),\n", " autosize=True, paper_bgcolor='rgba(0,0,0,0)',\n", " )\n", " \n", " return go.Figure(data=traces, layout=layout)\n", "\n", "@pn.cache\n", "def altair_view(year=1952, show_legend=True, height=\"container\", width=\"container\"):\n", " data = get_data(year)\n", " title = get_title(\"Altair/ Vega\", year)\n", " xlim = get_xlim(data)\n", " legend= ({} if show_legend else {'legend': None})\n", " return (\n", " alt.Chart(data)\n", " .mark_circle().encode(\n", " alt.X('gdpPercap:Q', scale=alt.Scale(type='log'), axis=alt.Axis(title=XLABEL)),\n", " alt.Y('lifeExp:Q', scale=alt.Scale(zero=False, domain=YLIM), axis=alt.Axis(title=YLABEL)),\n", " size=alt.Size('pop:Q', scale=alt.Scale(type=\"log\"), legend=None),\n", " color=alt.Color('continent', scale=alt.Scale(scheme=\"category10\"), **legend),\n", " tooltip=['continent','country'])\n", " .configure_axis(grid=False)\n", " .properties(title=title, height=height, width=width, background='rgba(0,0,0,0)') \n", " .configure_view(fill=\"white\")\n", " .interactive()\n", " )\n", "\n", "@pn.cache\n", "def hvplot_view(year=1952, show_legend=True):\n", " data = get_data(year)\n", " title = get_title(\"hvPlot/ Bokeh\", year)\n", " xlim = get_xlim(data)\n", " return data.hvplot.scatter(\n", " 'gdpPercap', 'lifeExp', by='continent', s='size_hvplot', alpha=0.8,\n", " logx=True, title=title, responsive=True, legend='bottom_right',\n", " hover_cols=['country'], ylim=YLIM, xlim=xlim, ylabel=YLABEL, xlabel=XLABEL\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Bind the plot functions to the widgets" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mpl_view = pn.bind(mpl_view, year=year, show_legend=show_legend)\n", "plotly_view = pn.bind(plotly_view, year=year, show_legend=show_legend)\n", "altair_view = pn.bind(altair_view, year=year, show_legend=show_legend)\n", "hvplot_view = pn.bind(hvplot_view, year=year, show_legend=show_legend)\n", "\n", "plots = pn.layout.GridBox(\n", " pn.pane.Matplotlib(mpl_view, format='png', sizing_mode='scale_both', tight=True, margin=10),\n", " pn.pane.HoloViews(hvplot_view, sizing_mode='stretch_both', margin=10),\n", " pn.pane.Plotly(plotly_view, sizing_mode='stretch_both', margin=10),\n", " pn.pane.Vega(altair_view, sizing_mode='stretch_both', margin=10),\n", " ncols=2,\n", " sizing_mode=\"stretch_both\"\n", ").servable()\n", "\n", "plots" ] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 4 }