Last active
August 9, 2017 12:28
-
-
Save loleg/fdcae371f08f4719789fb6f45c7ec672 to your computer and use it in GitHub Desktop.
Exploring Git data with Python, Pandas, Numpy and Bokeh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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