From 16758348f98180feff270aa2677a084b14494e0f Mon Sep 17 00:00:00 2001 From: Rubin Wei Date: Sun, 12 Mar 2017 14:36:14 -0400 Subject: [PATCH 01/40] Update link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change “A Gallery of Interesting IPython Notebooks” to "A gallery of interesting Jupyter Notebooks" and link. --- notebooks/01.08-More-IPython-Resources.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/01.08-More-IPython-Resources.ipynb b/notebooks/01.08-More-IPython-Resources.ipynb index bd6890902..f0182f8ba 100644 --- a/notebooks/01.08-More-IPython-Resources.ipynb +++ b/notebooks/01.08-More-IPython-Resources.ipynb @@ -37,7 +37,7 @@ "\n", "- [The IPython website](http://ipython.org): The IPython website links to documentation, examples, tutorials, and a variety of other resources.\n", "- [The nbviewer website](http://nbviewer.jupyter.org/): This site shows static renderings of any IPython notebook available on the internet. The front page features some example notebooks that you can browse to see what other folks are using IPython for!\n", - "- [A Gallery of Interesting IPython Notebooks](http://github.com/ipython/ipython/wiki/A-gallery-of-interesting-IPython-Notebooks/): This ever-growing list of notebooks, powered by nbviewer, shows the depth and breadth of numerical analysis you can do with IPython. It includes everything from short examples and tutorials to full-blown courses and books composed in the notebook format!\n", + "- [A gallery of interesting Jupyter Notebooks](https://github.com/jupyter/jupyter/wiki/A-gallery-of-interesting-Jupyter-Notebooks/): This ever-growing list of notebooks, powered by nbviewer, shows the depth and breadth of numerical analysis you can do with IPython. It includes everything from short examples and tutorials to full-blown courses and books composed in the notebook format!\n", "- Video Tutorials: searching the Internet, you will find many video-recorded tutorials on IPython. I'd especially recommend seeking tutorials from the PyCon, SciPy, and PyData conferenes by Fernando Perez and Brian Granger, two of the primary creators and maintainers of IPython and Jupyter." ] }, From 850b94d75f31f8f80cec20ad3c0a50ffc3ae11b8 Mon Sep 17 00:00:00 2001 From: Damian Poddebniak Date: Tue, 21 Mar 2017 16:43:04 +0100 Subject: [PATCH 02/40] Typo --- notebooks/02.04-Computation-on-arrays-aggregates.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/02.04-Computation-on-arrays-aggregates.ipynb b/notebooks/02.04-Computation-on-arrays-aggregates.ipynb index faad08614..0ed03afff 100644 --- a/notebooks/02.04-Computation-on-arrays-aggregates.ipynb +++ b/notebooks/02.04-Computation-on-arrays-aggregates.ipynb @@ -406,7 +406,7 @@ "|-------------------|---------------------|-----------------------------------------------|\n", "| ``np.sum`` | ``np.nansum`` | Compute sum of elements |\n", "| ``np.prod`` | ``np.nanprod`` | Compute product of elements |\n", - "| ``np.mean`` | ``np.nanmean`` | Compute median of elements |\n", + "| ``np.mean`` | ``np.nanmean`` | Compute mean of elements |\n", "| ``np.std`` | ``np.nanstd`` | Compute standard deviation |\n", "| ``np.var`` | ``np.nanvar`` | Compute variance |\n", "| ``np.min`` | ``np.nanmin`` | Find minimum value |\n", From 7964cb99909e3fd19d2d2d7a9fcf20d46f22abaa Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Fri, 21 Apr 2017 14:22:47 -0700 Subject: [PATCH 03/40] ERRATUM: make 02.06 example work on Python 2 --- notebooks/02.06-Boolean-Arrays-and-Masks.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb b/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb index ae7b5da78..af4fd6509 100644 --- a/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb +++ b/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb @@ -69,7 +69,7 @@ "\n", "# use pandas to extract rainfall inches as a NumPy array\n", "rainfall = pd.read_csv('data/Seattle2014.csv')['PRCP'].values\n", - "inches = rainfall / 254 # 1/10mm -> inches\n", + "inches = rainfall / 254.0 # 1/10mm -> inches\n", "inches.shape" ] }, From f287e4d312773a8756b054cf4bf4f6f73e3d873e Mon Sep 17 00:00:00 2001 From: Naty Clementi Date: Wed, 10 May 2017 15:06:57 -0400 Subject: [PATCH 04/40] Fix typo on 02.06-Boolean-Arrays-and-Masks do ---> to In phrase "We are then free do operate..." --- notebooks/02.06-Boolean-Arrays-and-Masks.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb b/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb index af4fd6509..813c891f6 100644 --- a/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb +++ b/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb @@ -890,7 +890,7 @@ "source": [ "What is returned is a one-dimensional array filled with all the values that meet this condition; in other words, all the values in positions at which the mask array is ``True``.\n", "\n", - "We are then free do operate on these values as we wish.\n", + "We are then free to operate on these values as we wish.\n", "For example, we can compute some relevant statistics on our Seattle rain data:" ] }, From 68fa576625cd9905bbe5d33727f7800761df3e49 Mon Sep 17 00:00:00 2001 From: Naty Clementi Date: Thu, 11 May 2017 16:45:47 -0400 Subject: [PATCH 05/40] Fix typo in 02.06-Boolean-Arrays-and-Masks Fixes #64 , 4 --> 8 --- notebooks/02.06-Boolean-Arrays-and-Masks.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb b/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb index 813c891f6..5a7e150c2 100644 --- a/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb +++ b/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb @@ -650,7 +650,7 @@ } ], "source": [ - "# are all values in each row less than 4?\n", + "# are all values in each row less than 8?\n", "np.all(x < 8, axis=1)" ] }, From 7a82169919daceb07918db2e8f46b85f33b3863f Mon Sep 17 00:00:00 2001 From: Naty Clementi Date: Fri, 26 May 2017 12:16:06 -0400 Subject: [PATCH 06/40] Fix typo in 02.07-Fancy-Indexing The second reference to `x[3]` in "You might expect that x[3] would contain the value 2, and x[3] would contain the value 3," should be x[4]. --- notebooks/02.07-Fancy-Indexing.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/02.07-Fancy-Indexing.ipynb b/notebooks/02.07-Fancy-Indexing.ipynb index b3680ffc2..b4aa99df8 100644 --- a/notebooks/02.07-Fancy-Indexing.ipynb +++ b/notebooks/02.07-Fancy-Indexing.ipynb @@ -709,7 +709,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "You might expect that ``x[3]`` would contain the value 2, and ``x[3]`` would contain the value 3, as this is how many times each index is repeated. Why is this not the case?\n", + "You might expect that ``x[3]`` would contain the value 2, and ``x[4]`` would contain the value 3, as this is how many times each index is repeated. Why is this not the case?\n", "Conceptually, this is because ``x[i] += 1`` is meant as a shorthand of ``x[i] = x[i] + 1``. ``x[i] + 1`` is evaluated, and then the result is assigned to the indices in x.\n", "With this in mind, it is not the augmentation that happens multiple times, but the assignment, which leads to the rather nonintuitive results.\n", "\n", From 8ae653956528eaeed4755270fabb052da0c1b22c Mon Sep 17 00:00:00 2001 From: Jake Vanderplas Date: Thu, 1 Jun 2017 10:25:29 -0700 Subject: [PATCH 07/40] Remove basemap from requirements.txt --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index cf1575e30..fe9cdd918 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,5 +12,4 @@ line_profiler memory_profiler numexpr pandas-datareader -basemap netcdf4 From d49356c040bd9111b694aa6c364f727d288dc233 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Mon, 14 Aug 2017 13:01:07 -0700 Subject: [PATCH 08/40] MAINT: regularize notebook titles --- .../01.02-Shell-Keyboard-Shortcuts.ipynb | 9 +- notebooks/01.03-Magic-Commands.ipynb | 9 +- notebooks/01.04-Input-Output-History.ipynb | 9 +- .../01.05-IPython-And-Shell-Commands.ipynb | 9 +- notebooks/01.06-Errors-and-Debugging.ipynb | 9 +- notebooks/01.07-Timing-and-Profiling.ipynb | 9 +- notebooks/01.08-More-IPython-Resources.ipynb | 9 +- notebooks/02.00-Introduction-to-NumPy.ipynb | 50 +- notebooks/03.05-Hierarchical-Indexing.ipynb | 497 ++++++++++++++---- notebooks/03.13-Further-Resources.ipynb | 27 +- .../05.01-What-Is-Machine-Learning.ipynb | 172 ++++-- .../05.02-Introducing-Scikit-Learn.ipynb | 420 +++++++++++---- ...Hyperparameters-and-Model-Validation.ipynb | 290 +++++++--- notebooks/05.04-Feature-Engineering.ipynb | 214 ++++++-- notebooks/05.05-Naive-Bayes.ipynb | 190 +++++-- notebooks/05.06-Linear-Regression.ipynb | 319 ++++++++--- .../05.09-Principal-Component-Analysis.ipynb | 283 +++++++--- notebooks/05.12-Gaussian-Mixtures.ipynb | 263 +++++++-- .../05.13-Kernel-Density-Estimation.ipynb | 239 +++++++-- notebooks/05.15-Learning-More.ipynb | 37 +- notebooks/06.00-Figure-Code.ipynb | 452 ++++++++++++---- 21 files changed, 2758 insertions(+), 758 deletions(-) diff --git a/notebooks/01.02-Shell-Keyboard-Shortcuts.ipynb b/notebooks/01.02-Shell-Keyboard-Shortcuts.ipynb index 5018ca578..a13f196c9 100644 --- a/notebooks/01.02-Shell-Keyboard-Shortcuts.ipynb +++ b/notebooks/01.02-Shell-Keyboard-Shortcuts.ipynb @@ -23,8 +23,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Keyboard Shortcuts in the IPython Shell\n", - "\n", + "# Keyboard Shortcuts in the IPython Shell" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "If you spend any amount of time on the computer, you've probably found a use for keyboard shortcuts in your workflow.\n", "Most familiar perhaps are the Cmd-C and Cmd-V (or Ctrl-C and Ctrl-V) for copying and pasting in a wide variety of programs and systems.\n", "Power-users tend to go even further: popular text editors like Emacs, Vim, and others provide users an incredible range of operations through intricate combinations of keystrokes.\n", diff --git a/notebooks/01.03-Magic-Commands.ipynb b/notebooks/01.03-Magic-Commands.ipynb index 489e0d6a7..4a47b373a 100644 --- a/notebooks/01.03-Magic-Commands.ipynb +++ b/notebooks/01.03-Magic-Commands.ipynb @@ -23,8 +23,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# IPython Magic Commands\n", - "\n", + "# IPython Magic Commands" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "The previous two sections showed how IPython lets you use and explore Python efficiently and interactively.\n", "Here we'll begin discussing some of the enhancements that IPython adds on top of the normal Python syntax.\n", "These are known in IPython as *magic commands*, and are prefixed by the ``%`` character.\n", diff --git a/notebooks/01.04-Input-Output-History.ipynb b/notebooks/01.04-Input-Output-History.ipynb index 4b0dcfd04..f29170244 100644 --- a/notebooks/01.04-Input-Output-History.ipynb +++ b/notebooks/01.04-Input-Output-History.ipynb @@ -23,8 +23,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Input and Output History\n", - "\n", + "# Input and Output History" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "Previously we saw that the IPython shell allows you to access previous commands with the up and down arrow keys, or equivalently the Ctrl-p/Ctrl-n shortcuts.\n", "Additionally, in both the shell and the notebook, IPython exposes several ways to obtain the output of previous commands, as well as string versions of the commands themselves.\n", "We'll explore those here." diff --git a/notebooks/01.05-IPython-And-Shell-Commands.ipynb b/notebooks/01.05-IPython-And-Shell-Commands.ipynb index 7c4f8ebb1..fb69844b7 100644 --- a/notebooks/01.05-IPython-And-Shell-Commands.ipynb +++ b/notebooks/01.05-IPython-And-Shell-Commands.ipynb @@ -23,8 +23,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# IPython and Shell Commands\n", - "\n", + "# IPython and Shell Commands" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "When working interactively with the standard Python interpreter, one of the frustrations is the need to switch between multiple windows to access Python tools and system command-line tools.\n", "IPython bridges this gap, and gives you a syntax for executing shell commands directly from within the IPython terminal.\n", "The magic happens with the exclamation point: anything appearing after ``!`` on a line will be executed not by the Python kernel, but by the system command-line.\n", diff --git a/notebooks/01.06-Errors-and-Debugging.ipynb b/notebooks/01.06-Errors-and-Debugging.ipynb index 0125168e4..f898cd91a 100644 --- a/notebooks/01.06-Errors-and-Debugging.ipynb +++ b/notebooks/01.06-Errors-and-Debugging.ipynb @@ -23,8 +23,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Errors and Debugging\n", - "\n", + "# Errors and Debugging" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "Code development and data analysis always require a bit of trial and error, and IPython contains tools to streamline this process.\n", "This section will briefly cover some options for controlling Python's exception reporting, followed by exploring tools for debugging errors in code." ] diff --git a/notebooks/01.07-Timing-and-Profiling.ipynb b/notebooks/01.07-Timing-and-Profiling.ipynb index 2a65b75a6..9b1e2f73c 100644 --- a/notebooks/01.07-Timing-and-Profiling.ipynb +++ b/notebooks/01.07-Timing-and-Profiling.ipynb @@ -23,8 +23,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Profiling and Timing Code\n", - "\n", + "# Profiling and Timing Code" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "In the process of developing code and creating data processing pipelines, there are often trade-offs you can make between various implementations.\n", "Early in developing your algorithm, it can be counterproductive to worry about such things. As Donald Knuth famously quipped, \"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.\"\n", "\n", diff --git a/notebooks/01.08-More-IPython-Resources.ipynb b/notebooks/01.08-More-IPython-Resources.ipynb index f0182f8ba..811ee8821 100644 --- a/notebooks/01.08-More-IPython-Resources.ipynb +++ b/notebooks/01.08-More-IPython-Resources.ipynb @@ -23,8 +23,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# More IPython Resources\n", - "\n", + "# More IPython Resources" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "In this chapter, we've just scratched the surface of using IPython to enable data science tasks.\n", "Much more information is available both in print and on the Web, and here we'll list some other resources that you may find helpful." ] diff --git a/notebooks/02.00-Introduction-to-NumPy.ipynb b/notebooks/02.00-Introduction-to-NumPy.ipynb index c9ad97f9c..177fa9342 100644 --- a/notebooks/02.00-Introduction-to-NumPy.ipynb +++ b/notebooks/02.00-Introduction-to-NumPy.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [More IPython Resources](01.08-More-IPython-Resources.ipynb) | [Contents](Index.ipynb) | [Understanding Data Types in Python](02.01-Understanding-Data-Types.ipynb) >" @@ -21,14 +27,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ - "# Introduction to NumPy\n" + "# Introduction to NumPy" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This chapter, along with chapter 3, outlines techniques for effectively loading, storing, and manipulating in-memory data in Python.\n", "The topic is very broad: datasets can come from a wide range of sources and a wide range of formats, including be collections of documents, collections of images, collections of sound clips, collections of numerical measurements, or nearly anything else.\n", @@ -56,7 +68,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -77,7 +91,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "For the pieces of the package discussed here, I'd recommend NumPy version 1.8 or later.\n", "By convention, you'll find that most people in the SciPy/PyData world will import NumPy using ``np`` as an alias:" @@ -87,7 +104,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -96,14 +115,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Throughout this chapter, and indeed the rest of the book, you'll find that this is the way we will import and use NumPy." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Reminder about Built In Documentation\n", "\n", @@ -126,7 +151,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [More IPython Resources](01.08-More-IPython-Resources.ipynb) | [Contents](Index.ipynb) | [Understanding Data Types in Python](02.01-Understanding-Data-Types.ipynb) >" diff --git a/notebooks/03.05-Hierarchical-Indexing.ipynb b/notebooks/03.05-Hierarchical-Indexing.ipynb index 95de26d26..0e76d8d5a 100644 --- a/notebooks/03.05-Hierarchical-Indexing.ipynb +++ b/notebooks/03.05-Hierarchical-Indexing.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Handling Missing Data](03.04-Missing-Values.ipynb) | [Contents](Index.ipynb) | [Combining Datasets: Concat and Append](03.06-Concat-And-Append.ipynb) >" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Hierarchical Indexing\n", - "\n", + "# Hierarchical Indexing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "Up to this point we've been focused primarily on one-dimensional and two-dimensional data, stored in Pandas ``Series`` and ``DataFrame`` objects, respectively.\n", "Often it is useful to go beyond this and store higher-dimensional data–that is, data indexed by more than one or two keys.\n", "While Pandas does provide ``Panel`` and ``Panel4D`` objects that natively handle three-dimensional and four-dimensional data (see [Aside: Panel Data](#Aside:-Panel-Data)), a far more common pattern in practice is to make use of *hierarchical indexing* (also known as *multi-indexing*) to incorporate multiple index *levels* within a single index.\n", @@ -39,7 +53,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -49,7 +65,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## A Multiply Indexed Series\n", "\n", @@ -59,7 +78,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### The bad way\n", "\n", @@ -71,7 +93,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -104,7 +128,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With this indexing scheme, you can straightforwardly index or slice the series based on this multiple index:" ] @@ -113,7 +140,9 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -137,7 +166,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "But the convenience ends there. For example, if you need to select all values from 2010, you'll need to do some messy (and potentially slow) munging to make it happen:" ] @@ -146,7 +178,9 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -169,14 +203,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This produces the desired result, but is not as clean (or as efficient for large datasets) as the slicing syntax we've grown to love in Pandas." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### The Better Way: Pandas MultiIndex\n", "Fortunately, Pandas provides a better way.\n", @@ -188,7 +228,9 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -210,7 +252,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Notice that the ``MultiIndex`` contains multiple *levels* of indexing–in this case, the state names and the years, as well as multiple *labels* for each data point which encode these levels.\n", "\n", @@ -221,7 +266,9 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -248,7 +295,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Here the first two columns of the ``Series`` representation show the multiple index values, while the third column shows the data.\n", "Notice that some entries are missing in the first column: in this multi-index representation, any blank entry indicates the same value as the line above it." @@ -256,7 +306,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now to access all data for which the second index is 2010, we can simply use the Pandas slicing notation:" ] @@ -265,7 +318,9 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -288,7 +343,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The result is a singly indexed array with just the keys we're interested in.\n", "This syntax is much more convenient (and the operation is much more efficient!) than the home-spun tuple-based multi-indexing solution that we started with.\n", @@ -297,7 +355,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### MultiIndex as extra dimension\n", "\n", @@ -309,7 +370,9 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -363,7 +426,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Naturally, the ``stack()`` method provides the opposite operation:" ] @@ -372,7 +438,9 @@ "cell_type": "code", "execution_count": 9, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -398,7 +466,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Seeing this, you might wonder why would we would bother with hierarchical indexing at all.\n", "The reason is simple: just as we were able to use multi-indexing to represent two-dimensional data within a one-dimensional ``Series``, we can also use it to represent data of three or more dimensions in a ``Series`` or ``DataFrame``.\n", @@ -409,7 +480,9 @@ "cell_type": "code", "execution_count": 10, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -488,7 +561,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "In addition, all the ufuncs and other functionality discussed in [Operating on Data in Pandas](03.03-Operations-in-Pandas.ipynb) work with hierarchical indices as well.\n", "Here we compute the fraction of people under 18 by year, given the above data:" @@ -498,7 +574,9 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -552,14 +630,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This allows us to easily and quickly manipulate and explore even high-dimensional data." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Methods of MultiIndex Creation\n", "\n", @@ -570,7 +654,9 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -635,7 +721,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The work of creating the ``MultiIndex`` is done in the background.\n", "\n", @@ -646,7 +735,9 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -678,14 +769,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Nevertheless, it is sometimes useful to explicitly create a ``MultiIndex``; we'll see a couple of these methods here." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Explicit MultiIndex constructors\n", "\n", @@ -697,7 +794,9 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -718,7 +817,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "You can construct it from a list of tuples giving the multiple index values of each point:" ] @@ -727,7 +829,9 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -748,7 +852,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "You can even construct it from a Cartesian product of single indices:" ] @@ -757,7 +864,9 @@ "cell_type": "code", "execution_count": 16, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -778,7 +887,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Similarly, you can construct the ``MultiIndex`` directly using its internal encoding by passing ``levels`` (a list of lists containing available index values for each level) and ``labels`` (a list of lists that reference these labels):" ] @@ -787,7 +899,9 @@ "cell_type": "code", "execution_count": 17, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -809,14 +923,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Any of these objects can be passed as the ``index`` argument when creating a ``Series`` or ``Dataframe``, or be passed to the ``reindex`` method of an existing ``Series`` or ``DataFrame``." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### MultiIndex level names\n", "\n", @@ -828,7 +948,9 @@ "cell_type": "code", "execution_count": 18, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -856,14 +978,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With more involved datasets, this can be a useful way to keep track of the meaning of various index values." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### MultiIndex for columns\n", "\n", @@ -875,7 +1003,9 @@ "cell_type": "code", "execution_count": 19, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -989,7 +1119,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Here we see where the multi-indexing for both rows and columns can come in *very* handy.\n", "This is fundamentally four-dimensional data, where the dimensions are the subject, the measurement type, the year, and the visit number.\n", @@ -1000,7 +1133,9 @@ "cell_type": "code", "execution_count": 20, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1069,14 +1204,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "For complicated records containing multiple labeled measurements across multiple times for many subjects (people, countries, cities, etc.) use of hierarchical rows and columns can be extremely convenient!" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Indexing and Slicing a MultiIndex\n", "\n", @@ -1086,7 +1227,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Multiply indexed Series\n", "\n", @@ -1097,7 +1241,9 @@ "cell_type": "code", "execution_count": 21, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1124,7 +1270,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We can access single elements by indexing with multiple terms:" ] @@ -1133,7 +1282,9 @@ "cell_type": "code", "execution_count": 22, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1153,7 +1304,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The ``MultiIndex`` also supports *partial indexing*, or indexing just one of the levels in the index.\n", "The result is another ``Series``, with the lower-level indices maintained:" @@ -1163,7 +1317,9 @@ "cell_type": "code", "execution_count": 23, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1186,7 +1342,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Partial slicing is available as well, as long as the ``MultiIndex`` is sorted (see discussion in [Sorted and Unsorted Indices](Sorted-and-Unsorted-Indices)):" ] @@ -1195,7 +1354,9 @@ "cell_type": "code", "execution_count": 24, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1220,7 +1381,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With sorted indices, partial indexing can be performed on lower levels by passing an empty slice in the first index:" ] @@ -1229,7 +1393,9 @@ "cell_type": "code", "execution_count": 25, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1253,7 +1419,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Other types of indexing and selection (discussed in [Data Indexing and Selection](03.02-Data-Indexing-and-Selection.ipynb)) work as well; for example, selection based on Boolean masks:" ] @@ -1262,7 +1431,9 @@ "cell_type": "code", "execution_count": 26, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1286,7 +1457,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Selection based on fancy indexing also works:" ] @@ -1295,7 +1469,9 @@ "cell_type": "code", "execution_count": 27, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1320,7 +1496,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Multiply indexed DataFrames\n", "\n", @@ -1332,7 +1511,9 @@ "cell_type": "code", "execution_count": 28, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1433,7 +1614,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Remember that columns are primary in a ``DataFrame``, and the syntax used for multiply indexed ``Series`` applies to the columns.\n", "For example, we can recover Guido's heart rate data with a simple operation:" @@ -1443,7 +1627,9 @@ "cell_type": "code", "execution_count": 29, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1468,7 +1654,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Also, as with the single-index case, we can use the ``loc``, ``iloc``, and ``ix`` indexers introduced in [Data Indexing and Selection](03.02-Data-Indexing-and-Selection.ipynb). For example:" ] @@ -1477,7 +1666,9 @@ "cell_type": "code", "execution_count": 30, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1539,7 +1730,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "These indexers provide an array-like view of the underlying two-dimensional data, but each individual index in ``loc`` or ``iloc`` can be passed a tuple of multiple indices. For example:" ] @@ -1548,7 +1742,9 @@ "cell_type": "code", "execution_count": 31, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1573,7 +1769,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Working with slices within these index tuples is not especially convenient; trying to create a slice within a tuple will lead to a syntax error:" ] @@ -1582,7 +1781,9 @@ "cell_type": "code", "execution_count": 32, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1600,7 +1801,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "You could get around this by building the desired slice explicitly using Python's built-in ``slice()`` function, but a better way in this context is to use an ``IndexSlice`` object, which Pandas provides for precisely this situation.\n", "For example:" @@ -1610,7 +1814,9 @@ "cell_type": "code", "execution_count": 33, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1680,14 +1886,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "There are so many ways to interact with data in multiply indexed ``Series`` and ``DataFrame``s, and as with many tools in this book the best way to become familiar with them is to try them out!" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Rearranging Multi-Indices\n", "\n", @@ -1698,7 +1910,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Sorted and unsorted indices\n", "\n", @@ -1713,7 +1928,9 @@ "cell_type": "code", "execution_count": 34, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1743,7 +1960,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "If we try to take a partial slice of this index, it will result in an error:" ] @@ -1752,7 +1972,9 @@ "cell_type": "code", "execution_count": 35, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1774,7 +1996,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Although it is not entirely clear from the error message, this is the result of the MultiIndex not being sorted.\n", "For various reasons, partial slices and other similar operations require the levels in the ``MultiIndex`` to be in sorted (i.e., lexographical) order.\n", @@ -1786,7 +2011,9 @@ "cell_type": "code", "execution_count": 36, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1814,7 +2041,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With the index sorted in this way, partial slicing will work as expected:" ] @@ -1823,7 +2053,9 @@ "cell_type": "code", "execution_count": 37, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1848,7 +2080,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Stacking and unstacking indices\n", "\n", @@ -1859,7 +2094,9 @@ "cell_type": "code", "execution_count": 38, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1918,7 +2155,9 @@ "cell_type": "code", "execution_count": 39, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1977,7 +2216,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The opposite of ``unstack()`` is ``stack()``, which here can be used to recover the original series:" ] @@ -1986,7 +2228,9 @@ "cell_type": "code", "execution_count": 40, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2013,7 +2257,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Index setting and resetting\n", "\n", @@ -2026,7 +2273,9 @@ "cell_type": "code", "execution_count": 41, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2105,7 +2354,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Often when working with data in the real world, the raw input data looks like this and it's useful to build a ``MultiIndex`` from the column values.\n", "This can be done with the ``set_index`` method of the ``DataFrame``, which returns a multiply indexed ``DataFrame``:" @@ -2115,7 +2367,9 @@ "cell_type": "code", "execution_count": 42, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2189,14 +2443,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "In practice, I find this type of reindexing to be one of the more useful patterns when encountering real-world datasets." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Data Aggregations on Multi-Indices\n", "\n", @@ -2210,7 +2470,9 @@ "cell_type": "code", "execution_count": 43, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2311,7 +2573,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Perhaps we'd like to average-out the measurements in the two visits each year. We can do this by naming the index level we'd like to explore, in this case the year:" ] @@ -2320,7 +2585,9 @@ "cell_type": "code", "execution_count": 44, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2397,7 +2664,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "By further making use of the ``axis`` keyword, we can take the mean among levels on the columns as well:" ] @@ -2406,7 +2676,9 @@ "cell_type": "code", "execution_count": 45, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2459,7 +2731,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Thus in two lines, we've been able to find the average heart rate and temperature measured among all subjects in all visits each year.\n", "This syntax is actually a short cut to the ``GroupBy`` functionality, which we will discuss in [Aggregation and Grouping](03.08-Aggregation-and-Grouping.ipynb).\n", @@ -2468,7 +2743,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Aside: Panel Data\n", "\n", @@ -2486,7 +2764,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Handling Missing Data](03.04-Missing-Values.ipynb) | [Contents](Index.ipynb) | [Combining Datasets: Concat and Append](03.06-Concat-And-Append.ipynb) >" diff --git a/notebooks/03.13-Further-Resources.ipynb b/notebooks/03.13-Further-Resources.ipynb index e9c47e717..54cad89be 100644 --- a/notebooks/03.13-Further-Resources.ipynb +++ b/notebooks/03.13-Further-Resources.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [High-Performance Pandas: eval() and query()](03.12-Performance-Eval-and-Query.ipynb) | [Contents](Index.ipynb) | [Visualization with Matplotlib](04.00-Introduction-To-Matplotlib.ipynb) >" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Further Resources\n", - "\n", + "# Further Resources" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "In this chapter, we've covered many of the basics of using Pandas effectively for data analysis.\n", "Still, much has been omitted from our discussion.\n", "To learn more about Pandas, I recommend the following resources:\n", @@ -42,7 +56,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [High-Performance Pandas: eval() and query()](03.12-Performance-Eval-and-Query.ipynb) | [Contents](Index.ipynb) | [Visualization with Matplotlib](04.00-Introduction-To-Matplotlib.ipynb) >" diff --git a/notebooks/05.01-What-Is-Machine-Learning.ipynb b/notebooks/05.01-What-Is-Machine-Learning.ipynb index a26f207be..d91c98ad4 100644 --- a/notebooks/05.01-What-Is-Machine-Learning.ipynb +++ b/notebooks/05.01-What-Is-Machine-Learning.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Machine Learning](05.00-Machine-Learning.ipynb) | [Contents](Index.ipynb) | [Introducing Scikit-Learn](05.02-Introducing-Scikit-Learn.ipynb) >" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# What Is Machine Learning?\n", - "\n", + "# What Is Machine Learning?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "Before we take a look at the details of various machine learning methods, let's start by looking at what machine learning is, and what it isn't.\n", "Machine learning is often categorized as a subfield of artificial intelligence, but I find that categorization can often be misleading at first brush.\n", "The study of machine learning certainly arose from research in this context, but in the data science application of machine learning methods, it's more helpful to think of machine learning as a means of *building models of data*.\n", @@ -39,7 +53,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Categories of Machine Learning\n", "\n", @@ -60,7 +77,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Qualitative Examples of Machine Learning Applications\n", "\n", @@ -72,7 +92,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Classification: Predicting discrete labels\n", "\n", @@ -83,7 +106,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-classification-1.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Classification-Example-Figure-1)" @@ -91,7 +117,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Here we have two-dimensional data: that is, we have two *features* for each point, represented by the *(x,y)* positions of the points on the plane.\n", "In addition, we have one of two *class labels* for each point, here represented by the colors of the points.\n", @@ -106,7 +135,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-classification-2.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Classification-Example-Figure-2)" @@ -114,7 +146,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now that this model has been trained, it can be generalized to new, unlabeled data.\n", "In other words, we can take a new set of data, draw this model line through it, and assign labels to the new points based on this model.\n", @@ -123,7 +158,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-classification-3.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Classification-Example-Figure-3)" @@ -131,7 +169,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This is the basic idea of a classification task in machine learning, where \"classification\" indicates that the data has discrete class labels.\n", "At first glance this may look fairly trivial: it would be relatively easy to simply look at this data and draw such a discriminatory line to accomplish this classification.\n", @@ -151,7 +192,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Regression: Predicting continuous labels\n", "\n", @@ -162,7 +206,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-regression-1.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Regression-Example-Figure-1)" @@ -170,7 +217,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "As with the classification example, we have two-dimensional data: that is, there are two features describing each data point.\n", "The color of each point represents the continuous label for that point.\n", @@ -184,7 +234,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-regression-2.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Regression-Example-Figure-2)" @@ -192,7 +245,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Notice that the *feature 1-feature 2* plane here is the same as in the two-dimensional plot from before; in this case, however, we have represented the labels by both color and three-dimensional axis position.\n", "From this view, it seems reasonable that fitting a plane through this three-dimensional data would allow us to predict the expected label for any set of input parameters.\n", @@ -201,7 +257,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-regression-3.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Regression-Example-Figure-3)" @@ -209,7 +268,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This plane of fit gives us what we need to predict labels for new points.\n", "Visually, we find the results shown in the following figure:" @@ -217,7 +279,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-regression-4.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Regression-Example-Figure-4)" @@ -225,7 +290,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "As with the classification example, this may seem rather trivial in a low number of dimensions.\n", "But the power of these methods is that they can be straightforwardly applied and evaluated in the case of data with many, many features.\n", @@ -244,7 +312,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Clustering: Inferring labels on unlabeled data\n", "\n", @@ -257,7 +328,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-clustering-1.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Clustering-Example-Figure-2)" @@ -265,7 +339,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "By eye, it is clear that each of these points is part of a distinct group.\n", "Given this input, a clustering model will use the intrinsic structure of the data to determine which points are related.\n", @@ -274,7 +351,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-clustering-2.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Clustering-Example-Figure-2)" @@ -282,7 +362,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "*k*-means fits a model consisting of *k* cluster centers; the optimal centers are assumed to be those that minimize the distance of each point from its assigned center.\n", "Again, this might seem like a trivial exercise in two dimensions, but as our data becomes larger and more complex, such clustering algorithms can be employed to extract useful information from the dataset.\n", @@ -293,7 +376,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Dimensionality reduction: Inferring structure of unlabeled data\n", "\n", @@ -306,7 +392,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-dimesionality-1.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Dimensionality-Reduction-Example-Figure-1)" @@ -314,7 +403,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Visually, it is clear that there is some structure in this data: it is drawn from a one-dimensional line that is arranged in a spiral within this two-dimensional space.\n", "In a sense, you could say that this data is \"intrinsically\" only one dimensional, though this one-dimensional data is embedded in higher-dimensional space.\n", @@ -325,7 +417,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.01-dimesionality-2.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Dimensionality-Reduction-Example-Figure-2)" @@ -333,7 +428,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Notice that the colors (which represent the extracted one-dimensional latent variable) change uniformly along the spiral, which indicates that the algorithm did in fact detect the structure we saw by eye.\n", "As with the previous examples, the power of dimensionality reduction algorithms becomes clearer in higher-dimensional cases.\n", @@ -345,7 +443,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Summary\n", "\n", @@ -371,7 +472,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Machine Learning](05.00-Machine-Learning.ipynb) | [Contents](Index.ipynb) | [Introducing Scikit-Learn](05.02-Introducing-Scikit-Learn.ipynb) >" diff --git a/notebooks/05.02-Introducing-Scikit-Learn.ipynb b/notebooks/05.02-Introducing-Scikit-Learn.ipynb index d144bfabd..e05db813d 100644 --- a/notebooks/05.02-Introducing-Scikit-Learn.ipynb +++ b/notebooks/05.02-Introducing-Scikit-Learn.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [What Is Machine Learning?](05.01-What-Is-Machine-Learning.ipynb) | [Contents](Index.ipynb) | [Hyperparameters and Model Validation](05.03-Hyperparameters-and-Model-Validation.ipynb) >" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Introducing Scikit-Learn\n", - "\n", + "# Introducing Scikit-Learn" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "There are several Python libraries which provide solid implementations of a range of machine learning algorithms.\n", "One of the best known is [Scikit-Learn](http://scikit-learn.org), a package that provides efficient versions of a large number of common algorithms.\n", "Scikit-Learn is characterized by a clean, uniform, and streamlined API, as well as by very useful and complete online documentation.\n", @@ -37,14 +51,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Data Representation in Scikit-Learn" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Machine learning is about creating models from data: for that reason, we'll start by discussing how data can be represented in order to be understood by the computer.\n", "The best way to think about data within Scikit-Learn is in terms of tables of data." @@ -52,7 +72,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Data as table\n", "\n", @@ -65,7 +88,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -150,7 +175,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Here each row of the data refers to a single observed flower, and the number of rows is the total number of flowers in the dataset.\n", "In general, we will refer to the rows of the matrix as *samples*, and the number of rows as ``n_samples``.\n", @@ -161,7 +189,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Features matrix\n", "\n", @@ -178,7 +209,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Target array\n", "\n", @@ -197,7 +231,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -219,7 +255,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "For use in Scikit-Learn, we will extract the features matrix and target array from the ``DataFrame``, which we can do using some of the Pandas ``DataFrame`` operations discussed in the [Chapter 3](03.00-Introduction-to-Pandas.ipynb):" ] @@ -228,7 +267,9 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -251,7 +292,9 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -272,14 +315,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "To summarize, the expected layout of features and target values is visualized in the following diagram:" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.02-samples-features.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Features-and-Labels-Grid)" @@ -287,21 +336,30 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With this data properly formatted, we can move on to consider the *estimator* API of Scikit-Learn:" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Scikit-Learn's Estimator API" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The Scikit-Learn API is designed with the following guiding principles in mind, as outlined in the [Scikit-Learn API paper](http://arxiv.org/abs/1309.0238):\n", "\n", @@ -324,7 +382,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Basics of the API\n", "\n", @@ -344,7 +405,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Supervised learning example: Simple linear regression\n", "\n", @@ -356,7 +420,9 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -382,14 +448,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With this data in place, we can use the recipe outlined earlier. Let's walk through the process: " ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### 1. Choose a class of model\n", "\n", @@ -401,7 +473,9 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -410,14 +484,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Note that other more general linear regression models exist as well; you can read more about them in the [``sklearn.linear_model`` module documentation](http://Scikit-Learn.org/stable/modules/linear_model.html)." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### 2. Choose model hyperparameters\n", "\n", @@ -444,7 +524,9 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -465,7 +547,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Keep in mind that when the model is instantiated, the only action is the storing of these hyperparameter values.\n", "In particular, we have not yet applied the model to any data: the Scikit-Learn API makes very clear the distinction between *choice of model* and *application of model to data*." @@ -473,7 +558,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### 3. Arrange data into a features matrix and target vector\n", "\n", @@ -486,7 +574,9 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -507,7 +597,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### 4. Fit the model to your data\n", "\n", @@ -519,7 +612,9 @@ "cell_type": "code", "execution_count": 9, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -539,7 +634,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This ``fit()`` command causes a number of model-dependent internal computations to take place, and the results of these computations are stored in model-specific attributes that the user can explore.\n", "In Scikit-Learn, by convention all model parameters that were learned during the ``fit()`` process have trailing underscores; for example in this linear model, we have the following:" @@ -549,7 +647,9 @@ "cell_type": "code", "execution_count": 10, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -571,7 +671,9 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -591,7 +693,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "These two parameters represent the slope and intercept of the simple linear fit to the data.\n", "Comparing to the data definition, we see that they are very close to the input slope of 2 and intercept of -1.\n", @@ -604,7 +709,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### 5. Predict labels for unknown data\n", "\n", @@ -617,7 +725,9 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -626,7 +736,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "As before, we need to coerce these *x* values into a ``[n_samples, n_features]`` features matrix, after which we can feed it to the model:" ] @@ -635,7 +748,9 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -645,7 +760,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Finally, let's visualize the results by plotting first the raw data, and then this model fit:" ] @@ -654,7 +772,9 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -675,14 +795,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Typically the efficacy of the model is evaluated by comparing its results to some known baseline, as we will see in the next example" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Supervised learning example: Iris classification\n", "\n", @@ -700,7 +826,9 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -711,7 +839,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With the data arranged, we can follow our recipe to predict the labels:" ] @@ -720,7 +851,9 @@ "cell_type": "code", "execution_count": 16, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -732,7 +865,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Finally, we can use the ``accuracy_score`` utility to see the fraction of predicted labels that match their true value:" ] @@ -741,7 +877,9 @@ "cell_type": "code", "execution_count": 17, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -762,14 +900,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With an accuracy topping 97%, we see that even this very naive classification algorithm is effective for this particular dataset!" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Unsupervised learning example: Iris dimensionality\n", "\n", @@ -789,7 +933,9 @@ "cell_type": "code", "execution_count": 18, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -801,7 +947,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now let's plot the results. A quick way to do this is to insert the results into the original Iris ``DataFrame``, and use Seaborn's ``lmplot`` to show the results:" ] @@ -810,7 +959,9 @@ "cell_type": "code", "execution_count": 19, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -832,7 +983,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see that in the two-dimensional representation, the species are fairly well separated, even though the PCA algorithm had no knowledge of the species labels!\n", "This indicates to us that a relatively straightforward classification will probably be effective on the dataset, as we saw before." @@ -840,7 +994,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Unsupervised learning: Iris clustering\n", "\n", @@ -856,7 +1013,9 @@ "cell_type": "code", "execution_count": 20, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -869,7 +1028,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "As before, we will add the cluster label to the Iris ``DataFrame`` and use Seaborn to plot the results:" ] @@ -878,7 +1040,9 @@ "cell_type": "code", "execution_count": 21, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -900,7 +1064,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "By splitting the data by cluster number, we see exactly how well the GMM algorithm has recovered the underlying label: the *setosa* species is separated perfectly within cluster 0, while there remains a small amount of mixing between *versicolor* and *virginica*.\n", "This means that even without an expert to tell us the species labels of the individual flowers, the measurements of these flowers are distinct enough that we could *automatically* identify the presence of these different groups of species with a simple clustering algorithm!\n", @@ -909,14 +1076,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Application: Exploring Hand-written Digits" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "To demonstrate these principles on a more interesting problem, let's consider one piece of the optical character recognition problem: the identification of hand-written digits.\n", "In the wild, this problem involves both locating and identifying characters in an image. Here we'll take a shortcut and use Scikit-Learn's set of pre-formatted digits, which is built into the library." @@ -924,7 +1097,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Loading and visualizing the digits data\n", "\n", @@ -935,7 +1111,9 @@ "cell_type": "code", "execution_count": 22, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -957,7 +1135,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The images data is a three-dimensional array: 1,797 samples each consisting of an 8 × 8 grid of pixels.\n", "Let's visualize the first hundred of these:" @@ -967,7 +1148,9 @@ "cell_type": "code", "execution_count": 23, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -996,7 +1179,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "In order to work with this data within Scikit-Learn, we need a two-dimensional, ``[n_samples, n_features]`` representation.\n", "We can accomplish this by treating each pixel in the image as a feature: that is, by flattening out the pixel arrays so that we have a length-64 array of pixel values representing each digit.\n", @@ -1008,7 +1194,9 @@ "cell_type": "code", "execution_count": 24, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1031,7 +1219,9 @@ "cell_type": "code", "execution_count": 25, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1052,14 +1242,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see here that there are 1,797 samples and 64 features." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Unsupervised learning: Dimensionality reduction\n", "\n", @@ -1072,7 +1268,9 @@ "cell_type": "code", "execution_count": 26, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1096,7 +1294,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see that the projected data is now two-dimensional.\n", "Let's plot this data to see if we can learn anything from its structure:" @@ -1106,7 +1307,9 @@ "cell_type": "code", "execution_count": 27, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1130,7 +1333,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This plot gives us some good intuition into how well various numbers are separated in the larger 64-dimensional space. For example, zeros (in black) and ones (in purple) have very little overlap in parameter space.\n", "Intuitively, this makes sense: a zero is empty in the middle of the image, while a one will generally have ink in the middle.\n", @@ -1142,7 +1348,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Classification on digits\n", "\n", @@ -1154,7 +1363,9 @@ "cell_type": "code", "execution_count": 28, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -1165,7 +1376,9 @@ "cell_type": "code", "execution_count": 29, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -1177,7 +1390,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now that we have predicted our model, we can gauge its accuracy by comparing the true values of the test set to the predictions:" ] @@ -1186,7 +1402,9 @@ "cell_type": "code", "execution_count": 30, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1207,7 +1425,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With even this extremely simple model, we find about 80% accuracy for classification of the digits!\n", "However, this single number doesn't tell us *where* we've gone wrong—one nice way to do this is to use the *confusion matrix*, which we can compute with Scikit-Learn and plot with Seaborn:" @@ -1217,7 +1438,9 @@ "cell_type": "code", "execution_count": 31, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1243,7 +1466,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This shows us where the mis-labeled points tend to be: for example, a large number of twos here are mis-classified as either ones or eights.\n", "Another way to gain intuition into the characteristics of the model is to plot the inputs again, with their predicted labels.\n", @@ -1254,7 +1480,9 @@ "cell_type": "code", "execution_count": 32, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1284,7 +1512,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Examining this subset of the data, we can gain insight regarding where the algorithm might be not performing optimally.\n", "To go beyond our 80% classification rate, we might move to a more sophisticated algorithm such as support vector machines (see [In-Depth: Support Vector Machines](05.07-Support-Vector-Machines.ipynb)), random forests (see [In-Depth: Decision Trees and Random Forests](05.08-Random-Forests.ipynb)) or another classification approach." @@ -1292,14 +1523,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Summary" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "In this section we have covered the essential features of the Scikit-Learn data representation, and the estimator API.\n", "Regardless of the type of estimator, the same import/instantiate/fit/predict pattern holds.\n", @@ -1310,7 +1547,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [What Is Machine Learning?](05.01-What-Is-Machine-Learning.ipynb) | [Contents](Index.ipynb) | [Hyperparameters and Model Validation](05.03-Hyperparameters-and-Model-Validation.ipynb) >" diff --git a/notebooks/05.03-Hyperparameters-and-Model-Validation.ipynb b/notebooks/05.03-Hyperparameters-and-Model-Validation.ipynb index a74734983..62b082058 100644 --- a/notebooks/05.03-Hyperparameters-and-Model-Validation.ipynb +++ b/notebooks/05.03-Hyperparameters-and-Model-Validation.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,20 +16,30 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Introducing Scikit-Learn](05.02-Introducing-Scikit-Learn.ipynb) | [Contents](Index.ipynb) | [Feature Engineering](05.04-Feature-Engineering.ipynb) >" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Hyperparameters and Model Validation" + ] + }, { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "source": [ - "# Hyperparameters and Model Validation\n", - "\n", "In the previous section, we saw the basic recipe for applying a supervised machine learning model:\n", "\n", "1. Choose a class of model\n", @@ -41,7 +54,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Thinking about Model Validation\n", "\n", @@ -54,7 +70,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Model validation the wrong way\n", "\n", @@ -66,7 +85,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -78,7 +99,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Next we choose a model and hyperparameters. Here we'll use a *k*-neighbors classifier with ``n_neighbors=1``.\n", "This is a very simple and intuitive model that says \"the label of an unknown point is the same as the label of its closest training point:\"" @@ -88,7 +112,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -98,7 +124,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Then we train the model, and use it to predict labels for data we already know:" ] @@ -107,7 +136,9 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -117,7 +148,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Finally, we compute the fraction of correctly labeled points:" ] @@ -126,7 +160,9 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -147,7 +183,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see an accuracy score of 1.0, which indicates that 100% of points were correctly labeled by our model!\n", "But is this truly measuring the expected accuracy? Have we really come upon a model that we expect to be correct 100% of the time?\n", @@ -159,7 +198,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Model validation the right way: Holdout sets\n", "\n", @@ -172,7 +214,9 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -202,7 +246,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see here a more reasonable result: the nearest-neighbor classifier is about 90% accurate on this hold-out set.\n", "The hold-out set is similar to unknown data, because the model has not \"seen\" it before." @@ -210,7 +257,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Model validation via cross-validation\n", "\n", @@ -232,7 +282,9 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -254,7 +306,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "What comes out are two accuracy scores, which we could combine (by, say, taking the mean) to get a better measure of the global model performance.\n", "This particular form of cross-validation is a *two-fold cross-validation*—that is, one in which we have split the data into two sets and used each in turn as a validation set.\n", @@ -272,7 +327,9 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -293,7 +350,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Repeating the validation across different subsets of the data gives us an even better idea of the performance of the algorithm.\n", "\n", @@ -306,7 +366,9 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -339,7 +401,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Because we have 150 samples, the leave one out cross-validation yields scores for 150 trials, and the score indicates either successful (1.0) or unsuccessful (0.0) prediction.\n", "Taking the mean of these gives an estimate of the error rate:" @@ -349,7 +414,9 @@ "cell_type": "code", "execution_count": 9, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -369,7 +436,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Other cross-validation schemes can be used similarly.\n", "For a description of what is available in Scikit-Learn, use IPython to explore the ``sklearn.cross_validation`` submodule, or take a look at Scikit-Learn's online [cross-validation documentation](http://scikit-learn.org/stable/modules/cross_validation.html)." @@ -377,7 +447,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Selecting the Best Model\n", "\n", @@ -399,7 +472,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### The Bias-variance trade-off\n", "\n", @@ -422,7 +498,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "To look at this in another light, consider what happens if we use these two models to predict the y-value for some new data.\n", "In the following diagrams, the red/lighter points indicate data that is omitted from the training set:\n", @@ -439,7 +518,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "If we imagine that we have some ability to tune the model complexity, we would expect the training score and validation score to behave as illustrated in the following figure:\n", "\n", @@ -459,7 +541,9 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "source": [ "### Validation curves in Scikit-Learn\n", @@ -487,7 +571,9 @@ "cell_type": "code", "execution_count": 10, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -503,7 +589,9 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "source": [ "Now let's create some data to which we will fit our model:" @@ -513,7 +601,9 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -533,7 +623,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We can now visualize our data, along with polynomial fits of several degrees:" ] @@ -542,7 +635,9 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -575,7 +670,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The knob controlling model complexity in this case is the degree of the polynomial, which can be any non-negative integer.\n", "A useful question to answer is this: what degree of polynomial provides a suitable trade-off between bias (under-fitting) and variance (over-fitting)?\n", @@ -588,7 +686,9 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -618,7 +718,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This shows precisely the qualitative behavior we expect: the training score is everywhere higher than the validation score; the training score is monotonically improving with increased model complexity; and the validation score reaches a maximum before dropping off as the model becomes over-fit.\n", "\n", @@ -629,7 +732,9 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -653,14 +758,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Notice that finding this optimal model did not actually require us to compute the training score, but examining the relationship between the training score and validation score can give us useful insight into the performance of the model." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Learning Curves\n", "\n", @@ -672,7 +783,9 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -693,7 +806,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We will duplicate the preceding code to plot the validation curve for this larger dataset; for reference let's over-plot the previous results as well:" ] @@ -702,7 +818,9 @@ "cell_type": "code", "execution_count": 16, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -733,7 +851,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The solid lines show the new results, while the fainter dashed lines show the results of the previous smaller dataset.\n", "It is clear from the validation curve that the larger dataset can support a much more complicated model: the peak here is probably around a degree of 6, but even a degree-20 model is not seriously over-fitting the data—the validation and training scores remain very close.\n", @@ -753,7 +874,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.03-learning-curve.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Learning-Curve)" @@ -761,7 +885,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The notable feature of the learning curve is the convergence to a particular score as the number of training samples grows.\n", "In particular, once you have enough points that a particular model has converged, *adding more training data will not help you!*\n", @@ -770,7 +897,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Learning curves in Scikit-Learn\n", "\n", @@ -781,7 +911,9 @@ "cell_type": "code", "execution_count": 17, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -821,7 +953,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This is a valuable diagnostic, because it gives us a visual depiction of how our model responds to increasing training data.\n", "In particular, when your learning curve has already converged (i.e., when the training and validation curves are already close to each other) *adding more training data will not significantly improve the fit!*\n", @@ -836,7 +971,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Validation in Practice: Grid Search\n", "\n", @@ -854,7 +992,9 @@ "cell_type": "code", "execution_count": 18, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -869,7 +1009,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Notice that like a normal estimator, this has not yet been applied to any data.\n", "Calling the ``fit()`` method will fit the model at each grid point, keeping track of the scores along the way:" @@ -879,7 +1022,9 @@ "cell_type": "code", "execution_count": 19, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -888,7 +1033,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now that this is fit, we can ask for the best parameters as follows:" ] @@ -897,7 +1045,9 @@ "cell_type": "code", "execution_count": 20, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -919,7 +1069,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Finally, if we wish, we can use the best model and show the fit to our data using code from before:" ] @@ -928,7 +1081,9 @@ "cell_type": "code", "execution_count": 21, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -954,7 +1109,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The grid search provides many more options, including the ability to specify a custom scoring function, to parallelize the computations, to do randomized searches, and more.\n", "For information, see the examples in [In-Depth: Kernel Density Estimation](05.13-Kernel-Density-Estimation.ipynb) and [Feature Engineering: Working with Images](05.14-Image-Features.ipynb), or refer to Scikit-Learn's [grid search documentation](http://Scikit-Learn.org/stable/modules/grid_search.html)." @@ -962,7 +1120,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Summary\n", "\n", @@ -975,7 +1136,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Introducing Scikit-Learn](05.02-Introducing-Scikit-Learn.ipynb) | [Contents](Index.ipynb) | [Feature Engineering](05.04-Feature-Engineering.ipynb) >" diff --git a/notebooks/05.04-Feature-Engineering.ipynb b/notebooks/05.04-Feature-Engineering.ipynb index c407d4525..01caddb28 100644 --- a/notebooks/05.04-Feature-Engineering.ipynb +++ b/notebooks/05.04-Feature-Engineering.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Hyperparameters and Model Validation](05.03-Hyperparameters-and-Model-Validation.ipynb) | [Contents](Index.ipynb) | [In Depth: Naive Bayes Classification](05.05-Naive-Bayes.ipynb) >" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Feature Engineering\n", - "\n", + "# Feature Engineering" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "The previous sections outline the fundamental ideas of machine learning, but all of the examples assume that you have numerical data in a tidy, ``[n_samples, n_features]`` format.\n", "In the real world, data rarely comes in such a form.\n", "With this in mind, one of the more important steps in using machine learning in practice is *feature engineering*: that is, taking whatever information you have about your problem and turning it into numbers that you can use to build your feature matrix.\n", @@ -36,7 +50,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Categorical Features\n", "\n", @@ -49,7 +66,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -63,7 +82,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "You might be tempted to encode this data with a straightforward numerical mapping:" ] @@ -72,7 +94,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -81,7 +105,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "It turns out that this is not generally a useful approach in Scikit-Learn: the package's models make the fundamental assumption that numerical features reflect algebraic quantities.\n", "Thus such a mapping would imply, for example, that *Queen Anne < Fremont < Wallingford*, or even that *Wallingford - Queen Anne = Fremont*, which (niche demographic jokes aside) does not make much sense.\n", @@ -94,7 +121,9 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -119,7 +148,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Notice that the 'neighborhood' column has been expanded into three separate columns, representing the three neighborhood labels, and that each row has a 1 in the column associated with its neighborhood.\n", "With these categorical features thus encoded, you can proceed as normal with fitting a Scikit-Learn model.\n", @@ -131,7 +163,9 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -155,7 +189,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "There is one clear disadvantage of this approach: if your category has many possible values, this can *greatly* increase the size of your dataset.\n", "However, because the encoded data contains mostly zeros, a sparse output can be a very efficient solution:" @@ -165,7 +202,9 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -187,14 +226,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Many (though not yet all) of the Scikit-Learn estimators accept such sparse inputs when fitting and evaluating models. ``sklearn.preprocessing.OneHotEncoder`` and ``sklearn.feature_extraction.FeatureHasher`` are two additional tools that Scikit-Learn includes to support this type of encoding." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Text Features\n", "\n", @@ -209,7 +254,9 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -220,7 +267,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "For a vectorization of this data based on word count, we could construct a column representing the word \"problem,\" the word \"evil,\" the word \"horizon,\" and so on.\n", "While doing this by hand would be possible, the tedium can be avoided by using Scikit-Learn's ``CountVectorizer``:" @@ -230,7 +280,9 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -255,7 +307,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The result is a sparse matrix recording the number of times each word appears; it is easier to inspect if we convert this to a ``DataFrame`` with labeled columns:" ] @@ -264,7 +319,9 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -330,7 +387,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "There are some issues with this approach, however: the raw word counts lead to features which put too much weight on words that appear very frequently, and this can be sub-optimal in some classification algorithms.\n", "One approach to fix this is known as *term frequency-inverse document frequency* (*TF–IDF*) which weights the word counts by a measure of how often they appear in the documents.\n", @@ -341,7 +401,9 @@ "cell_type": "code", "execution_count": 9, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -409,14 +471,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "For an example of using TF-IDF in a classification problem, see [In Depth: Naive Bayes Classification](05.05-Naive-Bayes.ipynb)." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Image Features\n", "\n", @@ -430,7 +498,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Derived Features\n", "\n", @@ -446,7 +517,9 @@ "cell_type": "code", "execution_count": 10, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -472,7 +545,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Still, we can fit a line to the data using ``LinearRegression`` and get the optimal result:" ] @@ -481,7 +557,9 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -506,7 +584,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "It's clear that we need a more sophisticated model to describe the relationship between $x$ and $y$.\n", "\n", @@ -518,7 +599,9 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -542,7 +625,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The derived feature matrix has one column representing $x$, and a second column representing $x^2$, and a third column representing $x^3$.\n", "Computing a linear regression on this expanded input gives a much closer fit to our data:" @@ -552,7 +638,9 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -575,7 +663,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This idea of improving a model not by changing the model, but by transforming the inputs, is fundamental to many of the more powerful machine learning methods.\n", "We explore this idea further in [In Depth: Linear Regression](05.06-Linear-Regression.ipynb) in the context of *basis function regression*.\n", @@ -584,7 +675,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Imputation of Missing Data\n", "\n", @@ -597,7 +691,9 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -612,7 +708,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "When applying a typical machine learning model to such data, we will need to first replace such missing data with some appropriate fill value.\n", "This is known as *imputation* of missing values, and strategies range from simple (e.g., replacing missing values with the mean of the column) to sophisticated (e.g., using matrix completion or a robust model to handle such data).\n", @@ -625,7 +724,9 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -652,7 +753,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see that in the resulting data, the two missing values have been replaced with the mean of the remaining values in the column. This imputed data can then be fed directly into, for example, a ``LinearRegression`` estimator:" ] @@ -661,7 +765,9 @@ "cell_type": "code", "execution_count": 16, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -682,7 +788,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Feature Pipelines\n", "\n", @@ -700,7 +809,9 @@ "cell_type": "code", "execution_count": 17, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -713,7 +824,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This pipeline looks and acts like a standard Scikit-Learn object, and will apply all the specified steps to any input data." ] @@ -722,7 +836,9 @@ "cell_type": "code", "execution_count": 18, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -742,7 +858,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "All the steps of the model are applied automatically.\n", "Notice that for the simplicity of this demonstration, we've applied the model to the data it was trained on; this is why it was able to perfectly predict the result (refer back to [Hyperparameters and Model Validation](05.03-Hyperparameters-and-Model-Validation.ipynb) for further discussion of this).\n", @@ -752,7 +871,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Hyperparameters and Model Validation](05.03-Hyperparameters-and-Model-Validation.ipynb) | [Contents](Index.ipynb) | [In Depth: Naive Bayes Classification](05.05-Naive-Bayes.ipynb) >" diff --git a/notebooks/05.05-Naive-Bayes.ipynb b/notebooks/05.05-Naive-Bayes.ipynb index 3de676f9a..d4672e5b8 100644 --- a/notebooks/05.05-Naive-Bayes.ipynb +++ b/notebooks/05.05-Naive-Bayes.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Feature Engineering](05.04-Feature-Engineering.ipynb) | [Contents](Index.ipynb) | [In Depth: Linear Regression](05.06-Linear-Regression.ipynb) >" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# In Depth: Naive Bayes Classification\n", - "\n", + "# In Depth: Naive Bayes Classification" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "The previous four sections have given a general overview of the concepts of machine learning.\n", "In this section and the ones that follow, we will be taking a closer look at several specific algorithms for supervised and unsupervised learning, starting here with naive Bayes classification.\n", "\n", @@ -35,7 +49,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Bayesian Classification\n", "\n", @@ -69,7 +86,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -81,7 +100,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Gaussian Naive Bayes\n", "\n", @@ -94,7 +116,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -116,7 +140,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "One extremely fast way to create a simple model is to assume that the data is described by a Gaussian distribution with no covariance between dimensions.\n", "This model can be fit by simply finding the mean and standard deviation of the points within each label, which is all you need to define such a distribution.\n", @@ -125,7 +152,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![(run code in Appendix to generate image)](figures/05.05-gaussian-NB.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Gaussian-Naive-Bayes)" @@ -134,7 +164,9 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "source": [ "The ellipses here represent the Gaussian generative model for each label, with larger probability toward the center of the ellipses.\n", @@ -147,7 +179,9 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -158,7 +192,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now let's generate some new data and predict the label:" ] @@ -167,7 +204,9 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -178,7 +217,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now we can plot this new data to get an idea of where the decision boundary is:" ] @@ -187,7 +229,9 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -210,7 +254,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see a slightly curved boundary in the classifications—in general, the boundary in Gaussian naive Bayes is quadratic.\n", "\n", @@ -221,7 +268,9 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -249,7 +298,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The columns give the posterior probabilities of the first and second label, respectively.\n", "If you are looking for estimates of uncertainty in your classification, Bayesian approaches like this can be a useful approach.\n", @@ -260,7 +312,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Multinomial Naive Bayes\n", "\n", @@ -273,7 +328,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Example: Classifying Text\n", "\n", @@ -287,7 +345,9 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -329,7 +389,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "For simplicity here, we will select just a few of these categories, and download the training and testing set:" ] @@ -338,7 +401,9 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -350,7 +415,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Here is a representative entry from the data:" ] @@ -359,7 +427,9 @@ "cell_type": "code", "execution_count": 9, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -393,7 +463,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "In order to use this data for machine learning, we need to be able to convert the content of each string into a vector of numbers.\n", "For this we will use the TF-IDF vectorizer (discussed in [Feature Engineering](05.04-Feature-Engineering.ipynb)), and create a pipeline that attaches it to a multinomial naive Bayes classifier:" @@ -403,7 +476,9 @@ "cell_type": "code", "execution_count": 10, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -416,7 +491,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With this pipeline, we can apply the model to the training data, and predict labels for the test data:" ] @@ -425,7 +503,9 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -435,7 +515,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now that we have predicted the labels for the test data, we can evaluate them to learn about the performance of the estimator.\n", "For example, here is the confusion matrix between the true and predicted labels for the test data:" @@ -445,7 +528,9 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -470,7 +555,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Evidently, even this very simple classifier can successfully separate space talk from computer talk, but it gets confused between talk about religion and talk about Christianity.\n", "This is perhaps an expected area of confusion!\n", @@ -483,7 +571,9 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -494,7 +584,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Let's try it out:" ] @@ -503,7 +596,9 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -525,7 +620,9 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -547,7 +644,9 @@ "cell_type": "code", "execution_count": 16, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -567,7 +666,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Remember that this is nothing more sophisticated than a simple probability model for the (weighted) frequency of each word in the string; nevertheless, the result is striking.\n", "Even a very naive algorithm, when used carefully and trained on a large set of high-dimensional data, can be surprisingly effective." @@ -575,7 +677,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## When to Use Naive Bayes\n", "\n", @@ -604,7 +709,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Feature Engineering](05.04-Feature-Engineering.ipynb) | [Contents](Index.ipynb) | [In Depth: Linear Regression](05.06-Linear-Regression.ipynb) >" diff --git a/notebooks/05.06-Linear-Regression.ipynb b/notebooks/05.06-Linear-Regression.ipynb index 892a7a44b..acc495597 100644 --- a/notebooks/05.06-Linear-Regression.ipynb +++ b/notebooks/05.06-Linear-Regression.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [In Depth: Naive Bayes Classification](05.05-Naive-Bayes.ipynb) | [Contents](Index.ipynb) | [In-Depth: Support Vector Machines](05.07-Support-Vector-Machines.ipynb) >" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# In Depth: Linear Regression\n", - "\n", + "# In Depth: Linear Regression" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "Just as naive Bayes (discussed earlier in [In Depth: Naive Bayes Classification](05.05-Naive-Bayes.ipynb)) is a good starting point for classification tasks, linear regression models are a good starting point for regression tasks.\n", "Such models are popular because they can be fit very quickly, and are very interpretable.\n", "You are probably familiar with the simplest form of a linear regression model (i.e., fitting a straight line to data) but such models can be extended to model more complicated data behavior.\n", @@ -38,7 +52,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -50,7 +66,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Simple Linear Regression\n", "\n", @@ -68,7 +87,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -91,7 +112,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We can use Scikit-Learn's ``LinearRegression`` estimator to fit this data and construct the best-fit line:" ] @@ -100,7 +124,9 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -129,7 +155,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The slope and intercept of the data are contained in the model's fit parameters, which in Scikit-Learn are always marked by a trailing underscore.\n", "Here the relevant parameters are ``coef_`` and ``intercept_``:" @@ -139,7 +168,9 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -158,14 +189,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see that the results are very close to the inputs, as we might hope." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The ``LinearRegression`` estimator is much more capable than this, however—in addition to simple straight-line fits, it can also handle multidimensional linear models of the form\n", "$$\n", @@ -181,7 +218,9 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -205,7 +244,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Here the $y$ data is constructed from three random $x$ values, and the linear regression recovers the coefficients used to construct the data.\n", "\n", @@ -215,7 +257,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Basis Function Regression\n", "\n", @@ -238,7 +283,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Polynomial basis functions\n", "\n", @@ -249,7 +297,9 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -274,7 +324,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see here that the transformer has converted our one-dimensional array into a three-dimensional array by taking the exponent of each value.\n", "This new, higher-dimensional data representation can then be plugged into a linear regression.\n", @@ -287,7 +340,9 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -298,7 +353,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With this transform in place, we can use the linear model to fit much more complicated relationships between $x$ and $y$. \n", "For example, here is a sine wave with noise:" @@ -308,7 +366,9 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -336,14 +396,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Our linear model, through the use of 7th-order polynomial basis functions, can provide an excellent fit to this non-linear data!" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Gaussian basis functions\n", "\n", @@ -354,7 +420,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.06-gaussian-basis.png)\n", "[figure source in Appendix](#Gaussian-Basis)" @@ -362,7 +431,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The shaded regions in the plot are the scaled basis functions, and when added together they reproduce the smooth curve through the data.\n", "These Gaussian basis functions are not built into Scikit-Learn, but we can write a custom transformer that will create them, as shown here and illustrated in the following figure (Scikit-Learn transformers are implemented as Python classes; reading Scikit-Learn's source is a good way to see how they can be created):" @@ -372,7 +444,9 @@ "cell_type": "code", "execution_count": 9, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -423,14 +497,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We put this example here just to make clear that there is nothing magic about polynomial basis functions: if you have some sort of intuition into the generating process of your data that makes you think one basis or another might be appropriate, you can use them as well." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Regularization\n", "\n", @@ -442,7 +522,9 @@ "cell_type": "code", "execution_count": 10, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -470,7 +552,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With the data projected to the 30-dimensional basis, the model has far too much flexibility and goes to extreme values between locations where it is constrained by data.\n", "We can see the reason for this if we plot the coefficients of the Gaussian bases with respect to their locations:" @@ -480,7 +565,9 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -517,7 +604,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The lower panel of this figure shows the amplitude of the basis function at each location.\n", "This is typical over-fitting behavior when basis functions overlap: the coefficients of adjacent basis functions blow up and cancel each other out.\n", @@ -527,7 +617,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Ridge regression ($L_2$ Regularization)\n", "\n", @@ -544,7 +637,9 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -566,7 +661,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The $\\alpha$ parameter is essentially a knob controlling the complexity of the resulting model.\n", "In the limit $\\alpha \\to 0$, we recover the standard linear regression result; in the limit $\\alpha \\to \\infty$, all model responses will be suppressed.\n", @@ -575,7 +673,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Lasso regression ($L_1$ regularization)\n", "\n", @@ -592,7 +693,9 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -614,7 +717,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With the lasso regression penalty, the majority of the coefficients are exactly zero, with the functional behavior being modeled by a small subset of the available basis functions.\n", "As with ridge regularization, the $\\alpha$ parameter tunes the strength of the penalty, and should be determined via, for example, cross-validation (refer back to [Hyperparameters and Model Validation](05.03-Hyperparameters-and-Model-Validation.ipynb) for a discussion of this)." @@ -622,7 +728,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Example: Predicting Bicycle Traffic" ] @@ -630,7 +739,9 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "source": [ "As an example, let's take a look at whether we can predict the number of bicycle trips across Seattle's Fremont Bridge based on weather, season, and other factors.\n", @@ -650,7 +761,9 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -661,7 +774,9 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -672,7 +787,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Next we will compute the total daily bicycle traffic, and put this in its own dataframe:" ] @@ -681,7 +799,9 @@ "cell_type": "code", "execution_count": 16, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -692,7 +812,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We saw previously that the patterns of use generally vary from day to day; let's account for this in our data by adding binary columns that indicate the day of the week:" ] @@ -701,7 +824,9 @@ "cell_type": "code", "execution_count": 17, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -712,7 +837,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Similarly, we might expect riders to behave differently on holidays; let's add an indicator of this as well:" ] @@ -721,7 +849,9 @@ "cell_type": "code", "execution_count": 18, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -734,7 +864,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We also might suspect that the hours of daylight would affect how many people ride; let's use the standard astronomical calculation to add this information:" ] @@ -743,7 +876,9 @@ "cell_type": "code", "execution_count": 19, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -782,7 +917,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We can also add the average temperature and total precipitation to the data.\n", "In addition to the inches of precipitation, let's add a flag that indicates whether a day is dry (has zero precipitation):" @@ -792,7 +930,9 @@ "cell_type": "code", "execution_count": 20, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -810,7 +950,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Finally, let's add a counter that increases from day 1, and measures how many years have passed.\n", "This will let us measure any observed annual increase or decrease in daily crossings:" @@ -820,7 +963,9 @@ "cell_type": "code", "execution_count": 21, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -829,7 +974,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now our data is in order, and we can take a look at it:" ] @@ -838,7 +986,9 @@ "cell_type": "code", "execution_count": 22, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1001,7 +1151,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With this in place, we can choose the columns to use, and fit a linear regression model to our data.\n", "We will set ``fit_intercept = False``, because the daily flags essentially operate as their own day-specific intercepts:" @@ -1011,7 +1164,9 @@ "cell_type": "code", "execution_count": 23, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -1030,7 +1185,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Finally, we can compare the total and predicted bicycle traffic visually:" ] @@ -1039,7 +1197,9 @@ "cell_type": "code", "execution_count": 24, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1059,7 +1219,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "It is evident that we have missed some key features, especially during the summer time.\n", "Either our features are not complete (i.e., people decide whether to ride to work based on more than just these) or there are some nonlinear relationships that we have failed to take into account (e.g., perhaps people ride less at both high and low temperatures).\n", @@ -1070,7 +1233,9 @@ "cell_type": "code", "execution_count": 25, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1104,7 +1269,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "These numbers are difficult to interpret without some measure of their uncertainty.\n", "We can compute these uncertainties quickly using bootstrap resamplings of the data:" @@ -1114,7 +1282,9 @@ "cell_type": "code", "execution_count": 26, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -1126,7 +1296,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With these errors estimated, let's again look at the results:" ] @@ -1135,7 +1308,9 @@ "cell_type": "code", "execution_count": 27, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1166,7 +1341,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We first see that there is a relatively stable trend in the weekly baseline: there are many more riders on weekdays than on weekends and holidays.\n", "We see that for each additional hour of daylight, 129 ± 9 more people choose to ride; a temperature increase of one degree Celsius encourages 65 ± 4 people to grab their bicycle; a dry day means an average of 548 ± 33 more riders, and each inch of precipitation means 665 ± 62 more people leave their bike at home.\n", @@ -1179,7 +1357,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [In Depth: Naive Bayes Classification](05.05-Naive-Bayes.ipynb) | [Contents](Index.ipynb) | [In-Depth: Support Vector Machines](05.07-Support-Vector-Machines.ipynb) >" diff --git a/notebooks/05.09-Principal-Component-Analysis.ipynb b/notebooks/05.09-Principal-Component-Analysis.ipynb index 8bc761f73..01f49a277 100644 --- a/notebooks/05.09-Principal-Component-Analysis.ipynb +++ b/notebooks/05.09-Principal-Component-Analysis.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [In-Depth: Decision Trees and Random Forests](05.08-Random-Forests.ipynb) | [Contents](Index.ipynb) | [In-Depth: Manifold Learning](05.10-Manifold-Learning.ipynb) >" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# In Depth: Principal Component Analysis\n", - "\n", + "# In Depth: Principal Component Analysis" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "Up until now, we have been looking in depth at supervised learning estimators: those estimators that predict labels based on labeled training data.\n", "Here we begin looking at several unsupervised estimators, which can highlight interesting aspects of the data without reference to any known labels.\n", "\n", @@ -39,7 +53,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -51,7 +67,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Introducing Principal Component Analysis\n", "\n", @@ -64,7 +83,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -87,7 +108,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "By eye, it is clear that there is a nearly linear relationship between the x and y variables.\n", "This is reminiscent of the linear regression data we explored in [In Depth: Linear Regression](05.06-Linear-Regression.ipynb), but the problem setting here is slightly different: rather than attempting to *predict* the y values from the x values, the unsupervised learning problem attempts to learn about the *relationship* between the x and y values.\n", @@ -100,7 +124,9 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -122,7 +148,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The fit learns some quantities from the data, most importantly the \"components\" and \"explained variance\":" ] @@ -131,7 +160,9 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -151,7 +182,9 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -168,7 +201,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "To see what these numbers mean, let's visualize them as vectors over the input data, using the \"components\" to define the direction of the vector, and the \"explained variance\" to define the squared-length of the vector:" ] @@ -177,7 +213,9 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -209,7 +247,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "These vectors represent the *principal axes* of the data, and the length of the vector is an indication of how \"important\" that axis is in describing the distribution of the data—more precisely, it is a measure of the variance of the data when projected onto that axis.\n", "The projection of each data point onto the principal axes are the \"principal components\" of the data.\n", @@ -219,7 +260,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![](figures/05.09-PCA-rotation.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Principal-Components-Rotation)" @@ -227,7 +271,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This transformation from data axes to principal axes is an *affine transformation*, which basically means it is composed of a translation, rotation, and uniform scaling.\n", "\n", @@ -236,7 +283,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### PCA as dimensionality reduction\n", "\n", @@ -249,7 +299,9 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -271,7 +323,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The transformed data has been reduced to a single dimension.\n", "To understand the effect of this dimensionality reduction, we can perform the inverse transform of this reduced data and plot it along with the original data:" @@ -281,7 +336,9 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -304,7 +361,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The light points are the original data, while the dark points are the projected version.\n", "This makes clear what a PCA dimensionality reduction means: the information along the least important principal axis or axes is removed, leaving only the component(s) of the data with the highest variance.\n", @@ -315,7 +375,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### PCA for visualization: Hand-written digits\n", "\n", @@ -329,7 +392,9 @@ "cell_type": "code", "execution_count": 9, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -351,7 +416,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Recall that the data consists of 8×8 pixel images, meaning that they are 64-dimensional.\n", "To gain some intuition into the relationships between these points, we can use PCA to project them to a more manageable number of dimensions, say two:" @@ -361,7 +429,9 @@ "cell_type": "code", "execution_count": 10, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -382,7 +452,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We can now plot the first two principal components of each point to learn about the data:" ] @@ -391,7 +464,9 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -416,7 +491,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Recall what these components mean: the full data is a 64-dimensional point cloud, and these points are the projection of each data point along the directions with the largest variance.\n", "Essentially, we have found the optimal stretch and rotation in 64-dimensional space that allows us to see the layout of the digits in two dimensions, and have done this in an unsupervised manner—that is, without reference to the labels." @@ -424,7 +502,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### What do the components mean?\n", "\n", @@ -450,7 +531,9 @@ { "cell_type": "markdown", "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "source": [ "![](figures/05.09-digits-pixel-components.png)\n", @@ -459,7 +542,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The upper row of panels shows the individual pixels, and the lower row shows the cumulative contribution of these pixels to the construction of the image.\n", "Using only eight of the pixel-basis components, we can only construct a small portion of the 64-pixel image.\n", @@ -468,7 +554,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "But the pixel-wise representation is not the only choice of basis. We can also use other basis functions, which each contain some pre-defined contribution from each pixel, and write something like\n", "\n", @@ -484,7 +573,9 @@ { "cell_type": "markdown", "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "source": [ "![](figures/05.09-digits-pca-components.png)\n", @@ -493,7 +584,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Unlike the pixel basis, the PCA basis allows us to recover the salient features of the input image with just a mean plus eight components!\n", "The amount of each pixel in each component is the corollary of the orientation of the vector in our two-dimensional example.\n", @@ -502,7 +596,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Choosing the number of components\n", "\n", @@ -514,7 +611,9 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -537,7 +636,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This curve quantifies how much of the total, 64-dimensional variance is contained within the first $N$ components.\n", "For example, we see that with the digits the first 10 components contain approximately 75% of the variance, while you need around 50 components to describe close to 100% of the variance.\n", @@ -547,7 +649,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## PCA as Noise Filtering\n", "\n", @@ -563,7 +668,9 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -591,7 +698,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now lets add some random noise to create a noisy dataset, and re-plot it:" ] @@ -600,7 +710,9 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -622,7 +734,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "It's clear by eye that the images are noisy, and contain spurious pixels.\n", "Let's train a PCA on the noisy data, requesting that the projection preserve 50% of the variance:" @@ -632,7 +747,9 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -653,7 +770,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Here 50% of the variance amounts to 12 principal components.\n", "Now we compute these components, and then use the inverse of the transform to reconstruct the filtered digits:" @@ -663,7 +783,9 @@ "cell_type": "code", "execution_count": 16, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -685,14 +807,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This signal preserving/noise filtering property makes PCA a very useful feature selection routine—for example, rather than training a classifier on very high-dimensional data, you might instead train the classifier on the lower-dimensional representation, which will automatically serve to filter out random noise in the inputs." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Example: Eigenfaces\n", "\n", @@ -705,7 +833,9 @@ "cell_type": "code", "execution_count": 17, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -727,7 +857,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Let's take a look at the principal axes that span this dataset.\n", "Because this is a large dataset, we will use ``RandomizedPCA``—it contains a randomized method to approximate the first $N$ principal components much more quickly than the standard ``PCA`` estimator, and thus is very useful for high-dimensional data (here, a dimensionality of nearly 3,000).\n", @@ -738,7 +871,9 @@ "cell_type": "code", "execution_count": 18, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -761,7 +896,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "In this case, it can be interesting to visualize the images associated with the first several principal components (these components are technically known as \"eigenvectors,\"\n", "so these types of images are often called \"eigenfaces\").\n", @@ -772,7 +910,9 @@ "cell_type": "code", "execution_count": 19, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -796,7 +936,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The results are very interesting, and give us insight into how the images vary: for example, the first few eigenfaces (from the top left) seem to be associated with the angle of lighting on the face, and later principal vectors seem to be picking out certain features, such as eyes, noses, and lips.\n", "Let's take a look at the cumulative variance of these components to see how much of the data information the projection is preserving:" @@ -806,7 +949,9 @@ "cell_type": "code", "execution_count": 20, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -828,7 +973,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see that these 150 components account for just over 90% of the variance.\n", "That would lead us to believe that using these 150 components, we would recover most of the essential characteristics of the data.\n", @@ -839,7 +987,9 @@ "cell_type": "code", "execution_count": 21, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -853,7 +1003,9 @@ "cell_type": "code", "execution_count": 22, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -882,7 +1034,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The top row here shows the input images, while the bottom row shows the reconstruction of the images from just 150 of the ~3,000 initial features.\n", "This visualization makes clear why the PCA feature selection used in [In-Depth: Support Vector Machines](05.07-Support-Vector-Machines.ipynb) was so successful: although it reduces the dimensionality of the data by nearly a factor of 20, the projected images contain enough information that we might, by eye, recognize the individuals in the image.\n", @@ -891,7 +1046,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Principal Component Analysis Summary\n", "\n", @@ -910,7 +1068,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [In-Depth: Decision Trees and Random Forests](05.08-Random-Forests.ipynb) | [Contents](Index.ipynb) | [In-Depth: Manifold Learning](05.10-Manifold-Learning.ipynb) >" diff --git a/notebooks/05.12-Gaussian-Mixtures.ipynb b/notebooks/05.12-Gaussian-Mixtures.ipynb index bd660315d..22d1fb7a5 100644 --- a/notebooks/05.12-Gaussian-Mixtures.ipynb +++ b/notebooks/05.12-Gaussian-Mixtures.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [In Depth: k-Means Clustering](05.11-K-Means.ipynb) | [Contents](Index.ipynb) | [In-Depth: Kernel Density Estimation](05.13-Kernel-Density-Estimation.ipynb) >" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# In Depth: Gaussian Mixture Models\n", - "\n", + "# In Depth: Gaussian Mixture Models" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "The *k*-means clustering model explored in the previous section is simple and relatively easy to understand, but its simplicity leads to practical challenges in its application.\n", "In particular, the non-probabilistic nature of *k*-means and its use of simple distance-from-cluster-center to assign cluster membership leads to poor performance for many real-world situations.\n", "In this section we will take a look at Gaussian mixture models (GMMs), which can be viewed as an extension of the ideas behind *k*-means, but can also be a powerful tool for estimation beyond simple clustering.\n", @@ -36,7 +50,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -48,7 +64,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Motivating GMM: Weaknesses of k-Means\n", "\n", @@ -62,7 +81,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -77,7 +98,9 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -101,7 +124,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "From an intuitive standpoint, we might expect that the clustering assignment for some points is more certain than others: for example, there appears to be a very slight overlap between the two middle clusters, such that we might not have complete confidence in the cluster assigment of points between them.\n", "Unfortunately, the *k*-means model has no intrinsic measure of probability or uncertainty of cluster assignments (although it may be possible to use a bootstrap approach to estimate this uncertainty).\n", @@ -116,7 +142,9 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -143,7 +171,9 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -164,7 +194,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "An important observation for *k*-means is that these cluster models *must be circular*: *k*-means has no built-in way of accounting for oblong or elliptical clusters.\n", "So, for example, if we take the same data and transform it, the cluster assignments end up becoming muddled:" @@ -174,7 +207,9 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -198,7 +233,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "By eye, we recognize that these transformed clusters are non-circular, and thus circular clusters would be a poor fit.\n", "Nevertheless, *k*-means is not flexible enough to account for this, and tries to force-fit the data into four circular clusters.\n", @@ -214,7 +252,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Generalizing E–M: Gaussian Mixture Models\n", "\n", @@ -226,7 +267,9 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -249,7 +292,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "But because GMM contains a probabilistic model under the hood, it is also possible to find probabilistic cluster assignments—in Scikit-Learn this is done using the ``predict_proba`` method.\n", "This returns a matrix of size ``[n_samples, n_clusters]`` which measures the probability that any point belongs to the given cluster:" @@ -259,7 +305,9 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -281,7 +329,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We can visualize this uncertainty by, for example, making the size of each point proportional to the certainty of its prediction; looking at the following figure, we can see that it is precisely the points at the boundaries between clusters that reflect this uncertainty of cluster assignment:" ] @@ -290,7 +341,9 @@ "cell_type": "code", "execution_count": 9, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -311,7 +364,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Under the hood, a Gaussian mixture model is very similar to *k*-means: it uses an expectation–maximization approach which qualitatively does the following:\n", "\n", @@ -332,7 +388,9 @@ "cell_type": "code", "execution_count": 10, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -372,7 +430,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With this in place, we can take a look at what the four-component GMM gives us for our initial data:" ] @@ -381,7 +442,9 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -402,7 +465,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Similarly, we can use the GMM approach to fit our stretched dataset; allowing for a full covariance the model will fit even very oblong, stretched-out clusters:" ] @@ -411,7 +477,9 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -432,14 +500,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This makes clear that GMM addresses the two main practical issues with *k*-means encountered before." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Choosing the covariance type\n", "\n", @@ -454,7 +528,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "![(Covariance Type)](figures/05.12-covariance-type.png)\n", "[figure source in Appendix](06.00-Figure-Code.ipynb#Covariance-Type)" @@ -462,7 +539,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## GMM as *Density Estimation*\n", "\n", @@ -476,7 +556,9 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -498,7 +580,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "If we try to fit this with a two-component GMM viewed as a clustering model, the results are not particularly useful:" ] @@ -507,7 +592,9 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -528,7 +615,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "But if we instead use many more components and ignore the cluster labels, we find a fit that is much closer to the input data:" ] @@ -537,7 +627,9 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -558,7 +650,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Here the mixture of 16 Gaussians serves not to find separated clusters of data, but rather to model the overall *distribution* of the input data.\n", "This is a generative model of the distribution, meaning that the GMM gives us the recipe to generate new random data distributed similarly to our input.\n", @@ -569,7 +664,9 @@ "cell_type": "code", "execution_count": 16, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -590,14 +687,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "GMM is convenient as a flexible means of modeling an arbitrary multi-dimensional distribution of data." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### How many components?\n", "\n", @@ -613,7 +716,9 @@ "cell_type": "code", "execution_count": 17, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -640,7 +745,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The optimal number of clusters is the value that minimizes the AIC or BIC, depending on which approximation we wish to use. The AIC tells us that our choice of 16 components above was probably too many: around 8-12 components would have been a better choice.\n", "As is typical with this sort of problem, the BIC recommends a simpler model.\n", @@ -651,7 +759,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Example: GMM for Generating New Data\n", "\n", @@ -665,7 +776,9 @@ "cell_type": "code", "execution_count": 18, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -687,7 +800,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Next let's plot the first 100 of these to recall exactly what we're looking at:" ] @@ -696,7 +812,9 @@ "cell_type": "code", "execution_count": 19, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -723,7 +841,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We have nearly 1,800 digits in 64 dimensions, and we can build a GMM on top of these to generate more.\n", "GMMs can have difficulty converging in such a high dimensional space, so we will start with an invertible dimensionality reduction algorithm on the data.\n", @@ -734,7 +855,9 @@ "cell_type": "code", "execution_count": 20, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -757,7 +880,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The result is 41 dimensions, a reduction of nearly 1/3 with almost no information loss.\n", "Given this projected data, let's use the AIC to get a gauge for the number of GMM components we should use:" @@ -767,7 +893,9 @@ "cell_type": "code", "execution_count": 21, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -791,7 +919,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "It appears that around 110 components minimizes the AIC; we will use this model.\n", "Let's quickly fit this to the data and confirm that it has converged:" @@ -801,7 +932,9 @@ "cell_type": "code", "execution_count": 22, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -820,7 +953,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now we can draw samples of 100 new points within this 41-dimensional projected space, using the GMM as a generative model:" ] @@ -829,7 +965,9 @@ "cell_type": "code", "execution_count": 23, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -850,7 +988,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Finally, we can use the inverse transform of the PCA object to construct the new digits:" ] @@ -859,7 +1000,9 @@ "cell_type": "code", "execution_count": 24, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -880,7 +1023,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The results for the most part look like plausible digits from the dataset!\n", "\n", @@ -890,7 +1036,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [In Depth: k-Means Clustering](05.11-K-Means.ipynb) | [Contents](Index.ipynb) | [In-Depth: Kernel Density Estimation](05.13-Kernel-Density-Estimation.ipynb) >" diff --git a/notebooks/05.13-Kernel-Density-Estimation.ipynb b/notebooks/05.13-Kernel-Density-Estimation.ipynb index 1336ac0ec..d66a08f45 100644 --- a/notebooks/05.13-Kernel-Density-Estimation.ipynb +++ b/notebooks/05.13-Kernel-Density-Estimation.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,20 +16,30 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [In Depth: Gaussian Mixture Models](05.12-Gaussian-Mixtures.ipynb) | [Contents](Index.ipynb) | [Application: A Face Detection Pipeline](05.14-Image-Features.ipynb) >" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# In-Depth: Kernel Density Estimation" + ] + }, { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "source": [ - "# In-Depth: Kernel Density Estimation\n", - "\n", "In the previous section we covered Gaussian mixture models (GMM), which are a kind of hybrid between a clustering estimator and a density estimator.\n", "Recall that a density estimator is an algorithm which takes a $D$-dimensional dataset and produces an estimate of the $D$-dimensional probability distribution which that data is drawn from.\n", "The GMM algorithm accomplishes this by representing the density as a weighted sum of Gaussian distributions.\n", @@ -40,7 +53,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -52,7 +67,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Motivating KDE: Histograms\n", "\n", @@ -67,7 +85,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -82,7 +102,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We have previously seen that the standard count-based histogram can be created with the ``plt.hist()`` function.\n", "By specifying the ``normed`` parameter of the histogram, we end up with a normalized histogram where the height of the bins does not reflect counts, but instead reflects probability density:" @@ -92,7 +115,9 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -112,7 +137,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Notice that for equal binning, this normalization simply changes the scale on the y-axis, leaving the relative heights essentially the same as in a histogram built from counts.\n", "This normalization is chosen so that the total area under the histogram is equal to 1, as we can confirm by looking at the output of the histogram function:" @@ -122,7 +150,9 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -144,7 +174,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "One of the issues with using a histogram as a density estimator is that the choice of bin size and location can lead to representations that have qualitatively different features.\n", "For example, if we look at a version of this data with only 20 points, the choice of how to draw the bins can lead to an entirely different interpretation of the data!\n", @@ -155,7 +188,9 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -167,7 +202,9 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -195,7 +232,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "On the left, the histogram makes clear that this is a bimodal distribution.\n", "On the right, we see a unimodal distribution with a long tail.\n", @@ -210,7 +250,9 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -249,7 +291,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The problem with our two binnings stems from the fact that the height of the block stack often reflects not on the actual density of points nearby, but on coincidences of how the bins align with the data points.\n", "This mis-alignment between points and their blocks is a potential cause of the poor histogram results seen here.\n", @@ -262,7 +307,9 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -288,7 +335,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The result looks a bit messy, but is a much more robust reflection of the actual data characteristics than is the standard histogram.\n", "Still, the rough edges are not aesthetically pleasing, nor are they reflective of any true properties of the data.\n", @@ -300,7 +350,9 @@ "cell_type": "code", "execution_count": 9, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -327,7 +379,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "This smoothed-out plot, with a Gaussian distribution contributed at the location of each input point, gives a much more accurate idea of the shape of the data distribution, and one which has much less variance (i.e., changes much less in response to differences in sampling).\n", "\n", @@ -337,7 +392,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Kernel Density Estimation in Practice\n", "\n", @@ -356,7 +414,9 @@ "cell_type": "code", "execution_count": 10, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -397,14 +457,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The result here is normalized such that the area under the curve is equal to 1." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Selecting the bandwidth via cross-validation\n", "\n", @@ -422,7 +488,9 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -438,7 +506,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Now we can find the choice of bandwidth which maximizes the score (which in this case defaults to the log-likelihood):" ] @@ -447,7 +518,9 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -467,14 +540,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "The optimal bandwidth happens to be very close to what we used in the example plot earlier, where the bandwidth was 1.0 (i.e., the default width of ``scipy.stats.norm``)." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Example: KDE on a Sphere\n", "\n", @@ -491,7 +570,9 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -508,7 +589,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "With this data loaded, we can use the Basemap toolkit (mentioned previously in [Geographic Data with Basemap](04.13-Geographic-Data-With-Basemap.ipynb)) to plot the observed locations of these two species on the map of South America." ] @@ -517,7 +601,9 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -553,7 +639,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Unfortunately, this doesn't give a very good idea of the density of the species, because points in the species range may overlap one another.\n", "You may not realize it by looking at this plot, but there are over 1,600 points shown here!\n", @@ -568,7 +657,9 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -623,14 +714,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Compared to the simple scatter plot we initially used, this visualization paints a much clearer picture of the geographical distribution of observations of these two species." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Example: Not-So-Naive Bayes\n", "\n", @@ -662,7 +759,9 @@ "cell_type": "code", "execution_count": 16, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -705,14 +804,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### The anatomy of a custom estimator" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Let's step through this code and discuss the essential features:\n", "\n", @@ -738,7 +843,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Next comes the class initialization method:\n", "\n", @@ -756,7 +864,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Next comes the ``fit()`` method, where we handle training data:\n", "\n", @@ -783,7 +894,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Finally, we have the logic for predicting labels on new data:\n", "```python\n", @@ -804,7 +918,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Using our custom estimator\n", "\n", @@ -816,7 +933,9 @@ "cell_type": "code", "execution_count": 17, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -834,7 +953,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Next we can plot the cross-validation score as a function of bandwidth:" ] @@ -843,7 +965,9 @@ "cell_type": "code", "execution_count": 18, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -876,7 +1000,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "We see that this not-so-naive Bayesian classifier reaches a cross-validation accuracy of just over 96%; this is compared to around 80% for the naive Bayesian classification:" ] @@ -885,7 +1012,9 @@ "cell_type": "code", "execution_count": 19, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -907,7 +1036,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "One benefit of such a generative classifier is interpretability of results: for each unknown sample, we not only get a probabilistic classification, but a *full model* of the distribution of points we are comparing it to!\n", "If desired, this offers an intuitive window into the reasons for a particular classification that algorithms like SVMs and random forests tend to obscure.\n", @@ -922,7 +1054,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [In Depth: Gaussian Mixture Models](05.12-Gaussian-Mixtures.ipynb) | [Contents](Index.ipynb) | [Application: A Face Detection Pipeline](05.14-Image-Features.ipynb) >" diff --git a/notebooks/05.15-Learning-More.ipynb b/notebooks/05.15-Learning-More.ipynb index 9bac85ebc..0d8716c27 100644 --- a/notebooks/05.15-Learning-More.ipynb +++ b/notebooks/05.15-Learning-More.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Application: A Face Detection Pipeline](05.14-Image-Features.ipynb) | [Contents](Index.ipynb) | [Appendix: Figure Code](06.00-Figure-Code.ipynb) >" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Further Machine Learning Resources\n", - "\n", + "# Further Machine Learning Resources" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "This chapter has been a quick tour of machine learning in Python, primarily using the tools within the Scikit-Learn library.\n", "As long as the chapter is, it is still too short to cover many interesting and important algorithms, approaches, and discussions.\n", "Here I want to suggest some resources to learn more about machine learning for those who are interested." @@ -32,7 +46,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Machine Learning in Python\n", "\n", @@ -49,7 +66,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## General Machine Learning\n", "\n", @@ -67,7 +87,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Application: A Face Detection Pipeline](05.14-Image-Features.ipynb) | [Contents](Index.ipynb) | [Appendix: Figure Code](06.00-Figure-Code.ipynb) >" diff --git a/notebooks/06.00-Figure-Code.ipynb b/notebooks/06.00-Figure-Code.ipynb index cfefaf4f5..edacd85a9 100644 --- a/notebooks/06.00-Figure-Code.ipynb +++ b/notebooks/06.00-Figure-Code.ipynb @@ -2,7 +2,10 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "\n", @@ -13,7 +16,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Further Machine Learning Resources](05.15-Learning-More.ipynb) | [Contents](Index.ipynb) |" @@ -23,8 +29,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Appendix: Figure Code\n", - "\n", + "# Appendix: Figure Code" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true + }, + "source": [ "Many of the figures used throughout this text are created in-place by code that appears in print.\n", "In a few cases, however, the required code is long enough (or not immediately relevant enough) that we instead put it here for reference." ] @@ -33,7 +47,9 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -47,7 +63,9 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -58,7 +76,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Broadcasting\n", "\n", @@ -69,7 +90,9 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -275,7 +298,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Aggregation and Grouping\n", "\n", @@ -284,7 +310,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Split-Apply-Combine" ] @@ -293,7 +322,9 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -418,7 +449,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## What Is Machine Learning?" ] @@ -427,7 +461,9 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -442,7 +478,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Classification Example Figures\n", "\n", @@ -455,7 +494,9 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -481,7 +522,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Classification Example Figure 1" ] @@ -490,7 +534,9 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -519,7 +565,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Classification Example Figure 2" ] @@ -528,7 +577,9 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -567,7 +618,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Classification Example Figure 3" ] @@ -576,7 +630,9 @@ "cell_type": "code", "execution_count": 9, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -610,7 +666,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Regression Example Figures\n", "\n", @@ -623,7 +682,9 @@ "cell_type": "code", "execution_count": 10, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -648,7 +709,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Regression Example Figure 1" ] @@ -657,7 +721,9 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -686,7 +752,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Regression Example Figure 2" ] @@ -695,7 +764,9 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -750,7 +821,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Regression Example Figure 3" ] @@ -759,7 +833,9 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -799,7 +875,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Regression Example Figure 4" ] @@ -808,7 +887,9 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -843,7 +924,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Clustering Example Figures\n", "\n", @@ -856,7 +940,9 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -874,7 +960,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Clustering Example Figure 1" ] @@ -883,7 +972,9 @@ "cell_type": "code", "execution_count": 16, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -910,7 +1001,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Clustering Example Figure 2" ] @@ -919,7 +1013,9 @@ "cell_type": "code", "execution_count": 17, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -946,7 +1042,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Dimensionality Reduction Example Figures\n", "\n", @@ -957,7 +1056,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Dimensionality Reduction Example Figure 1" ] @@ -966,7 +1068,9 @@ "cell_type": "code", "execution_count": 18, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -999,7 +1103,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Dimensionality Reduction Example Figure 2" ] @@ -1008,7 +1115,9 @@ "cell_type": "code", "execution_count": 19, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1043,14 +1152,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Introducing Scikit-Learn" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Features and Labels Grid\n", "\n", @@ -1061,7 +1176,9 @@ "cell_type": "code", "execution_count": 20, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1104,14 +1221,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Hyperparameters and Model Validation" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Cross-Validation Figures" ] @@ -1120,7 +1243,9 @@ "cell_type": "code", "execution_count": 21, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -1138,7 +1263,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### 2-Fold Cross-Validation" ] @@ -1147,7 +1275,9 @@ "cell_type": "code", "execution_count": 22, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1172,7 +1302,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### 5-Fold Cross-Validation" ] @@ -1181,7 +1314,9 @@ "cell_type": "code", "execution_count": 23, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1206,7 +1341,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Overfitting and Underfitting" ] @@ -1215,7 +1353,9 @@ "cell_type": "code", "execution_count": 24, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -1235,7 +1375,9 @@ "cell_type": "code", "execution_count": 25, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -1250,7 +1392,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Bias-Variance Tradeoff" ] @@ -1259,7 +1404,9 @@ "cell_type": "code", "execution_count": 26, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1297,7 +1444,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Bias-Variance Tradeoff Metrics" ] @@ -1306,7 +1456,9 @@ "cell_type": "code", "execution_count": 27, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1351,7 +1503,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Validation Curve" ] @@ -1360,7 +1515,9 @@ "cell_type": "code", "execution_count": 28, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1406,7 +1563,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "#### Learning Curve" ] @@ -1415,7 +1575,9 @@ "cell_type": "code", "execution_count": 29, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1460,7 +1622,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Gaussian Naive Bayes\n", "\n", @@ -1473,7 +1638,9 @@ "cell_type": "code", "execution_count": 30, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1522,7 +1689,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Linear Regression\n", "\n", @@ -1535,7 +1705,9 @@ "cell_type": "code", "execution_count": 31, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1610,7 +1782,9 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "source": [ "## Random Forests" @@ -1618,7 +1792,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Helper Code\n", "\n", @@ -1629,7 +1806,9 @@ "cell_type": "code", "execution_count": 32, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1729,7 +1908,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Decision Tree Example" ] @@ -1738,7 +1920,9 @@ "cell_type": "code", "execution_count": 33, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1793,7 +1977,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Decision Tree Levels" ] @@ -1802,7 +1989,9 @@ "cell_type": "code", "execution_count": 34, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1838,7 +2027,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Decision Tree Overfitting" ] @@ -1847,7 +2039,9 @@ "cell_type": "code", "execution_count": 35, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1874,14 +2068,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Principal Component Analysis" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Principal Components Rotation" ] @@ -1890,7 +2090,9 @@ "cell_type": "code", "execution_count": 36, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -1901,7 +2103,9 @@ "cell_type": "code", "execution_count": 37, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -1917,7 +2121,9 @@ "cell_type": "code", "execution_count": 38, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1963,7 +2169,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Digits Pixel Components" ] @@ -1972,7 +2181,9 @@ "cell_type": "code", "execution_count": 39, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -2024,7 +2235,9 @@ "cell_type": "code", "execution_count": 40, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2052,7 +2265,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Digits PCA Components" ] @@ -2061,7 +2277,9 @@ "cell_type": "code", "execution_count": 41, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2087,14 +2305,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Manifold Learning" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### LLE vs MDS Linkages" ] @@ -2103,7 +2327,9 @@ "cell_type": "code", "execution_count": 42, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -2133,7 +2359,9 @@ "cell_type": "code", "execution_count": 43, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -2153,7 +2381,9 @@ "cell_type": "code", "execution_count": 44, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2199,7 +2429,9 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "source": [ "## K-Means" @@ -2208,7 +2440,9 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "source": [ "### Expectation-Maximization\n", @@ -2222,7 +2456,9 @@ "cell_type": "code", "execution_count": 45, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2310,7 +2546,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Interactive K-Means\n", "\n", @@ -2322,7 +2561,9 @@ "cell_type": "code", "execution_count": 46, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2409,14 +2650,20 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Gaussian Mixture Models" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "### Covariance Type\n", "\n", @@ -2427,7 +2674,9 @@ "cell_type": "code", "execution_count": 47, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -2486,7 +2735,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "\n", "< [Further Machine Learning Resources](05.15-Learning-More.ipynb) | [Contents](Index.ipynb) |" From 60623973bd3a1a579d3ac573e4a63496411d01a8 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Mon, 14 Aug 2017 13:27:19 -0700 Subject: [PATCH 09/40] MAINT: fix title in index --- notebooks/Index.ipynb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/notebooks/Index.ipynb b/notebooks/Index.ipynb index 32d26c71d..a368faa84 100644 --- a/notebooks/Index.ipynb +++ b/notebooks/Index.ipynb @@ -4,8 +4,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Python Data Science Handbook\n", - "\n", + "# Python Data Science Handbook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "*Jake VanderPlas*\n", "\n", "![Book Cover](figures/PDSH-cover.png)" From ca600e3a4faa4e04791f1e5b55744f65f6a75101 Mon Sep 17 00:00:00 2001 From: Jake Vanderplas Date: Mon, 14 Aug 2017 14:54:22 -0700 Subject: [PATCH 10/40] Add website link on README --- README.md | 83 ++----------------------------------------------------- 1 file changed, 3 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 12bd69048..ff0dbd967 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ This repository contains the entire [Python Data Science Handbook](http://shop.oreilly.com/product/0636920034919.do), in the form of (free!) Jupyter notebooks. +You can read the book in its entirety online at https://jakevdp.github.io/PythonDataScienceHandbook/ + ![cover image](notebooks/figures/PDSH-cover.png) The book was written and tested with Python 3.5, though older Python versions (including Python 2.7) should work in nearly all cases. @@ -10,86 +12,7 @@ The book introduces the core libraries essential for working with data in Python Familiarity with Python as a language is assumed; if you need a quick introduction to the language itself, see the free companion project, [A Whirlwind Tour of Python](https://github.com/jakevdp/WhirlwindTourOfPython): it's a fast-paced introduction to the Python language aimed at researchers and scientists. -The following listing links to the notebooks in this repository, rendered through the [nbviewer](http://nbviewer.jupyter.org) service: - ---- -## [Table of Contents](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/Index.ipynb) - -### [Preface](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/00.00-Preface.ipynb) - -### [1. IPython: Beyond Normal Python](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/01.00-IPython-Beyond-Normal-Python.ipynb) -- [Help and Documentation in IPython](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/01.01-Help-And-Documentation.ipynb) -- [Keyboard Shortcuts in the IPython Shell](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/01.02-Shell-Keyboard-Shortcuts.ipynb) -- [IPython Magic Commands](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/01.03-Magic-Commands.ipynb) -- [Input and Output History](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/01.04-Input-Output-History.ipynb) -- [IPython and Shell Commands](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/01.05-IPython-And-Shell-Commands.ipynb) -- [Errors and Debugging](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/01.06-Errors-and-Debugging.ipynb) -- [Profiling and Timing Code](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/01.07-Timing-and-Profiling.ipynb) -- [More IPython Resources](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/01.08-More-IPython-Resources.ipynb) - -### [2. Introduction to NumPy](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/02.00-Introduction-to-NumPy.ipynb) -- [Understanding Data Types in Python](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/02.01-Understanding-Data-Types.ipynb) -- [The Basics of NumPy Arrays](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/02.02-The-Basics-Of-NumPy-Arrays.ipynb) -- [Computation on NumPy Arrays: Universal Functions](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/02.03-Computation-on-arrays-ufuncs.ipynb) -- [Aggregations: Min, Max, and Everything In Between](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/02.04-Computation-on-arrays-aggregates.ipynb) -- [Computation on Arrays: Broadcasting](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/02.05-Computation-on-arrays-broadcasting.ipynb) -- [Comparisons, Masks, and Boolean Logic](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/02.06-Boolean-Arrays-and-Masks.ipynb) -- [Fancy Indexing](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/02.07-Fancy-Indexing.ipynb) -- [Sorting Arrays](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/02.08-Sorting.ipynb) -- [Structured Data: NumPy's Structured Arrays](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/02.09-Structured-Data-NumPy.ipynb) - -### [3. Data Manipulation with Pandas](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.00-Introduction-to-Pandas.ipynb) -- [Introducing Pandas Objects](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.01-Introducing-Pandas-Objects.ipynb) -- [Data Indexing and Selection](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.02-Data-Indexing-and-Selection.ipynb) -- [Operating on Data in Pandas](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.03-Operations-in-Pandas.ipynb) -- [Handling Missing Data](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.04-Missing-Values.ipynb) -- [Hierarchical Indexing](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.05-Hierarchical-Indexing.ipynb) -- [Combining Datasets: Concat and Append](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.06-Concat-And-Append.ipynb) -- [Combining Datasets: Merge and Join](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.07-Merge-and-Join.ipynb) -- [Aggregation and Grouping](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.08-Aggregation-and-Grouping.ipynb) -- [Pivot Tables](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.09-Pivot-Tables.ipynb) -- [Vectorized String Operations](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.10-Working-With-Strings.ipynb) -- [Working with Time Series](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.11-Working-with-Time-Series.ipynb) -- [High-Performance Pandas: eval() and query()](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.12-Performance-Eval-and-Query.ipynb) -- [Further Resources](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.13-Further-Resources.ipynb) - -### [4. Visualization with Matplotlib](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.00-Introduction-To-Matplotlib.ipynb) -- [Simple Line Plots](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.01-Simple-Line-Plots.ipynb) -- [Simple Scatter Plots](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.02-Simple-Scatter-Plots.ipynb) -- [Visualizing Errors](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.03-Errorbars.ipynb) -- [Density and Contour Plots](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.04-Density-and-Contour-Plots.ipynb) -- [Histograms, Binnings, and Density](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.05-Histograms-and-Binnings.ipynb) -- [Customizing Plot Legends](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.06-Customizing-Legends.ipynb) -- [Customizing Colorbars](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.07-Customizing-Colorbars.ipynb) -- [Multiple Subplots](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.08-Multiple-Subplots.ipynb) -- [Text and Annotation](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.09-Text-and-Annotation.ipynb) -- [Customizing Ticks](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.10-Customizing-Ticks.ipynb) -- [Customizing Matplotlib: Configurations and Stylesheets](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.11-Settings-and-Stylesheets.ipynb) -- [Three-Dimensional Plotting in Matplotlib](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.12-Three-Dimensional-Plotting.ipynb) -- [Geographic Data with Basemap](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.13-Geographic-Data-With-Basemap.ipynb) -- [Visualization with Seaborn](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.14-Visualization-With-Seaborn.ipynb) -- [Further Resources](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.15-Further-Resources.ipynb) - -### [5. Machine Learning](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.00-Machine-Learning.ipynb) -- [What Is Machine Learning?](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.01-What-Is-Machine-Learning.ipynb) -- [Introducing Scikit-Learn](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.02-Introducing-Scikit-Learn.ipynb) -- [Hyperparameters and Model Validation](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.03-Hyperparameters-and-Model-Validation.ipynb) -- [Feature Engineering](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.04-Feature-Engineering.ipynb) -- [In-Depth: Naive Bayes Classification](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.05-Naive-Bayes.ipynb) -- [In-Depth: Linear Regression](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.06-Linear-Regression.ipynb) -- [In-Depth: Support Vector Machines](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.07-Support-Vector-Machines.ipynb) -- [In-Depth: Decision Trees and Random Forests](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.08-Random-Forests.ipynb) -- [In-Depth: Principal Component Analysis](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.09-Principal-Component-Analysis.ipynb) -- [In-Depth: Manifold Learning](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.10-Manifold-Learning.ipynb) -- [In-Depth: k-Means Clustering](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.11-K-Means.ipynb) -- [In-Depth: Gaussian Mixture Models](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.12-Gaussian-Mixtures.ipynb) -- [In-Depth: Kernel Density Estimation](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.13-Kernel-Density-Estimation.ipynb) -- [Application: A Face Detection Pipeline](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.14-Image-Features.ipynb) -- [Further Machine Learning Resources](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.15-Learning-More.ipynb) - -### [Appendix: Figure Code](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/06.00-Figure-Code.ipynb) - ---- +See [Index.ipynb](http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/Index.ipynb) for an index of the notebooks available to accompany the text. ## Required Packages From 48e4c48b7c6c85ea3be111f9e05eb9a45e3a236e Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Tue, 15 Aug 2017 09:13:22 -0700 Subject: [PATCH 11/40] BUG: fix link in hierarchical indexing --- notebooks/03.05-Hierarchical-Indexing.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/03.05-Hierarchical-Indexing.ipynb b/notebooks/03.05-Hierarchical-Indexing.ipynb index 0e76d8d5a..fcd751046 100644 --- a/notebooks/03.05-Hierarchical-Indexing.ipynb +++ b/notebooks/03.05-Hierarchical-Indexing.ipynb @@ -1347,7 +1347,7 @@ "editable": true }, "source": [ - "Partial slicing is available as well, as long as the ``MultiIndex`` is sorted (see discussion in [Sorted and Unsorted Indices](Sorted-and-Unsorted-Indices)):" + "Partial slicing is available as well, as long as the ``MultiIndex`` is sorted (see discussion in [Sorted and Unsorted Indices](#Sorted-and-Unsorted-Indices)):" ] }, { From d2d828b109d1a17eaa6cf5bf63efcc016e30f02a Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Mon, 14 Aug 2017 13:04:58 -0700 Subject: [PATCH 12/40] add website framework --- website/.gitmodules | 6 + website/Makefile | 132 ++++++++ website/content/images/PythonDataScience.jpg | Bin 0 -> 137085 bytes .../content/images/StatisticsDataMining.jpg | Bin 0 -> 95887 bytes website/content/images/WhirlwindTour.jpg | Bin 0 -> 6396 bytes website/content/images/jake.jpg | Bin 0 -> 5103 bytes website/copy_notebooks.py | 63 ++++ website/fabfile.py | 92 +++++ website/pelicanconf.py | 68 ++++ website/publishconf.py | 24 ++ website/theme/README.md | 4 + website/theme/static/css/icons.css | 60 ++++ website/theme/static/font/icons.eot | Bin 0 -> 5584 bytes website/theme/static/font/icons.svg | 17 + website/theme/static/font/icons.ttf | Bin 0 -> 5428 bytes website/theme/static/font/icons.woff | Bin 0 -> 3652 bytes .../theme/templates/_includes/analytics.html | 30 ++ .../templates/_includes/disqus_thread.html | 17 + website/theme/templates/about.html | 40 +++ website/theme/templates/archives.html | 28 ++ website/theme/templates/article.html | 43 +++ website/theme/templates/base.html | 94 ++++++ website/theme/templates/index.html | 53 +++ website/theme/templates/ipynb.css | 47 +++ website/theme/templates/main.css | 300 +++++++++++++++++ website/theme/templates/main.less | 316 ++++++++++++++++++ website/theme/templates/page.html | 22 ++ website/theme/templates/pygments.css | 61 ++++ website/theme/templates/tag.html | 22 ++ 29 files changed, 1539 insertions(+) create mode 100644 website/.gitmodules create mode 100644 website/Makefile create mode 100644 website/content/images/PythonDataScience.jpg create mode 100644 website/content/images/StatisticsDataMining.jpg create mode 100644 website/content/images/WhirlwindTour.jpg create mode 100644 website/content/images/jake.jpg create mode 100644 website/copy_notebooks.py create mode 100644 website/fabfile.py create mode 100644 website/pelicanconf.py create mode 100644 website/publishconf.py create mode 100644 website/theme/README.md create mode 100644 website/theme/static/css/icons.css create mode 100644 website/theme/static/font/icons.eot create mode 100644 website/theme/static/font/icons.svg create mode 100644 website/theme/static/font/icons.ttf create mode 100644 website/theme/static/font/icons.woff create mode 100644 website/theme/templates/_includes/analytics.html create mode 100644 website/theme/templates/_includes/disqus_thread.html create mode 100644 website/theme/templates/about.html create mode 100644 website/theme/templates/archives.html create mode 100644 website/theme/templates/article.html create mode 100644 website/theme/templates/base.html create mode 100644 website/theme/templates/index.html create mode 100644 website/theme/templates/ipynb.css create mode 100644 website/theme/templates/main.css create mode 100644 website/theme/templates/main.less create mode 100644 website/theme/templates/page.html create mode 100644 website/theme/templates/pygments.css create mode 100644 website/theme/templates/tag.html diff --git a/website/.gitmodules b/website/.gitmodules new file mode 100644 index 000000000..eeca5dc3c --- /dev/null +++ b/website/.gitmodules @@ -0,0 +1,6 @@ +[submodule "plugins/ipynb"] + path = plugins/ipynb + url = git://github.com/danielfrg/pelican-ipynb.git +[submodule "plugins/pelican-plugins"] + path = plugins/pelican-plugins + url = git://github.com/getpelican/pelican-plugins.git diff --git a/website/Makefile b/website/Makefile new file mode 100644 index 000000000..8a605aa7e --- /dev/null +++ b/website/Makefile @@ -0,0 +1,132 @@ +PY?=python3 +PELICAN?=pelican +PELICANOPTS= + +BASEDIR=$(CURDIR) +INPUTDIR=$(BASEDIR)/content +OUTPUTDIR=$(BASEDIR)/output +CONFFILE=$(BASEDIR)/pelicanconf.py +PUBLISHCONF=$(BASEDIR)/publishconf.py + +FTP_HOST=localhost +FTP_USER=anonymous +FTP_TARGET_DIR=/ + +SSH_HOST=localhost +SSH_PORT=22 +SSH_USER=root +SSH_TARGET_DIR=/var/www + +S3_BUCKET=my_s3_bucket + +CLOUDFILES_USERNAME=my_rackspace_username +CLOUDFILES_API_KEY=my_rackspace_api_key +CLOUDFILES_CONTAINER=my_cloudfiles_container + +DROPBOX_DIR=~/Dropbox/Public/ + +GITHUB_PAGES_REMOTE=git@github.com:jakevdp/jakevdp.github.io.git +GITHUB_PAGES_BRANCH=master + +GIT_COMMIT_HASH = $(shell git rev-parse HEAD) + +DEBUG ?= 0 +ifeq ($(DEBUG), 1) + PELICANOPTS += -D +endif + +RELATIVE ?= 0 +ifeq ($(RELATIVE), 1) + PELICANOPTS += --relative-urls +endif + + +help: + @echo 'Makefile for a pelican Web site ' + @echo ' ' + @echo 'Usage: ' + @echo ' make html (re)generate the web site ' + @echo ' make clean remove the generated files ' + @echo ' make regenerate regenerate files upon modification ' + @echo ' make publish generate using production settings ' + @echo ' make serve [PORT=8000] serve site at http://localhost:8000' + @echo ' make serve-global [SERVER=0.0.0.0] serve (as root) to $(SERVER):80 ' + @echo ' make devserver [PORT=8000] start/restart develop_server.sh ' + @echo ' make stopserver stop local server ' + @echo ' make ssh_upload upload the web site via SSH ' + @echo ' make rsync_upload upload the web site via rsync+ssh ' + @echo ' make dropbox_upload upload the web site via Dropbox ' + @echo ' make ftp_upload upload the web site via FTP ' + @echo ' make s3_upload upload the web site via S3 ' + @echo ' make cf_upload upload the web site via Cloud Files' + @echo ' make github upload the web site via gh-pages ' + @echo ' ' + @echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html ' + @echo 'Set the RELATIVE variable to 1 to enable relative urls ' + @echo ' ' + +html: + $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) + +clean: + [ ! -d $(OUTPUTDIR) ] || rm -rf $(OUTPUTDIR) + +regenerate: + $(PELICAN) -r $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) + +serve: +ifdef PORT + cd $(OUTPUTDIR) && $(PY) -m pelican.server $(PORT) +else + cd $(OUTPUTDIR) && $(PY) -m pelican.server +endif + +serve-global: +ifdef SERVER + cd $(OUTPUTDIR) && $(PY) -m pelican.server 80 $(SERVER) +else + cd $(OUTPUTDIR) && $(PY) -m pelican.server 80 0.0.0.0 +endif + + +devserver: +ifdef PORT + $(BASEDIR)/develop_server.sh restart $(PORT) +else + $(BASEDIR)/develop_server.sh restart +endif + +stopserver: + $(BASEDIR)/develop_server.sh stop + @echo 'Stopped Pelican and SimpleHTTPServer processes running in background.' + +publish: + $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS) + +ssh_upload: publish + scp -P $(SSH_PORT) -r $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) + +rsync_upload: publish + rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --delete $(OUTPUTDIR)/ $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) --cvs-exclude + +dropbox_upload: publish + cp -r $(OUTPUTDIR)/* $(DROPBOX_DIR) + +ftp_upload: publish + lftp ftp://$(FTP_USER)@$(FTP_HOST) -e "mirror -R $(OUTPUTDIR) $(FTP_TARGET_DIR) ; quit" + +s3_upload: publish + s3cmd sync $(OUTPUTDIR)/ s3://$(S3_BUCKET) --acl-public --delete-removed --guess-mime-type --no-mime-magic --no-preserve + +cf_upload: publish + cd $(OUTPUTDIR) && swift -v -A https://auth.api.rackspacecloud.com/v1.0 -U $(CLOUDFILES_USERNAME) -K $(CLOUDFILES_API_KEY) upload -c $(CLOUDFILES_CONTAINER) . + +publish-to-github: publish + ghp-import -n -m "publish-to-github from $(GIT_COMMIT_HASH)" -b blog-build $(OUTPUTDIR) + git push $(GITHUB_PAGES_REMOTE) blog-build:$(GITHUB_PAGES_BRANCH) + +publish-to-github-force: publish + ghp-import -n -m "publish-to-github-force from $(GIT_COMMIT_HASH)" -b blog-build $(OUTPUTDIR) + git push -f $(GITHUB_PAGES_REMOTE) blog-build:$(GITHUB_PAGES_BRANCH) + +.PHONY: html help clean regenerate serve serve-global devserver stopserver publish ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload cf_upload github diff --git a/website/content/images/PythonDataScience.jpg b/website/content/images/PythonDataScience.jpg new file mode 100644 index 0000000000000000000000000000000000000000..59ffc8d606990d9f7e6ea2028d91b7d3af06817d GIT binary patch literal 137085 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

$R21I=@s~!_K2FsWjYtK# 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 z%0DS@`_ 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 arHfH37lbAlE*WeF`H92pG4591C;kKZ_p7x4 literal 0 HcmV?d00001 diff --git a/website/content/images/StatisticsDataMining.jpg b/website/content/images/StatisticsDataMining.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ab1172912860a696efd3a049e3e990e7fa66fba7 GIT binary patch literal 95887 zcmb5WcRXBe*EYN{5rjmEnl@qd6484Qh8Zn-jS{^@?<7do8H`|XDTBf2y#~>PFiO5+WLan1+aihUo7n;1&Q7lMr2s`oD{a zm;`c#^eXtK4mChbL_!3)dIfTogaSfJbOpG4lZN)n{f8cOtU?OfR$-(G*%j?K6?Ht> zgr9|1j{FeuLeaxStzYF-bu8{*eXQ%vz^<1&y5z&5WRuu=xgogM<%a*C9slo!3ZP`f zM8pu1E2NMs5MofW%MEED4{7fUDO{n`X0`GNtGFqgklp@+O+;}-RL661pZ@W)aF{N; z5-O)d`R_D93H}W+4G9hK2spjE_O2NKphP49Mc4wML@fe*A%OrSN<=wi*3eKG5kO7y zk(du4LV;%|f|4*n$>k$#j5HYgV8&di2DEa5da8E}?S0gzXj&mvCO{RU$^-?Nq2vJ2 z2dJ`GhI=GjF%gIkOIIR5Xw~W4ZD0!Us9dPc$TQ1)QUn0dlByER)5`b30CFJmtB8^( z0+tA&Bw`?j#lvXj!yUr^K>$b?IZ-@02N~=G34{m=u=c@}LIDS2RcbXLj1~Z3l=0-z z@!;lHKghWp%L9NaJrjwt5ljI901+}K0H7vkC1ZwwKaD^riTNsFmxs;y?>Oc(;!NQ>AB=VH;BJBnURhn=o`O<4_ ztb*`stnKh9YgWo>@OX(z1X%84P&!K@@aU?{(C82B)Ry%Td}P!b#6+mjG#If`xGFU> zELVy?UYQHX9JaYpa~~oW4oFr=YI4NfRIrO zl17FrQ40dp03`s}*o4KCULH?~j2Td`ByNX=lEA>d!7BhH=SdXQP|P8UH9}}(oVE5T z11kbb&J48-3l-8Pktc!ym%j=S%YP)6|3ab^-`5q6tw>VABB*{C6acP-ssfNu;Lv_3Gd>g`B?CwwLlSgILjid}gsA7F^*6TW&3G5NLopIv zybT@VO>!+k=9w81 zM?UPDCDx-{YP^AHDu(d5I(MBa}xTRCIiHMN1n-KdtFlklZUhiL_^KYd@`nD#on z`owOLz3F4ktCk_&uuPsj>cHKkX5CYV&bhe#Lw!5RCbn$m`^)6<%(laq2QH<7vJcM# z8=^&>x`X&j?62NSxKS?cvE#8Ux6R`5Ncc{e%@93*T|H%7ka3%880QS_77IO zs~a<~XP1VkFW53~kwY^$!BSG0V9Uebwr;PC8B9r?oC%yfEGlv}Nw$@6#ERQGUsVjy zGSN0dZ@}d0XVM=WbSdFCW(@S@+HGbt z4EYBg=VBhweZf>G5ES>S${cH=Tf5cbVtU?h>|9Gvn=GH2IHj$P;kEVg^P=!xHPO@= zp>Q9=SYwbHiE(+$B+1U9NT1QNPJ@39C9!JandrmeMOz!_V<-d{oZobg-!Nj3{@@PvQXmuhPPjo$Sc>9gXDY>FVHZ}rY|Yk0E6w8=!I zW)gc#zf|VQ*-&bW=*_WJox~*1Z*<3hC`u&}OGfQz^N~5(bg9v#`#x^fR#rM#31vc2 zt``~7Bf1jhftLN*UioFTCYan#=pC&&rcx!H(>!RM6kle(TS_>iZ`sCk^WLkG6+7aU z)G!=5UHV4abQWE`pUV?!gW*ULqAkjWce17?-^bgE~|bE(D%8E3jyg(IOXn z{Q*6$K5ta(&eOBh>w}^RsD&raF7`iyP{|3Xfc~J2Dfi&ijyfp|n7>*~Q}Bu1P<^gG za(AmuMQn4l%VJ`QiSQEtb7S|9yWZj*@sFOjLCgY#K~+#p^l zk>c^X>H^IemdM=vqsC-2f~%yxXZMOumuA*mx_5MrVl&4_90I;}NTZ$&Z7pk7O`(5=qP8UdO$R5VN*rNX>x|wT}{b|P8w>8|%v!Yw5LpA6HehwR}zg;wo zT$@g7NNypkC*TZdJiee)8d`qtvFK||F2A~V18u$;7-jbTouN!=mtXRpi}Tb`U6yOM zpVu`+2eyVq)Un?qF?wke+u-;u`7fYmP&j5N$K)OOJozgFx)!lbENHY7g{$_T4H?21Q@mL@%}AzjaEmiVX~TxFw8z^&5AZb!GA4 zEMhnnEKqig9Jvaob>?=P|8^VmJ93#~%frZ3=7J-+uK^+VS+-!B-k~Fg*Pdy1-b}Y9 zSF*><(PcE?j8CBpIC18+1{ersi)H;ct7cZpmm+tqOnay{I&O; zW+v_ki9U|i+*Fd9>3LIA86!FH59>Ars{Lg&(7~q=+(a7ah%qxd&32vm@fLX z0e2J79{84daWJT@q2I5MleCwa-qrUQ@L)%Q(HkZt7S}Sz8Yx~nBI&nyMcG!*gtavb66HUm#^CHI_2?f{GOlGi( z!Xb`5GNz{~DT(@`eG_>rnE^GkgC6~tqUE|xHfL+un-bbmG`xatIZeK_tDBTS(zE}r zW2BE(3E$<-k1>$uSnN~be%Zj_q4LJwS9AX4gy3LeV)nH7H{*T-f!lO6+x!jkiSF>m z4~&Wb{CoGJX?7c{kIS(;1z8a)UCdl{m~>h6jQ$<`N|}>)a;?wcG0XUED~-QFJ5+}ObG0A&7}n1~Y~mOzRgLBGih7Oyu!`mH9a??L z*yrw{d}ubs%g-DV`xoGvZA>n9bxJI74e@g3x%aFp+PNb}Lwc2|;Px+f(eXxJ?_{tJlx1vrrFIU_eFi$8RlmpE!hpTaOGQ?+TW^Y^p~QOs0msjT4VuqSF)v-j zFgq^J6FK|tV_Y*ndRfyvgSrI`yZ)Y9H$HBRPL$MeyEgq~lWb;Vth(jYTq59PE|+#_ zuh*9CmH+S^Lh8CsEZa@gXAyUJS|qeI@*URzRd9c|`LJA%P}h#r#7$pp%UOK`p2CmU zo=5CU6$UopI+ixhkm#(N!jwL|y|7+psiwR@>FNqtFf4Z!kb-M%|sSLm>-Sy z;9K=gT+n!-UkAU`QSrbLG!M#%$Nd%@j7)j(wjosH)55RxBCw()30J zCZ=3XL@sNL`V_iXXNkDm6FL(JwSRaxdVS7`NiQ;}N@~ zon+z)%UlvDy)IbEOJSCFUmTq}_!sCLb`&TrDGsT1GEBn3*XTub-yvKLs0f)Kx>US# zC4QV0P8t`y=F_~ExO&J3BR=J&hyG?l zU%U<1r;@jOQhBtl%{#{gHC>yL4m?eMfcJf;0T(Vq#z;+^K6zWeNQl;bjY4&rW>UCM zx_TyAcPwwt(G7~E_k|OtZ`IWCnv9oVEw`GGdW}44xn(D+hEWr_xOIAl({H`gdWI*q zuonDZK<4E$2{1mM`~|8F-Deok=wojaI37%zQc^mbJd^qb1$sHN9u&^6p5}1n)D?K4 z)`#!i=lEIf!&kei7Bq-;erYVF=ZMET8fd?MXcevZPM>cBV=^!hh&E2b>6X&sz1(gr z3BNsXI}uvjs=)}j+02>ZG}mrcE8erOOCRKs)vJ1MwYs{xFFe*wlSRo0Q}>Fls<%`n zR=TXv5Q3zLColj2Ea?&e$g8khe)IeLGX9%3r~cBj1N2wc2GMEPJeyrYZU-|qXL006 zeDHSvVSW%?^p5VTqMY+sF+m|#Owr)t#=I>*XD(;->whM1dTHWRyp2UZ*{FVm0KL$| zzrfG1i^$fC^$Um0l-Dn%EqadZ#Ls8gUAIS*HVhh@8k^6gV@z%qH$KeOEEU%ykg4dU z`1;eiy|>b&H^}bltHD$&7H69F^_Ncx)wfOmc5`!m=;6kI=ON@{(;eKRfg3C_ z(VKl(-k_%6`coSt-_*8Wzd*`mbXMbTVRV!%YSfeMhYsens%Tgsk`1d&maCZE-mmExL$m&b58M~Ds9}ambZf~i<+&2=)uNdTHU-=MYm+7<%_W$x1sT; zD*l6-k2rT;m|ZAKSuqEd2mS@DAC1)BJCk<%>98NH^=she*hPWz(`yUX(*|OGlRgeL z9*V#FlbLy&0*>hQc^WZ#Hd)3TnR;lt7xv2H7Wp~Ct6UnbT4}4_Hy%gZ^SO+4w6@d- zNk2C+n>bT1xrtL;?S7bSgrsoP;GfRL2!AOpLYHaycq}ZhP3E2kMPt%mFa26LN@tsz z_HJa^T6WOXj<(Ig9|tCxTeY`0i?)TWJJnRCC*5XiTJNZ8!&O5YWU8X7e3eR{pVU5Z z!#@mQa;z@a#Mq_>hB(?$ttYCuHexkcKpQk43i$Jao$WfeTBanXLGLfHl2DL?dD=RH zb>q~K!1XhZJY~~pty=msRCDhy0A0;2v|s$NZFn&udvD9Tw&wM7v0)XS0T)teDrd!tItNpW}TvX`_G zbKfZ+MfQv{yckS)msihk$lx!xEOe{3R}<^nDslzJeEH_Iq$(R;=l=z|I6SsW4nk5WDQ3q*j9V=q^EuBEW^sRlkYW&E}6y=AUvz4dv#*5aG`-El3$R_Wmg;W0)?4}y-PNTN>WFM;Ds_R=EHR$=^-ri=w;AQ_T} zO3h`Vaceq%pv(qe>O5A{*jQjI%^PvlkFm?-L~7ybZ`(85T1jkW%Sm9lN>)ApJk|uQ zeN{aAn!U?=vu`_&$eLAtMuzVUllrcN!D?ybjUbMJIg`MOo4;f)STE%|OF1*OeN*Y0 zYqBIUee<8#iaDK4{ynsarGk12TPrGr%LYj$oHPd1;~){L7KqO)se2-4ksx@o0rM;4)7R>SwtkCQ=u%tqO9 zGEcF|;EP@6(qxaxr*RkaUI%N@B}T4+3*UFPYVH!IPHNHLoMH#-Z~;<0)m+Mw0^w1f z=$!%Go;1O1mF+{!YOHwlg-b-4Bm393uTJCK^R8;hBe_BDiGpg}$fmVET$G^uDYpUt zWO$E$qOiYcJGv^){tU>wn)S{peP*1i znE}coJi|F)U?FwxYK(^5Pxhbrk*(;!mw%q1iYuLT^AC2v8|h@fn@QTmA*F)-IEEhG z&~TDG(Rvi-EGE2@&Sd6V;Hqzz5%S6l{zA&uhdz0I-lX`p$gFcUfxTql?sDRG{+DEp zVn(kAgsE+(la!dh!1pIDuWiMDp>$W-a&=8i{5q@i(te6S&r4c+*SnKZ+9*mFgRrx(! z^tknVw#I0+4F1EjqfMY`aWf8=8{lDI+rE~5S9%6{>}2jTAazr2oX3a9RjNABXymg4 zUgVJbxtiJGSwVJQWQ;B*Q+RuQ$=Ys{O|EO2p1=0(*xAz({PwE+WrDFkJ4Bkty%9gcniPUx!#+cCl<0@Ca{-_Jm^DCU*z%3V0DmE3)xId z90)6mzhSje43s z{JHVF4c|*dt#8S4tB4C9e(fyTa8!>lOcgXB@+WfAcgkYRK5OdHT9dG~tH7YRv##B| z+-`VNin91GXD+uNg+s-RcdFjCh#m-=p=@h!D5q4`U>h;KqgZY~xW0p?;Be_u)w=Ee zUbw-C?2Vc^=ko~oqmnaM=V-qoJ^Xr+Sz})yX4)_nT~`}$a%XwAa1q3c|iU68cn zcKUIw6!(|>q6dG0N$w2&R{fqoBd9<9Eir*xz+cWu;xuzLJO1U-Xn|(%#s>j~qRP?+scKo6rN7L6eB70>- zD~y#9R%vXvVJQ%lS#L;W%^&O$pQ0Hy&ILN?f*J^Yei8V$bb+inu(ddoETl^t^+^eFd!#n&< zUz{TYJUY$u>#8=rx4x@vE-TM;2VxteP>Hda0L{3~)zmNTtn?%QK9mO!CXWA`> z>5Or;4IP4y$sKx=BCqd{aZQ@u1-~WS)TE=djJ-GYkjCLt*2lAN%ygG_ZQD-FhguNh z7@lr@lp#&7hshi1*XTzGG6eVYx#syY+A)242%&olQS-ws)ll8E@5RVhhLoEdBgHC5 zzJpRUSRMTJD4NmgMo+D`%RD}l1W8TWb^e3I;UyFOZZ%3Vi>i}}Djx@Ui9J`nxX#vE zp5D$n#ZvT}w$DuLNz*hul#hN7IP||- zIo06E>JqAYZQH<~QGwst)O^F@s#uIEmelmZ8R!@#6-bJ6%lNHC)u88|Rl23kj98Dj zC`PK0#b`vh^6Fdh7*{`e!;TmV%4WbAlx+1ox()f!-Ekdh+o3R@_sWWEviex>AH-6E zzpt1qbu%|7nF*dDu{xm6T8=h9ZTd7cW9u@za_~)P1i{BfI(G76dM(d+EXK(2^;1#v z`4-c_mb+fr{Zw|6-`;v}9#4$iC0pzX7D@Zlo9vMyh}}NV<1aFXM;aX0c#llV`j|E8 zBpu@VmSa8DCM|gNhs;N3P(7WuXtU{WGviwlDzOrJ1r%F)tIVC^b%~Kl<_6EUL?(8r zET>;^7Glgy&He)WaVI>u$eAr~e2JrWhEvjFzMm+)$*^8#VpqR~15Pltj}u9T_h#3}venY`zMyN)is(_JI$j3585EW`JJ4e$KiQ+H`C<@&%g zeZlUB-`H>IPMw*i6}1F<`RGZpOPCqv{Fc?&+(P)(n#8zP_k>efOHMt)1rW%at9fpb zPTX{wDe_Ea`3vCRn2b9+%`HocwUDDLr!+L0MQdp-6!itQJ+^y|&cvPJdAx8@C%wG1 zT%J?wo~Vjm%vz^41Qf}hTk(OeVzSXAM>v#9Y$X>)_UvkCNYC1fyJg`8^dPH z1#;XnYLhTSTxH{`nFaTA^`h5#d6Oc~g|tYc*B;s9?=-v2-JVelfjKrNi))!?`q<0j z1bDuM7 zY&@W1=lc_bWbOx5J-Gx;OaVgWv8IH(V~G{>B(9PnAmU8Dx;QHCx+J~Jp8XF0miJg; zYdVX*NsZGaj_DN3gK`y6-Wi{9@rV5iLRo0^cYIJ@WtDzB)ua|#+T!GM$B>EO^=7c^ zc86q#6P;R1qCxFeb~{5wYv10!lI91*>fXK$?o8I1MPMcuQ`lHux-1=ed{2#$k+Dd} zjlApm5ZRe+X>h=Lv`g(>^@p7^98Kq0$Tp?#3VmOSuSIHTy1V0VFBwM}FP+ME`V+L4j|Zr41`Ab89&Q(eFGl`9O>yQUSs zqAht&A!#Yfgu#iTnjxhAy+9o|E%w)ew5`4!VE4wdIQrHiZ(9Y{1L@66h%wP;U?+DE zI><`YdSOTsUd*mn|Id$+=A69p&rQOXU@y)GS5#H9JaSa?eY_sW5-{|>p$=>Rae7W4 z-Sg(abP*N_FYH=%+v8~h<0RfBd0n$i&A4kkNubn`yS_`=PB6`k*6iA2Q0LX4=!50X&RDqf4LiKUSd2qA z9$B3j!)<0}P`{yT6nJxG%c(>{CeE3Oeb?t=+80ZEBx*Qy9Pew-2Rds>i=v-{+V4=7AiHS7EG&sgxjAxwsN zA2Lz%HIZzS=U(VSf`$TYo)v8YshZa@7<%#~h;MmKyWO6BWNPfiXqorR3?2axG@%Bm z0*@rtZ{XV``Ygm=Sfpec5A_cAWUBc-AI^1U?tU!-$2-;0nP6r0i)4nL+U=*zeN5+1 z{;(9p7aaZ59X@PhIuhfro0lZ8HBn#xdX=Z5*q{jMiW7f5`c%KEH}#uj^~@TYhDmZl zuGi=bP7^QXW3wT)vpL>SW)f2sa)cYweE5RNG^PMwtL<1b94z3r)cXXHLB759$iXn} z(U7(B3{M*N_d8MXJCcNH#5%K}!N9A5VZN1dWqS^a%t7P+n!$m8#)GDXY8x<46LAPQ zl1IA(%{u+sFE4YKW5CeqwN<9}S_5lv{c4+TuA{SNGS`!WD=Z?}EP9JAg@kDD!oB4N zW~65lCVlPKp8+fOR|$q+zmsd%)p_%H8-#F9B|R^-;(5yUxk6NwbL;dDuc>DuI$+?1 zYHq27+Ly^nu`4y{82qwv>-?-r-ir`CuN@@U$x1gdmpGvx8( zKWlo{E?W(V)-W}laN~~eGP}ybQ|>SKo8QAj zi&pDeX0&mSxs?UZG=UzxE`w&6FOm ze}9wpsQ?{2F*Y^!yl||x;T=M;QX5C5^ENLn_!+@;bm22^rT##qbj~10xooZ#aw$g5 z>Bl6z#9QwYb_EMFq@5-{t$aN;v4KuB;+;0-c{6yg%1mJ>*0E1n)7|L_Dv2e@#4$f5 zz~L#ETBP(}!1K1Q5% zhP30d>{Hk5Q)PIWc&6kAO{T|^13ij$nzKi|PsQm=i@MQXIYKzje92wqk}=J$cXc^N zzsb5-*DLt5rVrH~tzL=xv@TXG5SP1{S!U>G2njem2{_t0=6pjPS9Ma9fu~owxy?yY zo3P3AA~H`GX*^`xs8y2*JJ0#tZ@kXmLoxHE&0)J={LT$6s+V?U$gMbY_Yw2-=X7Al z+x}Jm0c#UG<&RUNU$mn^>AGCx|L9o~14K|D;*v*3C4^kCS-FJV0lDjO>X*zoK_VIy zsXRZ{@K)yWft2Hz!~wd?miBkj7~1geo^Z}|azg)Xj(_myp0xAT?#N1b0)&zpLiCUP zVwab4u`xm=o~o9W#12UxqgatE zgEZdSVh%CdrhHNF%UJ+RA}DYdprnX@}m9R0<2TgaJgM z5KRQJDl8R>Rsi{XmlU#iR(TtcvqnV=0fm+6JM77a#0!C(F|eC&0|2e_A;Vv`M`z+} zQ-@@GJg)x*?#lFUId?88-l)*-HggDP>V*P;f`o_JNZ{|08_&MS4j0K%S zXKjzc=2hLn!(AH*s?d1Wj}C%@;4v#xY(1r|zO<5RdEM`J0*IhCmuI9RhJ*aOB%}K| z8>(-$!GCax$|E++B8YNB2s8Va2>ygTvffdMjm?UU_r#$|;QJt2+^qTTC#6U@BpLH$0F=e;>lOCtc(2|$LDj~D5KqAa5+K~fzMN(W1zRi*uxibrX)^6GPz zT%aY$ssmW=hbyZ>fR8NX%W3!1&a~HBISau)qH*l!{s4|#C8BtFki7Vgevn0f;(ZX~ ztZ#_+x8k$5G~u^v)}KW|m4LiM0vm*~kO_!@GRf!XiihQ?A0`yRarBj4A88@~K*CTY zzzu+kRv#rF3XGlIYdVs>arTmpy&--Pz62w335)*-2&oFhxbRsox!tO`Z8_MJib!w= z?<`2OL;wgjkdY{;z|~7Vkozz^h7oRS^3a1%heta*6cR5&1eFJ*f;DhkH=gNBM}7OM zgt)yRXbL4K2FOXG=1qh;`zX`VYD2-Q(L^7J!IKa{0UDQ3NE|F434N=IVj?D0V1-11 zmoKS^L~Fy4Bl*h{ivS~vg!I^t-}&9v^eb25xt+=f2h~s}Ht<7L0dm%ouy}wPuHLN8&Ag>L&o#}J zUG-Rr6~ROd342Bgv_pXrc4HVqJJcy8Ywq{mxbwDz6tDa1xi0T3pa>}F{_ih|qqP6> zKOylz0t$34QUr-QwWcr=&xkjNey%v}MlUX1>44$}lvasaNPcr)w4m@^`IaAx#aE?B zdr4RVXd_A}R{4)iP@AxD&^SQuC;&k~RoSWa6Jen+OMn=>6b~qc!tT2N&{6{HeFcCB zrXQ{vP8tRfh19*wp2`cJ7XDCNT36qrfBL%T+0}=vU~mAEBuX7dORFD$ZC$R~iaiJ2 z=JR4h>RqdUJ~9I~)T{_%`4P%+)k0Dvv?y0JQXraOw=vCCSm7>DNfZiz_dXP$Rf-ov zMw3{~oqx;TcscWn=`WxizaU0Qt6C0NqMi`T$3t=e6et{ck4OL-kOX_F0x&%EL6dBl zsTc```ni%yBh2*eQ4t~v1`#_X8$nY=ggxH|LGz(o zMCzaougJV%n1)hPLDCu7wqYXif+`C%6ng0*jKz5BlLuu}$12(X<04+4ML?U&0R%&7 zN#EPh@+dG1jVPnIaBTj$N|DS(>LWDHy_iReo}sjtbXSm0?0{nGgT_P2N$aNmtB+G5 z4CzO}GM)p7;E#vJ1LQoAP#AavLDfPnLt!@{3pM~Tg0+v6xR32he0;k>%*`$O_M{w# z=tyC1*0KK7N_iYlu!iFn>*L~C^NS94JN9;3@*IjVD6=Fn41A6Xa#&$x%$FYt%8F88 z)dq}gh?tpRpi2M{0P+@)nOe*Y1(gFjh7c1>FYF%$*kckNqHqW~3xtu7lQv{LWss^# zp^@5PiTbD6_+>@U_=U3n*KzQ%`zb?gEcZH}Mj;>gX>BZw>RWx>2Drj$XbV z=3j9l@w=@zQ*eVZ>YjC6@t=3hx<>vonxsea;r{Mn#_z$nKaTjArVu>f^HNLvm^=UXVVhRvDg^BM^P;DH8e-JitB4`Va>9{odr) zn|HJyQ!iwE&zu2*X5gdLG9i*)rk z{m_4%1GiEV-l-Jcs~R4|;;3E>7lMCP_D}nD?G|R#wkU0%t=AkY@s7zlz<(^~;Jq6g z##8;fF8RVcxM80;w*FP_;a$aJ&i8>bQ`eKDSE3Un`-tV)o0|>H0ZoYq5?tGKeRM+d z@q+SSAQ7O@3KAbDL~HeXQIYWGhp{X6Y!mNN>B~E*90DrXCMuu$!G$R01wmJ@y062t`$^)eDp*GZ_oJ;4cV=J}RB)Tsi%**us z1yV0ymPs53b*;S3HNILgUiY!#Tf@FPGehUPPPa29Q#-xn3YN~-GuDR3FF5KIa6j7u zEv(3SPOBz~gZWYqb3kT;A_d zPMNByXoxB-%s`XBlq#oR!==B~dhCxy3;K|KOHfv;>#XN?3!Lha-|35YFDyvagHmhS z829XN+-}h*e}@N!mGL}gBLctiUrs6PpbKCgT6s@oX_Kfg*T3S|nH@}-xhDMnE0{7s z%KC$BwY^8m$5KvZP2AkRcTB#{*8lv;kWs(#KHBriygz2A)-f7@T9Tu#GS{^K1z7hJ zl@^75Y9N9~#+kWu#cwljb>8Uv@et#0oM*0jlMf+y?LNrg7bO(_F>?L=p`YXZASjOt zci;yJ^l0KR9Wvc>tzQILya2KT1(lF9FZeoR&9M!n7Ezp$=6Q>x1xtNPFLHs@CkSHl z$B`n%IO-n5`stkn8XiFb0OaFSffRaZEzQ0ZZ`tiBgAUyvv~`vDS*vcZ)KinD!oaMg z-fTC~<*p%W*qGR0wi51@Pa;tOzy^@sknrcs``1W>m>}}7_fQlx6tE#y4QHa3 z{}1-~pTOEVe)nxz?wUPSzhHo|2qhf|{6?OLC_vsr8>55%__3-g0RI|OhJ*D(Kfw=1hu7qs%Xf!|H;R3Ufds_1C*e+FftN7Y2Jhl!&B!4o70CFjs+w*G!n0g z@!a2AF|XfOjgVpeP3Y)Yx**%A4dx{q;^q773F2hSFcd_Ub%0TU*=m&`shKKkoU+VSva)lK)zgAv5knYspRSFo$n^InBm5X@LlVfT1^FPMmpR2ToHW&; zS?C&FAAzwulD^uC{atu385sYrUV7){+z1dV16CmMq@m!PgFMUq`0mS@3uM-RLEq%| z@7y-S)0ZNk=4n=0D8s?o7eM|tB66Uea>T$W>cB{);?d&7`E2x3<&gJEz=(>DGT#Wx zeOeHSUIOFz!6fr%Ej_tvOW}ek9JbC>WmG*`>~BXZpFzRV7x}OS0LId~w|jm=?YA8K z(U>iWnZf!2#P*~`A<4aaWxJCfyLN5bJ6x1d@+c)E7Jh@ZKYn-gRM6^c{aXY4zn%-# z8YlRdDpB?^gY!Kw8omftqjF*mhCN>y=5gKCSKK8+X;D-k`m9pSzC@w~$vG&hjme2o z@PU9m4(tw-Q3DI70P%4}xT1;#6F8y*35SLE2Wtm5CKxm5i8k37u*0u>1URAcL^hRu z0z)*D1ll+Dya6yvA}A>2)BB;{y>RkB0U2tEp}sziAWd`Ch!XENv7EiB#@N!Mig9m3sbv+tUU+$Dp8 z3Y6zWxWRwhrqdX^NY^!u?Txot-Pc+3S{u^yVXai7q=h8JlYmH*h?u!(YWGI5vo(I*j8wE%1gT-Pi{H;edzDH9heIyRWz1D1NlT@WBzph4;V z%tbbiP}UZB;AdD1OV(ljkoUB#3Ovp)*_;f_RjmupqUHqJY2dJE5aq!Da0#ihFkflb zo*V}iFQ5hj!K)1PlP{LTS@M1-OfP8Bq`dl-E;0G+m@SPrx$V{I2Q{?|17sG?3|BL^ zD~VNKZWKtP6(ZpQKZzOybPmP;t9j7G|BWpohEFZ@{JzWWtwm=yAXO3*cNMpKI>&a#T_W&IFfTSNHLU(4pa(_?z=#}5aBe^ReP`MnZELqGDy@|| z;AbjO3ju35g(o251jbKa`fUWhJh_0&elEbcKyu{EX)bf>|EqX_*dd-H;=hW2@Kp%C zX6z9>HNZbr38Hf$`BZ2WI|Gf4D)7{`>p%>9$SoVb9JzYTF}XuS6&W2B9WO{q#$OlW z26r~pMr=PdFe7iOz&u$N&d6cmi|$_-{8TG+KPQGx9jBudFBPl}RxNLhwSS1V*%-jC zdv*c<9R#gw8e3$@TJrj5s?i|_5~2r2JR(}KQV9EplnhXCk+`GZW=n5tFFjjM4a5f! zKFLxm~fCOhn8PAJ#4Ds<~Ifz-Aji9m>pgh`acR7 zlx4d>8*7afX*)-@4{}NDMy)7c&fyUOHbg7oq+rn@6>KPC-deNIb0T7}xD~tO1{UHN z@ZnK(3bB0ZGo?_NUbwLBQWVS{6El*httKKu%?D7@gT)I}wU3hE+Bu#vZM)4h?3tEP z@OiZkpT-~02JMU2vm}cbq_Vh7&3PAlE4H)!0p0(o5`TIh&jbTV8^I`Z#Culr<6%%L z%|{p2cq(GDN`ydNupw@;AcZ4Ry|mf=*B-a$r&jibgUHVV012QTuZ&iHZA=RgK^?#f z90ry%rf$?r|2D|{*fX)uE|i**ffy1| zsdL3Ep+ws|l4B4hsv5@rx*e)YOmDl+ycarp$>=`>gSH_A9$K*E%dmlhSLxdptSU-hbBIu9As?1P;ziuZ`7}*%9^=#l> z=UB~L^iTE_yIe|2`}omp7iRj)E1$6 z4epth?diCFXC@X4JK_3V6Qq^8HV5jv{iaPP$NPUZA}TiwCQyy@Z@oPY@rN;?061z1 zp68i{-WhMgH;cKlFfh8u(pdsF@emLL73kh-YK0%0cJ^&6<)WtA5y5FvSf;_78o45y zq642+lb>@GA(E5%IB2+(6D;`v;BuT2xU9fyztwCEI&E>wM$DcDCb~U?>ZK+;Lxh*4 zfzU(RH4PRi}Gd&MLFg*Z{*y)r#> zvv!GgrusNb4trD>U`ZSmOlO;9Iz@wWdY$DfnO4McW?7u3oKU;}V%caj#MsbE6nQPK zD=k2@$Fo_-^1HJ8w*k-lTlep(x{Db~;eS0~%N$r2FlE$Mt(<(dek3K*!iRn1!)T1D z^Qj!LA?tx!UKZ3;T2){(knqwE>;>A&EL(!E0rd3}E98SYSC-{s=MwenzWxgQ+gpr) zCD}|8?^pCZV^MDuO1}ej@@x|&s;jJgEeuTj z)Wq^&b|5BI<@;ZM@ROa#;Z_1Ya6Z;FexTsvP8~K$S%ujH?5;3fwpl2NKxFw5CVzR3>*UMA%{E|L8N_8!09+2W zDSk!L-nI5qc3w}r%q0T$f2BHEMc9uiAMmO?bcccfryY_FcA&SuE6K*t{|wA+GX4*O z)RjR;MP~y<%vDA?IznUiO4`-hx&jWb5$t0dbv>Rx(>t}ey@Dep^GtJSmgAmoZybEb zdvT{eL@xSqTwF2F=e3cz1>L>pOOrS@xLt0nK;r{D`_|hxBR-2^C3Q<>11w)@lwBR( z@Y@VDPJZJ9rsKYU)A2JfhpB=+_W!{8bP(@=uznq!c~=EtfzU_7b+8PZ{1?^>--dq# zE>Si39@sYJu4zw$KbIDAPnDSUFVpi@+|zqp_9e@}{KlH|`_HisrM?$8MpFB;6#HLb z`K=?OG+1oFmL(XmP+(LClY(e*VcrR+*W8xL?k5ACXAUA{;JEm|9)t5?Y+)?J-Ar3? zz|=D~c)}478&47aoKL-6szf;hp3FX)S8ZFaFs!QFw*)LHlfW(*xIoDOL2|H%)vgjd zw4^M5dF^sRHf2N{_3G_XY0Y97ulaHKI^}+_|I#m=8YeMkLoGxKSGc?(@MNEyyIseZ zH^gw{Q26aCd8V!iEHat2Uglg>^ctDl6(|ZQx!J9bipydPUBCN8VX)4k@9B0ue zcPK4-CkOcsrzUCY=J}Hb0LR9`Z1i7^I|dqOq{ri?gNM`_r+6*lfaN6`SEc>GULXW| z0pXIuf7#n5P5W=+6$(m?rF#N~Ebtg$AP_<*F?$<>5Q}rcScZ7Z=i7_FfZU{*ig zk$@w@|JTo3HO#qk9>RM0%L5GB9~AAAXfc1FPL2Bg-bsu-Bz|G<#+{H zhUHUKI)=0G=bYvoAG1liJk|dp(qyB{1dzg@8)nBe6UH+`j4G-b%skn!(-qMZ0Hl9U)d6XU;-0YE!Ib{RV^P5bli=uwrlIB%9CCdH@H;8Q{w#k&`KUykq1{fQ1r)e`(lE)Tu{GGc|I%Wy;SpKxRSdJIHE5D?ly0Xi6pvf>$K zg%ybMG%>U0hDG}inCqM4Co~Ya2b@2oerYhZst-skF03ZG|8U9Frp=3vhr|O6Gbb&~ z_@~O871Eh|I+MPvm5FrnV8>mZ9`*rR8#Ny_ITnfV6e#)0^0ZPo4D$IJSZ~n_JG4^f z!ZjE^F;{N7OWS|ZZP4yEvb?keFtZRt$x*7r@Gufs7z!W>{f~x0?Se~?h?pu_`a!~$ z@u}OwvpS(nMEMA1SiC%d{D11E%J`o?5wU#5kX?8!GItS2!h#NRb=`{@X*c!HK8ichLhADKU2P1mWL29v(msK zaul#9m-xMG_X7U(b3vMmAjF6gGz=&~tFqy4%uCHm=R7I0C1QP=1LB7%a1k|H1sUD6#&w{(Zn-O?c{(lM0OpmZZCjYxNc zAl)F{?LFiFd*6G%-<_XWFfj9+bM{_)t+n@)jD-RLV)lN*kgwT|JBSek3GL(O)biC* zub32m)bf?zuQXDOH&>MkE)QaEbUYT+;`0f=iYGG*0C5|ctRWOCNGv806DS9Fs3{i^ z{BzHt00{Yb4xy^(4YTXOC3SKtwwlF<$AmQdmTNyB_DcnuVg*8|ua;{66=+S(IO^^; z$eL)e_Qh&4o^4zpV`_vdmrQENM|N|HTOd5MhQO zC1fx-NqGE*v8CKGU~1nx}a(;jy8_gjGS>;aYN69tUOR*s{AKbf})WImVh zvG5Ry`0hl>VSIUvIGJuF-h4;f`3ndXzi%k0DYiJs%zuD*4ysQ8$gRCV;MMv+ftQvx z0O|%%nownjTJADN$JW5{47rIN8N>LyO_y5khD2*5yp-Tl$<%1Lipbjyrwthli7&)A z!wm%&5L9)~!7sEIG~X89s+Qr0zqb%V=rL1$f%v4OW9F!2|6^0Y*U93($ob${v3w}} zl#9NY4;*df?6*d>TSY+dAf8LO?7c!y(i{Z#t>vYQ+4sV)YL3k-2#A3?>qc|Nr+j2pbR# z8-N3q9V(!km;b6>4_vc(+m&@+jlXxHocXb3*>e@RLg0hEK~Jwan5xQ(Wy(UY5xEY# zj8$s8HdtBk0KuaR3X!L*+LbmreWUhP-D#z!y9*`SQJIE303BT&`HH|7qL|T}UAU*H zsiiiyo9wm+vffG>1{PO*AkO3vq_GpAx zlEXY72oIO57Hsu`wWO0GLe~l_-!w$nlGf(W!pnb2NrXd?DFluDBfT5|=>^Kr zULG*S1s41bWE@h4Q!VO|(%s>(Ee?d!w{8d`L@#h)*Z<~4&_qvM6=a^U)A>N#gGbW& zHKC+gZw$aDD4;&M8%Lek>shR_6r&?V)th85-|1O(xEdHO0f4K(vc1Mf{%tg1Zm_WxAn-(*!$);;=$O&Y>dofT4PH&=D?zAurkS}1f-_X{f-G* zocZ2P&uy?&Vp{1J7r1nj?ves=4qS2YNaLP!P;fpJM{;BatZh{2cLN}35g|K=0d^e{ zIR(h2KDjlIA6vV?ox>K)XfCRDzv7nY%^PGeTSeHetut3he53jh9>DcNC0Yg*g=5&m z>b<1zf#1=1osdRSrwiKDI?Odoeah8<5*7^<+}`5#Pm$|K0$dZX7tc78y9Gl%|F9W? z2H~R}s%NYnCgEd1>46|I5b5%_{0!E{#@24y&6tTa5rjlU`_hu9^wJ+6hYoURrc!X! z86*+jFZjcm_^LVh=dX|uN-Ul64k~a^uRS2gYad-z&`vBA8uV$ zzPStsycsg+22SJe9xV24jt=5rWlcKPe40B;Y--+miPcS=I)w+TF^&d_1cefho^-QQ zZoWUQsxy$5!_(Xv-aB~m9wC71QH%dsRP>TQ z7fKQB>BN7WU)5NIsYPE@`A8u|0Zt{$t+rhvovSqqcGPFUg@9Tt8tsdbQjWWbC}&fl zdICvm!hM@pdr>t-8+(sr1}iu#U8*AbKj5N@J&NXQX?k!$6mEJd!TVlOKu&O;g5pjI zLEe$M-AX}~{;s}=InNYArqU!Z6MsPlBtmMiPitgleFnO^6^W~U_GG^}bC6O>VhPL7 z5GHGWz|g|>()rVHZ%Zur3ttQgv%M+T#7b@quB?D?_KiEK)_?H;V`L{rt_KU71codQ zy8yhCeXbr&Sr@Og=Bj03Zb6^GcaA4e(NEB|bgg7T7+MLWiA-@c6c~QEvmtk_Er&K- z`lU)S!Ayy2_aJ{QM>p6Y7^q^9i-vadN>5>70+3fHe-2rO?H`-;6o@kmV4RD3Qdkr@ zu^+SVpGQ?pNIxQ{YQ9fLAU$LjssUeYD5`p{=W?HAS3{tGfA)w!H$Sd=#Xyp(BCWg? z=?MaM24ge100(v9isf6K>$(p84e(5h%t>D&Jp8FR{bl_d*?+}Ic%FFY1%cJk_hx~=q6ZiHx znr*be)Re!_{9ovp-{@m^L%k@q|E1Bbf3*LB^qQPHdaCp1hE|Fu9UWY)h|ZJB(S4&evihZi%RiVmG;)Nk;J5v?j^)fr;J&Vu8gx zIUzpA#X%Zrc4G(=CJ_LICz;BT*(`&Z9U@01q(R@w$UlZ=8IgIV6g~sail>i_+-sv4 zt3i`J-tFf@W93KKxQ66e`&#%M?To|vyoU?ob?m?Bt5pOd4)>qOm&as;O57&|m=1(r z6iR5c6T3Yw`B&ht8Ij}kA5;3EwwGucUOix0FMTOgc9Yw?B=i?@JnXSxZaDwky}DAT zO@&E=`8sz&mkkHT4R^EN+FdYw>qHRlDArdqjK&U3DJ^KDcXer#?nm(HurGh2k91tpc z@Eu6Qf>_!>wj)(*E>;%r@!G-N7~)zXRtwsrXa6hE@Qk}%8CYPxm!%8JH} zSDYqnA=bHdMS}{){z7fvQ#H@+9!)tmJGEPUQWH+Z`LUyOr6;9VI1*2H{^e4}tH{iY z)=_U!vopHLt7Wxt+!=)9S_*2YDtpr154FYzfGi;KR>`jq}3k5pZuE2cDuN7^iA zY6@RM?m!T#7@-12G!>Yw2jQc2!%7rtO*N-zgB2(-zWwNs0`5>uJU}PIK$jU7UNqVY zCJz&l!@d)hp+DePU)e$=R3svEJMl=X;|Aq2}R-(S8PTtpt>*6{#On~B*HEr2qJ1ojE`38VI{34@<@ ztxvEW>zeAA*3&6h+d`4Y)?S^;eTC%@F2fPk*)z#gU|`enUuZ7sh+%zlOKs=ml;&2h z^j!63MS7a)gnH-T(UjZdjB;EIEv6H{5#`tA|?(~gZTBBq~ zn({tR*g@ymuyvttPsMD@IY&o9*SEWe&t-5iRrBd(CKj_(Hqm7~%Cp9H8ce>|h=iqoK43w}ov zfPXjO5aXYN0+i~YdO^xa5LyCq4}dj?fFOefCx$4|KU2bu1FeF@B}OUpnFFCf@E@r< zSjc5pqYaBeHwGyeFc<(rkvq+S6DcVJgSwFH7=()s;i3T%Ws0&yp-ysy$*dnEPz?7` zoCy(s_e;EG1|b-}vk$wDY1YbqZN=aYo0Z4Uag!U@gtLn`w+=I2t{x62dguNwqnWLU z)7yKgg_>&iYH_)A9jf-KenlrH1Oy+xMX5el(GLAdrCo!|!3?)xXt(dLu4imi!X0U_ z_yWpmfOal${S0>2K-o)y7O|MOnEGv zs;+tn-jkB!K38yCxYQ}5KRtA`o$m<*oW*=f(m zTlTs(gX*ul)-}KKyDaP2vT)VP>ldn{|`&==Uq~@{z4H z%RiaieEW7z$}J@f2=4fSe8>LR;7_IRt=vKSulgjTQ2{>&*ghy67|nAGNRjRZ;8QN6 zl!3$!A^ZS*6rf{3P!J#(zX2kR`oHrHhy`R}69`-zK+G8C>S78Mdl!9=5QF7CV_#MT zo|!^240stKRPa=?xnvPhbfL>;%Rf#gn_rjrO+8P%ZjH93A5h3sP`oGiHL85xnjIC@ z5_1~b=+AaajpfViW!)vww}bRABhQ$1qV^%J79%;%+-sKqOS6_^ z!zNUwqL%>_8!%mvpvf`DWUvMkvJykxmDTY~W|Ll9Ri8(cy^s7brX#rae#1|AGnEC? zS>?wLpGaG2)FfET7a&X&?LYVm&Zh`ZbM5E9VOsNc-CR^%slEK%KM)PMF034Pu&Vkz z*3`j)_pX?j4Hug${F(9=RrPSA`RYf!m)IP5o|<6u`XRz1$szcA56144Z7A3G4koQ7 z@6|nv6#H@)p^GX06QMMWc23_I5mD!n01VLQU;RE0q+3==v0&v z`1pa|$;v|V3{_?N?)=}qAEutQ9y{Np9RT^Zq}VpIM}N4{tB~m|GsLrEYs5j_g`)H6 zL1+5AS%abEmBE*3`gFVIY9^(WD8BJKa`=;S_BhQ=9ERBLNqPz_QCJfy@nb*7Te-Xy zBff9Wym{2lc>UOAJimUoIk#`$O@L%{M|wJHXx~kjWVGMKNI7JZAMYm`z?MLnxr^jd z&B0zlO8|ycN(DG_4$u!07&U{@Q~Pm; zT}@xaO1ygoBUIfJpr4I@liyZ+^ODAso$Y;)=ekD=umw|Lfb^zf=SU-4oCJh5gA&18 z3khyjy(7k@u(-Iy;WU+07Hx*0P-~TrlSUtv-nZ$WQs1&>1F8AV0t6yeEDcpxelpdD ztSN6`CxxBeui<`1zIc0l#5$?+AGX9FyCWNEO*0qLqhtU6HdZVB=e84`uhLyBr=%L& zmra*2A0`ai;a-!^P-T=6dVJceNj3nAt|8 zfTL(ukw2^*u6p33w>C(-qfq2C$e|iHXL@MMAN&JX1_aO)sKA*^W|%68Q^GQsN4q8j zNF1mbfYyUhz+hpLJ66K|7SDKUGoCxMRg=**C2p)PB&uAxdp#YuAzfv)kkqYhja`ep z+h4gUuPC#4CR)#`+;IGwuiu=mP-DN$*-WxO8n4ENF8`O|$bPo#(d_-bC^*2=iOp?? z+a7mP4<+2HRpu9iD9$;0@{+xLxTPPmd;+3ef*r4xnUPl{pE$ff4eU`X~T2O9CNh+8u; z|NOJ8?>wkjUQPBoWV|i6-H-UtNUdkgMsM{_YqOf5QHG$CtfSDx@0pfo0l)yl0j3v6 z$Ax9-P$kI>Q%i*YW((ZJ1LzBYDGXHT2P8HOnR1|u5#M#&c$uYdK|tTMW=JT9L;qpM z(4_0gTg#*;!2)X_-EX_+!C<>5qL?(>g06ORvno|N$UC*7=$HeFZQeeeb9EgmZi-Y5 zuiy9!@m)vp>SSnoJFc?jul{jm^elgJ21aw#ZZD0><|JEjjrLfbW&ZR*kZaxnsa=CA2E6}VnCMW57$zhOq(%r*5+g*fGl=in`SkLfxUr|$tlcYVnAMIj znc9g=#JgvfOy;wQq&dwGU|nhfPXV+c3Z)G2ix5*{cHdD52hwW|w_fqofdGSkx!Lm% z70gFW_fqcY2R6Is^S-6JYn8g%4R!}G?G%$#8j#?c4!e;b`HE~0NZ2&qelQv{#2A=L zDtV}~{*)3zdiXe?A3(h)6hq^{R=3D^Se{?=QvbasjnyNM1w7%`^^r`43keU}S8>g0 zLRWghRnIXpk#O|=`dfh0;l;_hMjqna@8{8h?>w8lWaiQ>>~yxf+Jf4q3t9WjXVwGi&``e{c; zhxM?bb50@-U+Gt{raGq<=V_M)h7@REI?)HOdj$vyOktu5J(mpYk5+yk8(!Mo=AVmB zT{sKPzK!ZLG!%SR?ziA34{?aOB)x= zI)?*4D`3Mg|E=a;DX>5ruIW0rDqptrVmfwnYL-V3;mxMw#Ed%kIa?47qMJkyp->{h z09@?380ss47m)G*gv*6e*2sR`GR|gB`=tCa-{*bb1YW9FMAJI4@s&|$t_zihEyZ()wN zp&!=d*Z-b5R7d96o$AR+7RCL9DxXurfGn-CN@Nfl*CsdbyMN)QrS*lk8^?35HQ6K0 zoXyfXM`nrT*EgNv4ua>+L~Un-p3D%ee0om9Jio=k$H-MHjDbIomDa_Y`+nXkV$j@n zV<-}TzC+c=^jaB5EiXR&;$lEkK2&NsE)P`#yy*a7z(Hp4a!C%e5iXhHL@qpJUi-I_ zB!EXn?nDd{2;~lV^}&{lcEz!&<5_jTXBL{ycGyJ!q-W=VqE8`mhc&zNS3)NWdB`71 zG{pBQ#fn10D#PPuak{g_1wSjtL+OYmQU!lP3eKMgPw8+DULOgwuOMC}x!y-+;quis zz|H;u(iXlL(80h`te^nB)_}EV#f#vW?cMvC4yPw63@4eJL2ug+#99u`r6K{bXBSNw z4GRYu9TmiExEM+xy3CIN5?BEBfBIOq4F?meO!bmJQNDNjn`nbh`?I*hypR&f_^U>F zqhCe$`yN1d#0XzXl5$#pS3&n+N(})3pH;uZQz`(Y2r6Qv4n*>AOOrsO4$MU!W(UyppdLxAGNjCTk!5tTI98XG zUqhe5=Pu;QbQZU&L`=U`i+KGuC||O8ktzw^DFjI%Ytb-MxCX5ODNKC|dH(PL;N`#o z>@c7d0-*pIV3C(5Z^-c0XlcbO*+818g0Anl(kuK5pRdzt z^C5>-P6|NlddW8Lsr?j!Vzqq-y|-`2uOwM347i1)G7k3BON1{(+;wseb9>EwCMpy* z(^(LmatdqGjEu2dTrx#v#(>~K3Y6Xda5Gu}P<>>$M*wN+>xOi`Q0*bU&6W7*SJwflBxPMJD z5vFH<*)~3=>Peg=;10T44>K_m&AkH$8!#vm zpjQzCdO$CagILa#XI3W9Hm$wCB}CBxrr0KNUy|RM#?Bobw`M!$IwG~HJs+H0@tS54 zjWrUUTOYMNzE$TqvtZs9lxV?TBrlw5Ow17Gnl!X=_A#M&Xc1d7wz@BI(Qla9k09*Y z{Un%yf=V>fFB+lEHtoG(P${3qy*Rx zuK0RS*=5j+IYpU1>fOp|$KZy==i3tB^eNv1;Rxr5C1 z@ce_EM+t#;L;!YC!6G3sEHotzdTmes=d{6dnYsQ#MMNd~912AVDj92n6^n-grI9AF zd?My*(L<(SjZm49B^7u+0Q?Z@EAHMxa(VJx6QAeo`cv^_LG9Ih#d}~@=TK}yT z2CWMcFe}29Ft%W+s4;iY4xdZiSEdXAHeniksvlz5xGhv!*vM^sH|dyO|oB$bHtmWlJB?T?mVLrzx1Cy zp?+VohV~<&D7?(utqVj1m?{7|Fh4w5XpYIUiK~|2iE*`SFsj*zOc{@Np)Fos-(x%V zVCgw1yFrNH%z^PkWVTAP?mSI2hRMQ7A|^~}gbf$Oav{=lI*J5IgdiV6XdsS3wwVCR zUV$Cydps#VdK1wcWTrj%@AYKP?jJ(y?n@+|s>mr_xLske3LCy3wA%;yx08|IzSf zuB|$nYZ4cjgRx}-n92EH7=w`Y4qqnZ&a-zJS@>eeT`#37%P*TvoXf!5Leu^s#Y3;W zn`b`?v14=Qr0bTB`TB()F-y0vug-~^c;J1JPh(b=hc+tLezcrwUmV}Zs)Y7sjj0|u zIALd%_mz~rWL3C)*juJf*d6biK8{U340>T8KfU?B=v@_(odnR}} zKjt7vzJVivEPvjckt4l|!-wpND$$EP5H?|H=!rn(;Vg-EeN0Ux&7f!6a9c>&il(VH zk{G51CdN1_SqKDHV8!Dp1F?D(8LOMxxy=&#Y!gg-+h5}`YM0Mf?Q!Sk6`eBI{tE&{ z_hb3HX+Kj#ztHqxaeAQXfH(84rm4N4VX)qq0uK`hvm$l7ZJRxe0V~?FjGqt__8f&U zMB<$>7wEu4fq~e-hnO&|b0y$wkgIp{J}a=P+M8)q7H_rE)ay8n8awt8KnU`sA0J&H z=r12_h~W;LmGcN}^;9NmrfZ6(9r%TvM~SwvEtGz=AAHnqzfx#$CY&0DP^OCx%`zaO z8h+y}jTRfjg^4QL^^VFku$E9V2IN<6NrhqLWPt$i#*ujy>34!8I54R&Owh3Mkx?DQ zL8Q+*01dhqJ3Y~eZJT|`f09xE?MmggL1lY$`v}k+Q=?OBlb`B1N3yEwbQBfeaNOin z+}rzkVqV-tc1c*?_d@Y>X7}oeOOo7&bLO!?AD6gniqZxU(@A6%77qkY%Y1M&oV1>5 zbT)S_HqB9(kt+~-CPh*M<0IfXAsCU~SjT$H{vmJ9j0mBs(;!FhXZx}GnnSKJ@kIP> zO`NjNE8NKX7EOjn6(r@aM$%pFyC%{o-@5O{Az(??t0Afw(_XQ+U6IM4)FH&96pJyAX2w?%djw~O zcV%R9NyRY4a?6b4GHXj9A;-J_@Df1V=E?;LnIoi$yKMN+bh5P@u?s+v^#2?0J?sF|B=aQ!xtHS7NjBn<}r; zdbvh~+mU2h{FJ{nyk7piam~&9m6Lag6kb%C_=oTM&1Kum!Um>Cg?>KH9-M2G4x=kroxNYj8^J@)>A8%s^h*}B)_Js9QtQ{ z;<~;x^H4Vq6MEtvJ~j|6OgjT>nc%E6V_~L|aF38~BK*x*I3E(}B~`}{ic1w6p0{jX z?>V0*4;v-f8r$WT_LWIobe~9%(?kTo73)dovq~|V@O)vl(w{ofNyGSbbKVyj-cp z41;p288dYC*eu!?3!mkQxNqNBrDGpPT||OWnWP(%yot)skM%`}g#hiuV=;Dg61`A$ zb&;kPyoQD(mQaDlfUP;`p8q??uRHvVp%6HlZ8T~7smoA51_8Kn#{dLiQ1JMKldnlQ zJ_&dhjtl(o1Y2&K*>xPryHa%kFmpEXCAL+PtEhxH!cf3sg5n3co_7?B+RE*}4n)B#g_=0`*tJTzpvTY+teZyhmEhLoMn1QS` zqs&P}_*)VyVIij73P*-hGjASmvNh82JL?m*om?|kuB~r)ix3%_u6R<<2!Eq|I&YTm zkS4u)_sCz_siicr;-|;-1;4Ds30|JZz`fKZS(%dij__`Pfc|AH-Y^^mv|lxg_S;z`g@K+%ExKT1dp6X|SPx3jU59Vt{a&t_;} z?!ewNnoOF^DP#Vr$LVFOq8eWP`hn){s4{VcVWkkaoZyWc^HoUP$UhMXV!mj&@+s$!Xin^fM-6#Zt+~__n!Q{Mc;b z2f=pXA}Nyd6$}F)YZUN(X&+$RG5Hs2XK~qQKMx3{KoI7CCUm41(%pA1dv9CP+~u*Q zWO-MNDZVUTU`~n>+t75xRD58~ju+K;H;ipfQ`t5~FL4RfE}23}UGCummw|giU@_HK z(kz(280NfLc(WwS5y?jls>DDJt}q(%d((vx?kw(Vaw5&AEhpqpjqWnd`HFs;Y}NEt z;-7O5U>sB|qt1!*(RDs+KxmexPA$!x7ujxheC=2=T~gwftBG@Di4f;~gq$7%eA)O4 z`6>axQ(N9u2RZ=Mtz*=1sjWz)GCl`WNzMJ~6(Y6~R&Te3JfKcNQ(z2VZ#72`8@gy%O@Kso%2l@7ufO?<$h zXV%hE(ef<-RSZH40PT3Rvaq~P4nw_u>qt|HbgJJ;j$n#sFI>_%e4YIZ^v|)rHg-aQ zXe+62l%M|<+Sk`k8e>}y<3NG6K1;UaYm%8rn~HzuG<_^-zeOd8%MM$N_7aCI19UJS zTV_fgZzaj0bG*wSLRYGw)5%Fw!xZay#TFh;rl3nM(}Ko0h}n!b>ccB6nm2Ahb zRjhLRTmPVb!}lXzyOZ>!)dZ82iHWi05 zAA0zbYLXeoINp;|L)6(p74_$Vq=Z6e0 zJuAA$lQ>^jf0M~(_SoRu?JyGq-xu^iKVeDKLg>urY6r3-*1T?VZ`{cyk!u5v3ixq% zkoeP!`(OAI5BP;kX$+xIZnG0gwdzgAA9R zw2L2|W}0(%>VKin%(Q~);McpCEzuBauis=QcWzP9Db3q+uN!Q(Z6;2hHxV*$8GQ8j zl;c+IhfflCqJWzkhvSF$)Wo-B#H*1=bU5;i6u2ne zAR}pI5}g)Ez=c{2n=106fr^sfr0K zhvo9azq|&X%yItJM+g;8yU@>Hg`C=&@NDO0DVC`1VE)0)exC*hb3{^6u$o%@5)l4` zek7dVzot$nUh{axd14(!FISuGDot=~0 zXSNYIF9a@`=UoX(xw?Ab?Wp?9*omYvqkvOm zk(1v&sS26nQ5EL_&z<*AMKTA%5}v=|e;5FbzBUm!bj;0VWOY#c`P0f*r71;GSR(ax z_*I{M6Ss7zB)CJIoMQs_OVzMWtlUqB9ObO|PTrX^-Led<>*7|nxi24Y355F6y-nkr zO@F#>UMhCHA3jr&{`R4rNMV0$)Whd4k#~w9fUAMZ3KL`bo?YS?%uT@U_3?Ay&~Hr`r`x9h15(xSr`f1xr1fAa#Gf;1V8 z!;YW}2g`xMyH(6T<%|-~SSWDcd&KEp4=`tpTID3$aZQl=-HRSo%&8U}RW!p06brzJ zE8iDiiSQP;Q2&~(J~}xzk%%B3bWa-1AJNINi6XnfvKo_`*^gdaBgr zju#g%iH)z)8dS797%Kdf=##Lzeq33kp2Z2@328H}tmFEcN)_|dhB30LHAXo(0hh)i z6A>xWSy<(SBX8=_Z}epE>y7W>#=RKTV_GEK1pTSQK-|@GG2c(IP_y%99j|J28y%H% z>6Dr@v%WTzZpJ~6S5-UWIrFdW&N*OPmd4vnxNWA6+Lp|$$@!Wuj+a`ItGTL1yu9Wt zOf+5Uq`J78R&f(q`sjOiS%byx!;jWp7W5VZH1z91jt8+_b0@YwGOi$tn^jnfhT?il09YHDeZ2W*f~NYh-~ z5|mK?a_?r^sCGTDk6k;d>*oGN)06=%U&2 zL3`NQOcR2y%%>?gSR~*Q7aFY)HNcs>B{cJ&ZJu|D)j0D9;v2c_`qxsCr`LgN7oW=h z_*V2;4u`v|j*ZF(se2W!dEc^57ckhRkH##X8$N|Q`1mypa#rkmF;;FBoxoB$i6|Nx zr-XEw4~xTZL_U?~mG&36j&0f9FKe#N$;(yjA$?K+*NM!xcGuJF-%Ew72iHmh zuUVe545o{8dX@B~lt+}**yws)9or)^#}SdLBaD0ro-P+FN7M!Ksym&fY%#0Wwgt_W zSIqhrYA!u?FX$d0&IcX+^kTS}lUp0KvMxOIS?_+zc1tXqH*gxqD1tjweqk;@WQ%UPbKcWW$?`sAtfUj23K(N)1y&p8#4jE1(7KnB( zLDx%hF6@ksLOYEdMl0sZrl9JpEzs!LzCzMwR3U-85M0_58;CXJO|X)um|Y%nC9C_h+lG?{bNSagZw+lB}E`mYjC zm4qf@QLTWksMiDWA_)Y~bd%>+34#(MISw1;wuCs`#QVbI68DxA1%3^H$-&H9-kcR? ziiMx(`Npz85~RtqviaBS?H9uet=A@C)qB5IC#@fkmOc^Pb*WzKPUBTw9C*lCNSjfS zZyjqv6l`f`L@*$ay)&~pVy%8|BcE*aGA-XkGmfDr+YrvV-Kh@vlReG}EBhS0xb-%~ z?@OXlBmI>zlW@E#Z`$k*eccb#wFCN`3yGeHSaI`Q79pCM>P(IMp7uA`v8Qe|-=&t) z+{xKxrTv4{yf4yJ_e1!;8-yzQ-RJ}!+)Y)&CJR-k;V*Csb8%2Y;0Rwwu@We`EXX5% z31*v`kn>zTjo)5-USOb_%lngfz|&k_WO?E*lw}gPBYWvL%|{Vzk%L`2U$br}oFwQ; zBgEG$U_p-abWUM$d}BQauDXWk+sagXlE|vfzOW*I9!yP}mUg)%IgZTO9w=^sHnn6#7`RSE}`+38Ubohlt z)fnwVm19?in+oF1?3R;xytV8zbvcbLV&>QBcL%|pYsQx~=x1`8^L?>=1ngQmhjLn9 z4`d1G3~*UBZg!hhEr%Desj={W9GJfDmX-9oy}L8ffxj5q%Tl;~T**INeE)nmwAD`@ zyC_*_5r4tC#M+Y6!#lG{OrI1x93wvpfv;I^joI^v4Ao5}%u3*F?!6*6Pkfa>py}*6n^In&Qd#HjO{0PcS&J0&z1yeCjA`Qx-A<9I-UoD1PE&s&sR4Bs z#cR>o)W48Jr#h)ovr|W@lsdQ%)EgBK%R`nD4Ml)9>K>_u#=T{txAZbHOW&|LU%wUK z@OkepT`{-Ee#v|Ig7vP-KcnAX0#VG@awVL z%&5Vzedf)0{qpz7_F7!#^~r8+n2Kz>yw2rRD>0Gv_!}afy4c@L2iSgmODOu zXjiRFsZ?ste3d4Nx3OsT8tZFjc&Xd>#KhW;-F6>so>nui>A0^L?b*fq4kR<~(ygSi zidl&+Lx(&$}dtg|#Z&bb<#=E6^s1?fxs!j=rWbIu3!Szaf-^zUbWBgQ3DnPV*sTYiYWIp3g_2i9n6>nL| z$hHe5xw*`|SESQs0x#^3XLQ=6<{JxKTKYC}uwquQUptD!!J%fj_nSpCUkO~kcx~;T z3V3+tQ$7bx_jS>(PU(z<{BFtYz%%`V4ozq4uVq;yZj4eXi5OJ{MqF>z79YX;V^ti? z>Mt`HtVjHMx>GE*<-&BVRT7+N4*l-Yeis*J($^oIN*?v9u$N3>ozQSjSS9E10XNt@ z_d~d^vdX$MVk$xUS7cot@XPnT=m~d%`KjDL;ni~8UYwU1ypCK0aBY_hpoJ?!2tEgb7T=h z8bkanqp;gNUb!2ffGxx6^U{27Wl$VuR3Wlw-k|sXB9^To!ma5fSoolj)@C)o3?909mqubtTw5*-h zGK%lb3iKmqnxx5ItkkW^OW$g)#XERV=us#7b)C(QPRc%Bdr5v=)}8BWczIS~D;;e@ zRBgQA*Kcw@5hL-TnbKPooSDB+xHqAHP-)`WVWH9Oi|gA@PhRh6PY<}b)+wqCmC-z9 z;`g1CSsb=bKqx8kch47%G)kzeb=;@6G2F`9Qp;A&%x948eX5bzsy16Q*j=x}%h%p; zG&ndm+Mla@mqeENEjT8_-bVMH*qHGqb*O|RT#{TJL?3%07?U$FFK<63F4_s^O#Hf$ z+;-Tq_Z58?vnc9ju~#NS-yCx1V|n^!p1-g3`S8-_?I{Jgz1i>>+oJNq?I(xniTHM& zvuq{oQi2lc>}6uzVBU?U+_c4BZh7+NuNBia0`Zx0*#sjm8I=_b4awPI^#i2@qfXOgC>EXVT!=iY z63zR5+q5K{PJ9*m7xIk~ZM{A9Z(1o$7q7_wDVV0AC{IzDx(bdfMa%wqyZyn%5molu z)^Fd63o^xcpPenn&(7LTacUh|;5JV0yN0*?`W?tQ8Xw+BlDqP~70@H!=+y(3bKGaQP zOEf`va(?#gd<}bno|f9ID1EhM)Dhu2?~8N7R={4rySnc$Uby4_2T*J7pq)_Ii0tZ@ zkEmK~YaK<~lIo81k@T9!$w}u@Q?6^B&W3Ne)xPV{plt#`vV~k_?^5eMgJfq7| ze?m6SMUuBtv$;R)JOA1JFrwmVc~mvINwcbI)aJo?wSJp>B20fXeeU{Z*UGKrPa*{xUTEdGO0lsC-7;?=QbPCG ztD;`^zkjWlldF~)_l$5Rno-dUUhhN!FTdfyXVeQ#6;J{%&kbA)O29K7XOnn}}=i z-O0A{(JO~5-R2`=E6|J<`xlxv%GVQ3yr4V{Ep$uO+iH=P+ttyrjUr9W&Uf^N)jrAy z)mR^!EYD66*xU~-N`w!k_ovNK(6Ql^iOd*@KN!&suG+nfyJElWzE!@68C;<77X7qk zRkCS>Uz~JPag*&I7hXxRB+BoXU3zP@61;O6sj*6U_$4EwKH*KuMchFc*$GG3l*!#n zC%J|*3vsiCsWIi<;}B0~x)Wh#oY;gwot@a^m4vS*Gc51X(yXKE6||VmZZP(PVLKHI z&CC&cgINCFsr(wTiL!~sjk@)tB2K>0%kXI(>v&hX!zlW7Pz@Iw73L7%g?^(0z zz~3sOXC8cV)|^;VQmP{C7V?t~{wvftFRWKuX=KiI$3`ln)hVBb@aSXi#mdQXoj1eK z)DuG9)y=Nua;Nz2&KZ^MLF-aRe}1yLJL48DemT>7n-{mAXqPfmR~dw==gD`jah|B~ znw6*4HdK_<hxDCPVFZDnGON+1fJM__`NUxuzI*KO~k=^lg?izOt-9 zX349nYlY@W-(x?Dd_^4b^VHaOK`{s|9FFb#`OxT5a|XIz1<6lq2#jkuq6NuBu2o|eMW`B z3llE|(q{+Hb-XSo+X+9Zo@aI=y0-)FPN=r)I-D*wUWIB`puf)3c`qy{^w!fdjXsw? zx0WZ?53>o@VlK-8J0Kh$`+Y^rQ<5CM%B`a81J|}8CmWdcgA0JcXzQXe?HS{}uMnEg zUw^AfWBiuJGZvTe-FFqyd!Vp3I>opho^i2$Ai!3_S)A`h{{DAx>5ypSrN-LG==Gpc zhw3SXOW%INP1QcZ;3XAB?O%vk=a#g|NEHMsTz}E8QNM1|?~gO8=M&IKN-LN(31p}3 zxGz&YqIfXc;p_q*z-~~zc*%(8(VcGPt>~oE!?ijzGMDkCjE;P}H`3F1P-Tgyml5Zq zcZBQhQ+0f2eTB^{otwzf&gTOj>lqdnAvf**O`UW>zSkgQwEh|#oAvU)Lr!p}>34-g zaMeYI9nUuU>h|)}L1z2t&9>-RQ*Lg)(a-djn}=sr(av?*7<+M>KiTzpYrCmO4=nlT zF^Z>elsQ|IBRFEGM2GrH?2EGUYhQEctHo+O3vYJ6_haSAO_%n|e6Op`>gI`j2;0z( zuGYsJhK_7*(&(Q(yLLiyY@v%kPOQqwpQ(i}2Z1TRcfTdT0X=TPJB2feOowC-anNsH zUyG`{ohJ$;b{<=>S>4t(pN%(1Al_E&*`8OFDxayGQ`he(Z-GA6(i(3O_A`RpD{}Ww zHvm8Tc2-|b@EqzBiT#DH?0lvKK7@&i z#BZ|3okgTocjO(&n@I3zSZKPM{6AcMXIPV2*KWqq5k*8%dQ<6L=_P=QfB^~8ODHNJ zO?t1HQHu1T1qmSxC4^9=ccOF%MIeCm8UmsBj^|D-i>SO1Tfc(x=|@QLbqQ`mpG1rNNe#Rk31d-BiB|;vjmGM?UuB@H6ORZeQY5eqsAf7 z`gC1elzRW-e%N1z_Q0L1IQx8EY(cHg1s{Hry{{${G}%PE?A2Iw?xei&3$$cgu#gcz z8%}Zeg#+8FG3^<#2uj`L=F%-dI=j2HO!1zfDzWThH|l*}tL#QRD9g>SSSfxI<$^+Q z#;+U^kBUMahbVv%q;W`2FHEhh6bntKQa9Uw?{4D4L3SJLXR;gc)V`0lbn-Abn7`E)+lLaf-2jX>M@^`8#d`!9I%cj#ddMa`n82xe) zj|W%42jzUHs?P59IwUd`q5L4-VN}up(kL4AdEaU*XzBvXn_?bxf+Ug%3Zk0toxG8C zD=9Yx57JIw;f0k{!IsC^E!90vk6l~goxbwH0Fe{q*~}KF%Iq%?VQl-HR>=IY^!!jF zfCok|s2eEwOvzb!FY%D+NF<{*+2wti4$);W_`9L0B_c%#X+^o84<$2;W zejlfjHX!Td*X$=^mz$zaqu%@iy{jR^F7B&`{Q~jW_Ag7pdpbz_;-@?(E5AUVzd(_E zd>?~=-$MI9dYiM}l8&Ifj$hCJ1?sT^h}?Al6Bi#f;?J`JGtc<@+m+K&2+Nj`!2Gz~l^ryC>-)@#hI8@)^x>MlJBaGuke~ zMYDl7=J|@YvS2^fo?#23KGK$*;DoPSw0Sw+7CWpZ*B-hl8HE0A?RsXk&Srr9xvsJ+rfZsIUjM#LYTd6e@QZyM)Ca?~FbtH~ z&(w)8+o?T<@Q!QZzpZc6cvh6_|44Feyd9)?v#moW!I0iY0Tuy;YVXxg^k0~YWR-c_ zIVDY7D#5u`2XyKL#I$ji<%;=uL`LtIBU*!uYbQv@syJX(4b30pQ|J0|XC@Qu@e||l zpX8D2K*G6vrmUyPr~>3w+}qF}pc_}0|83IR15Mih6nz&O zW^_a$K-KrPI|~(H?H~?$_(+F8`LgmmMC7?_I0c8L96V|w{qe^?0LS`INz?7X_(s-p z1**yaiD=@&?wtxiYx=<-paB4&3VN0Bvdczl9(kB$#Ft=5{`(j`ux@?hVZx`D=mzd4 z8gUl!4A~0*&MmM}E$PvV--$!xhSi={8XM5`msTY$VoXHLciTpZsn`(_6>xnimg(rODz zSl-g-qAc$>Sf+u`-{UC4^J5}$D(boVDy-GDBV%c5BN${wZQjaCcc7tR5d<3+r>>PZ z)c*=sxp4h%6ia;VthpSUYf%lb^BkvP$7`(Zbnm4<%3aU-7b=6IBAAeh#hc>LAQ^G~gZA zg&gaRDFp+Dxv1g$8LRhA2U@}`?`c7^Vzj8mTg)3z-}Gy#o&-#DC7qr&zCw4co9fG( zdQE>B9wdGp@79vs{?^grB{keeEoVK`T2TdS5j>vF-(c``glUXdV5x?qDOHn6T+D5S z&RhL)zYAqzi@T?EySrVsRagC^1d&y;zN91Z8M~@v`O1~yOht`5VLZ7yt;C7h!5t2+ zyy%VSRlnx59X%B+N-#;-xc@-jh|$oeG$h~PRMD;wys^+`vtdW6>J=>$0P4Dy_|z_z zeHIfbrfpuH04IA5*J|95A?^^dhkl$E__*xw{_ft1Jjx;E{+AsW;?MM`P_Fc~^F4mU zVLd^*PoI2Upr(FDflyi+qFPi9w%k~LKdF0sn|;T!`k@^$|3^N_(1SR4=tJt-Izb+V zc$P(_>-J>0;L!2Y$dHAQDCq;>zM9OWhsLQ?RCHMt#O_(PRvMV=b6rQ^OSRagjjT}! zaZQ_b7V&@9MJ>K;g`Ae6iq3n*#}#cPuxp16qWyk}~~0hFj$7GgRdmE+5Rs{5|8t zItvsf;6E_qS zwRgoUWp3~+_p87?tSmGP1pA+u8h#;aGWX9Zu4_$m<|QO20{%m3`chKDYTN?r)C!(c z4K}(?Rw58(BT?%3m#dzp`e&gC09#UKqn5SQ(D!n7(kL0yos(gzEpp`hW^299(0DDN zI&mP>;mK?D;KtrclB{Q?Kg-|!Y{JsjMW@KgfDv=d$S9|HAf0*|lQ}kVE>@4Buc@*+ zL-mFXGcVZ!vWc9dC$+}#7C>?{QWyyh86}xALqP?xnu~7UDEs`PfeZ+<*S^w<^fWD> zVSY^SuqgNO*>35KN?OT1<24@_(Zb^F8wLe}?y(jO`Utn=@8rMMExn_RsOtH`;*{$4 z?^n05E%uj}{^1Pps8t} zc&NIYPDQ0v0hG2+Gq51?PmU;VuyjtlgJk(`l)a|@ zJCcB+cpU8Gd@VXWa#g5SO{xKd@UXK>cB(CWJSMY;n=r)4&3kxxcDzD+Xz?@(yQ!!a z@rCW~ap}DvdD_r-y(t(Ye9^@T5D{N$WXUv}J(fOPY0dleE9>s1b5@)xTAnG5YT70t)_%Q~efkcF-~_pc)KsgGl(1-B*0c;L zgg!|zgt&D??#g>$uL-XjX>;kPoERw33GLkk|QRm z?xgV5+Tz@L;b_;|Lpj}m8EM}^zr>CL1x9Yq%@Tp`@fd!BM7TQ6KB4`=(9EaNVTXKl zWZnEOPs^U1K)%BYq}Z&*dgl3)WR{r%wCfQjIqb{Ul#}bHtc{eD4Jpa4N?GR^efmni ze0p{h+b1Sk>WZ6G>)17apD)@G=eKOg#$QKn1$Z~u>Wb0|sIaljq|Cb>HGVDoSp(iV z*V_BrE$S7S12O_2TBGbXTsqvwuyMRSOQ-aP25@z;DIxVs+#&wv8K_Edml zCocB@fCK#vnxO7%4#7Pa(FgKzO`XQ^dsFvgYbabd*(Bh+K&j!zpH~wA5aDkRo+e$T1Lb|Y189VRM6us;qDi%q`gs6C6M(Hr{+2%qKmu3% zO_F9Lp#6<8f@Py@&A!^+rjDPE)8)0yBo7HO?}fWh(*HISLyCG=X|0~B=qzL_-)5HQ zNNGXUoK0I~R3`E`XNq!g1m^~o1n1YZQ{P`HRIeB7;0Wi7aJ0%xh;bZ?o!&K<5^bn; z?k;k+g5J{*8O}pVkv;7jY&qUbI#_AxLIT`L87~N9Fw}@8&%Ok=8|#1JhyWzQ2GCdU zefr?)pTHwOlc#G!vHJ5Wx5*V6gs{e?l={l*qkwz$r%m|hqX&-~Q&HcDZ~pod#3BM95~pLv z(nFjBEIt=aUHWT0A`7$s2|)aQyR6*+Iqt`&zxS+_b_tEjl;bJeq)YtW`t(Kul{DlX_doudR?CCl z!&t*~IlBU0lB}ARs$SIQzT)PsXmF~WNXD|S1)o4cLe%;R(8l~wxS-L%L)HEmryT-R zuNlK@{bW&10=l93I>uT-xAD}KeOQK_p%YYNqSN4{4mKL6AB#B^3T(Q8@^?5RWOMqB z=9d$4bq_hnj^jU0r3j%c8u}|l6a9H4B6`DjSAD?Ytz{yZr%Qq?Ih2uhtGYLvY}@x0APoY^Z^$2T|SO2Hge7 zun!)CZj1SEb@x9S@O9)?vd{7Ko|~)9tF&aoLySOoNFOOUht4JQ02r|^Wb}g(F zZL#pNj?7%mm1tSfe{-YCyW?F$D#NXW$C6J)Gd0b!ueIyy9F}W1dYdqbu8Z~bT~ag( zikz-9KYtvV5WA+zTeGkjREhMTaxWIJ;pzxk%*mPY=*+ko@D#9tD0r_Mz54u~NmQSq z>1k1V6Q`L7QSF|I_9?R^AIk(RY&e_AFzG=lJ(fCP;Xc7P=5>J%|FgHkcd=>7ekl?U zn?H!V(?n3>B>e?&E%R38Ps;noDn5W1bW9Qsk;7~~g|z^X*IBc5nW%}l`JFjd<=MyA zS6+-gC@LeQ3f%cn`;Opz2^s}BOME_4t9xSbcS0Msk0k*e119dKp{T`}<*C%4LdBz5 zLOwc=f97=760X1b2JoN&66Mo)5O9!@0@R&}_WIkyjJuY4KL&XdyN0vM$CIEBfBJ)N zmS>hy?DH4WhhPn?u+bJi>0I#Z{>(01+@5e_ZnD0Dj5~4etFWLdh;Gz87|J zOWOm0J|i;vfo;QeMpUxYW%b!RRT^K2#;xcDjzqv4;Bn?qWlC;Q<`zAU&}e!s4Ynk_ipwn`^k+?fTCB2!MPFH_qeF?PKz=1(IFt^O zlVQVsXj617lZD*d^Bd_9IXGdO@v4hDL!#-<%huC8oPx<}m_mM{j(bQZP{~|Mm@cbk z(Xo-AY<>+n=vZf-F&GC!CAyykNU09tJu&SWT7R6mmL{gOta`1oaem0gnoGS;iShZ4 zx1XMXjpj|!yIE*yI6K*6Yu^b8Y_@aW-p$tYMn~lw$>tm$vL)$w4U0L33Ay+wVxETv zXjkgy;<^>4>7u5DHN@Eb;tTTA)l&LHTu0Zl_|uapU>q2Gss^_au^;xT({RIKbcb+! z(Ukr!wu`%}-nH9pU@Lek_Opqs>sAAEBP#P|@*XkTmi?Z;by;~0d8=+tQT-Oekh!?Z zJqCh}i8(ZquBzmtg0)v_Y4s9rKJMG6A}LW{w;8qfM98)`@y!4}MXULzvuS8XB4V^U z+i=)?@MG=>YqL)GFeW$U)8>vyQGP)M-mX}-baAVLK0;p zD6hTQL*#9{?LJwbLg(q5D$q0JVevGItE5fY2JM#>2d4^}NSQ&4_3+9%xelZk086n} zFaX+UX^Ouzt`AB@-FslwLFs-cKy*1RWx8v{`DBYK6CT(wLqy8~KQn zTX?~psHvEYCj@H;Xlu!vi#Pq;mMJ@d*Dj4&Z&VXLHLF&6VWX zxpq%FVym@FptXkC!rlTOgooZ~n5gr1fA5=v^?Rp6v`VPRvgEi3lKoz6uXdtnFL@SU zJK=ODc^p+sY#P5Wva~!@i4~D0PBWaj_OaUH@L&dXg(34dwJV2a=U7B7$?yv6DFB}dP-ZfyGt<-ay) z6V-N!@1NUEBG#b2rJ;l43>8Adxz*2;ak@aoMfMkh2Wx#q8YO-qA`P2RsSsdC6YMS z_FsodbS4fs-MkaZ0(}zcqTwVgGptl#qUAMon+Z_Zy`K7FvJhdp`Ow8y&2Yo(5rPes zn_{Vva%;y}z%WHyL?}IK{xo9jUG-fX?*O%F-59ZQwa1A=3`7`VE;$Z|PP4?MM<#1P zc3=!;QC5sH;EFdBhNT#zR}5VIILwjzxZ|wfv_Nhw`b;IkQTcbQrd3%(Q#-dGRz<$4 ze_$A9K?I#Rc6&sA4&R+_T4@a{r$EYmCNpv_l`VK-ag~x4F9y zT+PuVh*iyv843pqeJ5zCwZ)fNz+UB}xbc&~8 zo1%i2CV#e-K`xd>Hz(a(&zyIC@)cN=l5K6Dq}b3D*tmrJ0%_}2vIwc_?d1_nnz~PU zCzLaVs`%Oq*3)~QwCc5FJXwsat;oR;epE5&2ls2_EHGi1pg9Sv5UcQl?-ia%m$K&s zI%7yR&`cN3uIiW8iBlYWVre#1l$Fk6%2AOx#6(h@>c}V{8syLAe|bxeA24t)kVxz2 z%2bLRQ$v2lfo*E7WVa8=ZYzY&0aYU=oaW5F++g~8SHq+FG*hn?4U8xr5HK;T|>5t0U{3==v)md~&#U2@L@Z56VS0kOjG3rs1abHb`qfA?e-M zEqknKD8t-M4JT=gOmy{hO8gYH=eYL*m7A6KyE&UEk=fXZ>QC64dxIwSU}r5UGzV|6 zkg9vW%y%ngoV-bwR zp@w06dZ0)&((@s^iH;*%phV~=C#7=j{ETGzguXW6-0ti)X6q%3@jU}`B_V;frlTfK z!jGMOc^57n(m{8}x7Ffl`J>pTA&kqjcK4i|aR|o$`*~3yg!Rh z9Y(O_oKCY$$J#mU5ebACKbZ7~y+h-g$*aB&WFn(YBF%7ttohel zUPr7p!3>rf>+uHk--IvetC3>K3&V}9x1~Dq9*DnwJ_f`Cfh3vtQclj?NnMYyRuUyu z% z{K|)17rR!FGj_JMjotIVhkt=sO>UkF!@W&P@dHMj2=Wp23OqjLqD>0kuMYGJKb5XK z3uz5iq2|656L5mAnn0$Tg2`Xyq|qv|@U@f}8ir{(MiMg!d{7ui-$c;VD1?90lfP*5 zz!KiMe#(nR`^j`vnCR@(J^i((Z9^&iPkUr=ovq z1k~rWsh2F<gnbTyb7cv!vW#)~ z81XnerCB-kSoz0js}d>Dh+;jVJnjhh<4QC?>$%`Pv8;{zwgxn~R0)Up1&$*4T-@R0 zJiM=D=G~0#Th1~=;$FLML9zLEY)IN6i?5m00mKb#D|dElzvy_C{p-75Aikf!K>GtSp`YpS~n{mPXp*}GtKLF zw)&s$adaLO1e?O811;*;BdQ>aFF;KI19f(cZ9PQ#3_{W?CfXQPqi zXyBR6X79*49{c3m$&`~m^o!cq7N&aEHwoG%JzLfTQ)2KG^o*|~4!X6L#+=1>VPd1S zZBtpxe@>J0s=%Vx#dn|>;#5PLN5YTUNf@PY^APQv+1+d1kb-K`3cOu67)sp(Vd$LaWeDobqvcwaW)7 zCraU`Vso}XDEJlt%|YA&D7ig^B@gNA2l-~?Gdl1l^@Rhca^`5^X(|KoX6qhg&7*gY zz*H%p!=FoLxHiG{EUV1(l@ZuJKHFHY%SN?|*sXC(F=Bw@>9rC;&aG4@pB*Xq>K>QY z8Sw(Hm$5c_kRO;2|6Mjv=r|7mRzmAHI0=*wh2<5c7iF|lSNozE0wH{qrm(xD?6csU zpxl(>4nf!B&X)FCiraDu1sW;_I7&Mn=vo$A=+A7kb6lO-=Gr=cCAEq zR&5jd-H+2&GE;4T6i(BsRO0p258;2FIKd~4c1}3nXKeDEZW!Xf_8;P7nDARG7pGY> z7u~W5%OcD(&%W1qoA#T6 z6;97?+IyNJG0d{-o2o^1wY-T!2eGkyH!k>VngFEW=&Z-lzHF7W=qjfqOjX&Y_V*!9FqS{)vYf@PRakVA8Dg%qsab zqdDpbzpHypBR0k5^$T<$Xt~z(YEKG2_Z3(kOW5P^UAa>Zxf1QP;N>d6V3zXy@QywC zN-=r~=+BH}!qyPgnR4pbyhnTWV)P#2$WkJ>1zu>GNb$E|Ja?QsL5&ypbgj*|X%sXI zz#CkT9ucg@i(+iNT`GP+uG9?xj)GCJJQntNC9)vvrYuM;VQ~U zsQq6c@e8YZ<5FzwzP3^cm(JRe*^?DIp$nU{KGj43EOlAc zm$G7yXTofxoNHUQGXgbsJb*n}p4lRuu5l0#aPGL>2p3jL39xjZPjgl@JzFbbnZyD6 z0QMce0YE*#Dks6V87Xkwd_NAIfK_GgdZVbx0OWbgNA&q<$!J=A``;r0&3kXfB#u*rUR6tf!tJ3)J|${ z{+SWQ+nXow`%vJUod!Uhe|1mR>vhZgKKT6A6XBC|W{JPQMe#fWn9aYfo+B<=yp9Xt z-y=4B;X74<7vZ7voV_ZfeYuM^GrL;XK*1rm^X2`N79GNzq3h%VCANgVL=xvOk-Z*_ zlqNa>UcS!8=U+(p==jn37^7E?aXx!;;3o>A$q8#sSwCl%>*EL>XIJenLuo7J7YH49 zk$oU`s@NAdLYZ3lNjY-|0|lj+U!b49B{M1t@LE#QI?xsS(Jt=Ay)BIW(sU^Hdq=3W zi4q$a-LO$yca&zVs9aq?j6bzGyI!;)G#1{!2QInpC;`OIgGuwg{!CM(yXPL%-o^2* zTSo0!)apGvgt-H)JeRsWo^8ahiGY*XN^GP*I1VQk(0IPRjv|RT6$+OKqb%5SW6GV~ zdUQ9uk76;yrmqpKL5Rt>gc1XiWz~D|uhm z=8STFz*(-PUtLq(Z$jx1XyKI94wZV3?he>J8=)F4rna)(jBmy`$?5BRHpLS(V>$FT zT{^e9uw{mVM}CWYd>{1Lc09e$F)R`x{JH29fhDL}&5o^Yr2R^kTa05yu-_3IQq%UG zGn^|Wk)S~PY%ScOnuOi8#m_1l2IBR4x7o9I<-7`oYP<63P2Cf(Wp5w;b$=>**KT1k@K#m_Bn)$dBr&zmc(Ltg+iHVP5^*Ae5Qjj#v-Pk-4K`N zp@@Lg4d0LcF>J!&U@K)czY~`^OMXIp?@t9qqrCmLI-MwjR&lj^_da;@)}V-LTugvK zur0Ls&@3pW_pBFoP0z^dzQRo@dd1_(9E1mf&oWGKq|AZ!?QDlp`*Iw^S((Q{tL*_Y zUjqtt(P(k%*jV^6JKEflkJjt+gO?8FCK(9bTAO*Dtcc3wiBQjWx1b?vbKV;z4)Oas zd@>tR!qVvJ4%;~OFRP68uN{Jc@O-PkSs77H{=Y%^!||SfdcS%Fd|*#`EHHJC1;6zZ zPVv)+ONgcR{!|{f_sH0Kxf>{UoCoYW!}1MlllAKdIJ~@N%RNiF3*c~GJ)z;86iY3b z5FO?u=#lT0cgSaM!80>L*TqsOik0UdN0VufUNu!8v*7nn=!f7^${9p*fYf<@hG53G zKF*sL4$8`zL&c}zJ12;B`E&NuyrbtB-d`ZmFXXvIEd#VgeawO5vhHWM72hCtrvr4i z2TDjUI$!m>>M~U8Z55i)qyO8=c97&+lGDe2VfVAxAewGxoEjhe%k9h&@jx*qR&gz_I2ZNEV6jL`^-ZPZdNQE2?*n_9?EQkgXr@cU+^Bh zTCEc`sEC>VG2oDei!IDT1SF-LKlZnfdX?NwEJL2KoyZk)FgTB~Yv>h-bmfc$3|jW0 zRm9?o8$Xv^>cA*s8N4?7p?hFP8M)%_l7(}fsw?%hl^F|_-I-RidH z{e2zH6IVPqb>+>lND~l0%Eya`MGchPWVV~~FKG9;S`Jb=6_XZV$;!u3zI^BXDHQe3 zaBTpKM~f$J+tw@cuf=Ir{{rpEX_M|PH)uLsz*V{jHo2rbigFvQrgX*QikF%!@1?1@ zT%(AcBwFH%^Vtk+f7ClZdtJ4ha$#elJVgZd{CtemwrP+U0-vuB3ZPyW;(gYnE6=!lzEmMS#rAT z=_K_snKlE?X0AH25Pc*(+#edQxB~b}rTOyeA74h(dy2>b({HA=625)mUT_*$L`Lms zE??MsCj_EQ#xUqDIX?SIP?%kL%`!#6)?-RZ`=l%V?}gY~BH{O4{n^llw2D*-PeGs= zn@Y+6MqYByE^m>PFVeWJY!nER`Qg1_L6?OXd zGbC8nlHGMfdL^xlcXvX)A%LQs)m z`i%*7byYo6gjK2+f`da-#mHL7OZe@@xkkUb*}7o^iGFP=+i=`}MY61vzw14ng&l?k z5Sr!}KZ%hPi@VhqvsuYV|86!}UPF^ES#rO!x3R;ZiRZ&YNK!B>D%scOH3H^-w5H=$ zy-LhJ@XLqZeiDTUAAD;EN`!EAW&1w zcKj%+(&(xsc$?-gnBC_plN=l$wt8+@psp%nLfNlwXgrpV&F>_x7`GfU5g=woyB6+> zDU}Wrj>{FniO?dQ;juo2voV$+17zTqRmLVUMh(HZ5C?9&R=!Do9>k+4|8-%+#Q43Z zrMztiO&4C9)v870V`D3zwaXKu=d9b@+?8DJtZ`e7^j?vLM6(1S^pHEji z8gaZYFmk|lky?&VPQEBgf@fsV8tbB14F`;zeZtf{a?7!iX~=UW1(Pbs8zSp_ieaYpi|Oej6~boE8ZB;fi>C&Ul&<77LhzS(n(ZYgCB;GZZzG zcAth$qw0#Ar@ z*l4<=ziYGp;HOBpRs_RZd5KPbWlJYQ)+qNZR=cd3p;n~L#Y0fs;B7d+E!3y5G7d*j zEz2L4b&i5o%GMCx6+|5y)i^-p)Sje6)IF_~?#6;$(+=4O-0PsHGVn~nWCMsuD%N4z zpm3hD;_sfdz~;7nc(<@=0=dv3@>LD}ML*-V-)Z-Tcd|xO=5tejd4JD&*&yQ;lfX!m zg0k*p-F61$M~*|!?|H!R#QeS`{rCAQEZHa<8yDk~R2@$o66K@1o-eH)x+Xu2u_(sX z;PjL9d|QpKp7{fwG08P&GlHJ%$gd5UrP}ex#LCV$W#H7=wh>IO1@O#i{hk;N}lMp-10^n z2`a5-4b5ut9TQ_o3kzsDG*=#n2@P?{AA#ZQ$0mMs=UZ6<7Iw*<)A)MJD;)C}xUS?B z5B5c+jZ_LgBHi?~05`wN?kWleTAVt15txEE59Q`^LTp+i1*iM;7gSdU*sQ37 zHzkn0Uga3w?dHeOs6WrW)Y|PcyqKJWfHZdmS*IPG$}f(rxu8bqLVh5s5EW0qLMjGa5{so91Zurd!lprr9<`B zt`iEUMb`84Be(>cED2~vMKt+!O*EH&B&;5fxOqwLyCafgl@H|iS?Z-|4~wx8V|)hUU3ftwUVR|Cm;mQsvW zEu5=zRk*QDn=KYl!75vrtQrp?nZ_m^LLdazBt5Nijr=knmCz46hFxt&!cA?{gQLR+ z&s?!Uw-^b#Fcw>2*f#qIj{)Ee{VKPf5r~T%kjV#EAv_}umfW5?^#X5-|HU{xXQgLm z6dck%P+VSZRcx>Q+a7z$vG;`5wda$T^3TJ}&jDf9EcO>2jki=LB_8)m9Vw16mrN?= z3YfLI5*EW#=}VQ&QeSP%xND+!V#z=2aQC53+kVH)($h;3+>ZbS#?%K{OGuQ>^+SXl zpay-zwIL;>#-`OTRW)~La>%|!(!Yyjy>BI4_&MQQgL2`=lK8PP8TstYrV4NX(7%OV z|0AnGiDz=IsX;TsM+Vgn0tOA}>f_6Q|2t@|T%8PZ^VB-=%H1mQaN1 zzYKjE)RTnWEZicsW}R(MKCSD$1-R>)4|b;$}a#`=wH(c95-T#w;;t!ec*m zK`&H8iaWS~cgdTd`)+*Hv#fECmM-IJ#-#TPi60!=iq_q2-K*%#ddy63sT>4feOKbf zF@O%VkhS%`&HAKu%i*v458klgwCrJ{wD*1#yUk8^VG{+&X>Z=V0bo$yfUNdk?f9+@ z>D)kZA)d}5!6-JPM)tBY0q6ILPbB&Rr?o-=LF6u;M^%KZMa@Ue+%$C5PBXCJ^`61p zq}n9pq9+MB44#CrJx1JXH$6f}H|YLURig>M&N9%f>x8Q9>Z+BVa1m(|LBGmfJj&S_ z8@r$NHAFw20R-Wx>gC-ToNJv9OSIwrJgHPgPAB{YL%Q>_0vg|Pp|Jyh0@MHP(f0>0 z0qK8`Y8m9w7&j`YT!lv8jMLEUG-H3(#~z#9+}A4!xJaUg3fF+IGUA$m3>f7cx46c0 zjVH{8pZBXjiQw2oqs)VyuS-2yUnI2rKBQRe=7*p&m*vUs$`v8Iwp^|t&!Ro;cexS)fDGyPmC(ss>9kUuf7KnAQBq``!Y1d4Dqb=Q3C^-B8{Qn-KO1}tF8Lv zxMYn4gfU%gik5c8*^Ed@pOPDz+y3~Einsg>NE>~%V!Xwil=!L0=I=`=+^s7*1w7yJ zsWx(=Br`qUVjI$i82M%FRT&R4F*ha8aKvYgiVTKxnfu<5jV_&+e?3>b!D21WA{-ql zsgu=NE**LALEew6@mGPuCdl~Btvmn9&2nG<9#_6S{p~?Wgz5lH*jP2<3gikE_h0{7 z5`ZGEPyx3SK)&^h@!F-b-K{IamzNa~0G6KFDDat_y0BiTKrQ>oa(!584Ew)P^FX28+DI=AnnEH2XFp-aQQMLHw(#sDEhvU zv-OLo3EMWAH%vsCeu2LGKI@%h^D!Un9d3rsXSK0dp15vUK&X(U zAR!-$$GVjOWXixn+h}<~WJ=0{a}HMH%P!GsltP<_q4u`lj@G~m~4(}z37mvV#l;gg>RZ12@R@6SA7M&y%{V01Z zNZVXZs)IfIxV*k>U7nxk5Pfh41D~rlxP!-f`=GCWdL)CM0^bIZiv|4w8sfSNtXkj; ze=tc?`+yXD_TcH|5&GuutA9SY1jzzmVnDU<$}@%=4?76V#|!9?;wYUI@DS z*PlR)d1Spv^|yy6dp7XOORh7pPws&LJC0`w#xMQ^wN$K26}g%F0&9`;_uMF73yz-g zG+7Idj{BR6w*cMY>+0{PSI*gHj5fN?XGGOS_Q)tGjM85bTifjRC$O0>pXPs!-=6}) z=aSEK?UR8=wl>I?j=CrGNjif5-#NO1TA-@!(|8Jd1eKNsSwM>Wq(wPykwZEDm`Rw(AW2X%6(zGJCz) z%ob{UotVk@z9IvLx&ww_L+T7qx=?SPvi$!Wj|QO813I4e|5}Dcab221nWm~mk8=a& z_EcP^IreQt91oLvRM3THTOw1pOPB&8gbmF=rrRLrv^jpRfvGOV&^DR|91>1|GF+yC zg=Oh&_W-;~)t#1|RkG;jKtQZ&Z%+ZSXYyT*s_h*t?X`0k1;-)-HnC%cIn!LGIAxrI zqrD-@Ue7poX~J@|a{e^syc0F-HvgW{tRU86^YByf5I#m$2<9`aNzaQWFWkTQu#}r3 z=T{XNk4|~Dct*G#++X39la0^*MA+Z3Sjb;Ba8&G-lG)mUl@IHEjD)zV#|XHA(eB*( zw*Uco&{fd2kpU=dFd*ZeDPV}J>(}vcN|pC(!qlWFMCvb?lZSw~C=kK`X;hULlIAy< zE8YXE{r!NN)}7bZfF3e1n7GR#p_)o?E>m3(NOIz$iO2oYkd%AoFZA6)3~70;3%CMj z?b9zjcj7%w=%wwVs1jb>;0Bh&$Cm|wmh9#DP40^IpI1>84&Lc339T@50-rVdk%!2Z zTHP?sOe#Hc{`05S=<5Q5m%$Wt)#TkvamXXc1K|HR-LxbJ?YFfXlE(d>&d!+1yLfvl z(Y64(QPSHbep9NGnzSBIN(Vu|U41iZ6C@arVamb5Vb?yce=ynHt$$q9H!N6+9A-?q z;CMBgWM;wK!e7%MZu1g|l>dAD_RrIStAHuV1AE|9zFA?yOmEkl^|LIxH>;k|?a7s& zJ#+Y`-hHttXIOt7@uZ&s+|8t}MSjU8)3t6e=%HjcRjxaw1#AkB5?gECr92w%? zH>FCc*-6mx>E(k_ z4d>>dtjo|#QEE^ze%?_@S1}QDneZmDaa}oHkGi0>HqX z-2G>}W*)utsQV+#knfsFv2B<$2WQ`qGuFvtiLh>-+m1-t!ra#rNNKjEe?R5&ll~q5 zJr{lh{l@(pH=w5Y%+RW&vU<;ZV8ubXp}JYQf6-G#^W4x~EiyX5(A^@v&)lCvH!3F- zmC<@Qm^J!wj&Dod>|yffVy24^lu)Q6(_GwA550qiR<~BSc7RKijE;^mjNL6Vve_)& zD>P}9@Jfbf93h@^t}*e!(Zp61t6yLYOBbsD`8sZFy)=0uA8ipZ7_0;zB!%i$ZjV)4 zd#vpBx%7M`>0;!|n|pGmZTM2+r2A?i&*f~*N89se_gR&?C9@J1+N#^|_qvv*j;}AAAs&V1V~7%|6K^}YEJjVaSJ?`gVHuVx-G?g8n5DH*jae@!FraAwPW5SsKGTDH)n_R<-CsV+7c!wH+J&3C1WKd!RsU@L{O+ z=)~=O|I$hX-MR9C?iqqRg8R?Q2Lp14P*WXbYl34>#ryf4k51t1@Br(ThQb=(!(w>P zo?xw3K;O6va19bqW0wQchsVkp-#$u9!<$hA0btl@N!4g+^-$-!85{Jett=~^kz1~49 zk+JkXvN%w8sW7hZekQllom1aok7E$SvZjgqdTgvx8 zgU|DR-@pFtpTkI9plPQsFuB3;2?uUvB@y8kQt# zBp~0x&|@(|u%FRc1VZZn*IHO5>qhg;;8(Nq0dgh?#O8Gt=&}a9jgAiBUT`6ildJ<~ z?9XE$)Y!Nrs_$)mF2eKAf57y~XFPpU?2I%xu8VcpNC3*VFq1L^K<)#2Xpo zMk$CC9S)@B8ulwXOnaJ+ucE8cYqXfPuyM5(LixX|eJ5B_BUrO%o_besuQ59URl=IO z>lGciEG~t`iwli`17fJFZ3t{5pS}{^U`p$WoAe77^VDb7UzWAx=G{5mTk^R(^W$^x z#t7@VfLOp7>Zd1)<@2?#5DPMFY3BNSPqRhr#>i&4&g=I0(ufjkzagF~J-WBHrRP+c zDsis%OT2_xzxss=}ekKbrio`H@r_Mv}*dCn7yIZQw}w) zH3_{W?Tn5wO1Ox5wkF=NxR0(Rb{t&4y1Ex{B=i+OqK2(&)!Vs?w!PK!^PN0PjaZ-j zZMW1EP_|k(h4OmOgf{uol%|tpD(hiVBgb*JBT|raqdVp35jyv7iEP6otFA(oJnEMc zG6FnlC;M$)5 z)g_I23T`F3JG16Kd_bCRPtd6$>frU{RX0Cgi{x2^QkUO^`&&{McUOqae)+-otWWig+iFsS6tCBac>Jx2U!N6&i=lgldR@B;)e_;nImbbkK1X4}N5v z&Penz{9RrWoPTA(*!-Jr$w<~LwR1cvAbYhXLE*ow&8?R`Jy+On$4IW-;L!5 zh1`{(f-ymBv}Q$9uNcbR^kNn1F5J{s+*O{WYpti(R2mtl{769iG4IqZBn&>BQLd$da#z=QPe!of3Yw;r^*&ZL zCbZ`~ST0B_4ayd8(0(e;WqZiClky$Kv%E(yZq1TnT2w~H*4bD+ z>#r~VfPg|$VRmX>OLe<%JXAE^K+))dyN_q8$i^e7{o>)inL372N~Sta8j{$A6)3^$ zZ;Y6h^$-XVgoGFLucy%SXnOE-GCecl1x2b3z0(Y_Q7aBkAmk@l8~~Bh{assaSMH>R z1#nkL={^B76L<|n02udFHrfK#dn=PE1cDPx7en_d3I}X|e+OeAcHqs2v@D+xcE=Bv z#a9RWY>pVP+7J{I{Uq(7zmtb|cdgBqV|1cta~5l>23BL({gFjq$KQr9%6?V|{O2N| zkm@#Hw^Hjbri5o+avLbyAc_EOS8-0S2m-na2CN=HxCm9!h*1$NPwo=QsEXzAHqDBz ztQObsr_gCua%dm=x_+>`$B>(*9CKWknp~N4Go-aN_DBR7S8e@*8yQUE5CxoDEO58? zR6;Gy8I-r#wjVonU-12ThV4u@e9`>oI17JaRl5i#5-Wf53{2rgjjI~Sv zNo(aiRCIFic|UbLpLk?H4aJYriDmQNNXg|6E(+3xYB+C~JBJ#Nwqj&+*nc3IBI@BT zhw{(L3(VhjtWUgPQ}Wk&_uK4`(<#m92E+Z>z8XU|1nq{sf~F#0DD=KVqM}oZXu5v) zN_SE;Bb*7am1EIi0byYV79|jxw11xcz{Nlntt{ub=RXo}bGt4DF}a4J!srAXQosSF zBT*)P?Gh~)DriVW1>!$HI#>ZhkO{<&#xOn(V1$lK!i)3jI>s9{fk33LNkK5<8svIJ zMAo3V5HKZUl0mNRD9YOcpW>K;KECN>n_mhl|Lj;w`+Vj!dPu016jwrFwIXi)Aw3kZ zd;WLb0Q)Z+=f?S)C!|;@DZUifv!Yw6#ZR~H;48{i;6rk9kyrQ6$&wY}g$iMa!s zO9z^a7}B*Byzq=iPV&6D;U7=&<&M%xsmlyNviRdEYwfr0{6IyUyjoV zDSih(Zf*y304I4CIopNmVrB4<$i=wUXR$BW zLXUyCAvdCO=Wc9F!)2R-T@~tuzwk_#+vFcvseTKvG~)TCzqv6S$rmzNvi`&w!K1#y zPgO5*QjJdlPDP?C~?d!Q#rK;&oCb9JnmM57)}^? zj_`z(k+=gq|0J*GS5K^rvXH7%-#|_;WgwFGZ9dd{pFf_&Xur!go-zR_#+3%TL{toj zYd754Pt#nt=Jbj1?m!>|ovPB!BMIBJomf}OOg|K~6PDiG6{j1gCWn){ zUA-a!U@5W4mBTNXYEvQ!^&9Y-Vk)C-YlCj)OHvtpJP^@CQNYHlEuSpGYl5Lq*f`Bc z?mc^+Gu}M8d_VhXNn0^ls&pxv`AWE7N6B(lY1pP^e2#Wo%Hj9ZJN!I#G zK=PjK@_S{Wjkk!7ZU-5kf%CA~%~3u6@{DOyEe%a5&l=5>k7 z5$ln&;PVNg)h0PU<0_uz7_UXN`C~uQ<({S&?5G9T(A~k*#C9BkVL4_egXMCe=i_|m z`*BT((pUXl_M1rSf~xa~c)}D#+7I&SIiM*@j%391@sZ=r8^&T_X^;#=qg!HbK6 zE5;_q>v-@&|Im?sqFpXI%_Jnh_l1pMLxtlAHTtw6lWH(ebckJC<2B zly{xIpupCHwMj(d6K-pAggoJv<6h^V#(fXv`Ej|KN7DqImx;H1$+FSN!PpRS7k(>o zwqn;Ry6J><DVvuL~o~r7{>GxY!FA0`E8cHoaN0 z4I4bh(LR!URQ+aCUv@NsXPIN=h8$Lo0$Vt1aiZ)%CWcjN{n*OGQS%>?FP{moP1bfE z++OBWx>-U*e*TW{Tnnvp0Uz9oKh_s}n*uaM231VMOr19fwzb&@{eLgHHRPTr{*rNT z29I8v#G~OHRH)QG*H~U{Upp`Rx$Z(~AArO}cJ)=h<`cZQX?md;?Z#5GeM1<@|-1c&de!C(r6=+ws z$gPBS&~dxxOVqe^MqhnIUYh->=O+g6U{}<+;8|GXt__9e(OyjLSt}|Mi>0_)DaO;J z8)xo{EsHQaP-g4A)nWW4$b*$2k572(t7XJpYKp*a8de{jz>aU6IqI#;KBXk)EaWm- zaZX;T-aBe7XUcA0MjAcldJw7 zVoBAe>(?Xyu$BaZ6?ALskx}HDy}Wu_%I$;DC6)6G*pFhA|LXchw>E*VvZ`S;{!vA} zgoK~sgrhgT>Fv!=Uw_&FTnNEpRmqD}wpiE?t%@~QbXoo0nN~_J)9TClV^l_u(P!a{i(|m*kKb{H5+}I6Xm~n{3|A zp3R}no}zyl%gB$)_r^!$@Hu!Q0}`VLa9QO!yVQ81RrE@Xl)f&Pk3D(iPD7SN0wD;* z4uq74tMGI;juwhOs4dEhvneH1;s~8>92i1~X2n_Gd_(o^1?H_#-oz7VxS5YA^mxFK z>(99ysliM09)ufj1JAc*DQ>G z`Kj2!tzeB0XZwAOZY{Ky@?6V#YsD`_iBou=e<|E<+uX^FBCS~lHk&zuh@(mdk$ljq zhSjpp%X!6Kqq{S{^cMCGOOyO}-xcmeGxkW%q#i;X0H?rBOsI?U+9dO5lRWsUAGadP zIDeOhC}s6LOMXa2DX1h{77e1~%%PY>&Wr5I_JxT#15xvyE`y2CVna2_ZF$b+ANx*} zrY1p|wOoN6e*qmX-H5#=htfi=tN1!-YR$nAVX4FWz29vPZTZ5v`zc(O+43s{GJpw6 zKGPFNZjP5%3aN55+&iU)-)GB)@erH3Yu-}aSzHRP_gV@mwoZy#5eB{C!NUUbY&|94 z$O3WJL}Yz=e)1-0<04Vcq8CuIwn z#R9jSGD+xCuR1~|Mg<1A154E^_$!>>3J72D?R-`54L>8ArRNAu@-m?4={o1%RVK%e zaQ#jPfplI2G^CI*2Z`7iRuJRZo8OdB)0Ea_Vs-`0 z)G*^ZWX6_8Uh>i9?c`C0*kmUj9QK%Qr`DxaCmt3V7%>SRgb*fS*-1Sz-8TEf@%&|Q zm`|AY;ByNb2qD0@{;4iER;K#yF(&tS(dxy?k$je^dU*74!Hz~p&*dziKE2Qj=}Uw4PR>RobM?-D1+!YVl4|7 zL3l>>QyG&t+|d^FP{B!OzrH5Pp03_*i^JUW-)D&_^I?`c!WN=b`CU=K)r3HZ=^#8+ z{x7_;{2%@Jtl@im%Sr8Lb&i{I=!A5S4K{#CHR~ZKIyaMn@A;RjMfD;+qWF z)rc(Yy4#MDT6y?TZ7(N1;c9j8j;>N777%_uaV;7G+C%9vj7+V0dCjk*|ATQzW z9NEg3_jaq-)ApkS=f$yg1OFRB27bA*8>j5)ws6$~voqX^;q9~u#+)dSU;`a(!9iap zUV5fgBPiS5@BsOFAOr#^G{IJ%V`!6o%+=ukQPWVaBl}St8_fs7uSdkMd8rr!>8OiE zC9RD8q&->12yvt_;Udgnc~%+D=9LUM?+kYh`odKKTF{IFdXIy( za!2dkIu@|FxWMW;cfC5GiWl;l&6S)`d3(8+lw3 z>-&W13~E?`1p-Vc17dI}$5FRWMZ2-=4U1l%qI!r+l+;%u%ykK_As(r(7||*eoxuwZ z%Ihu(9ml(hCy=Wm80;^oq)y2`@a4UwZ(r9aeS|Hy6`m4TGxQ zHZQLVe=YlfUFS`dnIOI_FRv1a-(XBv2Ue70Wdy@V+CT5L?TN)U zy{)cn;|J(+imWLv$^iF<&Dw8qQ>%G4tTWE zgK|TR-XI6Jkm%_)x0JvuhV=M~*rZ`}g)&1N zuRk!w;8M0+rvo8EEV`@QO3?7pUtFCe^2H|fm{qJ7C-^#edXNtTUsfQJn$h`uN?X2e z4cg+kWPJjU^7S9Jcns8nv+?{(Zp|@=u^WR}Z&V1U;8&YexLl}!d*aQW^`Xr^dy$96 zm6ww8LNhPq%`i-yBp}%ay68fPq{R($dV&0ceJN&~!CcV8he=~~aSOsk5GXz4ecSYm zjWkf^T$spUJ?Q!!Mf06BUkYqBBc>A2_Ezf=iZ`oo?j^vWa~RyZ+DmLkpEur5MP)LE zJseu8gAJxi#Tjn_Ug9E6Wuqra0-W&IjPv35TV1T{Df1!yHQ~%1bRjl^Cyd3IdW<%oyci1O}_41qS8Mkpox$Cy3NUE6ehE zN3_IP9pkNA;t>3_z>1Or?+UQr64S}VIHEvWasnZMdqk-!V1gY80ep!6HBMJj7~2dT zo9+S}Eix2RuLx2V%aL=IEeGO)=K={c9iv-MHfmQIieqVmsW5Q@Y!d8OQBn|IJdG`G znta{IHF-)o4y__0CE_{$_v z<{Dx-C+}CMK@zMyS)77z|b_-HYBOKeF6!D z7z=*njKzthwcJN5H@&*(qO<<9Z866U3wbkU8O(z~qCi1Q2V*?N=wG{KE<1Lkf32@r z4H_pRKqHq4QHcqmoA4uVS%t6PM-A0p8Z@(2tc#fsM`-ek|G0Gx)C{}|L^!Z~;NdP5 z4(k#FiS3{w!A=k;;j<@L3+tknW&i~>ym%}$m&VN);q3D>i>0d+mun&R7_zGe} z)SIB@wt`Tv5G!WTGqk=UFy>7xXY2`Xp~t-j0a+d(OnNAbd}Uqc$Wi9~wy=_})kAAo zUUpwoPabi85*;$hB1G09vBfPAv~1{$Z#Vs;`PUX^F;q$sV7<`S{ z`C$KLXN!RmX;DqCTWxA0Nv*ShCc_&8PeKq%9qhJ=?HKZF@z`GF! zw3M96g))3mS|*UcZdt*$<$v8WyLDCpK?pV;1Si3h`ypr4EMcWX%p9l4{B%U{Xri#` zqkBv5HP*QX;0N0r92!f(+BJ1D8_>O%VXpjcv4YP*-Q#yqY7CJgf4(K3?P$?oV%UKx)?2?*~92(R_T5KIiChVj0j?WUgY#9>YVnEWV zaWhJ~pdDGqVAqhOKmKmjX@IOIKN%CT1Qgv;*WIhQI5Zdj;I5a@aB!}c!@byw^)+Ye zp9tJz`C-I6FKZGGl}3 zS+_hm>lf;XU#=DVk};%y?x3>(niYuo>=nZERLV%*19xM{mU#uTnB5qNC!km#O*&qF zvVE33S59BD@+=jg_J>F^p^06;zGPu5;cdVj3tChx-t$Sy7MDp%xXwbZ9%mU`$l3-V zI2hRL9*KfRUn-xCf7vJp>@5O?nu7r2X$FtKO+qNrb)Oz9R0{YI}91AUFhc4bkuGkH}MO7K%SAK3pK{hN9L zUXvr53wTXQF4v^4P1)E1|8>G=F~M5otxaP(`N#Vb;f@L91RKDE4PFsu25uCXRUi=r z%kuWHcjhiyMlC0qr{S+LjMx9bXo4WJNO%fdK)FS~2aJD|*Aqok2hd`9V#D-O5Xc=y ztlVdG$(GL2jG*Wx5L^nFLus5sicwH*Wvg+FEH(%$Nk8fcJCSqWghiO?4uH11bifb4 zCKUoaeGC|^Si^up=QL1JMK2WsRH^Dpm53jQ@c>mS!Kl*1K1T>&lvInR_D28&vw(ZF zEOf4Rw#`?AN_-E+ejejuNd*hjWbGO6pu+-i6oQ3;gA}F3M8`b!n`c>nWhB=pagVGM zL{xF$4#sR9Y?FzK~;eO$os&Y4!NX=%hIFVXB^^!QXTFtp{NK^3tKYiN@`NR#0x{oPyc&z5>7pP*7H9qY z&B>taXxB^__v}$OTJ}6MzC?zBAt_UjImv+R>%KH-+YA9E(M z;u=hxwFDoDQV;yUG;624t~6Hsh?d!X+bmX$Z~&*9LLtQL8)UxX|06PRW`$R)Jnpdb z$<1B7`S_TwmM>`PPZu?%&)}R{>32xPI)1E!uG6em@Cy#foKNLF#a=QytC&b2vr`!v z_lic)CkW(*)K>_h@8g|BrX#`9y6~CcS*6mo(D9~BJEKkmkY2Y6$<@5!tPto>w)rJM z2BBT{t|4jTIH#x0UoL7qlog7p+_)Ob*QT^bmy(J(_%SZ%l^*Gr>DBP2t^#H&DG1Jn zn7g-dMDoXSxACSdLf!eQUz5C7P>Jpq=KH+Y88KyO7;vGC!Z{ZjS(!Sc)#kBSDLddB z@s54@Vl}qgYEM@oL__MjY@+>%aWD1Iu(jil(g$!?3dhjVIx;p&3Y(Z{N{-hZ<;9p9 zHa&L!PcnfR{CK5p`)gLWKtKX)O0y|COSlk)_X6%cgZ>L0Bg{=OM+t=B|60U< z(hgS^QQR3YNG+(sAAZ*+`i2yUQ*jTjgpC3Bi+rZ38+g7x5i#)?*{T1G z#?k#8!YD-}=b0WdP`jzLpYkO(`SF!W1oH*B14sI%>ZteY{psF?{Rn6Yxf}YKGRFPI z`P}!Ru_%}X!I=@fx+if)-C{~J+So|Bp{A9x=UYc5lHf%4RtiL0Ksf{fDA66O_}v}D z1IGj>jxUMH-AQ$zik5_UphALuK!`pEUf-q<8kjXb5=bBE$z81Bwi0$1P(Wnj_Qcp+ zIb}HDAPrV#pM-J7km9zr`RXfU51o}W{?SeL>2GczND=}pTbusm9$Xl;jV#vZZ&)*f zUnqo8NF;!|z0{0+FdGP@1tJCfW0Nv6_P!YO_n6;l`ZCM2@)AqpQlR*-yXw4jCEOM* zP+_LisKbL2aSSdsdvjen7r+nE&)S(C<>6G7YE1Y@a3NC0dj-c280rlPS{zM9RRh7% zj85FNW(QDuR)BtL$dvfeb9nSJn(Tx>we35Eh@DqDxBtxwVgLkmT~i{4{xl#R&{k3F z9IU!n*Ojk9qXr1dIeW8&bwl})z)Z(;16zNEG-}e~d;OBe0-#`xRwU7~%0|;e{$F8z zNP7*Q2{fL=qsJ8*SM!aGIJCtvcipDgy+VjGDj3^%Wfxh)jhSL|4A9)fp79N#!?y}FgZeBNnh}aPpGv_ z?Y0%S+++8QNfNoe>6<>okhfxG$RJlC+-sb1<a74#K3UXYujnh$ZoehoK3(}eAHY}?6CTa(mxQ*{eZ3(@$BBQ1XD;^Y z(^qN}lVpp#Hs$=7^D9H_a-bbE1nbIAv;5gZzCs$j26Du*2{JMNV<`1~uavFWzbB8c z_8JZ@M7m_?&FZ1J%|sHsPPQGitukzb9r5q3a|m!kq5$}ZlVi%0bJerJ=?iuUN#4I< zrtUrf@*nZ6(--%!u>Hga8z{tiV%CR6aj}f2>&2O>jXOh}g$FJW9RwVo!X{TwPh$D? z2TOv#9Ih))@x13j!qJ*NQ=D7Ne9{Zh-)pz%EUCp!sA}G%{Er_4y0a`E<;j0n7*lKn z1{QPE$2OyPg&>PhUBld&if+PQlkfW??exHqLHTF52^xtcZZ_M**jeQ2eR3x}OR~G5 zf*&(LnAjmBQ_RokWXTD4H!H!3vlmv9JD0iS*1Mb{C;C)5gMkjjaDgTkji&W%j>qqi zbr{yuH-!?YfN$FdVgifRe^hOSyR9f|NO| z&KwCfTfn}on12BD1`vo{#jn#e!52LaGk3?fBm!z1)H=g&#>@APbe?31@2t~jk&1=?fYL$!mE(~sCR(xrq9s=Hy07TL9Y8fH zfdvs_E3e?z^d(_dFHNj&l)lE`$CQ`b^JK5sTqx~y@5y(T=!3VQ4DDKR>DoWdsfORy=5G2%!}{SZhXz2pRQy9M+5nJ zZ5m|DEO|kb_9&@qkZWi`nwqA8-xa#B*<}_9a)VKIJ@fXZ@H}p^w;HvU%O4(c3SLly zjV7Z+SHw`B@1=fUQqhmq;`!8r59kgEHb?|q&l91D2gGQ4Ja-s{;@{Y-qaLvAD6cOw zn)Y$Fz*54#ANu!vQ@2?|#s!mcV&Vc(*!Q>`_C^!$FCSU`3=*rL>Mv(-H-4;r-oFOx zJbqg1X@%%f31ODQF9(mAJGfCzT{vp)iCE~uEYfeDH!1l5rnNfrQ~T>FyA&Y;JduQrepE9y4Y z|5n!{h6FQrP+3Ak2i8mUC!ANBmKc|ONA}k_lbLC}f8m=>Y>iufcL{l-wRpF(H>qSwJnXj{y&ta?qCB{Z{ksf{E=ZTUykFFp)1QNbrgvJ2 zTX>{bb^tD=4=#|t{&wKB+(k+O&>WHya@%De9ESTb382)+Y_NEUVw z66HV;K!wud3S!LsMKAl6ncp+`lDXA2RwfiFJ~!$>I&iic1BMI2lj2k23_Ly_ zO`BV53ciQJpMC|G21*KIN;b*S24psAETfTvdS9}&OJv8ccIOdKsNxA@sNTZAHrw6AvLoBrX0>jVJ#w;nA)mC((0LNMRlW`NQ*3x( zCXxNrJ!RowdD>!Wf*p_O1qu-y6LL+84s-BZ3hW6aP!cJUTc$2dbLqa_Y@ie;j8iA_ z3&-q$lhE1S{ielp`->+J&hCgzOO$%!8Fub|JM6qYs))MVrLyDj#q!>Zdgu#)4Wx+v z5rH!}6pA^C24)Jv^T19pOT#e7eLpZJY4A!Q$;=t9W9u`KD=5c|6ET_u@#7;gMi-|+ zwHs`o`RjZYZbFX3bsyAF$?ax0l&&E4SAy3!^G!^=89;3)9WLg=f8QamLI?!!S>9yi ziZMTINy*&*#Pq;e0J`ZmGVF$oh`uHV9^|h}3oI_iuQI^`uTTMepAHLRb4`W_JfvV7 z?)}VKhU>$;ym&|uVgGs!bA5mn8N(PMn9l}+U!cHhgDsRX>A50jkQRW3Ae{8|uTjQ| z!okFzFdKtQG~r#F1nFxxqF&KKem+EkBM4w>*HmC>iT!Kt>VxK1D|VMCH!E^?0o_e0 zOs)r9g5*Y&K30?*Uam9@?5^NmE~*CuMAxtbuR-D^2UosWCM@XRD{TPLm1H&Z^pdPp z3q&9zg!+b@6u<*uC;5tz;lG3E>i{Ggv+3cLOO>nQ+=zY+Wqu|@s+=VYCBgwQMj&m# z%^QvRI6!ll-#~~NLJ5%uxr+sb0sgiit*LL{FNkH6A)>m^S%yJO|QYdO~A7M@4yDkAizDN!;NCbA{l1{1v0#N zx!Rd{%yPJ2L1KqjiSeqRGCh$2GU{CfgeCyu%e_HDOt!hZ{UF#M~ubQ$ACz{ zKPW*IFI2$ijllo`KiEnIg#Tf96RtKIcE~H&ApehDM9E z$t%1v0UI{fC#|ce3ACORiO!JAvE9*Hm^9o^#BKGid51Pio~d|l*i%}it)H^#>Yr3- zoL!&erlE;a+Z_7t`w1=G+3Ll3;zOIuPprn3!eGZ|{;X=tG|VyMGMQ_9Jsx_CL9jLNt)g@vJRyd z!U{!maP!3z$@Ji^wtmLfQ@Y~I<6_j|r#vph+u^)OeOITw0D6RQ6O{(auuE%RP9t0! z#!Z?By419{`8AXA$~jFASj@g(9r(s0f*r(o9UyYCGmRS)S2s6j%Nka#BsMef|Rt3$2Nb zTUdzWYX~wruJueK8Z2ekND!Dx>a?o({S&F#wiA1LeR7g}y9#l{Ht>#8E}l}cHXzcNZvSC{~g9lfu5G%?<6FYxO^649)+Y5(!QrI zhRzs_)V$M+bJ)@%vsF(BjW2{+N~?v28n`rbZXwNO727J<9MWRsug8^Ejixiu?#(oB zl0W9ko1AG?jIGGbfW6bJ(6(FuIiv8qQGzMj2?rc^hN|q_zTj9K+H1JyVld>JvMn4g z?wli5$7F;+4e9B}rjDAG%^Zql$Sf_BT|T;qNHWH4;}00=^y|uj@z+1swqsMzToq?C zi;G(h?ow+il3}6H!lO#VnTdZw*=nUnLnpA z^;^&0YB8i%R~xH&`|+Ukhq6@s3yRRPeQ9j$@g>XT`_r+rW$72@ujIA1QnPnsW;Vab zj*JXZwp!Md&s=mfROgG6){ihLrlzv9Nf$&V$ApDN(ky3|Lajd(Yc#Hm7|!`MD5QX zSNK$$R-6P?@>->jZ~91*O36nYk!&fd=-M`2cd`|mqJwfyHKjT5JN3~2_0F(3_t^e$ zKTmP;<7pGS8=yZ*t+I15-~&CvxM+oK^qH3%4rMJ1+A_8WLzkM2kL6Y8>;}@BCx;Jz z{>WYy%2Q1kQt?_gSxQgum{?v+8X;71E$r>5Z+Kj$SS%|q>!ZzdQv<(>O=F#{gg?Uj zfX6D~c_g{!=I${Etb|la~*sApQ>@3Ob)UW8?puVC-w{FlhP!F^Tj>sOV8g?|q zmtYte`i9&CS#iyg`sDe%IE~}XQRDWqC3XMU;nteXP6?j(ZdLBpY?>%`(L#+%Q>sIR ztz!1Hagdl_qbmDhMXJ^*Q-`7-1WR~^$ z(C0^Kdp-~`W^aS?cQMMIX?he2tIUjzGi7aM6HG+rG)Ffj8?lZVkSy4CrtS_qcXf82 zvt>v0`l1o&!3^dzy|XjX?AbNW&SvaJRaSMeZ<X!gHw8P0qu?994fZrC{~A;`M&f z?;l0NCmxx9sw(L`hOf9P!S#;XOT;KIb0iIaQ|A}c$lgjX%5+g?oK z%$!TvU|G1XGG-Iu;Xb1%SZp)fE#jWKUi{cyOrF0RG@|boD=83Gbj!ESe;&`vwz{nX;+l6;arM+`59EWP zeU47ViV|*#Ut4WCJ?hJPoz^=lhNWReo5#1dY7~((kI6Q)-iqr*)nuc);w9JB?j_z_ zQPC5dxX+ngtSRovZUHxXy0t~{Q}k0m+OSUR>R*(;SWCd7@NY(GL&6lyxfvM*Gm3UwAy8d4425Mn-Fx?J_OaR}A2lZyx)DeU6T z%yNQ`<7wqctj^C(BZNK1@{HWsSYbJ^(6@PtO)QFb z{C!D0WVNdeO5&N>leQ%s9P80>)Pm{MXqNy{A{KIL4bL7gKsm9{=bU zn%%`+&!0x$c6ZDCW=0;;9@XdCPQkRh?d_AKIu-ZuKGSWd#fdxSS}9G9?zRP*@sDgp z*$P5T1=EY@>)(;(y}#Afy`q|+=~E~=cFRtpnN+??Gr$V5u*VS^=dgHw0RPRb`LsE# zbuC43kK*Ds*`*54kNC&$3BKnPySPw0h$GN_kMCO4KIT&rJ)qaCbNdw<;c4$W@UxvG zLREL-ap>`V7TEFP-n2Mh;#s}A`iZsV!enY2*=Uo%@3=uTxAL)>#U&&KMU|z4i#K$j zDnkgSKA`d_ET}#@E~mw^$k~Z@RfheoL%2pya{Eh#mGbqjq#1&zJbM98yd7@6(3u$R zuW+yz-|`f%eXcf8-K7}ty}GqA{nj_i>g4o+2-SBpN0i^tjdu<8?cDM-eE7H&#p56A z&NUU!^rjvSS()AhM?M|CRNZ5)2?bGY^CUN?%(kxndmF=5c|uEb&?nI&RIBQiUqYG( zXkts>D``Jo0v-M9znA)Z!*nmPZS;pOKh$m{f_?=Ix7=tY=$i|977&^tb}wT8r1EqF zW-^<;yXFpZ;c(-TxQxM}Ayw6T%X;=yUf))0KWK|~sdfr}YVbX$PtZ5WA~VqRko>a^ z7z>0y=O4dc-(WA9oan3_(wbLwXF@V1ob&Y^t3+_9Y|7JU9QC>U2f}2Md-QU;;`@Uj zf&V~oK3c*Ymf_o;+8fgndp~lAUlPmdt%iwG9A=06J+{gdxm0PM>*%kxtv^YqWY9pI zvAdU8ihGSw#N$@QjnSNVCa-4~X1dvn*;1iDZ4EzdT*T{6TyiX*aLy6Y(=0Tq&=c3& z*N8LyZ59LNR`}MdoOl0joKB{A!C)K7f$rDlD7scS>MD9nfMKM=v@ zcWEb*1XDAuOa}&Yzq~8uds=DZvgcYPl~&we7(@L%wRbaA-&Gn5l^Byp=)(kmMxLew zWIkg1Q3X?~)QCj)c!*GQa_>Ca=t4Id3(w!!H9D|oXw83T^uf50x1f)ZD={Fc^FI*N zDgA{->nX|Js_eq5Y4rRv$?(o-v6aFX;1SC9nd|o)gr2NNyfka7Yi+$8oVT~Whd57z zElaqmlzIz&7t*Gq>32XbK#uQ|P(94e;MIKZ|uW1+*%C9~-t z*IZ8i?2VG4;nG4g%*U&u+q&@0tIaJDjmZVOqBYPQrWHC86fbIK?+F)b@p87#DDoq3 zb>1`MxV7M|r?599=3>t89dMnXaJaZ)nQ9aTH!^CQe7l-wEQQW5!E<>I4j4a23ULs( z6S(eeM4s?2#2t>i_TT;dg-U!%leDSqQ(11JiaXP z=h2JN9QGzzGBI-!-PtM58RyJe6_M8d*!J22n3}r9B4e#{UD1AcAz_Wg0h{x3)g{;9K4u zWRhJ|xKPZ|HxADlREcDg*#xa`;n}`_$WV1yfNPql<(crlYSu(KQWiS# zIO}Lrwcgc5`&r7zp}HWAf>IBJoxa>8adC32Exvhm>C5a!Dk2p5ycs zI}pl8DN2ZT861pHl%Y#vZDy6q3l)={KsxfBV7f#_ETN2lGU~+N_*i(TJFWWsPuTf~ zYJd5A_V>I7b6^WweOuytz3lok^4cX_#Y&Xz0`}%9FUmUezbA5EBH0tzODbdIP7k)` zm{SM?97{EtEa8)SZTxX2sBa`Gqr)&G5m^_99f6wnn*&IO2sS^DC8IAqm8zNvUgo=V zNudbiQ*yUN$I35bJiU+J3GKGjkevHOT5kT) znIV#1jN7feQr29o-mH+7(9y*glzxjJ9YDLcFSi$GvFbNV-u=t+U6cKj@(Gw04WjZCYiCdvPSN;GFuDEXU<3=BS`xP4cQ~ zc9q93tYQHNYEDk%WXcIyOscuEN%`P{{mC{nH%WYX$~UKXw?T$@?gncmHn_WcF_aXb~VL@|(KV#V%mzL2nvYVdX;y zxtvy65qzi^E91%mV*;tIi+$r#Cd$z4t*5p2Xj{Hmw}tcL%n8I6O8ZL{N~5pRMZLhS zmA%}lg4MaZ-(zuL(2nWF2zicBO_N_Ap*C^JZS>r&v*d?tu;%J@aux?m_94T8VtD@I zbhb5PMsE6(0^L&vt?478{=;^jg&w;d#VHEZaX>HX%jRHC_*}DqF^{Ay&%z(4b1Ij` z{whC;&H%Tn^35|2b_C!1d+t<>Wl6Q}ADb#7HVl*BKM6p)k7xv?$&6lkjU&d!lrqePi+#&*IwS*hlZ5qAv}n^apB- zT{N-_^;L@&aXW`1;VO=b9JjD%3l2=xuE&Pzem{*b?QxDzg)+~s6lXZx6Te5#pKd!j zJ)~JM8lN+eKmGYPm8M93`31MDL1HgOIeWhLcEB{+m(PhiB4N7<_Qt+5p_w*znNp42r#a(+-2*k)kafFX!PvW2ue||#DB978%1gl};2qu^SB$M6oI0;< zQDb{309$%?r0`Kg=mYD@3@5gMV#M12A?!V%nq0cJ;fSb!ih_cGpa?`D6zNSsR9dJK znsjL*U3ynUQRxsu5D0K60YdK`6cCVv-aFDeNU!>Q6MWA5z3W@+U;n+&tn7AlG-@hgV_T?rM4f*92fo_W*i&bd`<~SW9qnE}sA3DXm#0(2 z)$b22e*UxnSo4B;k;4(?LNASxsL)uNhIVFMdN#DGZ0h@yk;g(cxi>8Ni!vW9d&$jT zrQlw_C(pzr)bAqh=%5M|ZS9YW_w~4-%{=WZ88iwXAHH=-s!J^IUKZBTN^eymR z41F~sBOabUb=$VOY4K!nQ)uj27{`+s;m?}EA9M`9BPQy$1TgdB;!-1Z zZY@;L!hht86lz;Ked@(irO(HPk^=edb= zS**)ztmGtI#ct7dG>U6DAJUc6yvH)c*vLI8>wpj9ZtkRrj9V{$r9UXQ&#qF<%EWVB zp4t@Ob(QGHLWAJ*C!mOaG=BWO5r9Q|ns zt&G&eR<252-+wSV3~$nnG6KWk+qTtna%;g%m&?14Ii)o9=d+#r)8{#z>`On5+U-cT z@7%S_A#88`=rQiSktl&w#fM^HKBG#i=3<-U2oWV1{H@LcGD83b6pu0;Q@mf=b%P)` zGH(z67OR2dMPlg&F|XA8HlC?pi#?wSf_p3>)|VS&Vww{~NR}S)JiVM_=Y0d6xmH4uT*091*eM2b-Kss!>HF6C0o|z`T9|Bl>%8xi*8a#4ApG&L;&}46HMEo=Lx= zN?KfbJjy_rR!#C6UFYj4;pLosLtF6{fiWmktU^j+{l{f(7gS%g10sRoHH(Sdqjky= z>@eSEUmG}bsCD-0YR?`r?aq{Nd#jAV@)$9$>LlvPi7mGrgqlCBC8+CK%*?j2FNLo5 z43TrX*xREU7friy;{6V#-u6;LoP&uHZiR(qa#ANjAM<}S%bBCQy44j7)8}@&+`#6* zg_~(N)9BJuk&W=zaK?`E?anXhRV-gdAM3=l+*O7-fesZOCOOT!K0i_X`q?@rMq5s` ztDeuiYFG=VmOhquCxJ;=!YxxR(T>|KHR*Rt_n;nG*7khKE?-i36`YVPHRvU%mCGsK zgG-|>51o~hDhMExX^K4nD{hXr#8?&vSnULg$OFtiD3gNc1?nY?!mx$Zq z#POOqgt0*M@=Y6sEv1S9B6nz?-H5KPt%4mkl>T=|wN(GGK7#oMR#BW==J zzFmU#e79(|R)>YlkG4~UmJ3sd_I{9FaV&%xgRwim-(%5U=?_G_R(?~21t%fDC z#1HWx2FGFz!cU94G(^pcHj0jJ>U+W_Y^{>p0|#5NnkjG7Gr2q~5kQzUQ>-B!<6Ki1 zCA^YJ$$M%rPm_eEo?dS%r)Myro}?%awW4vM3@=``k#cIeJ$a&PmDlRCMLjUaOFN~E zpObZsQ3$z=Gu;tf57vyiyX{v;zMLrGT5S~`5XO78e9{nU$Uhn8x*eRHY=PD>epqga zdykU*EH|BNRjdGCTj8Br3#U)%$<`Mb(%=~zJl*K@RWp&cnG$xyBvdo6Zja6f)bNCW zGQYaoHUpf^EvS4hwF)`f!d><%hs_z|2_v*zD^lH;+iJ$r7=7Kwa8iWONv>`-RxMcm z+K)yFna5jOwwtey>95R0f+8$MdP&yi^X25;Hsg%^Z97NEqLG_f9Rq54`uP$x2EiqH zVokqVC2#SvOlYk|P*NKQz0l75aF?3!rXVhVCe-GM){2}G4a>AxJtdE68nL>B#>8zk zn1+*&;01JJiu~)WL>nab76ose^?;Vyff@ORL4{RNpF=T$1q_~g+69_r(~BTgTQWT4 zXsL!-83?fPprp>ZKh{6X#6O4RPb|im-zs$%4s;5|pf{a$(cO7!t~RYk5>ZYVV;x=L zZ^58IkY@N&!ae%t76ZWqg^@8&CF^yT>jTCL33d^$gc9ktky3l> zU7cS&SJfN^oE_9}kNKQLVGlYFnngGGvOjCzp<@X)?lh3sFVb#GcZ;dbQ5}O-b>>Hi z&3?V+lwLMiHuli!ve@AlzwpwXG4U2Ji%k2m&V4MGf+Ip?H>ahn**jhf_Jn*UQ5Zwe z^mM#@8M@mS9#1(SG&e|XkbCe_w^0}VG%!8M;7@C+LcAO1H@1kwu#*P1o9&v$(H8?7 z;HbnoP0>~+3azYV@bb6Z%~oj~y=SgwR5!Q(@<)n2t|v37zZH&fNz^ZCy{Z{)U%(Q( zVpqfHh+Ho|CC~q&+Z)<7lAyCPuj9IkcY{4!UV%}jpJ;X`2~N#zg__=e+s_L(r#CDK z#L)jnRwOHoK`U-woWxS-l!PfLy${X$6Q7MEmlKLla;Xyex`vP-n41>~$2qo{|9Ze9 z3B7Qs_#mos_$E6-*v;^X6PAJ6H6PaJ#;Ne(WglS?gAOnM(a;*bm}KqzX68=P)h!pJ z3L(3RIQbDZA(m;1Xg5Y-{Ik$xCeB(pFV@>-v__vqG0qRPH3}r9IzGy$vQb%}W$B?& zv>-$S2Ow$?k#KhuVb0-ArGO(t+Q34J1lUp{zJhvq;(y@|*cF4g&yWv5Kc3kia0Ve~ zR{*ggbAW3W-jH_lRu!#=T~56G7ri%}K2dIZ2{k>mIf`EOcCT5&+s>DL`qUgwD%nP( zRTAN1JEM*^(m#o_F;IQeyFI%B``MFgr-Bv0wpkE?*hJM8kg|o|oSd&aA}zklD)$gB z&7&|wfNfi>8--DoM)*YU@yw@*+WOo$5t4i~X&wOeZ6hCj+Tk5FpR&+Kqt@|zD4D%7 zC#Sm_DD!(Yj2#Qm*j-}^4`nx!^ieBN`wP4n(k^-_264^FVHYM&)Fo71Q8v2<^1~2CpxTao% z2I41V`r-i;iEuQ#t=Mx}{rhjKV(fvSdwz59p!Bs#Bkh~I?U8@L>xJCWBAbm&%-*nf znbhd6xyBz;W9?ZGWCBI++)8N?dgy2!+2E*_r}YU zU$wL|;VJ{VH?^k1|5z$dlNXaK1{XjZ)u&Le5}=I_T;y&~F6Vj`;8LSdxLuyS$Eq*I z5F38&O5&XKmGv)gp?)0*H`5Eh$C~CpRl(7nDc_ONT|m96s^w+c{PrbdL=P@I)}Q`0 z4(m{yn*3i(dI-*I8A|1DdmilhxOceormb}fD?gAcEBJeMJhl$R+M(OP>%*nMd7ICw>T zk+a|6$;Z4+*EcT<3qqWuldcNl=(v55BF>>yNHuw{@wQ*%wV_a39|lE77Su>9bh2&h)p7_*G&YROOANT;zwqYP!Ff@T+f5A*H|ncJSl6c;g3u3 zC$S@;w)k1hOrQlijCn1eK8?J+CZO366SHM!Q=5jfF+^-^%B2^F<|JQk5?_vcv3x)7 z0^A}1Ap4d0?b9pkn&|J2?smO^MK2WuwjQyW#g*jN~vLdk3QeWas)Q3e7~dE7|3$SYQl zD}kESIcmk_{j?6>HiIahu!D(D9ljlZGYlgL0E3UgwxTM?9UGo7~PpHNoS znHlPgcXkoVae2-&K?qCLi|KswMBre907d-Ty%yu!vHf+nRe@baw?`KA-(Ks85^ax< z<&MKAkO810NZoEEk$K2rD8)$jqiXNmlBA9;9ifgbj*{yzQokH9-Sr)CSmJdVS&$$70dP}>iW$OSaUT1( zadms0NBX&*&uS%y9MI&85$?ARcj;tgCT(oU--x{-6#5!K=uxv~lS}uwasYo0(#giFv?2Dh; zKHxhnUyPLA`;vKR(hwUfNMRyLccn8DVjcNdteBoo0&SaByA8T$tCct%>`ZeeWT6vP zMMWCq53T}XIR`kcJ=VO{dq+1Y(;nU`wyFP_oZMZO69ss&?W*6voBZE~GcZ~Xji32r zSDHliJh#-P(>&ecIt%;^qzfUJ5b+CK-I3s07PniWX)kS-^Vrzkd{ z0#T4jhU^?DG10~*6U|?g6$IXv`4Zs&7Q|TsWUbq#=?>T+2Vw_F0542zJ<=!(B(oxb zsT1jXM9RUrp7Q4bb8{W3h2hqnl}U}HV|>RmsH)B zcOqKvw)eR@+Y5eq=@tqh>L*C_-??`eUoW3y9hU`2Y& z_GHx!_od)+ARpp?Ud3NcfH_Tmm3_eX&*!B~^Y{t8EpTX!bPb8903i$qOVmQV|(M#oTFQL;8-0X}>obA-u!G z^xvuLD89>_qB`6ws`o6srX^>4PC7?~D}ZtgXX5ds^u3jy@8=^@O6I~}xkPpFOUOmS zY1`NV8z$g8J=;5G3xM+XXJ+EM)o-j?fJ=85w_g$8es1q663oN}B3{5;YuSKXPlTF( za*|Z(7+{>k2~_gJ1^9H&w*hkjN*@qEG9LH(O&&haz5FugU;xnc>pp62n6;s=w%ykS zXbOJ;GGKDwjuql8Z zM6-ccanXDR`lpQlf6e;e&MQ)K#wjp!I|t4NP63?q3MmT~k^bj_0eH)dJdaO96r9nn zCGy=qX&t8_?fArzz_>(Gwl?m^#c)v8fGrAeCIGdDhV!oC4f7C@rDoqK25<(NUYsLOr+mQ(4xQ;~)+U*#iKKLmB1h;ndz|8KYboYih-10J0;f!pT+vC|UgZ>!wn z-UUlQ>#(+8V{WAmt90SpEL8K|o zXZR$kunG{>-2O|~_+O?4<-XXSvqh5n!+`c#GT;Hnz1py{QWl!%ac;plbUIselv^62 zOa!?~lGje~zXUDq6l=u)bk)8V@EwkH*ZiIARK;;+wwq%o$wd3>I2lfxiBKP(= z!D->NlEX+HaGK||g;&qDBK3X0*}w>izyI@f(W0z$^P`ai;#u*z%VPJjVVgBxuX5=| zpzJ@IJ&xO4j&GtD<@`9Z0=^O72AJh38_5s3dh&W@)(f|loe%Xp4xk<37hkRgrPA7N z!s`t}IOuCk%>1AdzWm8No0IBnx*rjoVNIBsN(C;D9j@btR5yzM{7o#fGR?t;GYL53ZaRhKYx42GZC4mk@qPp)F*m2q!4<-On zKKQ2ZNaukB?YGBla5e~(n*K^Ll;Q@nKcGUDtRDkKkkGYdcE1+MxPLtntqc`gg5CdZ z)RzULu_ESD9j3j(+ggS9&#?j7#K%2nq0itkrCYyC=hJ^)ko(Ia;k4vb7K(s%wUF@- z@j=dK65EvEeCKS7to~&mk*z`mov{Ij`FF%XAn#bf=gVs+wnKCJ597vqZEEtTL(SiK z#<%^%S8~!b?bfnxSbD})AV1eEdn$~B`tFh{$BBWm~OT5|W`C8;sgxO8pR?<JdnQx z?OA|jJG@YW`vPs=v3e+>(+FrZEHXV;Xq!=%^ky(E5QnZxPo)XvWMLE8HY;1Bc|6R@ z=tEUy$*Gt@OtBAq`t1Rtu7mA9wj&NO$ePSD9y?h?!Ub|aKL5tXS-l874BUf?iAEZiev_ld6w8BdJHh^ ztGg=hnnd$3;q`h62%Z=dXNsx3+tXToXWpqt7-NF$kF`SXDv0rv-9^BfFh5DbRz zwefTY!uT`FB(3VzYoc5WU6dRg$ui1Tr!NG~97fJrQ}~oHE=5W}C@EC8#S zxbAGYu-{d1$J7Dg$366R#3W&fFj5o}{}iQ2Yi;WMTS2oN%7`e+{fL;@)#6aqqc{UqU957^H&3pZMn-7+Bn8 zex5tWwBhqsz|65PCI5Ux3TC<_;nT80KuqgT8b$&HUt~cLM12++fbdhol{4FkFx#TO z^a@QyU~g#`UKOuB@OVHHHaB@)Lrx`N=0)V$2Df#<Z&Sj57nG9B@!Y@f>JZAmhS)av7}9 z|6i-ZnIRvil-ykvo3`?1wta}t1cXEYN-bN&0(k{)&Of)*P`E7*v3PM& z5#&r_MC<-L2@#iN|4u@#@6MQhz@o(G1qT^Y0Y0N~N+3o|27<=jMrsK8;^CI0v@duj z3q7_V5Q~A}8qgej&NRQze$!)lr3?S?w@*;;f~8#%_3t#z54vymRg%q6tyLdzl_atd z2=hg7Chcf_trxO_(@I%n{n7xjqpF)zc<{{;F3D`m0J7o5^~4d7R?G8YJaEuB%do*6 ztSM1|SJwyPsTiO$7e@yW3um47|J$_P%v6wgi|cI}8mZ}h%s9VH$DVlYek&{Z!wvpD z;22;Vmi)rBK;8y~PP~9ysv!4}&Y%i83zA)(ekzkp9Jm;6V2?;jO!Csy{4tXN(YS$v zp-XpUSlURzGz0t=gz}AATH3itKFN0=(38Iu6>v#)52yozRK%KNou5JnLl-fAN{(ht z2k(6jxnl$R7+bLBngi)H(h(PbWAuPLsQGJKzymVq#XvYaerM5|8N+tSO4 zVrmbLV&N|q6Oo8Ueu0mxrX(`w-8&7^Pxa+yTnvxWHN3bxj!bbpn&F6>o<9S=)j> z7wMXL{B``8>DoUFM)IVcnkf;ikf%f=oQu5VPifun3SO%X>>H@=5z|9JFu8tj@2-%w zRJcubxcAHF5`_#OFP?w)?VAl9-GzJa-pvI7scaG%@OyB`%c2`Vjof7oZya`(>Csc| zqBdRxF!-n}ge{W!95V>7&*DYOT##o)xvaidjcPgiF&~%c59XeS4}r@PJOT>>hw*06 zBNr_7z=D7{`XDkuUlBAagS)Ty(Jz%+4}q!^=!wEvNW&AXS>>P7O$LbPV8KAjbTzSI z=lIsU4)+D0r17fX6zVQOoTeE25j@>hfpZ+iWcP!Ag48SU!4FL0dQ9Mi|N3B$s@`%I zaw<0e^Rfb<56PTmEe0%(!79#AUS}6QxfOvEb?4Oh@(s97vO;)10pxC}`qV(MYEika zoFJI2Df+S5QW0du4fv2T<2e#Aj%{xHp@7JDi@+4p?E)5yCo zo~;+vAt>=30&yJa${tGYu2r**6|$^(j;WgaR8%U0BD%Kd)bd&V3nw-Mo$N{mE_4S zKxHuqSZrQ*QA^7L4--jLd;v6dzR&2E6XJYlS=6Dc(IFu50ZhF&h_8FaU+5cy#fx_< zbgbv10$wwGB>N_E?qVA+%VT@2s-W?;Y=RuBSW>aClyjc*K~2>*4=c>_(|gRb6_Rh@ z`X52(20EU0BmD#Ds2kCwibRw-~Tdgf!6|=yQ@ADMlWl1{H6{b z+_*i&9rEfawR*F!?(XME>~;eRR|sJSuLb!AxkOjqYiK-24N|VzMHlD}%V_pqCNTQW zjdA{MhM-$u4hAqXEC zw!~JDb<->gtBE@cJ2Lx;t$+jxIvI$o&6~VG>M6a)YL@*j^qt{F)QX@`GmzgRjl741 z$&g$;*C`R6LVOlQsCwDm_IgvQznNzSf_Xfk)VGJHawBEVfzl2n^5CS4pfR}pfv+K( zLq27`C?nxMpf&xeZr`F-m9`ZcH66>EY#jZeXrlSiO|9h*l~Fp6og%>^6sQUOU``Yh z7p7^B4q*FL?3z9x<~&|I;urO!M4Yi@d;73%EoBf*`j{C4Q48H|Q*JnY*d_-A9>Y4M zoF?k>wsv?2H3){4mhJPXhae#Y#0|i4d+n8Z{$YLB1L`dW;gT-4C)#D&;bL;rUPn<$ zmMF0o2Bkh+I}f7o%W_O$Q|1SC9eNzkTZ<~H;_ICDUq} zHmLD)L&4ltG#D{7C2E$TXtpNUZy7y4r`mv7>Brn*tCC*ONXr&L7=9*ck_E|1lFOV8 zZ8KclY}%!83~SWCu6&-pNm_vimVS&K5o&qDZi530Q{T;Z`)f3SVRRvL= zm?4KykUnxbra>>n?52{VxPg=rOfdkQ0*>=o%VFcMg3nzs<$H7%2laN%XZfRlkpDIq z2?Uhc7~6drvti|U76aX%{H{ITMQheUFNBX_gM7J|!%H~rnhTG2=dW<#c?0%sIUW8^ z&BTu83OgG^{l{uxHB8^>_Bj$l!b~fh18YkS)B_77O0Ry`&mdGKfuO|l&l)Tov#7e^ z{N9*SaV%OKfm{gQQuu9BNA(%K|9d(k;`pmK7kAQh^X<{A{a8hrum~1~tX16Ql2pskm zbsNctU*p^!XePON$n9}7+CEHPm)qnCjl(J3w;uLXo(%q#z*VPlt+1;_`rz~vxwNYv zd1KR-^7tom%1zl8JE!n-zYhPnzMCANweby$AQo%B`y`58Qfo00L%B^-gb3AhUK4V6nK=<3LUp>SE20!*o3HWcZ{ zF7Ch*^@d;?35fpl2MHU9!5YAtpY9HbP>5%e6E`AIaN6flAZ!7tVrC>z zYz6*7ne7Fg+IuGm$WJIjn8A(6Ng%RFFizRP76b&CK@Rzj8h(-dItknL?77r&Y829e z-%-i2N$Ptmy`CqFeO3hx+9g+{KwX#&VXIFl{=g|3!nvO=ATGSM!dmZAUL`47aa zIb-N(M1lNbeB8+;7+8`qyq3rEhMP23y)!b0lxWruxPOS`|=3FMujNiPFVG-bSPNJh($aM7Yo9>z_p@d5cFyVB?i%?;N@`mDC*%Gw#_;5R-!9TM}D*!RUX zrZ(+&Uf4OC&@4ppB4G=}y^QiVFX82)?w z0m@{u5nH?XWFwJ=UY>W_OyfP?)3?ufM=2Y{{r%_UX=p+J6TD$gnZi{U5#z}xp~C$* zG7q_qX>oJsiMnHYT?0&Etc#CBp!Sv$-20JN#K?hqsy8o9`#2^?tAEPqfbE2`8BLWH~1;K603-78$T%58_yUzo{AB_?GdQiNR(7%~2Pw>ed+# z75?fN6fr``E?!<5SM4mLZ=Rnm;@B{`7F1tc$xGj0Dwy2lP;BTny2DX+lt^)+6ewkb zwQ+LY#bq2cw^?#~rNs;BvBeuCx(xIg*k~%>jxMdq)R$Q56lTSU-dX%PTlMU| zCAUl4qWnmmYfe{56Nbqpa+lJ5@@otQybnH*Bi4gWqHx|k^bHT=fwi3&K+#UNd(;_U zt#Gvuf<7L1t^~3h--@vr$UrTsIjNzByfrrdRNfx47g2xLz_u^8k)|oYLs5$$yRp5`G!&kwOkv&+_SXtY5B|0hmgn9 zVJY{8VDJPP5>F5|n&?Zrg~**%yX=mWjSAUjEDeOI zO1jpq#`_QqKR-RzIasMywqMqT&=fdXc3`@!~@iufouaU zUc^PRivY?2zX6mxkwAi37D;*zl;#G-*GO-G3R(AxvcSBDYy`gmvnT00q9`1MniM3X zRKWS91Zrl|1~4^}b=q*^RXLhP#(pAa8dYmqpwOFRBaHT8myFXlGvYNn9=`foBj>l_ z`1bpHzpx<W17>nYPxJ@P?hV` ze<1W`aLWU}>bbIYJf$xeQdz9)d*b`(>Kud+mj+hnlL0m}P1tPk_lTk37E3!U?H1dp zUx@V~7Ka&dphEfNx+sW)YQ(tzKmc|1w*{b-u7CZsy%cl*-Ztj&qi}AD@lSMj(iAG$w(Az0=`m-%E+EK?dxldHzLse zi89hLo7G=l-xU6}fF+-te7TeC{_xsJ!mG|c!Qkl17+vi)%eEEUx0PW=$iQLsOj1uv z{u1Rl?yAK{L5{^G4ukxTQj9B_>TQw*aYi;ulSUnqEcVO4PDJWu-M{V=C_z1>kI<}> z5NSE#*oaBN3 z&Yls5W$kC)iVa-o4XV{gr!#L= z%GT`3joY=M*%6m+(FDy)FUn^)OkU%SlWWfRH1Lh<8!MYuN^ruef$DJK!LLJ5*-|6B ze-uRG1u)g|!3!R?ixep_H|Kekk)3J|!-YC0B3kGpn@kw;RGV?oq_2A^byHNkdBgM2 zMXb!*v1Cc?;XGhYXQyh5pjd@4uMAuxLncw0Z>fuU@j20rT{n(so@~b+$qQl;G^{Slvb%6N zENU9#CC9Q+^*e$3xs!$b%3z%sTv+&1gvls(VH&;trP$pRgtd@;@DF78Hw?ab`y`M~5K zeU>H5b(pkNDR174TplCVaIKP=3ZKxmYGuv(u8X_NIo?-g7tae7M{=T7^m9uw|f zD|;%rr*O$VB%|xy++pmb=AVK!4@k!6m8JZG%A+kXRSKpI!h7L zJHdf}Zv9A_Y$KDeuVZcJJ-+|-SWfos=KGC*U`gGj7;3drTTNWxDBRL&vhIiW`f^6H z-PI@Bw%Mq+Ih>LEFF!DDvUA8IPPAWHrXR<3O0%u9v*p151A(hw%?2!MBw@yO&a6z| zPUM5mQsYdiSq)N+PJU-G3wNCEJXn-g$Ae0Gbi<*y6w4G_jnU!g!6kA@OkxPq54an1 z?(g|d0fszhK)16|6f6f@GqSrl_G3I!mR8S@(v)gcgMnCfJ@p-Z00O|3L1h45hu+bdGKjk?gvu zt5ilYGwPTmMbpwu0wZ;#Yb-9!sj6O6}#IS~xI41_Tp+eV%xN<%;oO5X_+{ zjgctKDAW27tt#p9OL>xcR-!hO+jQIP$A}u%hV>E@`Vqa19+4P;Y3GUcW91s2#L$=F z>W|9Ce%|$uU0;w_EcNr zTbI1twPCd){P9hQF(B|#;AbyxY@qebAyI5_h}$8=$@`6%xGhFoP;a3Nacs$BsoA2X zdFn%hA9c;IR(kD{;#_2;u%W(n`Uccjvr=O_YIXGHgt4pT1YhP$<}@$a<0wkBRdOyr z5!TNaNPdinSHnbm=_gN2TASrBb8bOb;<{w>dv%t!$BU9^%NJLFNf=Oebf-`_dnFyI z-yY1^i9m{%iEwe-+j%SrZ3Jss)^9FomgX&THme8Sgmq|`UZ-HnEp`MZZ>8+u#az7OjCFcBRS3dIGA+4TXt2{pn|q7*)`p(& zdJehT2Ua=uhhBX+Ab70&q$Szo2SME?EF#33Nn)KlQ&!ujAtUx%KDMLzJMKv$!+x8Z zKmj84*$OXT#7vu|k@o#@R2>y2zuyK**FybCvh8c?UYne?&WT};+F;ppgj!)P45>Ic_i)RZLCLf=4Z%&}D^I2uO;jc^0Zxp$r|-s}0s?1o^< zlbqPn_$Hx2uj()^rSc?QmW~u9ELXe-YFVk&oBc^C=Vlh#IX(8RN9B`ei4GZCT^tFk z#d)%B*F$2tg(`SQw)1pqS38AYb!!bsxY|gHD~r1^`Xt}9a*UnH#^fnTXqJy(;-e9;U;aH}5^L z%&v4j5GKWD&OxYaV_!%<48z(4Qia|Xt*3l$JyBgXV&{h{EZJkNaCc`_$}6MWEwz1; z5&tX{%5ji48CY6TK2J}GHfs3_Z=xGfk;-b5maCDTbgHe%;ecyqXSq82LO-+pZstK5 zT@UuLsE^0x8I{;ZPd6R0RMXyw@%tz97J~;>CYkCl~W%mIjj8$s{x!2WhfM?pFFY(_Z#(NvrJB|?1S5xU;4iF zDQ2r7BFH+oc({@q$9wBnn^ozc@XO^IPjQ<-1Me>A`Z4O1qqdOP&)J^`N5sHTxY{;Hd$_{a zh;M7yMmLGMUN5}lx>9+atV6JKID_@u7b9BPT2(n-2vk;!)~q}V=8&MLT3%D>E4LkL z6Q<#{h!lz}?vO3O7&;e5k585448-I55KE=~P9INKKe>kTeqb58+{Id;YL9k$$lCOY zFsZSA;X_WZ(G8_C`j#^84KPn;pbS%ifSSVu!I$If-@=dN7rh*Prd=80~0ASS9a?!P;Dp(YC!L?>Zai zl0F-nd$8rX!=>E=#A!We7L0#O;$n1kDl2Ij_MWBz^;E45s63WJ4=A8WG9?j_e8t7;gJmrf31w&NZ1c9JVe?uzr_Ndp=6yblB%@9enu zRN#1$mRcyay(##+YxL2Wg7@Mp7@f?ZdOa(hR2)I>TdX9noNjrUDc8ent$BnNXSQ}$ zD+6G=)--2KZpf`-*mASg$!jyr?L{6BxSA)?e60JvKCQE`k=VY}pCUhcLusX?2OF`xz#+MzB7phAVW6I}IKO^2eloAl(c~EP?}Izqu9t**=2| zv^J9Y94VD$kgOh?Kbs6$BpDFn24}qUfA-1{a-FEr3>W_^15HhaR0OF}|NBH_mP!%J z3!r`hwwccnFPB0M?-=zFahs=sjlSX$h=G!h&m~OdM7O5&$!+CHDkS3?a(cQn*)Urd zst*dhdaT4J>qsB7f-nJKN(I1@)1R5-#Whw)_TUHWRuwl%ye2ArPYL13Q?Bpg;`LHn zn_;#wq*$w40AOFhBM~>Z`Ob=nLe72;Qnip5Bw!Z@Xl?(rIDc_qWRI6N&7 zTU(CmIwZejpJLn0FZ5ftP=zw1%t_Pe@~MKKD|V{3m7m0hY9WG0ZZHELMHEQ;GcZRX zhz|xb%+MgnMS$ZnqybXre2+mA6_lw0h9hoIgTH`}cR%nu_yCS~ENmCgvAl>9kO8O< z5JG{f8l;bz3Dox?{t!4mWX(lF+gFR4AvF2%kct1;r>pH4VLRB83m_?_>@`^DI%|yp? zPI(?Pia@qs^B+ng1bfnclq)gbjhnW8Gm@D%wrI&atnj39av82v)|;b#O=y7bsgFUD zMwi`63+olZ3RN{MT#4Wro55%E)kP~?LS0pkTuhREP?^hR++0U&h_+QNGXfubzNx9f zxTV@HZ&@q49m@#c(2MtU^oW0uRX`|frhnzQv%6JvVE9MgDC#X|OP$RCOICJoAdmh% zy2oAzTZX<@-Do%}8!sn`V~%+=;8Ru!k zN6Umpw+#F1uf!}b9o#0G;FY9S`k+o zm6WA#f5t#B1##anDlBt)0xxhvZ@%h#mdD_R+wIQOQG#ipP-(P>Ugq!eTnU_&wSr0O zMm9H|#m~ThUn<@(%)<@j2*+OF?9?}_1p>CW24nL1WtTQw`s4|BRugzD>af~nN_i4t zPS4__m0K^cYfbkT+UjKFc2IH^W!q>Nb4e}g2fFoQIp#~+-A&xHwTG-I;8G=3_SkuE zUH-Bv6fC>Q8LPwh%uCN+sKVpXJG}wJ)g7+qElYJCQ`##b@v+o+pEq)oUB;bxU*Y=R z>6seh!gSD^r$~A4?0)X);I*(?i6a9ArU=O?sW4ZO7;=IvS1%@+tR>yRw^n6dE=teI zQ}kng2(*bl;}5h#)D_3J(jO>vXp0Q@vI zYC7+zCbGYcXI<9rE`n4Q8!;g?rGtQq5;j4CX_OE^T_6OcgbvbFa965^5SG4_5E7cy z08#`61SCLc(xiqey$k3&?Ec>SM{+W`cQTorGr4o0&+~lKz~EH9qLB0z@QZtLq2bqj z@4CpYd$941`BB0tspqaP8@Wy-M6<-q`F%3qu7sYQ&(<55@?>>~eww}Afy+1#d-AQw zV$h28&nz30?4UuhnC@5=Wn#rm-4m#Z#KJj0v zbiBcZc-zQfgyk@gwuYjnbvE5INxpBiWT+VQ(!wz;UC1!8*-tU46v$74PMny0%qp>m z2b{~{MWQ6n|IUJpe!{D%en?;TXvNljArUB_iO>ex{TL%g zvzV7YLchl4A-C!Zv>qEm(w=!v#iVGAQEZ@q{x}(u8$k&7@7D)-!DIHxe%BHvByeGl zsZTTQiwt4%88UM-|6O`&7$2^PXm*D@Sb5n(iLml{SUuPRMO1-5^_u9(zO2zleh*K* zn}hLmkBUTTt&K%rxXevFC6q zO6;*f{P?V1&$b@p{o<|dI=WwvZ;??eq`oPuD9ex@7#Xq|OoZcP zn$nT@9Fz(R@*cYs)aR$xg~^Q>?{_Duy+y+w(6lm_Bh))<%9xwcr2$NMoJmA>{dKC# zc@xgo4-oOx{H_jfbaaf{4gq)ekjJKWl6;0A2AbWVEyKd^rgu&|O@u$ZW8dK#t8W#W&dzD5x9zQdumqnkD7X}C%N_f+eN$@cP)&n>t)sTB424A6pT2eenjWt(8&y(6!H|F}cI3PFtCDClos1JD5SE?fWz-Kx7R4FW{h z11}TcWPSh`;xb+;t|$Be4Fqwy{JnzjO|QBF!O?p+vGO90dcvHb>Db9Rt*GSl7`H%0 zzbMV^1Ve5S(y`xK_~<_UFJ!K7j4xL-NLkvB|B$553EMhO*<5;pP@1arn2~4N$f>pde;Cl5Ax zYLU687o*C{c&npMeK5N%60E_s2UAAuYZGm?;{g=h^j**O^J-P{e(Q4+O|7pJYJ$j( zzNEwL`quk5wY680w%R|;e`V);t&l$%fAs@qNgDo^2)uj1=w`Tp5E4F8&t#)gEJx@k2}=Vze{a!X~&Mi=OY ztQBkFjxs6R*w($K&@9f^^^qg{rzWP~j1|u|_VzJZk{&EoLgv*XHqz2bF(}b;Y#8!* zThwB*x{mA^MK4ezUMCI&3|_*F(-yz-^^*Q6Hc?<2bi&y@j^df(m8;j1$O(z7})Ef{{3vFeI=m@5dXBLYvZr}%zlU+wQ=Il}aU(B@)#f)N^8^3(hnD@}K)t2`g z@+6sFP=LGD1lfb~*iau6wpq+cGZ6|kt9SXj%8&{(GrsW#bDEm(&Ofpm-t}$-$LbEu1V?yG$fZBuV-4l^ zPeDV&j3XCQ<>DQkx?0NFSBi-%q1(G{Z;jaMsrChMyuB7uFzF!=reqvw>KzE&xk$#H z*A}Ep1yEv`_&s^+#K-5-A|MeH-Tj3!_j?GXrFbn*hUjdg7G4HCHk`CAK4)up<%F2( zNcT)feG=EB6=qqj3(w05kUTTQ7gN8=s!y1}xP;uZXZ33)$hdF z&nEbDX9`i7GaPBUt(3gW7fzBWZoWRC7g(aYtI!%viXtfvAR6)VP7JU(x4;nH%s4N& z=({qqsO9n6LAg+2DEj7yOolCv6Y|CSV}xV<4Sl3=NSdd?y^{Y|6P`4W(n_kjP>L0J zn@a^K`A3u{MVy{U@{MFBn!<$B`cw_TNj_|rA7=x~tYdYz@?>ZJC}z2A8X!GnQF*_k zFxhzwby$H0HAT}!tw?RB1&IJJX_AvK*p9|JH7{a3?V;lFH&oDxd<}PV02FnubDqjG+{t_sdt8v~r$V>cDf=#$ z&pcCysgbLBu9XO8#nnBjMnYu?f-L8iClm;}XJ*DBF=o-_;943N=t;#|eK1k?Q4Z_E z9s@|-myo0}n+CNn&5XDopag2*F+3ICN-vz~OyW(C6}Iohbfs;|kw%Xl;geU#|tY`u_qRE5e~wDq%P zSmr!$O1r4Fym8?P)*KxzjX!=mt;BA&0j|LF^vLTp#&E^FPkt*JLbWNi@4O-L8zbGv z%!sG zhkByF?rPxrj^&hNQHVygS$2A_zv%Uke=q0V9SUltLK@`Hqp6MqO}h6syu4JhbJ0w3 zTkzF5f*Ds;SANOg-x$_P(q+bsFpVu`1*2|aV>ziiz3rC6awGWW6N@sk4s@$0p3Zz)audO$!VutVo!fU zOoStwN5y;rLYtg5`LU_W0csARpQj@NnWDvF{cv9y()th3bGz<-`kNV&CYn>0fx$xz zG6;^{6a?wFCrI7jpe^AGaYBd|V|u`;4K2c@`ayJr)tB6c%>?X#sCya`mjSj?aE{Z< z0UzFy?>F??^WHh@>rNoET5ouBf3;Stn46EKG`bH;s64J@_hxlwZoy-`UH7QrKE%Q^ z82&Vz^=;Lz(iaBykZK)(x7#cX-#|KTvgh(3U-LE{&D`4S6qG4EO+ubPPu3Jqya$b_otU_o=F3o7T`2F$W?fG_;6|BU)r-7#lK$*aGhx`B4lQI-{e@^N(~4-95>N zvMp);-V#>DpT3HdnN!8ohBj(z%j&M_Hsxrk*g9@Ha3^rR9;zs(ajBBpZ3 zi8pH+37Xslh5>zhFz86i0u5cv{yGa+ae(!DezO#Q9-dv=Vo6*XuWsPmh%70YQR^b0 zr8DES3nFe@$tM5R4K1ab9E#SpwBzf4NU(-b40R)@;$HQh#j0k{eYTQ2`P=z+taG|+ zBod97GRpN2o6sZ>iJF)kEp;LML#_LsD(!)fcw^OcvNh3_N8z`PX4l zVI{k&fho8Ngw82!N3W`;xAS=FVW8}gOX7#Zn!Q#0hBWMJ`tDskY3A=BL>oWMpe#~ z?Gmt#T!u(o>&VEEeNAOD%!7YiIhPN;-3T)eqXdvb_H@Cr`N}b^b){RVgz7)*|L2Jy8$*6Rx*R{!6bEEj6 zdP~bAtDEk?-Hp~=GGnObej3@i@=Kq1*-$-KH*?3WPw&x>7b|lzh~!8tNge~|l^MB_-JvEJmbJZpBRbN-11JJhD*8>^ zIbho^#UavDWz$a9SXQwcj$lYrZB+_jIVAF?!@f2d8RZcejSMH0dM_@Iw^shMu;xQQ z%bmL4O(*~+S^w%rDRsp zqqS4LniI8(SVB#XRUWh>)B%@1G|$b`jYN3gb{O9g-?`5Q>ciB0)~e}pPXElNWt>^N z+N(=!Dw;*An-PaE?hb#L00EumJnfHx~oy_=9bp%xp4RHCsG7aRYozx2%C`2u2^>qkKl_a z9;t2bSRZ~vpbhdPF5Eb;Q?$G#B9?M>CCaDStG7J4;b>!Mo~NW6QSz#nEW{$)5@!qRgdc?J0u?)vr`KD zQtf}QDjOvpjzm=Lem`=jdk~+G4fkx8bL-#jC_67YKQzR~q#^S|QNmF#5&6np!?jcJ zEQIOtH9F_YZ3}O6x9F3$MT5GlOZH9Ht>^%k!^phJf7^f+HhSR!a?Ai1&wNoki#SMZ|6 zNaaYa^;tb-nskO7P>jdl)x-qaW35a6&dABac3abURD^n2gPQk-{osE5ATb~-$rhCo z=8T)%noWp1>9Q?W^tU-F{zJL7`X_F(5v_Bwa&oFdftXqg9@LBdFl*7@N}RSZ9>~eo z5qvfRX33G>c70-?%DEbgAxH9K*a7K2RZMA-<;FbT1*>L!<|Zr&4AZ@ep+D^%XOJqjM9R zDZ1bnsqPnG?QhuIQn)ym5k=qr&8!F}941H`C_t1RtpEz}SS?OO8%XCm)qGm_M+zm& z=&Nnhhgw`V2EWLbA{hi#vIkxZI!}{M!8X0%uPBXEar)S5HJ;BM?<*w{bD#>DS1;)>41xUuBw3+^qc=_315( ztUu7rDNiP#9AcPAEEBIb;9&UYh54syJtwB8WaM<#KIfFjSRn^DFkCY~_8 zWP&W-!r^s}c%Vvo!;`r3d7gzhN5TpGynRKIqyhKLZTU^cFJ6t_!E)DQNUCwJ)6!({ zo-!KQjk4Hhdl(-cbww@fZ|oylR$&T}YlO=33$rE4Qk~pe2KPrEVar&&$8nwA4NxPz zA2`es+yA~nyvApG8Tz)IWwOYdQtQ+C1!5vu3E`=0Okn>M zZ}R)HC&6%ykV$rSe7U=?^F2$g|Awl_z(3n2CPO>B)yMbqfjTmY%*MsUqQq&8d_X`$ zi7@e$;WulsNz)JWfi}dTJlEO}8)rrVT46B)6J~Wx?n*1IJkh0M0j_=Bu;Vob{@2AM zw16<|7C=kbz6MfO03D+aRCh@%_|DZ^!Uw7b;HP?ZkWLefaUcS{K!@&v05tFEWr63? zr=?#3NMG~6Q=bFFSumj190K~B7WhZt#jSs*NdSfMAR_o@0bn3t8waTbO93TVZLS@> zKJ@PBN6>+Ua`BiTFzA2nAa@DK0|J(8Bm~YeirCz7kwsbdtMqBunKWa)@VFz8FA{ZCy2V(bJVpkqKedtqsy%K?K9a3Oz7zkGD-r)vOn2z-BFn^4FB z(Es#6E&@Gx`V80v>{H;UQP8`K4^9De$pPCB687!v>O0XzUe|I<<6Xe2@Q zA`+kn!652gK_kHU5DW?i{REtD0P+X2wNwNpLE?w%q+cBUC;)_<0OKx$-bn(X?T14@ z9DK0Q@4)~_c@YHI>i@$cpGyDd?mqP3sE|M~(EsQ&Aa(7hXSV<~>nEUM?Ln&pQExpR z1tRz&q5-4$Enqm%1cEP~1a=6Nf&KYn@WC_Cq+bgN9h@;BG4k~eIft= literal 0 HcmV?d00001 diff --git a/website/content/images/WhirlwindTour.jpg b/website/content/images/WhirlwindTour.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4dc9ab983e2b795e2132864579c8705f71014367 GIT binary patch literal 6396 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 E0F?Uz=>Px# literal 0 HcmV?d00001 diff --git a/website/content/images/jake.jpg b/website/content/images/jake.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6d8076066698faf28a659f3baa54afca514cfc33 GIT binary patch literal 5103 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 T?E3Nq8iw1@#R+_sJD>P3oa~xj literal 0 HcmV?d00001 diff --git a/website/copy_notebooks.py b/website/copy_notebooks.py new file mode 100644 index 000000000..4ebeb0bea --- /dev/null +++ b/website/copy_notebooks.py @@ -0,0 +1,63 @@ +""" +This script copies all notebooks from the book into the website directory, and +creates pages which wrap them and link together. +""" +import os +import nbformat + +PAGEFILE = """title: {title} +slug: {slug} +Template: page + +{{% notebook notebooks/{notebook_file} cells[2:] %}} +""" + + +def abspath_from_here(*args): + here = os.path.dirname(__file__) + path = os.path.join(here, *args) + return os.path.abspath(path) + +NB_SOURCE_DIR = abspath_from_here('..', 'notebooks') +NB_DEST_DIR = abspath_from_here('content', 'notebooks') +PAGE_DEST_DIR = abspath_from_here('content', 'pages') + + +def copy_notebooks(): + nblist = sorted(os.listdir(NB_SOURCE_DIR)) + name_map = {nb: nb.rsplit('.', 1)[0] + '.html' + for nb in nblist} + + for nb in nblist: + base, ext = os.path.splitext(nb) + if ext != '.ipynb': + continue + print('-', nb) + + content = nbformat.read(os.path.join(NB_SOURCE_DIR, nb), + as_version=4) + title = content.cells[2].source + if not title.startswith('#'): + raise ValueError('title not found in third cell') + title = title.lstrip('#').strip() + + # put nav below title + content.cells[1], content.cells[2] = content.cells[2], content.cells[1] + + for cell in content.cells: + if cell.cell_type == 'markdown': + for nbname, htmlname in name_map.items(): + if nbname in cell.source: + cell.source = cell.source.replace(nbname, htmlname) + nbformat.write(content, os.path.join(NB_DEST_DIR, nb)) + + pagefile = os.path.join(PAGE_DEST_DIR, base + '.md') + with open(pagefile, 'w') as f: + f.write(PAGEFILE.format(title=title, + slug=base.lower(), + notebook_file=nb)) + +if __name__ == '__main__': + copy_notebooks() + + diff --git a/website/fabfile.py b/website/fabfile.py new file mode 100644 index 000000000..79e9a93ce --- /dev/null +++ b/website/fabfile.py @@ -0,0 +1,92 @@ +from fabric.api import * +import fabric.contrib.project as project +import os +import shutil +import sys +import SocketServer + +from pelican.server import ComplexHTTPRequestHandler + +# Local path configuration (can be absolute or relative to fabfile) +env.deploy_path = 'output' +DEPLOY_PATH = env.deploy_path + +# Remote server configuration +production = 'root@localhost:22' +dest_path = '/var/www' + +# Rackspace Cloud Files configuration settings +env.cloudfiles_username = 'my_rackspace_username' +env.cloudfiles_api_key = 'my_rackspace_api_key' +env.cloudfiles_container = 'my_cloudfiles_container' + +# Github Pages configuration +env.github_pages_branch = "master" + +# Port for `serve` +PORT = 8000 + +def clean(): + """Remove generated files""" + if os.path.isdir(DEPLOY_PATH): + shutil.rmtree(DEPLOY_PATH) + os.makedirs(DEPLOY_PATH) + +def build(): + """Build local version of site""" + local('pelican -s pelicanconf.py') + +def rebuild(): + """`build` with the delete switch""" + local('pelican -d -s pelicanconf.py') + +def regenerate(): + """Automatically regenerate site upon file modification""" + local('pelican -r -s pelicanconf.py') + +def serve(): + """Serve site at http://localhost:8000/""" + os.chdir(env.deploy_path) + + class AddressReuseTCPServer(SocketServer.TCPServer): + allow_reuse_address = True + + server = AddressReuseTCPServer(('', PORT), ComplexHTTPRequestHandler) + + sys.stderr.write('Serving on port {0} ...\n'.format(PORT)) + server.serve_forever() + +def reserve(): + """`build`, then `serve`""" + build() + serve() + +def preview(): + """Build production version of site""" + local('pelican -s publishconf.py') + +def cf_upload(): + """Publish to Rackspace Cloud Files""" + rebuild() + with lcd(DEPLOY_PATH): + local('swift -v -A https://auth.api.rackspacecloud.com/v1.0 ' + '-U {cloudfiles_username} ' + '-K {cloudfiles_api_key} ' + 'upload -c {cloudfiles_container} .'.format(**env)) + +@hosts(production) +def publish(): + """Publish to production via rsync""" + local('pelican -s publishconf.py') + project.rsync_project( + remote_dir=dest_path, + exclude=".DS_Store", + local_dir=DEPLOY_PATH.rstrip('/') + '/', + delete=True, + extra_opts='-c', + ) + +def gh_pages(): + """Publish to GitHub Pages""" + rebuild() + local("ghp-import -b {github_pages_branch} {deploy_path} -p".format(**env)) diff --git a/website/pelicanconf.py b/website/pelicanconf.py new file mode 100644 index 000000000..9f8e90391 --- /dev/null +++ b/website/pelicanconf.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- # +from __future__ import unicode_literals + +AUTHOR = 'Jake VanderPlas' +SITENAME = 'Python Data Science Handbook' +SITESUBTITLE = u'Essential Tools for Working with Data' +SITEURL = '' +PATH = 'content' +TIMEZONE = 'America/Los_Angeles' +DEFAULT_LANG = 'en' + +# Feed generation is usually not desired when developing +FEED_ALL_ATOM = None +CATEGORY_FEED_ATOM = None +TRANSLATION_FEED_ATOM = None +AUTHOR_FEED_ATOM = None +AUTHOR_FEED_RSS = None + +# Set the article URL +ARTICLE_URL = 'blog/{date:%Y}/{date:%m}/{date:%d}/{slug}/' +ARTICLE_SAVE_AS = 'blog/{date:%Y}/{date:%m}/{date:%d}/{slug}/index.html' + +DEFAULT_PAGINATION = 10 + +# Uncomment following line if you want document-relative URLs when developing +#RELATIVE_URLS = True + +#MARKUP = ('md', 'ipynb') +#PLUGINS = ['ipynb.markup'] + +MARKUP = ['md'] +PLUGIN_PATHS = ['./plugins', './plugins/pelican-plugins'] +PLUGINS = [ + 'summary', # auto-summarizing articles + 'feed_summary', # use summaries for RSS, not full articles + 'ipynb.liquid', # for embedding notebooks + 'liquid_tags.img', # embedding images + 'liquid_tags.video', # embedding videos + 'liquid_tags.include_code', # including code blocks + 'liquid_tags.literal' +] +IGNORE_FILES = ['.ipynb_checkpoints'] + +# for liquid tags +CODE_DIR = 'downloads/code' +NOTEBOOK_DIR = 'downloads/notebooks' + +# THEME SETTINGS +THEME = './theme/' + +ABOUT_PAGE = '/pages/about.html' +TWITTER_USERNAME = 'jakevdp' +GITHUB_USERNAME = 'jakevdp' +STACKOVERFLOW_ADDRESS = 'http://stackoverflow.com/users/2937831/jakevdp' +AUTHOR_WEBSITE = 'http://vanderplas.com' +AUTHOR_CV = "http://staff.washington.edu/jakevdp/media/pdfs/CV.pdf" +SHOW_ARCHIVES = True +SHOW_FEED = False # Need to address large feeds + +ENABLE_MATHJAX = True + +STATIC_PATHS = ['images', 'figures', 'videos', 'downloads', 'favicon.ico'] + +# Footer info + +LICENSE_URL = "https://github.com/jakevdp/jakevdp.github.io-source/blob/master/LICENSE" +LICENSE = "MIT" diff --git a/website/publishconf.py b/website/publishconf.py new file mode 100644 index 000000000..dc28af32d --- /dev/null +++ b/website/publishconf.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- # +from __future__ import unicode_literals + +# This file is only used if you use `make publish` or +# explicitly specify it as your config file. + +import os +import sys +sys.path.append(os.curdir) +from pelicanconf import * + +SITEURL = 'http://jakevdp.github.io' +RELATIVE_URLS = False + +SHOW_FEED = True +FEED_ALL_ATOM = 'feeds/all.atom.xml' +CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml' +FEED_USE_SUMMARY = True # from the feed_summary plugin + +DELETE_OUTPUT_DIRECTORY = True + +DISQUS_SITENAME = "pythonicperambulations" +GOOGLE_ANALYTICS = "UA-34061646-1" diff --git a/website/theme/README.md b/website/theme/README.md new file mode 100644 index 000000000..f79086600 --- /dev/null +++ b/website/theme/README.md @@ -0,0 +1,4 @@ +# Pythonic Perambulations Theme + +This theme was adapted from that at https://github.com/danielfrg/danielfrg.github.io-source; the original is released under the Apache v2.0 license. +Adaptations are contained in this directory. \ No newline at end of file diff --git a/website/theme/static/css/icons.css b/website/theme/static/css/icons.css new file mode 100644 index 000000000..329e1ad75 --- /dev/null +++ b/website/theme/static/css/icons.css @@ -0,0 +1,60 @@ +/* Copied from https://github.com/porterjamesj/crowsfoot */ + +@font-face { + font-family: 'icons'; + src: url('../font/icons.eot?79801659'); + src: url('../font/icons.eot?79801659#iefix') format('embedded-opentype'), + url('../font/icons.woff?79801659') format('woff'), + url('../font/icons.ttf?79801659') format('truetype'), + url('../font/icons.svg?79801659#icons') format('svg'); + font-weight: normal; + font-style: normal; +} +/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ +/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ +/* +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'icons'; + src: url('../font/icons.svg?79801659#icons') format('svg'); + } +} +*/ + + [class^="icon-"]:before, [class*=" icon-"]:before { + font-family: "icons"; + font-style: normal; + font-weight: normal; + speak: none; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: .2em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} + +.icon-stackoverflow:before { content: '\e032'; } /* '' */ +.icon-twitter:before { content: '\e801'; } /* '' */ +.icon-facebook:before { content: '\e802'; } /* '' */ +.icon-rss:before { content: '\e800'; } /* '' */ +.icon-mail-alt:before { content: '\f0e0'; } /* '' */ +.icon-github:before { content: '\f113'; } /* '' */ \ No newline at end of file diff --git a/website/theme/static/font/icons.eot b/website/theme/static/font/icons.eot new file mode 100644 index 0000000000000000000000000000000000000000..7c6b12539b66c32407ebc1b860bf8900b27503c1 GIT binary patch literal 5584 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 c + + +Copyright (C) 2012 by original authors @ fontello.com + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/theme/static/font/icons.ttf b/website/theme/static/font/icons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..12180992588af5d83a47e4e924130f24c7e82415 GIT binary patch literal 5428 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 literal 0 HcmV?d00001 diff --git a/website/theme/templates/_includes/analytics.html b/website/theme/templates/_includes/analytics.html new file mode 100644 index 000000000..ac8137766 --- /dev/null +++ b/website/theme/templates/_includes/analytics.html @@ -0,0 +1,30 @@ +{% if GOOGLE_UNIVERSAL_ANALYTICS %} + +{% elif GOOGLE_ANALYTICS %} + +{% endif %} diff --git a/website/theme/templates/_includes/disqus_thread.html b/website/theme/templates/_includes/disqus_thread.html new file mode 100644 index 000000000..a347fa408 --- /dev/null +++ b/website/theme/templates/_includes/disqus_thread.html @@ -0,0 +1,17 @@ +{% if DISQUS_SITENAME and SITEURL and article.status != "draft" %} +

+

Comments

+
+ +
+{% endif %} diff --git a/website/theme/templates/about.html b/website/theme/templates/about.html new file mode 100644 index 000000000..e80730320 --- /dev/null +++ b/website/theme/templates/about.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} +{% block title %}{{ page.title }}{% endblock %} +{% block headerimg %}{% if page.headerimg %}{{ page.headerimg }}{% else %}{{ DEFAULT_HEADER_BG }}{% endif %}{% endblock %} + +{% block content %} + +
+
+
+

{{ page.title }}

+ {% if page.date %} + + {% endif %} +
+ +
+ {{ page.content }} +
+ +
+
+{% endblock %} diff --git a/website/theme/templates/archives.html b/website/theme/templates/archives.html new file mode 100644 index 000000000..24efb6fc1 --- /dev/null +++ b/website/theme/templates/archives.html @@ -0,0 +1,28 @@ +{% extends "base.html" %} +{% block title %}Archives{% endblock %} +{% block headerimg %}{{ DEFAUT_HEADER_BG }}{% endblock %} + +{% block content %} +
+ +
+{% endblock %} diff --git a/website/theme/templates/article.html b/website/theme/templates/article.html new file mode 100644 index 000000000..f38b8660a --- /dev/null +++ b/website/theme/templates/article.html @@ -0,0 +1,43 @@ +{% extends "base.html" %} +{% block title %}{{ article.title }}{% endblock %} +{% block headerimg %}{% if article.headerimg %}{{ article.headerimg }}{% else %}{{ DEFAULT_HEADER_BG }}{% endif %}{% endblock %} + +{% block extra_head %} +{% if 'angular' in article.include %} + +{% endif %} +{% if 'jquery' in article.include %} + +{% endif %} +{% endblock %} + +{% block content %} +
+
+
+

{{ article.title }}

+ +
+ +
+ {{ article.content }} +
+ +
+
+ {% for tag in article.tags %} + {{ tag }} + {% endfor %} +
+
+
+ + {% include '_includes/disqus_thread.html' %} + +
+ + + +{% endblock %} diff --git a/website/theme/templates/base.html b/website/theme/templates/base.html new file mode 100644 index 000000000..a06b63a8e --- /dev/null +++ b/website/theme/templates/base.html @@ -0,0 +1,94 @@ + + + + + + + + + {% block title %}{% endblock %} | {{ SITENAME }} + + + + + + + + + + + + + + + {% if ENABLE_MATHJAX %} + + + {% endif %} + + {% block extra_head %}{%endblock%} + + + + + +
+ {% block content %}{% endblock %} +
+ + {% include '_includes/analytics.html' %} + + diff --git a/website/theme/templates/index.html b/website/theme/templates/index.html new file mode 100644 index 000000000..fed18ce36 --- /dev/null +++ b/website/theme/templates/index.html @@ -0,0 +1,53 @@ +{% extends "base.html" %} +{% block title %}Home{% endblock %} +{% block headerimg %}{{ DEFAULT_HEADER_BG }}{% endblock %} + +{% block content %} +
+ + {% for article in articles_page.object_list %} + +
+ {% endfor %} + + + +
+ + + +{% endblock %} diff --git a/website/theme/templates/ipynb.css b/website/theme/templates/ipynb.css new file mode 100644 index 000000000..217627202 --- /dev/null +++ b/website/theme/templates/ipynb.css @@ -0,0 +1,47 @@ +{ + max-width: 700px; +} + +.text_cell .prompt { + display: none; +} + +div.cell { + padding: 0; +} + +div.text_cell_render { + padding: 0; +} + +div.prompt { + font-size: 13px; +} + +div.input_prompt { + padding: .7em 0.2em; +} + +div.output_prompt { + padding: .4em .2em; +} + +div.input_area { + margin: .2em 0.4em; + max-width: 580px; +} + +table.dataframe { + font-family: Arial, sans-serif; + font-size: 13px; + line-height: 20px; +} + +table.dataframe th, td { + padding: 4px; + text-align: left; +} + +pre code { + background-color: inherit; +} diff --git a/website/theme/templates/main.css b/website/theme/templates/main.css new file mode 100644 index 000000000..293ec2a60 --- /dev/null +++ b/website/theme/templates/main.css @@ -0,0 +1,300 @@ +body { + margin: 0; + padding: 0; + font: 15px 'Source Sans Pro', sans-serif; + line-height: 1.6em; + color: #222; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; +} +a { + color: #007EE5; + text-decoration: none; +} +a:hover { + color: #007EE5; + text-decoration: none; +} +header.main-header { + background: none repeat scroll 0% 0% #205F29; + margin-bottom: 0px; +} +header.main-header a { + color: #fff; +} +header.main-header .container { + max-width: 1000px; +} +header.main-header .container nav a:hover { + background-color: #5C881C; +} +article { + margin: 0; +} +article header.about { + margin-bottom: 0px; + padding-bottom: 0px; +} +article header { + margin-bottom: 20px; + padding-bottom: 20px; +} +article header h1 { + margin-bottom: 2px; + font-weight: 700; + color: #000; +} +article header time { + color: #9E9E9E; + font-size: 0.85em; + float: right; +} +article header time.left { + color: #9E9E9E; + font-size: 0.85em; + float: left; +} +article div.social-links ul { + padding: 0px; +} +article div.social-links li { + display: inline; + font-size: 20px; +} +article div.social-links li a { + color: #000; + padding: 10px; +} +article div.social-links li a:hover { + color: #666; + text-decoration: none; +} +article p { + font-size: 16px; + margin-bottom: 20px; + line-height: 1.6em; +} +article p.note { + background: #f5f5f5; + border: 1px solid #ddd; + padding: 0.533em 0.733em; +} +article p.update { + background-color: #FEEFB3; + border: 1px solid #e6e68a; + padding: 0.533em 0.733em; +} +article p.alert { + background-color: #ffe2e2; + border: 1px solid #ffb2b2; + padding: 0.533em 0.733em; +} +article ul, +article ol { + margin-top: 0px; + margin-bottom: 25px; +} +article li { + font-size: 16px; + line-height: 1.6em; +} +article a:hover { + text-decoration: underline; +} +article blockquote { + border-left: 2px solid #c7c7cc; + color: #666; + margin: 30px 0; + padding: 0 0 0 25px; +} +article img { + max-width: 100%; +} +article code { + color: #333; + background-color: #EEE; + border-radius: 0; + font-size: 13px; +} +article .meta { + font-size: 11px; +} +article .meta a:hover { + text-decoration: none; +} +article .meta div { + margin-bottom: 20px; + display: block; +} +article .meta a.tag { + margin: 0 10px 10px 0; + padding: 1px 12px; + display: inline-block; + font-size: 14px; + color: rgba(0, 0, 0, 0.8); + background: rgba(0, 0, 0, 0.05); +} +article .meta a.tag:hover { + background: rgba(0, 0, 0, 0.15); +} +article .meta a.read_more, +article .meta a.comments_btn { + font-size: 14px; + font-weight: 800; + padding: 10px 20px; + color: #205F29; + background: #FFF; + border: 1px solid #205F29; +} +article .meta a.read_more:hover, +article .meta a.comments_btn:hover { + color: #FFF; + background: #5C881C; +} +.index { + max-width: 700px; +} +.index article header h2 { + font-size: 36px; + margin-bottom: 2px; + font-weight: 700; +} +.index article header h2 a { + color: #000; +} +.index article header h2 a:hover { + color: #007EE5; + text-decoration: none; +} +.index .separator { + padding: 40px 0 0 0; + margin: 0 0 40px 0; + height: 10px; + border-bottom: solid 1px #CCC; +} +.index .pagination { + display: block; + margin-bottom: 100px; +} +.index .pagination .left { + text-align: right; +} +.index .pagination .right { + text-align: left; +} +.index .pagination a { + display: inline-block; + border: 2px solid #5C881C; + margin: 0 5px; + padding: 8px 20px; + font-weight: bold; + color: #5C881C; +} +.index .pagination a:hover { + color: #FFF; + background: #5C881C; +} +.post { + max-width: 700px; +} +.post h2:before { + content: "# "; + font-weight: bold; + color: #DDD; +} +.post h3:before { + content: "## "; + font-weight: bold; + color: #DDD; +} +.post h4:before { + content: "### "; + font-weight: bold; + color: #DDD; +} +.post article .meta { + margin: 50px 0 100px; +} +.list { + max-width: 700px; +} +.list ul.double-list { + margin: 0 auto 60px; + padding: 0; + list-style-type: none; +} +.list ul.double-list li { + padding: 5px 0; +} +.list ul.double-list li h2 { + font-size: 1em; + display: inline; + font-weight: normal; +} +.list ul.double-list li span { + font-family: sans-serif; + text-transform: uppercase; + text-align: right; + float: right; + padding-top: 3px; + font-size: 12px; + color: #999; +} +.full-width-content { + padding-top: 10px; + padding-left: 0px; + padding-right: 0px; + margin-left: -20px; + margin-right: -20px; +} +.col-xs-1, +.col-sm-1, +.col-md-1, +.col-lg-1, +.col-xs-2, +.col-sm-2, +.col-md-2, +.col-lg-2, +.col-xs-3, +.col-sm-3, +.col-md-3, +.col-lg-3, +.col-xs-4, +.col-sm-4, +.col-md-4, +.col-lg-4, +.col-xs-5, +.col-sm-5, +.col-md-5, +.col-lg-5, +.col-xs-6, +.col-sm-6, +.col-md-6, +.col-lg-6, +.col-xs-7, +.col-sm-7, +.col-md-7, +.col-lg-7, +.col-xs-8, +.col-sm-8, +.col-md-8, +.col-lg-8, +.col-xs-9, +.col-sm-9, +.col-md-9, +.col-lg-9, +.col-xs-10, +.col-sm-10, +.col-md-10, +.col-lg-10, +.col-xs-11, +.col-sm-11, +.col-md-11, +.col-lg-11, +.col-xs-12, +.col-sm-12, +.col-md-12, +.col-lg-12 { + padding-right: 0px; + padding-left: 0px; +} diff --git a/website/theme/templates/main.less b/website/theme/templates/main.less new file mode 100644 index 000000000..a11ef6db5 --- /dev/null +++ b/website/theme/templates/main.less @@ -0,0 +1,316 @@ +// out: ./main.css, compress: true + +@text-color: #222; +@link-color: #007EE5; + +body { + margin: 0; + padding: 0; + font: 15px 'Source Sans Pro', sans-serif; + line-height: 1.6em; + color: @text-color; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; +} + +a { + color: @link-color; + text-decoration: none; +} + +a:hover { + color: @link-color; + text-decoration: none; +} + +header.main-header { + background: none repeat scroll 0% 0% #205F29; + margin-bottom: 0px; + + a { + color: #fff; + } + + .container { + max-width: 1000px; + + nav { + a:hover { + background-color: #5C881C; + } + } + } +} + +article { + margin: 0; + + header.about { + margin-bottom: 0px; + padding-bottom: 0px; + } + + header { + margin-bottom: 20px; + padding-bottom: 20px; + + h1 { + margin-bottom: 2px; + font-weight: 700; + color: #000; + } + + time { + color: #9E9E9E; + font-size: 0.85em; + float: right; + } + + time.left { + color: #9E9E9E; + font-size: 0.85em; + float: left; + } + } + + div.social-links { + ul { + padding: 0px; + } + li { + display: inline; + font-size: 20px; + a { + color: #000; + padding: 10px; + } + a:hover { + color: #666; + text-decoration: none; + } + } + } + + p { + font-size: 16px; + margin-bottom: 20px; + line-height: 1.6em; + } + + p.note { + background: #f5f5f5; + border: 1px solid #ddd; + padding: 0.533em 0.733em; + } + + p.update { + background-color: #FEEFB3; + border: 1px solid #e6e68a; + padding: 0.533em 0.733em; + } + + p.alert { + background-color: #ffe2e2; + border: 1px solid #ffb2b2; + padding: 0.533em 0.733em; + } + + ul, ol { + margin-top: 0px; + margin-bottom: 25px; + } + + li { + font-size: 16px; + line-height: 1.6em; + } + + a:hover { + text-decoration: underline; + } + + blockquote { + border-left: 2px solid #c7c7cc; + color: #666; + margin: 30px 0; + padding: 0 0 0 25px; + } + + img { + max-width: 100%; + } + + code { + color: #333; + background-color: #EEE; + border-radius: 0; + font-size: 13px; + } + + .meta { + font-size: 11px; + + a:hover { + text-decoration: none; + } + + div { + margin-bottom: 20px; + display: block; + } + + a.tag { + margin: 0 10px 10px 0; + padding: 1px 12px; + display: inline-block; + font-size: 14px; + color: rgba(0,0,0,0.8);; + background: rgba(0,0,0,0.05); + } + + a.tag:hover { + background: rgba(0,0,0,0.15); + } + + a.read_more, a.comments_btn { + font-size: 14px; + font-weight: 800; + padding: 10px 20px; + color: #205F29; + background: #FFF; + border: 1px solid #205F29; + } + + a.read_more:hover, a.comments_btn:hover { + color: #FFF; + background: #5C881C; + } + } +} + +.index { + max-width: 700px; + + article { + header { + h2 { + font-size: 36px; + margin-bottom: 2px; + font-weight: 700; + + a { + color: #000; + } + a:hover { + color: @link-color; + text-decoration: none; + } + } + } + } + + .separator { + padding: 40px 0 0 0; + margin: 0 0 40px 0; + height: 10px; + border-bottom: solid 1px #CCC; + } + + .pagination { + display: block; + margin-bottom: 100px; + + .left { + text-align: right; + } + + .right { + text-align: left; + } + + a { + display: inline-block; + border: 2px solid #5C881C; + margin: 0 5px; + padding: 8px 20px; + font-weight: bold; + color: #5C881C; + } + + a:hover { + color: #FFF; + background: #5C881C; + } + } +} + +.post { + max-width: 700px; + + h2:before { + content: "# "; + font-weight: bold; + color: #DDD; + } + + h3:before { + content: "## "; + font-weight: bold; + color: #DDD; + } + + h4:before { + content: "### "; + font-weight: bold; + color: #DDD; + } + + article { + .meta { + margin: 50px 0 100px; + } + } +} + +.list { + max-width: 700px; + + ul.double-list { + margin: 0 auto 60px; + padding: 0; + list-style-type: none; + + li { + padding: 5px 0; + + h2 { + font-size: 1em; + display: inline; + font-weight: normal; + } + + span { + font-family: sans-serif; + text-transform: uppercase; + text-align: right; + float: right; + padding-top: 3px; + font-size: 12px; + color: #999; + } + } + } +} + +.full-width-content { + padding-top: 10px; + padding-left: 0px; + padding-right: 0px; + margin-left: -20px; + margin-right: -20px; +} + +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + padding-right: 0px; + padding-left: 0px; +} diff --git a/website/theme/templates/page.html b/website/theme/templates/page.html new file mode 100644 index 000000000..97b1e59df --- /dev/null +++ b/website/theme/templates/page.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% block title %}{{ page.title }}{% endblock %} +{% block headerimg %}{% if page.headerimg %}{{ page.headerimg }}{% else %}{{ DEFAULT_HEADER_BG }}{% endif %}{% endblock %} + +{% block content %} +
+ +
+
+

{{ page.title }}

+ {% if page.date %} + + {% endif %} +
+ +
+ {{ page.content }} +
+ +
+
+{% endblock %} diff --git a/website/theme/templates/pygments.css b/website/theme/templates/pygments.css new file mode 100644 index 000000000..98db4dd5f --- /dev/null +++ b/website/theme/templates/pygments.css @@ -0,0 +1,61 @@ +.highlight .hll { background-color: #ffffcc } +.highlight .c { color: #60a0b0; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #007020; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #007020 } /* Comment.Preproc */ +.highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #808080 } /* Generic.Output */ +.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0040D0 } /* Generic.Traceback */ +.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #007020 } /* Keyword.Pseudo */ +.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #902000 } /* Keyword.Type */ +.highlight .m { color: #40a070 } /* Literal.Number */ +.highlight .s { color: #4070a0 } /* Literal.String */ +.highlight .na { color: #4070a0 } /* Name.Attribute */ +.highlight .nb { color: #007020 } /* Name.Builtin */ +.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ +.highlight .no { color: #60add5 } /* Name.Constant */ +.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #007020 } /* Name.Exception */ +.highlight .nf { color: #06287e } /* Name.Function */ +.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ +.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #bb60d5 } /* Name.Variable */ +.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #40a070 } /* Literal.Number.Float */ +.highlight .mh { color: #40a070 } /* Literal.Number.Hex */ +.highlight .mi { color: #40a070 } /* Literal.Number.Integer */ +.highlight .mo { color: #40a070 } /* Literal.Number.Oct */ +.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ +.highlight .sc { color: #4070a0 } /* Literal.String.Char */ +.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4070a0 } /* Literal.String.Double */ +.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ +.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ +.highlight .sx { color: #c65d09 } /* Literal.String.Other */ +.highlight .sr { color: #235388 } /* Literal.String.Regex */ +.highlight .s1 { color: #4070a0 } /* Literal.String.Single */ +.highlight .ss { color: #517918 } /* Literal.String.Symbol */ +.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ +.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ +.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ +.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/website/theme/templates/tag.html b/website/theme/templates/tag.html new file mode 100644 index 000000000..1e83db83e --- /dev/null +++ b/website/theme/templates/tag.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% block title %}Archives{% endblock %} +{% block headerimg %}{{ DEFAUT_HEADER_BG }}{% endblock %} + +{% block content %} +
+ +
+{% endblock %} From fc3f8b488a12cb3f068a8799f996bd97a6d990a3 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Mon, 14 Aug 2017 13:26:42 -0700 Subject: [PATCH 13/40] copy figures as well --- website/copy_notebooks.py | 43 +++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/website/copy_notebooks.py b/website/copy_notebooks.py index 4ebeb0bea..0ce666858 100644 --- a/website/copy_notebooks.py +++ b/website/copy_notebooks.py @@ -4,12 +4,13 @@ """ import os import nbformat +import shutil PAGEFILE = """title: {title} slug: {slug} Template: page -{{% notebook notebooks/{notebook_file} cells[2:] %}} +{{% notebook notebooks/{notebook_file} cells[{cells}] %}} """ @@ -24,38 +25,58 @@ def abspath_from_here(*args): def copy_notebooks(): - nblist = sorted(os.listdir(NB_SOURCE_DIR)) + nblist = sorted(nb for nb in os.listdir(NB_SOURCE_DIR) + if nb.endswith('.ipynb')) name_map = {nb: nb.rsplit('.', 1)[0] + '.html' for nb in nblist} + figsource = abspath_from_here('..', 'notebooks', 'figures') + figdest = abspath_from_here('content', 'figures') + + if os.path.exists(figdest): + shutil.rmtree(figdest) + shutil.copytree(figsource, figdest) + + figurelist = os.listdir(abspath_from_here('content', 'figures')) + figure_map = {os.path.join('figures', fig) : os.path.join('/figures', fig) + for fig in figurelist} + for nb in nblist: base, ext = os.path.splitext(nb) - if ext != '.ipynb': - continue print('-', nb) content = nbformat.read(os.path.join(NB_SOURCE_DIR, nb), as_version=4) - title = content.cells[2].source - if not title.startswith('#'): - raise ValueError('title not found in third cell') - title = title.lstrip('#').strip() - # put nav below title - content.cells[1], content.cells[2] = content.cells[2], content.cells[1] + if nb == 'Index.ipynb': + cells = '1:' + title = 'Python Data Science Handbook' + else: + cells = '2:' + # put nav below title + title = content.cells[2].source + if not title.startswith('#') or len(title.splitlines()) > 1: + raise ValueError('title not found in third cell') + title = title.lstrip('#').strip() + content.cells[1], content.cells[2] = content.cells[2], content.cells[1] for cell in content.cells: if cell.cell_type == 'markdown': for nbname, htmlname in name_map.items(): if nbname in cell.source: cell.source = cell.source.replace(nbname, htmlname) + for figname, newfigname in figure_map.items(): + if figname in cell.source: + cell.source = cell.source.replace(figname, newfigname) + nbformat.write(content, os.path.join(NB_DEST_DIR, nb)) pagefile = os.path.join(PAGE_DEST_DIR, base + '.md') with open(pagefile, 'w') as f: f.write(PAGEFILE.format(title=title, slug=base.lower(), - notebook_file=nb)) + notebook_file=nb, + cells=cells)) if __name__ == '__main__': copy_notebooks() From 8f4475ff95569df476142886ba838c10d401d54a Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Mon, 14 Aug 2017 14:02:21 -0700 Subject: [PATCH 14/40] Update publish info --- website/Makefile | 4 ++-- website/copy_notebooks.py | 13 +++++++++---- website/pelicanconf.py | 1 + website/publishconf.py | 4 ++-- website/theme/templates/about.html | 3 +++ 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/website/Makefile b/website/Makefile index 8a605aa7e..b419e74ea 100644 --- a/website/Makefile +++ b/website/Makefile @@ -25,8 +25,8 @@ CLOUDFILES_CONTAINER=my_cloudfiles_container DROPBOX_DIR=~/Dropbox/Public/ -GITHUB_PAGES_REMOTE=git@github.com:jakevdp/jakevdp.github.io.git -GITHUB_PAGES_BRANCH=master +GITHUB_PAGES_REMOTE=git@github.com:jakevdp/PythonDataScienceHandbook.git +GITHUB_PAGES_BRANCH=gh-pages GIT_COMMIT_HASH = $(shell git rev-parse HEAD) diff --git a/website/copy_notebooks.py b/website/copy_notebooks.py index 0ce666858..de2166abd 100644 --- a/website/copy_notebooks.py +++ b/website/copy_notebooks.py @@ -8,7 +8,7 @@ PAGEFILE = """title: {title} slug: {slug} -Template: page +Template: {template} {{% notebook notebooks/{notebook_file} cells[{cells}] %}} """ @@ -38,7 +38,7 @@ def copy_notebooks(): shutil.copytree(figsource, figdest) figurelist = os.listdir(abspath_from_here('content', 'figures')) - figure_map = {os.path.join('figures', fig) : os.path.join('/figures', fig) + figure_map = {os.path.join('figures', fig) : os.path.join('/PythonDataScienceHandbook/figures', fig) for fig in figurelist} for nb in nblist: @@ -50,16 +50,20 @@ def copy_notebooks(): if nb == 'Index.ipynb': cells = '1:' + template = 'page' title = 'Python Data Science Handbook' else: cells = '2:' - # put nav below title + template = 'booksection' title = content.cells[2].source if not title.startswith('#') or len(title.splitlines()) > 1: raise ValueError('title not found in third cell') title = title.lstrip('#').strip() - content.cells[1], content.cells[2] = content.cells[2], content.cells[1] + # put nav below title + content.cells[0], content.cells[1], content.cells[2] = content.cells[2], content.cells[0], content.cells[1] + + # Replace internal URLs and figure links in notebook for cell in content.cells: if cell.cell_type == 'markdown': for nbname, htmlname in name_map.items(): @@ -76,6 +80,7 @@ def copy_notebooks(): f.write(PAGEFILE.format(title=title, slug=base.lower(), notebook_file=nb, + template=template, cells=cells)) if __name__ == '__main__': diff --git a/website/pelicanconf.py b/website/pelicanconf.py index 9f8e90391..6fa6cbf7c 100644 --- a/website/pelicanconf.py +++ b/website/pelicanconf.py @@ -54,6 +54,7 @@ GITHUB_USERNAME = 'jakevdp' STACKOVERFLOW_ADDRESS = 'http://stackoverflow.com/users/2937831/jakevdp' AUTHOR_WEBSITE = 'http://vanderplas.com' +AUTHOR_BLOG = 'http://jakevdp.github.io' AUTHOR_CV = "http://staff.washington.edu/jakevdp/media/pdfs/CV.pdf" SHOW_ARCHIVES = True SHOW_FEED = False # Need to address large feeds diff --git a/website/publishconf.py b/website/publishconf.py index dc28af32d..157ad0ccf 100644 --- a/website/publishconf.py +++ b/website/publishconf.py @@ -10,10 +10,10 @@ sys.path.append(os.curdir) from pelicanconf import * -SITEURL = 'http://jakevdp.github.io' +SITEURL = 'http://jakevdp.github.io/PythonDataScienceHandbook' RELATIVE_URLS = False -SHOW_FEED = True +SHOW_FEED = False FEED_ALL_ATOM = 'feeds/all.atom.xml' CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml' FEED_USE_SUMMARY = True # from the feed_summary plugin diff --git a/website/theme/templates/about.html b/website/theme/templates/about.html index e80730320..98d471047 100644 --- a/website/theme/templates/about.html +++ b/website/theme/templates/about.html @@ -16,6 +16,9 @@

{{ page.title }}

{% if AUTHOR_WEBSITE %}
  • website
  • {% endif %} + {% if AUTHOR_BLOG %} +
  • blog
  • + {% endif %} {% if AUTHOR_CV %}
  • CV
  • {% endif %} From 5bb4cc8545bd1f633036ed17f64b420aee87b6ee Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Mon, 14 Aug 2017 14:11:00 -0700 Subject: [PATCH 15/40] Fix link issues --- website/content/favicon.ico | Bin 0 -> 1150 bytes website/content/pages/about.md | 27 ++++++++++++++++++ website/copy_notebooks.py | 2 +- website/theme/templates/booksection.html | 34 +++++++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 website/content/favicon.ico create mode 100644 website/content/pages/about.md create mode 100644 website/theme/templates/booksection.html diff --git a/website/content/favicon.ico b/website/content/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..91c0253f556c2b3d9b3ae362c16539e81e613622 GIT binary patch literal 1150 zcmb`GO=^Qs6op?xC!t!TN+&_+NQw){79>NL&=Mq_D1y|Xon-~xg`=z>kTpmk4s_zo zOfzZp^ghEYwrFdAuAF!EdcX57{xCM8Ls1y5w`_UA*bQUsjzlG@oa>YrqkGqvXEoRV zw_K6c11TD)CH1K!5uAM`VW4X23rHgPzC&5j_ab1*@4R6c2*VJw*-Z5HR%CzNH0{eIu5C<+{pN32#WjK^c) z(aZDsqbP!+C{R@urfFig+aXC3Xqxsd&*zWh7>mUMj^i|5hrtlgR|Su7A*UI{p0YxKAhF@^A0eb>8OhiTla-%l)26BKZHq VJ)(hnq&|)G=KxLeJo=qD`v#Oq2SNY< literal 0 HcmV?d00001 diff --git a/website/content/pages/about.md b/website/content/pages/about.md new file mode 100644 index 000000000..ca2332f6f --- /dev/null +++ b/website/content/pages/about.md @@ -0,0 +1,27 @@ +title: About +slug: about +Template: about + + +My name is Jake VanderPlas. +I am an astronomer by training, and currently am the director open software +at the University of Washington's +[eScience Institute](http://escience.washington.edu/), where I spend time on +research, teaching, and developing software tools for science. +
    + +If you like my blog, you might also like my books: + + +The *Python Data Science Handbook* by Jake VanderPlas (O'Reilly Media, 2016). This is a comprehensive introduction to the most important data science tools in the Python world. Several examples used in the book are drawn from posts on this blog. +The content is also available as Jupyter notebooks [on GitHub](http://github.com/jakevdp/PythonDataScienceHandbook).
    + + +*A Whirlwind Tour of Python* by Jake VanderPlas (O'Reilly Media, 2016). This free e-book is a fast-paced intro to Python aimed at researchers and engineers. It's content is drawn from Python intro courses and tutorials that I have taught over the years. +The content is also available as Jupyter notebooks [on GitHub](http://github.com/jakevdp/WhirlwindTourOfPython).
    + + +*Statistics, Machine Learning, and Data Mining in Astronomy* by Zeljko Ivezic, Andy Connolly, Jake VanderPlas, and Alex Gray (Princeton Press, 2014). This is a graduate-level text on statistical methods in astronomy, with emphasis on Python code. +Code from the book is available at [http://astroML.org/](http://astroML.org).
    + +Thanks for visiting! \ No newline at end of file diff --git a/website/copy_notebooks.py b/website/copy_notebooks.py index de2166abd..3b4130d97 100644 --- a/website/copy_notebooks.py +++ b/website/copy_notebooks.py @@ -78,7 +78,7 @@ def copy_notebooks(): pagefile = os.path.join(PAGE_DEST_DIR, base + '.md') with open(pagefile, 'w') as f: f.write(PAGEFILE.format(title=title, - slug=base.lower(), + slug=base, notebook_file=nb, template=template, cells=cells)) diff --git a/website/theme/templates/booksection.html b/website/theme/templates/booksection.html new file mode 100644 index 000000000..6c012761f --- /dev/null +++ b/website/theme/templates/booksection.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% block title %}{{ page.title }}{% endblock %} +{% block headerimg %}{% if page.headerimg %}{{ page.headerimg }}{% else %}{{ DEFAULT_HEADER_BG }}{% endif %}{% endblock %} + +{% block content %} + +
    +

    + +This is an excerpt from the Python Data Science Handbook by Jake VanderPlas; Jupyter notebooks are available on GitHub. +

    +

    +The text is released under the CC-BY-NC-ND license, and code is released under the MIT license. If you find this content useful, please consider supporting the work by buying the book! +

    +
    + + +
    + +
    +
    +

    {{ page.title }}

    + {% if page.date %} + + {% endif %} +
    + +
    + {{ page.content }} +
    + +
    +
    +{% endblock %} From 8944be78cb2bb4a04efc4ac2758bd0f6eda8a9ed Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Mon, 14 Aug 2017 14:16:46 -0700 Subject: [PATCH 16/40] Fix some links --- website/.gitignore | 4 ++++ website/pelicanconf.py | 1 + website/theme/templates/base.html | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 website/.gitignore diff --git a/website/.gitignore b/website/.gitignore new file mode 100644 index 000000000..1af125fab --- /dev/null +++ b/website/.gitignore @@ -0,0 +1,4 @@ +content/pages/*.md +output +content/figures +content/notebooks \ No newline at end of file diff --git a/website/pelicanconf.py b/website/pelicanconf.py index 6fa6cbf7c..dfbe61eba 100644 --- a/website/pelicanconf.py +++ b/website/pelicanconf.py @@ -6,6 +6,7 @@ SITENAME = 'Python Data Science Handbook' SITESUBTITLE = u'Essential Tools for Working with Data' SITEURL = '' +SITESUBURL = 'PythonDataScienceHandbook/pages/' PATH = 'content' TIMEZONE = 'America/Los_Angeles' DEFAULT_LANG = 'en' diff --git a/website/theme/templates/base.html b/website/theme/templates/base.html index a06b63a8e..f7eb484dd 100644 --- a/website/theme/templates/base.html +++ b/website/theme/templates/base.html @@ -56,7 +56,7 @@ - {{ SITENAME }} + {{ SITENAME }}