python_reference/benchmarks/cython_least_squares.ipynb

1255 lines
183 KiB
Plaintext
Raw Normal View History

2014-05-04 22:57:02 +00:00
{
"metadata": {
"name": "",
2014-05-04 23:11:16 +00:00
"signature": "sha256:14c3265ea9accf7a70bb5032b9e2b881a487f6b81b55999ef8578645edf4e3dd"
2014-05-04 22:57:02 +00:00
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Sebastian Raschka](www.sebastianraschka.com) \n",
"last updated: 05/04/2014\n",
"\n",
2014-05-04 23:11:16 +00:00
"- [Link to this IPython Notebook on GitHub](https://github.com/rasbt/python_reference/blob/master/benchmarks/cython_least_squares.ipynb) \n",
2014-05-04 22:57:02 +00:00
"- [Link to the GitHub repository](https://github.com/rasbt/python_reference)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### All code was executed in Python 3.4"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<hr>\n",
"I am really looking forward to your comments and suggestions to improve and \n",
"extend this little collection! Just send me a quick note \n",
"via Twitter: [&#64;rasbt](https://twitter.com/rasbt) \n",
"or Email: [bluewoodtree@gmail.com](mailto:bluewoodtree@gmail.com)\n",
"<hr>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Implementing the least squares fit method for linear regression and speeding it up via Cython"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"sections\"></a>\n",
"<br>\n",
"<br>\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#Sections"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- [Introduction](#introduction)\n",
"- [Least squares fit implementations](#implementations)\n",
"- [Generating sample data and benchmarking](#sample_data)\n",
"- [Compiling the Python code via Cython in the IPython notebook](#cython_nb)\n",
"- [Bonus: How to use Cython without the IPython magic](#cython_bonus)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"introduction\"></a>\n",
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Introduction"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[[back to top](#sections)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Linear regression via the least squares method is the simplest approach to performing a regression analysis of a dependent and a explanatory variable. The objective is to find the best-fitting straight line through a set of points that minimizes the sum of the squared offsets from the line. \n",
"The offsets come in 2 different flavors: perpendicular and vertical - with respect to the line. \n",
"![](https://raw.githubusercontent.com/rasbt/python_reference/master/Images/least_squares_vertical.png) \n",
"![](https://raw.githubusercontent.com/rasbt/python_reference/master/Images/least_squares_perpendicular.png) \n",
"\n",
"Here, we will use the more common approach: minimizing the sum of the perpendicular offsets.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In more mathematical terms, our goal is to compute the best fit to *n* points $(x_i, y_i)$ with $i=1,2,...n,$ via linear equation of the form \n",
"$f(x) = a\\cdot x + b$. \n",
"Here, we assume that the y-component is functionally dependent on the x-component. \n",
"In a cartesian coordinate system, $b$ is the intercept of the straight line with the y-axis, and $a$ is the slope of this line."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In order to obtain the parameters for the linear regression line for a set of multiple points, we can re-write the problem as matrix equation \n",
"$\\pmb X \\; \\pmb a = \\pmb y$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$\\Rightarrow\\Bigg[ \\begin{array}{cc}\n",
"x_1 & 1 \\\\\n",
"... & 1 \\\\\n",
"x_n & 1 \\end{array} \\Bigg]$\n",
"$\\bigg[ \\begin{array}{c}\n",
"a \\\\\n",
"b \\end{array} \\bigg]$\n",
"$=\\Bigg[ \\begin{array}{c}\n",
"y_1 \\\\\n",
"... \\\\\n",
"y_n \\end{array} \\Bigg]$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With a little bit of calculus, we can rearrange the term in order to obtain the parameter vector $\\pmb a = [a\\;b]^T$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$\\Rightarrow \\pmb a = (\\pmb X^T \\; \\pmb X)^{-1} \\pmb X^T \\; \\pmb y$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"The more classic approach to obtain the slope parameter $a$ and y-axis intercept $b$ would be:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$a = \\frac{S_{x,y}}{\\sigma_{x}^{2}}\\quad$ (slope)\n",
"\n",
"\n",
"$b = \\bar{y} - a\\bar{x}\\quad$ (y-axis intercept)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"where \n",
"\n",
"\n",
"$S_{xy} = \\sum_{i=1}^{n} (x_i - \\bar{x})(y_i - \\bar{y})\\quad$ (covariance)\n",
"\n",
"\n",
"$\\sigma{_x}^{2} = \\sum_{i=1}^{n} (x_i - \\bar{x})^2\\quad$ (variance)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"implementations\"></a>\n",
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"## Least squares fit implementations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[[back to top](#sections)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1. The matrix approach"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First, let us implement the equation:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$\\pmb a = (\\pmb X^T \\; \\pmb X)^{-1} \\pmb X^T \\; \\pmb y$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"which I will refer to as the \"matrix approach\"."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import numpy as np\n",
"\n",
"def lin_lstsqr_mat(x, y):\n",
" \"\"\" Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
" X = np.vstack([x, np.ones(len(x))]).T\n",
" return (np.linalg.inv(X.T.dot(X)).dot(X.T)).dot(y)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 1
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2. The classic approach"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, we will calculate the parameters separately, using only standard library functions in Python, which I will call the \"classic approach\"."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$a = \\frac{S_{x,y}}{\\sigma_{x}^{2}}\\quad$ (slope)\n",
"\n",
"\n",
"$b = \\bar{y} - a\\bar{x}\\quad$ (y-axis intercept)"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"def classic_lstsqr(x, y):\n",
" \"\"\" Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
" x_avg = sum(x)/len(x)\n",
" y_avg = sum(y)/len(y)\n",
" var_x = sum([(x_i - x_avg)**2 for x_i in x])\n",
" cov_xy = sum([(x_i - x_avg)*(y_i - y_avg) for x_i,y_i in zip(x,y)])\n",
" slope = cov_xy / var_x\n",
" y_interc = y_avg - slope*x_avg\n",
" return (slope, y_interc)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 4
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3. Using the lstsq numpy function"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For our convenience, `numpy` has a function that can also compute the leat squares solution of a linear matrix equation. For more information, please refer to the [documentation](http://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.lstsq.html)."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"def numpy_lstsqr(x, y):\n",
" \"\"\" Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
" X = np.vstack([x, np.ones(len(x))]).T\n",
" return np.linalg.lstsq(X,y)[0]"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 5
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 4. Using the linregress scipy function"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The last approach is using `scipy.stats.linregress()`, which returns a tuple of 5 different attributes, where the 1st value in the tuple is the slope, and the second value is the y-axis intercept, respectively. The documentation for this function can be found [here](http://docs.scipy.org/doc/scipy-0.13.0/reference/generated/scipy.stats.linregress.html)."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import scipy.stats\n",
"\n",
"def scipy_lstsqr(x,y):\n",
" \"\"\" Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
" return scipy.stats.linregress(x, y)[0:2]"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 6
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name='sample_data'></a>\n",
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generating sample data and benchmarking"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[[back to top](#sections)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In order to test our different least squares fit implementation, we will generate some sample data:\n",
"- 500 sample points for the x-component within the range [0,500) \n",
"- 500 sample points for the y-component within the range [100,600) \n",
"\n",
"where each sample point is multiplied by a random value within\n",
"the range [0.8, 12)."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import random\n",
"random.seed(12345)\n",
"\n",
"x = [x_i*random.randrange(8,12)/10 for x_i in range(500)]\n",
"y = [y_i*random.randrange(8,12)/10 for y_i in range(100,600)]"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 7
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>\n",
"#### Visualization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To check how our dataset is distributed, and how the straight line, which we obtain via the least square fit method, we will plot it in a scatter plot. \n",
"Note that we are using our \"matrix approach\" here for simplicity, but after plotting the data, we will check whether all of the four different implementations yield the same parameters."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%pylab inline\n",
"from matplotlib import pyplot as plt\n",
"\n",
"slope, intercept = lin_lstsqr_mat(x, y)\n",
"\n",
"line_x = [round(min(x)) - 1, round(max(x)) + 1]\n",
"line_y = [slope*x_i + intercept for x_i in line_x]\n",
"\n",
"plt.figure(figsize=(8,8))\n",
"plt.scatter(x,y)\n",
"plt.plot(line_x, line_y, color='red', lw='2')\n",
"\n",
"plt.ylabel('y')\n",
"plt.xlabel('x')\n",
"plt.title('Linear regression via least squares fit')\n",
"\n",
"ftext = 'y = ax + b = {:.3f} + {:.3f}x'\\\n",
" .format(slope, intercept)\n",
"plt.figtext(.15,.8, ftext, fontsize=11, ha='left')\n",
"\n",
"plt.show()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Populating the interactive namespace from numpy and matplotlib\n"
]
},
{
"output_type": "stream",
"stream": "stderr",
"text": [
"WARNING: pylab import has clobbered these variables: ['random']\n",
"`%matplotlib` prevents importing * from pylab and numpy\n"
]
},
{
"metadata": {},
"output_type": "display_data",
"png": "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\nrFc+dvf8+ho1avD999/zv//9
"text": [
"<matplotlib.figure.Figure at 0x105785f60>"
]
}
],
"prompt_number": 8
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>\n",
"#### Comparing the results from the different implementations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As mentioned above, let us confirm that the different implementation computed the same parameters (i.e., slope and y-axis intercept) as solution for the linear equation."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import prettytable\n",
"\n",
"params = [appr(x,y) for appr in [lin_lstsqr_mat, classic_lstsqr, numpy_lstsqr, scipy_lstsqr]]\n",
"\n",
"print(params)\n",
"\n",
"fit_table = prettytable.PrettyTable([\"\", \"slope\", \"y-intercept\"])\n",
"fit_table.add_row([\"matrix approach\", params[0][0], params[0][1]])\n",
"fit_table.add_row([\"classic approach\", params[1][0], params[1][1]])\n",
"fit_table.add_row([\"numpy function\", params[2][0], params[2][1]])\n",
"fit_table.add_row([\"scipy function\", params[3][0], params[3][1]])\n",
"\n",
"print(fit_table)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"[array([ 0.95181895, 107.01399744]), (0.95181895319126741, 107.01399744459181), array([ 0.95181895, 107.01399744]), (0.95181895319126764, 107.01399744459175)]\n",
"+------------------+----------------+---------------+\n",
"| | slope | y-intercept |\n",
"+------------------+----------------+---------------+\n",
"| matrix approach | 0.951818953191 | 107.013997445 |\n",
"| classic approach | 0.951818953191 | 107.013997445 |\n",
"| numpy function | 0.951818953191 | 107.013997445 |\n",
"| scipy function | 0.951818953191 | 107.013997445 |\n",
"+------------------+----------------+---------------+\n"
]
}
],
"prompt_number": 9
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>\n",
"#### Initial performance comparison"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For a first impression how the performances of the different least squares implementations compare against each other, let us do a quick benchmark using the `timeit` module via IPython's `%timeit` magic."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import timeit\n",
"\n",
"for lab,appr in zip([\"matrix approach\",\"classic approach\",\n",
" \"numpy function\",\"scipy function\"],\n",
" [lin_lstsqr_mat, classic_lstsqr, \n",
" numpy_lstsqr, scipy_lstsqr]):\n",
" print(\"\\n{}: \".format(lab), end=\"\")\n",
" %timeit appr(x, y)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"matrix approach: 1000 loops, best of 3: 270 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"\n",
"classic approach: 100 loops, best of 3: 2.83 ms per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"\n",
"numpy function: 1000 loops, best of 3: 372 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"\n",
"scipy function: 1000 loops, best of 3: 594 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n"
]
}
],
"prompt_number": 10
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"The timing above indicates, that the \"classic\" approach (Python's standard library functions only) is significantly worse in performance than the other implemenations - roughly by a magnitude of 10."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>\n",
"<a name=\"cython_nb\"></a>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Compiling the Python code via Cython in the IPython notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[[back to top](#sections)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Maybe we can speed things up a little bit via [Cython's C-extensions for Python](http://cython.org). Cython is basically a hybrid between C and Python and can be pictured as compiled Python code with type declarations. \n",
"Since we are working in an IPython notebook here, we can make use of the IPython magic: It will automatically convert it to C code, compile it, and load the function. \n",
"Just to be thorough, let us also compile the other functions, which already use numpy objects."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%load_ext cythonmagic"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 15
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython\n",
"import numpy as np\n",
"import scipy.stats\n",
"cimport numpy as np\n",
"\n",
"def cy_lin_lstsqr_mat(x, y):\n",
" \"\"\" Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
" X = np.vstack([x, np.ones(len(x))]).T\n",
" return (np.linalg.inv(X.T.dot(X)).dot(X.T)).dot(y)\n",
"\n",
"def cy_classic_lstsqr(x, y):\n",
" \"\"\" Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
" x_avg = sum(x)/len(x)\n",
" y_avg = sum(y)/len(y)\n",
" var_x = sum([(x_i - x_avg)**2 for x_i in x])\n",
" cov_xy = sum([(x_i - x_avg)*(y_i - y_avg) for x_i,y_i in zip(x,y)])\n",
" slope = cov_xy / var_x\n",
" y_interc = y_avg - slope*x_avg\n",
" return (slope, y_interc)\n",
"\n",
"def cy_numpy_lstsqr(x, y):\n",
" \"\"\" Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
" X = np.vstack([x, np.ones(len(x))]).T\n",
" return np.linalg.lstsq(X,y)[0]\n",
"\n",
"def cy_scipy_lstsqr(x,y):\n",
" \"\"\" Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
" return scipy.stats.linregress(x, y)[0:2]"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 16
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Comparing the compiled Cython code to the original Python code"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import timeit\n",
"\n",
"for lab,appr in zip([\"matrix approach\",\"classic approach\",\n",
" \"numpy function\",\"scipy function\"],\n",
" [(lin_lstsqr_mat, cy_lin_lstsqr_mat), \n",
" (classic_lstsqr, cy_classic_lstsqr),\n",
" (numpy_lstsqr, cy_numpy_lstsqr),\n",
" (scipy_lstsqr, cy_scipy_lstsqr)]):\n",
" print(\"\\n\\n{}: \".format(lab))\n",
" %timeit appr[0](x, y)\n",
" %timeit appr[1](x, y)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"\n",
"matrix approach: \n",
"1000 loops, best of 3: 274 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"1000 loops, best of 3: 260 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"\n",
"\n",
"classic approach: \n",
"100 loops, best of 3: 2.82 ms per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"1000 loops, best of 3: 212 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"\n",
"\n",
"numpy function: \n",
"1000 loops, best of 3: 379 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"1000 loops, best of 3: 370 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"\n",
"\n",
"scipy function: \n",
"1000 loops, best of 3: 608 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"100 loops, best of 3: 613 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n"
]
}
],
"prompt_number": 17
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>\n",
"As we've seen before, our \"classic\" implementation of the least square method is pretty slow compared to using numpy's functions. This is not surprising, since numpy is highly optmized and uses compiled C/C++ and Fortran code already. This explains why there is no significant difference if we used Cython to compile the numpy-objects-containing functions. \n",
"However, we were able to speed up the \"classic approach\" quite significantly, roughly by 1500%.\n",
"\n",
"The following 2 code blocks are just to visualize our results in a bar plot."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import timeit\n",
"\n",
"funcs = ['classic_lstsqr', 'cy_classic_lstsqr', \n",
" 'lin_lstsqr_mat', 'numpy_lstsqr', 'scipy_lstsqr']\n",
"labels = ['classic approach','classic approach (cython)', \n",
" 'matrix approach', 'numpy function', 'scipy function']\n",
"\n",
"times = [timeit.Timer('%s(x,y)' %f, \n",
" 'from __main__ import %s, x, y' %f).timeit(1000)\n",
" for f in funcs]\n",
"\n",
"times_rel = [times[0]/times[i+1] for i in range(len(times[1:]))]"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 18
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%pylab inline\n",
"import matplotlib.pyplot as plt\n",
"\n",
"x_pos = np.arange(len(funcs))\n",
"plt.bar(x_pos, times, align='center', alpha=0.5)\n",
"plt.xticks(x_pos, labels, rotation=45)\n",
"plt.ylabel('time in ms')\n",
"plt.title('Performance of different least square fit implementations')\n",
"plt.show()\n",
"\n",
"x_pos = np.arange(len(funcs[1:]))\n",
"plt.bar(x_pos, times_rel, align='center', alpha=0.5, color=\"green\")\n",
"plt.xticks(x_pos, labels[1:], rotation=45)\n",
"plt.ylabel('relative performance gain')\n",
"plt.title('Performance gain compared to the classic least square implementation')\n",
"plt.show()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Populating the interactive namespace from numpy and matplotlib\n"
]
},
{
"metadata": {},
"output_type": "display_data",
"png": "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\nJSUFcXFxqFOnjvCdUqnEiBEj
"text": [
"<matplotlib.figure.Figure at 0x105a7fe10>"
]
},
{
"metadata": {},
"output_type": "display_data",
"png": "iVBORw0KGgoAAAANSUhEUgAAAbwAAAFhCAYAAAALEB8uAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XdYFNfXB/Dv0HvvSBFLbKhoVMQCBAt2xdiiGMXYEo2G\nFImxkGjsxhKNURNrLLEiYo0i6M+GGkuQCDZQkSKCUgV2Oe8fvjthAbvMIns+z+MjW+7cM3fKmTsz\nd1YgIgJjjDFWzWmoOgDGGGNMCpzwGGOMqQVOeIwxxtQCJzzGGGNqgRMeY4wxtcAJjzHGmFqQNOGl\npaWhffv2MDExwddffy1l1e+krl27YuPGjaoOo8pydXXF0aNHJatPQ0MDt27dqtQ6hg0bhqlTp1ba\n9I2NjZGYmPhKZaKiouDk5FQ5AVVzjRo1wvHjx9/6dBMTE6GhoYGSkpK3Pm1VGzt2LGbOnFkp09Z6\n0RdcXV2Rnp4OTU1NGBoaokuXLli2bBkMDQ1fubJVq1bBxsYG2dnZrxWsutm/f7+qQ6jSBEGAIAgV\nfjZs2DA4OTlhxowZrzVtHx8fBAYGYsSIEW8S4it73jy9DTk5OZU27TfxpsurqoqNjVV1CJJ61eW4\nbt06/P777zhx4oT43ooVKyorvBf38ARBQEREBHJycvD333/j/Pnzr5x9iQglJSVISkpC/fr1XytQ\nmUz2WuXYu6GqLd/KTDovws+CqJqq2jrKXgO9gKurKx09elR8/dVXX1H37t2JiOj06dPUunVrMjMz\noyZNmlBUVJT4PW9vb/ruu++oTZs2pK+vT0OGDCFtbW3S0dEhIyMjOnr0KBUWFtKECRPIwcGBHBwc\naOLEiVRYWEhERMeOHSNHR0eaO3cu2dnZUWBgIIWGhtKHH35IQ4YMIWNjY3J3d6eEhASaNWsW2djY\nkLOzMx0+fFiMYc2aNVS/fn0yNjYmNzc3WrlypfiZYvoLFy4kGxsbsre3p7Vr14qf5+fnU3BwMLm4\nuJCpqSm1bduWCgoKXjjfZV24cIGaNm1KxsbG1K9fP+rfvz9NmTKFiIgyMzOpW7duZG1tTebm5tS9\ne3e6d++eUhv+9ttvRES0du1aatOmDX311Vdkbm5ONWvWpAMHDjyz3jt37lCfPn3I2tqaLC0tady4\ncUREJJfLacaMGeTi4kI2NjY0dOhQevz4MRER3b59mwRBoLVr15KTkxNZWFjQihUrKCYmhtzd3cnM\nzEycjiImLy8vGjduHJmamlK9evWU1pWXaX/F8h06dCiVlJTQ7NmzqVatWmRpaUn9+/enzMxMscyG\nDRvI2dmZLC0t6ccffyy3biqsXLlSaV3r2bMnERHFxcWRt7c3mZmZUcOGDSk8PLzCtps8eTJpamqS\nnp4eGRkZ0fjx44mISBAE+vXXX6lOnTpkZmZGn332mVK533//nerXr0/m5ubUuXNnSkpKeubyOXHi\nhLgOOTk50fr164mIaNiwYS+9fqxdu5bc3NzI2NiYatasSZs2bSIiouvXr1P79u3J1NSUrKysaMCA\nAWIZQRDo5s2bRPT8dby0Y8eOUY0aNcTXycnJFBAQQNbW1lSzZk1aunSp+NnZs2fJ09OTzMzMyN7e\nnsaNG0dFRUXi5xMnTiQbGxsyMTEhd3d3io2NfebyKquiskREGRkZ1KNHDzIxMaGWLVvSlClTqG3b\ntkT03zotl8vF6ZTerm7cuEG+vr5kaWlJVlZWNHjwYHr06JH4XRcXF5o7dy65u7uTnp4eyeXyV9r+\nXVxcxHV0+vTpr7T/8vb2ppCQEGrZsiWZmJhQr169xO2h7Hw9evSIgoKCyN7enhwdHWnKlCniZ4rt\n9IsvviAzMzOqVasWnTx5ktasWUNOTk5kY2Mjrn9ERE+ePKEvv/ySnJ2dydbWlsaMGSOuF8/bbz5r\nOSq2aWNjY2rQoAHt3r2biJ5uj3p6eqSpqUlGRkZkbm5OREQff/yxuA0QEa1atYpq165NFhYW1LNn\nT7p//7742Yu2ybJeKuEdOXKEiJ7uRBs2bEjTpk2je/fukaWlpbjT/euvv8jS0pIyMjLEheXi4kJx\ncXEkl8upuLiYhg0bRlOnThWnPXXqVGrdujU9ePCAHjx4QF5eXuLnx44dIy0tLQoJCaGioiIqKCig\n6dOnk56eHh0+fJhkMhkNHTqUXFxcaNasWSSTyWj16tVUs2ZNcfr79u2jW7duERFRdHQ0GRgY0N9/\n/600/enTp5NMJqP9+/eTgYGBuLJ/+umn5OvrS/fv3xdX8sLCwmfO94MHD8q1XWFhITk7O9PSpUtJ\nJpPRrl27SEdHR5zHhw8f0q5du6igoIBycnKoX79+1Lt3b7G8j48P/f7770T0dKXV1tam3377jUpK\nSmjFihXk4OBQ4TKTyWTUuHFjCg4Opvz8fHry5AmdPHmSiJ7ulGvXrk23b9+m3NxcCggIoMDAQCL6\nbyMaO3YsFRYW0uHDh0lHR4d69+5NDx48oOTkZLKxsaHo6GgxJi0tLVq8eDHJZDL6888/ydTUVNwo\nX6b9Sy/fxYsXU+vWrSk5OZmKiopo9OjRNGjQICIiunr1KhkZGdGJEyeosLCQgoODSUtLq8KER0Tl\n1rWioiKqVasWzZ49m4qLiykyMpKMjY0pPj6+wvKl215BEATq0aMHPX78mO7cuUPW1tZ08OBBIiIK\nCwuj2rVr07Vr10gul9PMmTPJy8urwmknJiaSsbExbd26lWQyGT18+JAuXbokxq3Y2J+3fuTm5pKJ\niQklJCQQEVFqaipdvXqViIgGDhxIs2bNIqKn66Bi2SvmQZHwnrWOl1U64cnlcmrWrBnNmDGDiouL\n6datW+Tm5kaHDh0ioqcHeGfPniW5XE6JiYlUv359Wrx4MRERHTx4kJo3by4eYF27do1SUlIqXF5l\nPa/sgAEDaMCAAZSfn0+xsbHk6OhI7dq1I6KKE17pZXvjxg06cuQIFRUV0YMHD6h9+/Y0ceJE8bsu\nLi7k4eFB9+7doydPnrzS9k+k3GF41f2Xt7c3OTo60tWrVykvL4/69u1LQ4YMqXC+evfuTWPGjKH8\n/HxKT0+nli1bigeYiu103bp1VFJSQlOmTCFHR0fxYOTw4cNkbGxMeXl5RPT0wKJXr16UlZVFOTk5\n1KNHD/r222/FdeF5+82KluP27dvFZfXnn3+SoaEhpaamEhHRunXrxIMThdLTOHr0KFlZWdHFixep\nsLCQxo8fT+3btxe/+7xtsiIvTHguLi5kZGREZmZm5OLiQp999hkVFBTQnDlzxB2lQufOncUjBR8f\nH5o+fXq5GSmduWvVqqXUSzl06BC5uroS0dOG1dHRUdoAp0+fTp06dRJfh4eHk5GREZWUlBARUXZ2\nNgmCIG4UZfXu3ZuWLFkiTl9fX19pQ7CxsRE3Vn19fbpy5Uq5abxovkuLjo4mR0dHpffatm37zA37\n4sWL4lEOUfmEV7t2bfGzvLw8EgSB0tLSyk3n1KlTZG1trTRvCh988AGtWLFCfB0fH0/a2tokl8vF\njaj0EZSlpSVt27ZNfN23b19xB7Z27dpySbdly5a0cePGCuevbPuXXb7169dXSmD3798nbW1tkslk\n9P3334vJTzH/Ojo6z014pde148ePk52dndJ3Bg0aRKGhoRWW9/HxEXsBCoIgKCWP/v3709y5c4mI\nyN/fXylByuVyMjAwoDt37pSb9qxZsyggIOCl4i6t9PqRm5tLZmZmtHPnTsrPz1f63tChQ2nUqFFK\nvcHS83Dz5s3nruNllU54Z86cIWdn53LzM3z48ArLLlq0iPr06UNET3dedevWpTNnzpRbN58330RE\nkZGRFZaVyWSkra2tdOAyefLk5/bwKjqYUdi9ezd5eHiIr11dXZXO/LzK9q8oXzrhvcr+y8fHR0w0\nRE97RDo6OlRSUqI0X6mpqaSrq6vUO9+8eTP5+voS0dPttE6dOuJnV65cIUEQKD09XXzP0tKSLl++\nTCUlJWRoaCgeFBE93Z8oEvHz
"text": [
"<matplotlib.figure.Figure at 0x1066dcef0>"
]
}
],
"prompt_number": 19
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"cython_bonus\"></a>\n",
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Bonus: How to use Cython without the IPython magic"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[[back to top](#sections)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"IPython's notebook is really great for explanatory analysis and documentation, but what if we want to compile our Python code via Cython without letting IPython's magic doing all the work? \n",
"These are the steps you would need."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 1. Creating a .pyx file containing the the desired code or function."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%file ccy_classic_lstsqr.pyx\n",
"\n",
"def ccy_classic_lstsqr(x, y):\n",
" \"\"\" Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
" x_avg = sum(x)/len(x)\n",
" y_avg = sum(y)/len(y)\n",
" var_x = sum([(x_i - x_avg)**2 for x_i in x])\n",
" cov_xy = sum([(x_i - x_avg)*(y_i - y_avg) for x_i,y_i in zip(x,y)])\n",
" slope = cov_xy / var_x\n",
" y_interc = y_avg - slope*x_avg\n",
" return (slope, y_interc)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Writing ccy_classic_lstsqr.pyx\n"
]
}
],
"prompt_number": 11
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 2. Creating a simple setup file"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%file setup.py\n",
"\n",
"from distutils.core import setup\n",
"from distutils.extension import Extension\n",
"from Cython.Distutils import build_ext\n",
"\n",
"setup(\n",
" cmdclass = {'build_ext': build_ext},\n",
" ext_modules = [Extension(\"ccy_classic_lstsqr\", [\"ccy_classic_lstsqr.pyx\"])]\n",
")"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Writing setup.py\n"
]
}
],
"prompt_number": 12
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br>\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"####3. Building and Compiling"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"!python3 setup.py build_ext --inplace"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"running build_ext\r\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"cythoning ccy_classic_lstsqr.pyx to ccy_classic_lstsqr.c\r\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"building 'ccy_classic_lstsqr' extension\r\n",
"creating build\r\n",
"creating build/temp.macosx-10.6-intel-3.4\r\n",
"/usr/bin/clang -fno-strict-aliasing -Werror=declaration-after-statement -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g -I/Library/Frameworks/Python.framework/Versions/3.4/include/python3.4m -c ccy_classic_lstsqr.c -o build/temp.macosx-10.6-intel-3.4/ccy_classic_lstsqr.o\r\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\u001b[1mccy_classic_lstsqr.c:2040:28: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function '__Pyx_PyObject_AsString'\r\n",
" [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject* o) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:2037:32: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function\r\n",
" '__Pyx_PyUnicode_FromString' [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(char* c_str) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:2104:26: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function '__Pyx_PyObject_IsTrue'\r\n",
" [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:2159:33: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function '__Pyx_PyIndex_AsSsize_t'\r\n",
" [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:2188:33: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function '__Pyx_PyInt_FromSize_t'\r\n",
" [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:1584:32: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function '__Pyx_PyInt_From_long'\r\n",
" [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:1631:27: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1mfunction '__Pyx_PyInt_As_long' is not\r\n",
" needed and will not be emitted [-Wunneeded-internal-declaration]\u001b[0m\r\n",
"static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *x) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:1731:26: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1mfunction '__Pyx_PyInt_As_int' is not\r\n",
" needed and will not be emitted [-Wunneeded-internal-declaration]\u001b[0m\r\n",
"static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *x) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"8 warnings generated.\r\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\u001b[1mccy_classic_lstsqr.c:2040:28: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function '__Pyx_PyObject_AsString'\r\n",
" [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject* o) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:2037:32: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function\r\n",
" '__Pyx_PyUnicode_FromString' [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(char* c_str) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:2104:26: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function '__Pyx_PyObject_IsTrue'\r\n",
" [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:2159:33: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function '__Pyx_PyIndex_AsSsize_t'\r\n",
" [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:2188:33: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function '__Pyx_PyInt_FromSize_t'\r\n",
" [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:1584:32: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1munused function '__Pyx_PyInt_From_long'\r\n",
" [-Wunused-function]\u001b[0m\r\n",
"static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:1631:27: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1mfunction '__Pyx_PyInt_As_long' is not\r\n",
" needed and will not be emitted [-Wunneeded-internal-declaration]\u001b[0m\r\n",
"static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *x) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m\u001b[1mccy_classic_lstsqr.c:1731:26: \u001b[0m\u001b[0;1;35mwarning: \u001b[0m\u001b[1mfunction '__Pyx_PyInt_As_int' is not\r\n",
" needed and will not be emitted [-Wunneeded-internal-declaration]\u001b[0m\r\n",
"static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *x) {\r\n",
"\u001b[0;1;32m ^\r\n",
"\u001b[0m"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"8"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
" warnings generated.\r\n",
"/usr/bin/clang -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g build/temp.macosx-10.6-intel-3.4/ccy_classic_lstsqr.o -o /Users/sebastian/Github/python_reference/benchmarks/ccy_classic_lstsqr.so\r\n"
]
}
],
"prompt_number": 13
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 4. Importing and running the code"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import ccy_classic_lstsqr\n",
"\n",
"%timeit classic_lstsqr(x, y)\n",
"%timeit cy_classic_lstsqr(x, y)\n",
"%timeit ccy_classic_lstsqr.ccy_classic_lstsqr(x, y)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"100 loops, best of 3: 2.9 ms per loop\n",
"1000 loops, best of 3: 212 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"1000 loops, best of 3: 207 \u00b5s per loop"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n"
]
}
],
"prompt_number": 20
}
],
"metadata": {}
}
]
}