{
 "metadata": {
  "name": "",
  "signature": "sha256:bcf38cdab63bfa8910d297c98ac2f088e038e68c88bc843aa61b6dd5af16c826"
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import numpy as np\n",
      "import pandas as pd\n",
      "from bokeh.plotting import *"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "# Generate some synthetic time series for six different categories\n",
      "cats = list(\"abcdef\")\n",
      "y = np.random.randn(2000)\n",
      "g = np.random.choice(cats, 2000)\n",
      "for i, l in enumerate(cats):\n",
      "    y[g == l] += i // 2\n",
      "df = pd.DataFrame(dict(score=y, group=g))"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "# Find the quartiles and IQR foor each category\n",
      "groups = df.groupby('group')\n",
      "q1 = groups.quantile(q=0.25)\n",
      "q2 = groups.quantile(q=0.5)\n",
      "q3 = groups.quantile(q=0.75)\n",
      "iqr = q3 - q1\n",
      "upper = q2 + 1.5*iqr\n",
      "lower = q2 - 1.5*iqr"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "# find the outliers for each category\n",
      "def outliers(group):\n",
      "   cat = group.name\n",
      "   return group[(group.score > upper.loc[cat][0]) | (group.score < lower.loc[cat][0])]['score']\n",
      "out = groups.apply(outliers).dropna()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "# Prepare outlier data for plotting, we need coordinate for every outlier.\n",
      "outx = []\n",
      "outy = []\n",
      "for cat in cats:\n",
      "    for value in out[cat]:\n",
      "        outx.append(cat)\n",
      "        outy.append(value)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "output_notebook()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "figure(tools=\"previewsave\", background_fill=\"#EFE8E2\", title=\"\")\n",
      "hold()\n",
      "\n",
      "# stems\n",
      "segment(cats, upper.score, cats, q3.score, x_range=cats, \n",
      "        line_width=2, line_color=\"black\")\n",
      "segment(cats, lower.score, cats, q1.score, x_range=cats,\n",
      "        line_width=2, line_color=\"black\")\n",
      "# boxes\n",
      "rect(cats, (q3.score+q2.score)/2, 0.7, q3.score-q2.score,\n",
      "     fill_color=\"#E08E79\", line_width=2, line_color=\"black\")\n",
      "rect(cats, (q2.score+q1.score)/2, 0.7, q2.score-q1.score,\n",
      "     fill_color=\"#3B8686\", line_width=2, line_color=\"black\")\n",
      "\n",
      "# whisters (0-height rects simpler than segments)\n",
      "rect(cats, lower.score, 0.2, 0, line_color=\"black\")\n",
      "rect(cats, upper.score, 0.2, 0, line_color=\"black\")\n",
      "\n",
      "# outliers\n",
      "circle(outx, outy, size=6, color=\"#F38630\", fill_alpha=0.6)\n",
      "\n",
      "xgrid().grid_line_color = None\n",
      "ygrid().grid_line_color = \"white\"\n",
      "ygrid().grid_line_width = 2\n",
      "xaxis().major_label_text_font_size=\"12pt\"\n",
      "\n",
      "show()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [],
     "language": "python",
     "metadata": {},
     "outputs": []
    }
   ],
   "metadata": {}
  }
 ]
}