{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is a `bqplot` recreation of Mike Bostock's [Wealth of Nations](https://bost.ocks.org/mike/nations/). This was also done by [Gapminder](http://www.gapminder.org/world/#$majorMode=chart$is;shi=t;ly=2003;lb=f;il=t;fs=11;al=30;stl=t;st=t;nsl=t;se=t$wst;tts=C$ts;sp=5.59290322580644;ti=2013$zpv;v=0$inc_x;mmid=XCOORDS;iid=phAwcNAVuyj1jiMAkmq1iMg;by=ind$inc_y;mmid=YCOORDS;iid=phAwcNAVuyj2tPLxKvvnNPA;by=ind$inc_s;uniValue=8.21;iid=phAwcNAVuyj0XOoBL_n5tAQ;by=ind$inc_c;uniValue=255;gid=CATID0;by=grp$map_x;scale=log;dataMin=194;dataMax=96846$map_y;scale=lin;dataMin=23;dataMax=86$map_s;sma=49;smi=2.65$cd;bd=0$inds=;modified=60). It is originally based on a TED Talk by [Hans Rosling](http://www.ted.com/talks/hans_rosling_shows_the_best_stats_you_ve_ever_seen)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import os\n",
    "\n",
    "from bqplot import (\n",
    "    LogScale, LinearScale, OrdinalColorScale, ColorAxis,\n",
    "    Axis, Scatter, Lines, CATEGORY10, Label, Figure, Tooltip\n",
    ")\n",
    "\n",
    "from ipywidgets import HBox, VBox, IntSlider, Play, jslink"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "initial_year = 1800"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Cleaning and Formatting JSON Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "data = pd.read_json(os.path.abspath('../data_files/nations.json'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def clean_data(data):\n",
    "    for column in ['income', 'lifeExpectancy', 'population']:\n",
    "        data = data.drop(data[data[column].apply(len) <= 4].index)\n",
    "    return data\n",
    "\n",
    "def extrap_interp(data):\n",
    "    data = np.array(data)\n",
    "    x_range = np.arange(1800, 2009, 1.)\n",
    "    y_range = np.interp(x_range, data[:, 0], data[:, 1])\n",
    "    return y_range\n",
    "\n",
    "def extrap_data(data):\n",
    "    for column in ['income', 'lifeExpectancy', 'population']:\n",
    "        data[column] = data[column].apply(extrap_interp)\n",
    "    return data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = clean_data(data)\n",
    "data = extrap_data(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "income_min, income_max = np.min(data['income'].apply(np.min)), np.max(data['income'].apply(np.max))\n",
    "life_exp_min, life_exp_max = np.min(data['lifeExpectancy'].apply(np.min)), np.max(data['lifeExpectancy'].apply(np.max))\n",
    "pop_min, pop_max = np.min(data['population'].apply(np.min)), np.max(data['population'].apply(np.max))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_data(year):\n",
    "    year_index = year - 1800\n",
    "    income = data['income'].apply(lambda x: x[year_index])\n",
    "    life_exp = data['lifeExpectancy'].apply(lambda x: x[year_index])\n",
    "    pop =  data['population'].apply(lambda x: x[year_index])\n",
    "    return income, life_exp, pop"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Creating the Tooltip to display the required fields\n",
    "\n",
    "`bqplot`'s native `Tooltip` allows us to simply display the data fields we require on a mouse-interaction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "tt = Tooltip(fields=['name', 'x', 'y'], labels=['Country Name', 'Income per Capita', 'Life Expectancy'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Creating the Label to display the year\n",
    "\n",
    "Staying true to the `d3` recreation of the talk, we place a `Label` widget in the bottom-right of the `Figure` (it inherits the `Figure` co-ordinates when no scale is passed to it). With `enable_move` set to `True`, the `Label` can be dragged around. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "year_label = Label(x=[0.75], y=[0.10], default_size=46, font_weight='bolder', colors=['orange'],\n",
    "                   text=[str(initial_year)], enable_move=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Defining Axes and Scales\n",
    "\n",
    "The inherent skewness of the income data favors the use of a `LogScale`. Also, since the color coding by regions does not follow an ordering, we use the `OrdinalColorScale`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_sc = LogScale(min=income_min, max=income_max)\n",
    "y_sc = LinearScale(min=life_exp_min, max=life_exp_max)\n",
    "c_sc = OrdinalColorScale(domain=data['region'].unique().tolist(), colors=CATEGORY10[:6])\n",
    "size_sc = LinearScale(min=pop_min, max=pop_max)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "ax_y = Axis(label='Life Expectancy', scale=y_sc, orientation='vertical', side='left', grid_lines='solid')\n",
    "ax_x = Axis(label='Income per Capita', scale=x_sc, grid_lines='solid')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Creating the Scatter Mark with the appropriate size and color parameters passed\n",
    "\n",
    "To generate the appropriate graph, we need to pass the population of the country to the `size` attribute and its region to the `color` attribute."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Start with the first year's data\n",
    "cap_income, life_exp, pop = get_data(initial_year)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "wealth_scat = Scatter(x=cap_income, y=life_exp, color=data['region'], size=pop,\n",
    "                      names=data['name'], display_names=False,\n",
    "                      scales={'x': x_sc, 'y': y_sc, 'color': c_sc, 'size': size_sc},\n",
    "                      default_size=4112, tooltip=tt, animate=True, stroke='Black',\n",
    "                      unhovered_style={'opacity': 0.5})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "nation_line = Lines(x=data['income'][0], y=data['lifeExpectancy'][0], colors=['Gray'],\n",
    "                       scales={'x': x_sc, 'y': y_sc}, visible=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Creating the Figure"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "time_interval = 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = Figure(marks=[wealth_scat, year_label, nation_line], axes=[ax_x, ax_y],\n",
    "             title='Health and Wealth of Nations', animation_duration=time_interval)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Using a Slider to allow the user to change the year and a button for animation\n",
    "\n",
    "Here we see how we can seamlessly integrate `bqplot` into the jupyter widget infrastructure. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "year_slider = IntSlider(min=1800, max=2008, step=1, description='Year', value=initial_year)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When the `hovered_point` of the `Scatter` plot is changed (i.e. when the user hovers over a different element), the entire path of that country is displayed by making the `Lines` object visible and setting it's `x` and `y` attributes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hover_changed(change):\n",
    "    if change.new is not None:\n",
    "        nation_line.x = data[data['name'] == wealth_scat.names[change.new]]['income'].values[0]\n",
    "        nation_line.y = data[data['name'] == wealth_scat.names[change.new]]['lifeExpectancy'].values[0]\n",
    "        nation_line.visible = True\n",
    "    else:\n",
    "        nation_line.visible = False\n",
    "        \n",
    "wealth_scat.observe(hover_changed, 'hovered_point')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On the slider value `callback` (a function that is triggered everytime the `value` of the slider is changed) we change the `x`, `y` and `size` co-ordinates of the `Scatter`. We also update the `text` of the `Label` to reflect the current year."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "def year_changed(change):\n",
    "    wealth_scat.x, wealth_scat.y, wealth_scat.size = get_data(year_slider.value)\n",
    "    year_label.text = [str(year_slider.value)]\n",
    "\n",
    "year_slider.observe(year_changed, 'value')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Add an animation button"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Link(source=(Play(value=1800, interval=10, max=2008, min=1800), 'value'), target=(IntSlider(value=1800, descri…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "play_button = Play(min=1800, max=2008, interval=time_interval)\n",
    "jslink((play_button, 'value'), (year_slider, 'value'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Displaying the GUI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "8062f9630f0c482d87e750f7103cdca1",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(HBox(children=(Play(value=1800, interval=10, max=2008, min=1800), IntSlider(value=1800, descrip…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "VBox([HBox([play_button, year_slider]), fig])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
