Skip to content

Instantly share code, notes, and snippets.

@loleg
Last active August 9, 2017 12:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save loleg/fdcae371f08f4719789fb6f45c7ec672 to your computer and use it in GitHub Desktop.
Save loleg/fdcae371f08f4719789fb6f45c7ec672 to your computer and use it in GitHub Desktop.
Exploring Git data with Python, Pandas, Numpy and Bokeh
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Here's looking at you Git, data science style\n",
"\n",
"This notebook is based on examples from [GitPandas](https://github.com/wdm0006/git-pandas), a project to create handy wrappers for the [GitPython](http://gitpython.readthedocs.io/en/stable/) library, in order to use [Pandas](http://pandas.pydata.org/) data structures for analysing git repositories.\n",
"\n",
"**You are warmly encouraged to leave feedback or ask questions [over here](https://forum.schoolofdata.ch/t/interpreting-git-with-python/259/1).**\n",
"\n",
"The basic use of this is to make GitHub-style pretty project graphs on your own processor time. It was prompted by a team working on a similar idea at [Hackergarten Bern 3/2017](https://www.meetup.com/Hackergarten-Bern/events/240752468/), who used Python to parse the output of the `git` command, and I wanted to explore an alternative approach. Or at least just have some fun poking at Git:\n",
"\n",
"> ![](http://data.whicdn.com/images/189352667/original.jpg)\n",
"\n",
"> Image © [Jamie Hewlett](https://en.wikipedia.org/wiki/Jamie_Hewlett) via [fabiola garcia](http://weheartit.com/entry/group/77416151) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Getting started\n",
"\n",
"We begin with some basic initialisations. You might like to install [Anaconda](https://www.continuum.io/downloads) to set up an environment with something like: \n",
"\n",
"`conda create --name gitdatasci python=3 numpy pandas bokeh jupyter`\n",
"\n",
"..or use your own approach to set up the essential dependencies for this type of analysis. Then run: \n",
"\n",
"`pip install gitpandas` \n",
"\n",
"..and put a Git repository somewhere you can find on your filesystem, as you'll see below. Finally:\n",
"\n",
"`jupyter notebook` \n",
"\n",
"..will open a web browser where you can make a notebook like this one - which you are free (as in beer) to [download](https://soda.camp/workshops/2017/hackergarten/gitpandasbokeh/git-bokeh-demo.ipynb) and reuse.\n",
"\n",
"We start by loading the relevant modules:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" <div class=\"bk-root\">\n",
" <a href=\"http://bokeh.pydata.org\" target=\"_blank\" class=\"bk-logo bk-logo-small bk-logo-notebook\"></a>\n",
" <span id=\"97f38c94-5a36-4355-af1e-926eb7316f7b\">Loading BokehJS ...</span>\n",
" </div>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"<div style='background-color: #fdd'>\\n\"+\n",
" \"<p>\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"</p>\\n\"+\n",
" \"<ul>\\n\"+\n",
" \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n",
" \"<li>use INLINE resources instead, as so:</li>\\n\"+\n",
" \"</ul>\\n\"+\n",
" \"<code>\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"</code>\\n\"+\n",
" \"</div>\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"97f38c94-5a36-4355-af1e-926eb7316f7b\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" try {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" }\n",
" finally {\n",
" delete window._bokeh_onload_callbacks\n",
" }\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"97f38c94-5a36-4355-af1e-926eb7316f7b\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '97f38c94-5a36-4355-af1e-926eb7316f7b' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.6.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.6.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"97f38c94-5a36-4355-af1e-926eb7316f7b\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.6.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.6.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.6.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.6.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"97f38c94-5a36-4355-af1e-926eb7316f7b\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import os\n",
"from gitpandas import ProjectDirectory, Repository\n",
"import numpy as np\n",
"from pandas import set_option\n",
"from bokeh.plotting import figure, output_notebook, show\n",
"\n",
"set_option('display.max_rows', 500)\n",
"set_option('display.max_columns', 500)\n",
"set_option('display.width', 1000)\n",
"\n",
"output_notebook() # are we happy?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Baby steps\n",
"\n",
"Okay, now let's grab a locally checked out git project - say, https://github.com/okfn/licenses.git which I've put in the parent folder. Create a basic repository object and do some basic checks."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Repository [licenses] instantiated at directory: /home/oleg/Localdev/Dribdat/_inspiration/licenses/\n"
]
},
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"path = os.path.abspath('../licenses')\n",
"p = ProjectDirectory(working_dir=path)\n",
"r = Repository(path)\n",
"\n",
"# is it an empty repository? expect False\n",
"\n",
"r.is_bare()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Data collection\n",
"\n",
"Now let's extract some useful information from that famous Git database. From the commit history, which we filter somewhat, we can get the git logs:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>author</th>\n",
" <th>committer</th>\n",
" <th>message</th>\n",
" <th>lines</th>\n",
" <th>insertions</th>\n",
" <th>deletions</th>\n",
" <th>net</th>\n",
" </tr>\n",
" <tr>\n",
" <th>date</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2014-08-11 23:11:59</th>\n",
" <td>Mike Linksvayer</td>\n",
" <td>Mike Linksvayer</td>\n",
" <td>minimally get deploy script working with renam...</td>\n",
" <td>24</td>\n",
" <td>12</td>\n",
" <td>12</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2014-04-12 16:56:23</th>\n",
" <td>Rufus Pollock</td>\n",
" <td>Rufus Pollock</td>\n",
" <td>Merge branch 'master' into gh-pages\\n</td>\n",
" <td>67</td>\n",
" <td>40</td>\n",
" <td>27</td>\n",
" <td>13</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2013-12-06 00:26:38</th>\n",
" <td>Mike Linksvayer</td>\n",
" <td>Mike Linksvayer</td>\n",
" <td>Merge pull request #29 from enyst/gov\\n\\nUpdat...</td>\n",
" <td>2</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2013-12-05 23:56:13</th>\n",
" <td>enyst</td>\n",
" <td>enyst</td>\n",
" <td>Re-deploy with the changes to gov licenses. (f...</td>\n",
" <td>2</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2013-12-04 16:47:37</th>\n",
" <td>Mike Linksvayer</td>\n",
" <td>Mike Linksvayer</td>\n",
" <td>Merge pull request #26 from enyst/retired\\n\\nU...</td>\n",
" <td>31</td>\n",
" <td>23</td>\n",
" <td>8</td>\n",
" <td>15</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" author committer message lines insertions deletions net\n",
"date \n",
"2014-08-11 23:11:59 Mike Linksvayer Mike Linksvayer minimally get deploy script working with renam... 24 12 12 0\n",
"2014-04-12 16:56:23 Rufus Pollock Rufus Pollock Merge branch 'master' into gh-pages\\n 67 40 27 13\n",
"2013-12-06 00:26:38 Mike Linksvayer Mike Linksvayer Merge pull request #29 from enyst/gov\\n\\nUpdat... 2 1 1 0\n",
"2013-12-05 23:56:13 enyst enyst Re-deploy with the changes to gov licenses. (f... 2 1 1 0\n",
"2013-12-04 16:47:37 Mike Linksvayer Mike Linksvayer Merge pull request #26 from enyst/retired\\n\\nU... 31 23 8 15"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ignore_dirs = [\n",
" 'docs/*',\n",
" 'tests/*',\n",
"]\n",
"\n",
"# gets a data frame in the format [ date, author, committer, message, lines, insertions, deletions, net(?) ]\n",
"\n",
"ch = r.commit_history('HEAD', limit=None, include_globs=['*.py'], ignore_globs=ignore_dirs)\n",
"ch.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Data wrangling\n",
"\n",
"Now let us start putting dots together. What do everyones contributions look like? For that we shall group by \"committer\" (notice the duplicates)."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>lines</th>\n",
" <th>insertions</th>\n",
" <th>deletions</th>\n",
" </tr>\n",
" <tr>\n",
" <th>committer</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>David Read</th>\n",
" <td>2</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Mike Linksvayer</th>\n",
" <td>101</td>\n",
" <td>57</td>\n",
" <td>44</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Rufus Pollock</th>\n",
" <td>1127</td>\n",
" <td>329</td>\n",
" <td>798</td>\n",
" </tr>\n",
" <tr>\n",
" <th>david.read@okfn.org</th>\n",
" <td>174</td>\n",
" <td>162</td>\n",
" <td>12</td>\n",
" </tr>\n",
" <tr>\n",
" <th>dread</th>\n",
" <td>9</td>\n",
" <td>6</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>enyst</th>\n",
" <td>79</td>\n",
" <td>46</td>\n",
" <td>33</td>\n",
" </tr>\n",
" <tr>\n",
" <th>johnbywater</th>\n",
" <td>334</td>\n",
" <td>253</td>\n",
" <td>81</td>\n",
" </tr>\n",
" <tr>\n",
" <th>rgrp</th>\n",
" <td>421</td>\n",
" <td>317</td>\n",
" <td>104</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ww@eu8.okfn.org</th>\n",
" <td>4</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ww@styx.org</th>\n",
" <td>97</td>\n",
" <td>91</td>\n",
" <td>6</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" lines insertions deletions\n",
"committer \n",
"David Read 2 1 1\n",
"Mike Linksvayer 101 57 44\n",
"Rufus Pollock 1127 329 798\n",
"david.read@okfn.org 174 162 12\n",
"dread 9 6 3\n",
"enyst 79 46 33\n",
"johnbywater 334 253 81\n",
"rgrp 421 317 104\n",
"ww@eu8.okfn.org 4 2 2\n",
"ww@styx.org 97 91 6"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"attr = ch.reindex(columns=['committer', 'lines', 'insertions', 'deletions']).groupby(['committer'])\n",
"attr = attr.agg({\n",
" 'lines': np.sum,\n",
" 'insertions': np.sum,\n",
" 'deletions': np.sum\n",
"})\n",
"attr"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Data visualisation\n",
"\n",
"At this point, we can sort and visualise the committers in a simple [Bokeh plot](http://bokeh.pydata.org/en/latest/docs/), made even simpler thanks to [bkcharts](https://github.com/bokeh/bkcharts) (though it sounds like we could try something like [Holoviews](http://holoviews.org/) next). **Unfortunately, this does not work in GitHub's preview. See [HTML version here](https://soda.camp/workshops/2017/hackergarten/gitpandasbokeh/git-bokeh-demo.html).**"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" <div class=\"bk-root\">\n",
" <div class=\"bk-plotdiv\" id=\"b6bd6122-0a7d-46d4-af75-aaa871613f17\"></div>\n",
" </div>\n",
"<script type=\"text/javascript\">\n",
" \n",
" (function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
" \n",
" var force = false;\n",
" \n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
" \n",
" \n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 0;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
" \n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"<div style='background-color: #fdd'>\\n\"+\n",
" \"<p>\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"</p>\\n\"+\n",
" \"<ul>\\n\"+\n",
" \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n",
" \"<li>use INLINE resources instead, as so:</li>\\n\"+\n",
" \"</ul>\\n\"+\n",
" \"<code>\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"</code>\\n\"+\n",
" \"</div>\"}};\n",
" \n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"b6bd6122-0a7d-46d4-af75-aaa871613f17\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
" \n",
" function run_callbacks() {\n",
" try {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" }\n",
" finally {\n",
" delete window._bokeh_onload_callbacks\n",
" }\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
" \n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"b6bd6122-0a7d-46d4-af75-aaa871613f17\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'b6bd6122-0a7d-46d4-af75-aaa871613f17' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
" \n",
" var js_urls = [];\n",
" \n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" (function() {\n",
" var fn = function() {\n",
" var docs_json = {\"b4fa1b2a-0257-4380-8718-58711df3fe06\":{\"roots\":{\"references\":[{\"attributes\":{\"plot\":null,\"text\":null},\"id\":\"24460fb2-f074-49a7-b9b7-0383c3496f6e\",\"type\":\"Title\"},{\"attributes\":{\"axis_label\":null,\"formatter\":{\"id\":\"5729ce9d-e053-4886-ba1a-2002cfc78d8b\",\"type\":\"BasicTickFormatter\"},\"plot\":{\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"169154bb-116e-495b-95c9-fc067a30dcce\",\"type\":\"BasicTicker\"},\"visible\":false},\"id\":\"1b2a767c-4c51-4246-aba4-dab97e406d1e\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"169154bb-116e-495b-95c9-fc067a30dcce\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"5729ce9d-e053-4886-ba1a-2002cfc78d8b\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"below\":[{\"id\":\"69555c52-ac1e-47a6-a423-8ed74bd0a3d4\",\"type\":\"LinearAxis\"}],\"css_classes\":null,\"left\":[{\"id\":\"1b2a767c-4c51-4246-aba4-dab97e406d1e\",\"type\":\"LinearAxis\"}],\"plot_height\":400,\"plot_width\":400,\"renderers\":[{\"id\":\"89aa00cb-8c0b-4391-86bd-9f16911fefb6\",\"type\":\"BoxAnnotation\"},{\"id\":\"973cb8e1-ce98-4505-944d-d1b35bbba8de\",\"type\":\"GlyphRenderer\"},{\"id\":\"0b53bc68-28f2-41ca-9cb6-c359c434ed16\",\"type\":\"GlyphRenderer\"},{\"id\":\"848abeee-7d2e-4aa1-9283-e7637c4b5985\",\"type\":\"Legend\"},{\"id\":\"69555c52-ac1e-47a6-a423-8ed74bd0a3d4\",\"type\":\"LinearAxis\"},{\"id\":\"1b2a767c-4c51-4246-aba4-dab97e406d1e\",\"type\":\"LinearAxis\"}],\"title\":{\"id\":\"24460fb2-f074-49a7-b9b7-0383c3496f6e\",\"type\":\"Title\"},\"tool_events\":{\"id\":\"09307ede-9f16-42bb-8251-a1fdadce0318\",\"type\":\"ToolEvents\"},\"toolbar\":{\"id\":\"c8cab589-c6e6-4885-9ae8-f1f81d8b3e53\",\"type\":\"Toolbar\"},\"x_range\":{\"id\":\"2e91cdf2-67e2-479e-8dbe-4275b275fe23\",\"type\":\"Range1d\"},\"x_scale\":{\"id\":\"99e9ddf3-d8b4-4e96-a59f-1c7bb02014f2\",\"type\":\"LinearScale\"},\"y_range\":{\"id\":\"94db8d64-280f-4f8f-a786-c241c7435aa3\",\"type\":\"Range1d\"},\"y_scale\":{\"id\":\"7cf56b33-428c-4abc-abb0-320c8b611df7\",\"type\":\"LinearScale\"}},\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"99e9ddf3-d8b4-4e96-a59f-1c7bb02014f2\",\"type\":\"LinearScale\"},{\"attributes\":{\"callback\":null,\"end\":1.6500000000000001,\"start\":-1.6500000000000001},\"id\":\"2e91cdf2-67e2-479e-8dbe-4275b275fe23\",\"type\":\"Range1d\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"lightgrey\"},\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":{\"value\":1.0},\"line_color\":{\"value\":\"black\"},\"line_dash\":[4,4],\"line_width\":{\"value\":2},\"plot\":null,\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"89aa00cb-8c0b-4391-86bd-9f16911fefb6\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"callback\":null,\"plot\":{\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"},\"tooltips\":[[\"Value\",\"@values\"]]},\"id\":\"0554ec88-da98-4845-ad96-cb85ccedd58d\",\"type\":\"HoverTool\"},{\"attributes\":{},\"id\":\"f2318d52-f748-456b-a979-8bb988dae8d9\",\"type\":\"BasicTicker\"},{\"attributes\":{\"plot\":{\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"}},\"id\":\"46d32e8f-decc-4ae0-8657-e0510ad4c3e7\",\"type\":\"PanTool\"},{\"attributes\":{\"plot\":{\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"}},\"id\":\"2e9cf219-c6e5-4a7d-8fcb-d8ff91cf4546\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"location\":\"top_left\",\"plot\":{\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"}},\"id\":\"848abeee-7d2e-4aa1-9283-e7637c4b5985\",\"type\":\"Legend\"},{\"attributes\":{\"overlay\":{\"id\":\"89aa00cb-8c0b-4391-86bd-9f16911fefb6\",\"type\":\"BoxAnnotation\"},\"plot\":{\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"}},\"id\":\"169b8810-e514-4566-8ff0-8f48fce7f4ea\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"09307ede-9f16-42bb-8251-a1fdadce0318\",\"type\":\"ToolEvents\"},{\"attributes\":{\"plot\":{\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"}},\"id\":\"26e05741-4c75-47a2-906a-690c553d9987\",\"type\":\"SaveTool\"},{\"attributes\":{\"plot\":{\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"}},\"id\":\"d91a9145-2371-4c9c-aadd-32fa06748012\",\"type\":\"ResetTool\"},{\"attributes\":{\"end_angle\":{\"field\":\"end\",\"units\":\"rad\"},\"fill_alpha\":{\"value\":0.8},\"fill_color\":{\"field\":\"color\"},\"inner_radius\":{\"field\":\"inners\",\"units\":\"data\"},\"line_color\":{\"value\":\"White\"},\"outer_radius\":{\"field\":\"outers\",\"units\":\"data\"},\"start_angle\":{\"field\":\"start\",\"units\":\"rad\"},\"x\":{\"value\":0},\"y\":{\"value\":0}},\"id\":\"e4443f32-3ad5-43b0-96f3-3a2c08eec1b6\",\"type\":\"AnnularWedge\"},{\"attributes\":{\"plot\":{\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"}},\"id\":\"36a92526-3cf0-43b4-ba77-8ed58bc7ff3e\",\"type\":\"HelpTool\"},{\"attributes\":{\"angle\":{\"field\":\"text_angle\",\"units\":\"rad\"},\"text_align\":\"center\",\"text_baseline\":\"middle\",\"text_font_size\":{\"value\":\"10pt\"},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"3cc256ea-f8e6-4c03-861b-f84d4d5d3b9f\",\"type\":\"Text\"},{\"attributes\":{\"data_source\":{\"id\":\"b8c24ef7-b976-4b08-81bc-d97822d428f3\",\"type\":\"ColumnDataSource\"},\"glyph\":{\"id\":\"3cc256ea-f8e6-4c03-861b-f84d4d5d3b9f\",\"type\":\"Text\"},\"hover_glyph\":null,\"muted_glyph\":null},\"id\":\"0b53bc68-28f2-41ca-9cb6-c359c434ed16\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"axis_label\":null,\"formatter\":{\"id\":\"de7d8664-2125-4cce-88d1-4b8bff08fcd2\",\"type\":\"BasicTickFormatter\"},\"plot\":{\"id\":\"7949e52f-aa26-44de-a86f-801da009a3f1\",\"subtype\":\"Chart\",\"type\":\"Plot\"},\"ticker\":{\"id\":\"f2318d52-f748-456b-a979-8bb988dae8d9\",\"type\":\"BasicTicker\"},\"visible\":false},\"id\":\"69555c52-ac1e-47a6-a423-8ed74bd0a3d4\",\"type\":\"LinearAxis\"},{\"attributes\":{\"callback\":null,\"end\":1.6500000000000001,\"start\":-1.6500000000000001},\"id\":\"94db8d64-280f-4f8f-a786-c241c7435aa3\",\"type\":\"Range1d\"},{\"attributes\":{},\"id\":\"de7d8664-2125-4cce-88d1-4b8bff08fcd2\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"46d32e8f-decc-4ae0-8657-e0510ad4c3e7\",\"type\":\"PanTool\"},{\"id\":\"2e9cf219-c6e5-4a7d-8fcb-d8ff91cf4546\",\"type\":\"WheelZoomTool\"},{\"id\":\"169b8810-e514-4566-8ff0-8f48fce7f4ea\",\"type\":\"BoxZoomTool\"},{\"id\":\"26e05741-4c75-47a2-906a-690c553d9987\",\"type\":\"SaveTool\"},{\"id\":\"d91a9145-2371-4c9c-aadd-32fa06748012\",\"type\":\"ResetTool\"},{\"id\":\"36a92526-3cf0-43b4-ba77-8ed58bc7ff3e\",\"type\":\"HelpTool\"},{\"id\":\"0554ec88-da98-4845-ad96-cb85ccedd58d\",\"type\":\"HoverTool\"}]},\"id\":\"c8cab589-c6e6-4885-9ae8-f1f81d8b3e53\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"7cf56b33-428c-4abc-abb0-320c8b611df7\",\"type\":\"LinearScale\"},{\"attributes\":{\"callback\":null,\"column_names\":[\"end\",\"level\",\"start\",\"values\",\"inners\",\"outers\",\"centers\",\"color\",\"committer\"],\"data\":{\"centers\":{\"__ndarray__\":\"AAAAAAAA8j8AAAAAAADyPwAAAAAAAPI/AAAAAAAA8j8AAAAAAADyPwAAAAAAAPI/AAAAAAAA8j8AAAAAAADyPwAAAAAAAPI/AAAAAAAA8j8=\",\"dtype\":\"float64\",\"shape\":[10]},\"color\":[\"#f22c40\",\"#5ab738\",\"#407ee7\",\"#df5320\",\"#00ad9c\",\"#c33ff3\",\"#f22c40\",\"#5ab738\",\"#407ee7\",\"#df5320\"],\"committer\":[\"David Read\",\"Mike Linksvayer\",\"Rufus Pollock\",\"david.read@okfn.org\",\"dread\",\"enyst\",\"johnbywater\",\"rgrp\",\"ww@eu8.okfn.org\",\"ww@styx.org\"],\"end\":{\"__ndarray__\":\"JbHMOezrdT+MtoIW2KPRP8hebT/iVApA4qY/43gODkBxs8G2yz8OQBw+NzC/8A9A8sCIXpmLE0DcdOrNOA0YQDVbB8QuGBhAGS1EVPshGUA=\",\"dtype\":\"float64\",\"shape\":[10]},\"inners\":{\"__ndarray__\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\"dtype\":\"float64\",\"shape\":[10]},\"level\":{\"__ndarray__\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\"dtype\":\"float64\",\"shape\":[10]},\"outers\":{\"__ndarray__\":\"AAAAAAAA+D8AAAAAAAD4PwAAAAAAAPg/AAAAAAAA+D8AAAAAAAD4PwAAAAAAAPg/AAAAAAAA+D8AAAAAAAD4PwAAAAAAAPg/AAAAAAAA+D8=\",\"dtype\":\"float64\",\"shape\":[10]},\"start\":{\"__ndarray__\":\"AAAAAAAAAAAlscw57Ot1P4y2ghbYo9E/yF5tP+JUCkDipj/jeA4OQHGzwbbLPw5AHD43ML/wD0DywIhemYsTQNx06s04DRhANVsHxC4YGEA=\",\"dtype\":\"float64\",\"shape\":[10]},\"values\":[2,101,1127,174,9,79,334,421,4,97]}},\"id\":\"29d0a9b8-ae77-4d7a-abdd-3b001b7fb0fd\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"callback\":null,\"column_names\":[\"text\",\"x\",\"y\",\"text_angle\"],\"data\":{\"text\":[\"David Read\",\"Mike Linksvayer\",\"Rufus Pollock\",\"david.read@okfn.org\",\"dread\",\"enyst\",\"johnbywater\",\"rgrp\",\"ww@eu8.okfn.org\",\"ww@styx.org\"],\"text_angle\":{\"__ndarray__\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\"dtype\":\"float64\",\"shape\":[10]},\"x\":[1.1249959720337852,1.1139161596889544,-0.2375311486023388,-1.043632284081843,-0.9106746782860803,-0.8267766511185887,-0.30326928685237153,0.7562702770265529,1.0857522322344297,1.1155385028529037],\"y\":[0.0030104663690043902,0.15753028021244714,1.099638101124026,-0.4200674417544343,-0.6605275394172025,-0.7629320868629996,-1.083352546335704,-0.8328747013123768,-0.2945625403848101,-0.1455982439890749]}},\"id\":\"b8c24ef7-b976-4b08-81bc-d97822d428f3\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"data_source\":{\"id\":\"29d0a9b8-ae77-4d7a-abdd-3b001b7fb0fd\",\"type\":\"ColumnDataSource\"},\"glyph\":{\"id\":\"e4443f32-3ad5-43b0-96f3-3a2c08eec1b6\",\"type\":\"AnnularWedge\"},\"hover_glyph\":null,\"muted_glyph\":null},\"id\":\"973cb8e1-ce98-4505-944d-d1b35bbba8de\",\"type\":\"GlyphRenderer\"}],\"root_ids\":[\"7949e52f-aa26-44de-a86f-801da009a3f1\"]},\"title\":\"Bokeh Application\",\"version\":\"0.12.6\"}};\n",
" var render_items = [{\"docid\":\"b4fa1b2a-0257-4380-8718-58711df3fe06\",\"elementid\":\"b6bd6122-0a7d-46d4-af75-aaa871613f17\",\"modelid\":\"7949e52f-aa26-44de-a86f-801da009a3f1\"}];\n",
" \n",
" Bokeh.embed.embed_items(docs_json, render_items);\n",
" };\n",
" if (document.readyState != \"loading\") fn();\n",
" else document.addEventListener(\"DOMContentLoaded\", fn);\n",
" })();\n",
" },\n",
" function(Bokeh) {\n",
" }\n",
" ];\n",
" \n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"b6bd6122-0a7d-46d4-af75-aaa871613f17\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
" \n",
" }\n",
" \n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
" }(this));\n",
"</script>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from bkcharts import Donut\n",
"\n",
"pie_chart = Donut(attr)\n",
"show(pie_chart)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Rock and roll.\n",
"\n",
"Thanks for watching!\n",
"\n",
"Now go listen to some music ...\n",
"\n",
"![](https://upload.wikimedia.org/wikipedia/commons/thumb/e/ef/De_La_Soul_Demon_Days_Live.jpg/1024px-De_La_Soul_Demon_Days_Live.jpg)\n",
"\n",
"Image CC BY 2.0 Brad Barrish via [Wikipedia](https://en.wikipedia.org/wiki/Gorillaz#/media/File:De_La_Soul_Demon_Days_Live.jpg)"
]
}
],
"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.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment