{
 "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.GridBox(\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.Matplotlib(mpl_view, format='png', sizing_mode='scale_both', tight=True, 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
}
