{ "metadata": { "name": "", "signature": "sha256:b37b9ad7d3cc8e16d69ab00f68c6214e92bb54d73f04ddad827e3d826dc31231" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Scientific Computing with Hy: Linear Regressions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "*A tutorial adventure with Python 3, cytoolz, NumPy, and matplotlib*\n", "\n", "This tutorial was adapted from the [Linear Regression tutorial](http://data-sorcery.org/2009/06/04/linear-regression-with-higher-order-terms/) for the [Clojure](http://clojure.org/) scientific computing platform [Incanter](http://data-sorcery.org/). It has been significantly modified to take advantage of the following, however:\n", "\n", "* [Python 3](https://docs.python.org/3/)\n", "* [Hy](http://docs.hylang.org/en/latest/) - Clojure-Flavoured Python\n", "* [cytoolz](http://toolz.readthedocs.org/en/latest/api.html) - a Cython implementation of the famous pytoolz suite\n", "* [NumPy](http://www.numpy.org/) - a full-featured numeric library for Python\n", "* [matplotlib](http://matplotlib.org/) - a Python mathematical plotting library, originally inspired by MATLAB\n", "\n", "Okay, to be honest, we don't really use cytoolz in the linear regression exercise; but we really *wanted* to :-) The opportunity just didn't arise. Seriously, though, cytoolz some some pretty sweet stuff, and provides fast utility functions you may be missing if you're coming (or returning!) to Python after living in the land of functional programming. In particular, it borrows heavily from the Clojure API (which is, quite bluntly, awesome)." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Introduction" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Curve Fitting and Linear Regressions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From wikipedia:\n", "\n", "
Curve fitting is the process of constructing a curve, or mathematical function, that has the best fit to a series of data points, possibly subject to constraints. Curve fitting can involve either interpolation, where an exact fit to the data is required, or smoothing, in which a \"smooth\" function is constructed that approximately fits the data. \n", "\n", "
\n", "A related topic is regression analysis, which focuses more on questions of statistical inference such as how much uncertainty is present in a curve that is fit to data observed with random errors. Fitted curves can be used as an aid for data visualization, to infer values of a function where no data are available, and to summarize the relationships among two or more variables. Extrapolation refers to the use of a fitted curve beyond the range of the observed data, and is subject to a degree of uncertainty since it may reflect the method used to construct the curve as much as it reflects the observed data.\n", "" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Scientific Computing in Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The suite of tools available for scientific computing in Python are pretty stunning. Here's an abridged list:\n", "\n", "* NumPy\n", "* SciPy\n", "* matplotlib\n", "* SymPy\n", "* Pandas\n", "* IPython\n", "* scikit-learn\n", "* AstroPy\n", "\n", "There are so many more, so much to enjoy." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "About Hy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hy is a Lisp dialect that converts its structure into Python\u2019s abstract syntax tree. It is to Python what [LFE](http://lfe.org/) is to [Erlang](http://www.erlang.org/).This provides developers from many backgrounds with the following:\n", "\n", "* A lisp that feels very Pythonic\n", "* A great way to use Lisp\u2019s crazy powers but in the wide world of Python\u2019s libraries\n", "* A great way to start exploring Lisp, from the comfort of python\n", "* A pleasant language that has a lot of neat ideas :-)\n", "\n", "To support different languages in IPython notebooks, one either needs to create an IPython kernel for the desired language, or create a cell magic command. To the best of our knowledge, there is as yet no Hy IPython kernel, however, [yardsale8](https://github.com/yardsale8) has created [HyMagic for hylang](https://github.com/yardsale8/hymagic), and this entire notebook depends upon it. Thanks, @yardsale8!" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Preparation" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Getting the Code" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you are reading this with the [IPython nbviewer]() and you want to run the examples on your machine, just do the following:\n", "\n", "```bash\n", "$ git clone https://github.com/oubiwann/linear-regression-tutorial.git\n", "$ cd linear-regression-tutorial\n", "$ make\n", "```\n", "\n", "That will:\n", "* create a virtualenv\n", "* download all the dependencies, and then\n", "* start up this notebook using a local IPython HTTP server" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Using Hy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that since this tutorial is written in Hy and there is no Hy IPython kernel yet, we use the ``%%hymagic`` command for each cell, which will look like this:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%hylang\n", "\n", "(* 2 (reduce + (range 1 7) 0))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 1, "text": [ "42" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's get started with the notebook prep, now." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "IPython Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook is started with a custom IPython profile (defined in a ``make`` include file), and that profile has already loaded the Hy lang cell magic extension, so we don't need to.\n", "\n", "We do need to set up matplotlib for IPython, though:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import matplotlib\n", "matplotlib.use('nbagg')\n", "%matplotlib inline" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Imports and Color Palette Config" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, switching to Hy, we can do all our imports:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%hylang\n", "\n", "(import [functools [reduce]]\n", " [pprint [pprint]]\n", " [cytoolz [itertoolz]]\n", " [numpy :as np]\n", " [matplotlib :as mpl]\n", " [matplotlib.pyplot :as plt]\n", " [seaborn :as sns])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can configure the color map we'll use in our plots:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%hylang\n", "\n", "(def palette_name \"husl\")\n", "(def colors (sns.color_palette palette_name 8))\n", "(colors.reverse)\n", "(def cmap (mpl.colors.LinearSegmentedColormap.from_list palette_name colors))" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 4 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Loading the Observed Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the case of this tutorial, the \"observed data\" is the same data set as that used in the Incanter linear regression tutorial: the NIST [Filip.dat](http://www.itl.nist.gov/div898/strd/lls/data/LINKS/DATA/Filip.dat) file. In the ``./notebooks`` directoy we've saved a local copy of ``Filip.dat`` as well as a CSV file converstion of the data (``filip.csv``).\n", "\n", "Let's read it in:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%hylang\n", "\n", "(def data (apply np.genfromtxt [\"filip.csv\"] {\"dtype\" float \"delimiter\" \",\" \"names\" True}))" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's set some of that data to variables for use later on:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%hylang\n", "\n", "(def x (get data \"x\"))\n", "(def y (get data \"y\"))\n", "(def [x-min x-max] [(x.min) (x.max)])" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "[-8.7814644949999998, -3.1320024900000001]" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's what it looks like:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%hylang\n", "\n", "x" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ "array([-6.86012091, -4.32413005, -4.35862506, -4.35842675, -6.95585238,\n", " -6.66114525, -6.35546294, -6.11810203, -7.11514802, -6.81530857,\n", " -6.51999306, -6.20411998, -5.85387196, -6.10952309, -5.79832982,\n", " -5.48267212, -5.17179139, -4.8517059 , -4.51712642, -4.14357323,\n", " -3.70907544, -3.49948909, -6.3007695 , -5.95350484, -5.64206515,\n", " -5.03137698, -4.6806857 , -4.32984695, -3.9284862 , -8.56735134,\n", " -8.36321131, -8.10768274, -7.82390874, -7.52287874, -7.21881928,\n", " -6.92081875, -6.62893214, -6.32394687, -5.99139983, -8.78146449,\n", " -8.66314018, -8.47353149, -8.24733706, -7.97142875, -7.67612939,\n", " -7.3528127 , -7.07206532, -6.77417401, -6.47886192, -6.15951751,\n", " -6.83564714, -6.53165267, -6.22409842, -5.91009489, -5.59859946,\n", " -5.29064522, -4.97428462, -4.64454848, -4.29056043, -3.88505558,\n", " -3.40837896, -3.13200249, -8.72676717, -8.66695597, -8.51102647,\n", " -8.16538858, -7.88605665, -7.58804376, -7.28341242, -6.99567863,\n", " -6.69186262, -6.39254498, -6.06737406, -6.68402965, -6.37871983,\n", " -6.06585519, -5.75227217, -5.13241467, -4.8113527 , -4.09826931,\n", " -3.66174277, -3.2644011 ])" ] } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "%%hylang\n", "\n", "y" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 8, "text": [ "array([ 0.8116, 0.9072, 0.9052, 0.9039, 0.8053, 0.8377, 0.8667,\n", " 0.8809, 0.7975, 0.8162, 0.8515, 0.8766, 0.8885, 0.8859,\n", " 0.8959, 0.8913, 0.8959, 0.8971, 0.9021, 0.909 , 0.9139,\n", " 0.9199, 0.8692, 0.8872, 0.89 , 0.891 , 0.8977, 0.9035,\n", " 0.9078, 0.7675, 0.7705, 0.7713, 0.7736, 0.7775, 0.7841,\n", " 0.7971, 0.8329, 0.8641, 0.8804, 0.7668, 0.7633, 0.7678,\n", " 0.7697, 0.77 , 0.7749, 0.7796, 0.7897, 0.8131, 0.8498,\n", " 0.8741, 0.8061, 0.846 , 0.8751, 0.8856, 0.8919, 0.8934,\n", " 0.894 , 0.8957, 0.9047, 0.9129, 0.9209, 0.9219, 0.7739,\n", " 0.7681, 0.7665, 0.7703, 0.7702, 0.7761, 0.7809, 0.7961,\n", " 0.8253, 0.8602, 0.8809, 0.8301, 0.8664, 0.8834, 0.8898,\n", " 0.8964, 0.8963, 0.9074, 0.9119, 0.9228])" ] } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our preparations are complete -- let's get started!" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Viewing the Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's take a look at our data with a quick plot:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%hylang\n", "\n", "(apply plt.scatter [x y])\n", "(plt.show)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "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\nl16tfghpNg1+AHgfQAhhFfBkjXNWxBjzFa83UlzbJoTwBoqj8x+31CpJknRMs5H1OHB+COFA6fXG\nEMKlwKkxxp0hhEFmq77Lbgd2hRAeK1/TbApckiTV1zCsSwVjV1S9/VTF8UPAu6qu+TWwoV0NlCSp\n17kpiiRJCWdYS5KUcIa1JEkJZ1hLkpRwhrUkSQlnWEuSlHCGtSRJCWdYS5KUcIa1JEkJZ1hLkpRw\nhrUkSQlnWEuSlHCGtSRJCWdYS5KUcIa1JEkJZ1hLkpRwhrUkSQlnWEuSlHCGtSRJCWdYS5KUcIa1\nJEkJZ1hLkpRwhrUkSQlnWEuSlHCGtSRJCWdYS5KUcIa1JEkJZ1hLkpRwhrUkSQlnWEuSlHCGtSRJ\nCWdYS5KUcIa1JEkJZ1hLkpRwhrUkSQlnWEuSlHCGtSRJCWdYS5KUcIa1JEkJZ1hLkpRwhrUkSQln\nWEuSlHCGtSRJCWdYS5KUcIa1JEkJZ1hLkpRwhrUkSQlnWEuSlHCGtSRJCWdYS5KUcIa1JEkJZ1hL\nkpRwhrUkSQl3UqODIYSlwC3AmcBLwKYY4zOlY68F9lSc/g7gamAncGutayRJUuuajazXA8tijGcD\n1wA3lA/EGH8SYzw3xngucC3wBMWgHgZOrnWNJElqXbOwPgd4ECDGOAGsqD4hhLAEuBm4IsY4U7rm\ngUbXSJKk+WsW1gPAVMXro6Wp8UoXAX8fY3y6hWskSdI8NVyzphi6/RWvl8YYX6465wPA51u8ptqS\nwcH+Jqekm/1Lryz3Dexf2tm/3tBsxHsAeB9ACGEV8GSNc1bEGPMtXiNJkuap2ch6HDg/hHCg9Hpj\nCOFS4NQY484QwiDwYrNr2tdcSZJ6z5KZmZlut0GSJDVg4ZckSQlnWEuSlHCGtSRJCWdYS5KUcM2q\nwTsuhDAMvD/G+IHS61UU79v+NbA/xvhfu9m+dgghvBq4AzgN+CXwxzHGH3a3Ve0RQjgF+ArFvh0B\nPhhj/El3W9U+IYSrgT8svXw18NoY4+u72KS2CiG8ArgR+F1gGfCZGOOD3W1V+5R2WPwn4KnSW/kY\n47VdbFJHhBD+NfA4cHqM8Ui329MOIYRXAl9m9nfL5THG/9vdVrVPCOFVwF0U9yVZBnwyxvh4vfO7\nOrIOIWwHPgssqXj7VuDSGOO/BVaGEN7Rlca117XAgRjju4FtFLdnzYrLgH+MMb4HuBv4VJfb01Yx\nxq0Ve+D/H2BDt9vUZhuAk0r/3tYD/6bL7Wm3NwNPlH+GGQ3qAYrPYCh0uy1ttgn469LvlruALV1u\nT7tdBTwcY/w9YBT4i0Ynd3sa/ABwBaWwLv2lOznGOFk6/hDwB11qWzudQWmPdeDbwHu62JZ2+xXw\nmtL/v4riJ+DMCSH8e+BnMcZvdrstbXYB8KMQwtcpPojnf3S5Pe32u8C/DCE8GkLYF0J4a7cb1E6l\nmYP/DvwpxX+LmRFjLA/mAN4E/LyLzemEm4DbSv/fR5Of36JMg4cQPgz8SdXbozHGr4YQfq/ivep9\nxQ8Dv9nh5rVVnb7+EFgH/G3pv6csdrvaoUbfZoCPA9eEEP6B4jTx6m60rR0a/D19guIT5EYWv1Xt\nU6d/h4BfxRjXhhBWA7tI6YfJOv37KPDZGOO9IYRzKI7Qzlr0xrVBnf79ANgTY3wyhABzZylTo9G/\nvRDCI8BvU/xgmUpN+vc64E7gykZfo+ubopTC+j/FGC8tjazzMca3lY5dSXGKLtWP2QwhnEpx6vvN\nwD7gIzHG3+puq9ojhHAbxamqnSGE3wHuijG+vdvtaqcQwhnA52OMqf1lUU8I4SvAPTHGr5Ve/zhj\na/L/Avh1jHG69PqfYoz/qsvNapsQwtMU1+QBVgETpWnVTAnFTyL7svJ7s6z0O/MrwH+JMT7U6Nxu\nT4PPEWOcAo6EEH6zNL1zAfBYl5vVDu8BdpbWXp4B/meX29NOr2R2NuQQxdmRrPkD4BvdbkSH/C9m\n9/J/O8WRWpZ8htKIptS/TBR2lsUY31JRU/EcKR59Vgsh/GkIoVwj8guKRceZURoE3EOxRqthUEMC\nqsEpTqVWDu83A18CXgE8FGP86660qr3+N7C79AHkZ2Rrv/RrgZ0hhI9R/Pu0qcvt6YS3Avu73YgO\n2QncGkIoP4xnczcb0wH/DbgrhPA+ir/sR7vbnI7K2t7Rt1P8vfkhinmQpd+bUFyPXwbcXFrCeCHG\nOFzv5K5Pg0uSpMYSNQ0uSZKOZ1hLkpRwhrUkSQlnWEuSlHCGtSRJCWdYS5KUcIa1JEkJ9/8Bhk7a\nkrRL9voAAAAASUVORK5CYII=\n", "text": [ "