{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# NumPy tutorial" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Python 3\n", "\n", "Since the focus of this notebook is math, we note only one difference between Python 2 and 3: division of integers does not truncate or round down:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.5" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1/2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "OK, that's it for Python 3!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## NumPy\n", "\n", "NumPy is the standard package for all kinds of operations on multidimensional arrays. Be aware that it originated as a merge between two packages and has evolved a lot over time, and there is a lot of stuff in there that you shouldn't necessarily use. For example, there is a `matrix` class, but the general consensus is that you should not use it.\n", "\n", "The array class (that is, the one that you should use) is called `ndarray`. Every array has a _shape_, which is a tuple of nonnegative sizes, one for each dimension of the array. For example, a 2x3 matrix has shape `(2,3)`.\n", "\n", "Dimensions in NumPy are also called axes. Note that they are not the same as the dimensions of a vector space in linear algebra.\n", "\n", "### Creating arrays" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0., 0., 0.],\n", " [ 0., 0., 0.]])" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.zeros((2, 3))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.41841411, 0.18258245, 0.62852128],\n", " [ 0.34715359, 0.61273193, 0.08825056]])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.uniform(0, 1, (2, 3))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0., 1., 2.],\n", " [ 3., 4., 5.]])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.array([[0., 1., 2.], [3., 4., 5.]]) # default is row-major order" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Indexing and slicing" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], "source": [ "a = np.array([[0., 1., 2.], [3., 4., 5.]])" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "5.0" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[1, 2]" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0., 1., 2.])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[0]" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0., 1., 2.])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[0,:] # synonymous with a[0]" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0., 3.])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[:,0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that unlike slices of lists, slices of arrays \"point\" back to the original array:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 6., 1., 2.],\n", " [ 3., 4., 5.]])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = a[0]\n", "b[0] = 6.\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Elementwise operations" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a = np.array([[0., 1., 2.], [3., 4., 5.]])\n", "b = np.array([[6., 7., 8.], [9., 10., 11.]])" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 6., 8., 10.],\n", " [ 12., 14., 16.]])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + b" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[-6., -6., -6.],\n", " [-6., -6., -6.]])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a - b" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0., 7., 16.],\n", " [ 27., 40., 55.]])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * b" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0. , 0.14285714, 0.25 ],\n", " [ 0.33333333, 0.4 , 0.45454545]])" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a / b" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1. , 2.71828183, 7.3890561 ],\n", " [ 20.08553692, 54.59815003, 148.4131591 ]])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.exp(a)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1.79175947, 1.94591015, 2.07944154],\n", " [ 2.19722458, 2.30258509, 2.39789527]])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.log(b) # natural log" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0. , 0.76159416, 0.96402758],\n", " [ 0.99505475, 0.9993293 , 0.9999092 ]])" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.tanh(a) # yes, we'll actually use this" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Broadcasting\n", "\n", "In some cases, it's possible to apply a binary elementwise operation (like `+`) to two arrays with different shapes. Namely, if an axis has size 1, it can be \"broadcast\" to any size. This is easier to demonstrate by example." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(1, 3)" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.zeros((2, 3))\n", "b = np.array([[1., 2., 3.]])\n", "b.shape" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1., 2., 3.],\n", " [ 1., 2., 3.]])" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + b" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(2, 1)" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = np.array([[1.],[2.]])\n", "b.shape" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1., 1., 1.],\n", " [ 2., 2., 2.]])" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If one array has fewer axes than the other, its shape is _left_ padded with ones:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(3,)" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = np.array([1., 2., 3.])\n", "b.shape" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1., 2., 3.],\n", " [ 1., 2., 3.]])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Reductions\n", "\n", "Reductions perform some operation along an axis: for example, on all the rows or all the columns of a matrix. (If you don't specify an axis, the operation will be performed on the entire array.)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0., 0., 0.],\n", " [ 0., 0., 0.]])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0., 0., 0.])" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.sum(a, axis=0) # all the columns" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0., 0.])" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.sum(a, axis=1) # all the rows" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0., 0., 0.])" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.max(a, axis=0)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0., 0., 0.])" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.min(a, axis=0)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 0, 0])" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.argmax(a, axis=0) # which element is the max?" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 0, 0])" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.argmin(a, axis=0) # which element is the min?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Transposing axes" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0., 0.],\n", " [ 0., 0.],\n", " [ 0., 0.]])" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.T" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Transpose operations don't create new arrays; they create views." ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0., 0., 0.],\n", " [ 7., 0., 0.]])" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = a.T\n", "b[0,1] = 7.\n", "a" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": false }, "outputs": [], "source": [ "c = np.zeros((2,3,4,5))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I hope you don't need it, but more complex rearrangements of axes are possible too:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(2, 4, 3, 5)" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d = np.moveaxis(c, 1, 2) # move axis 1 to become axis 2\n", "d.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Changing shape\n", "\n", "Arrays can be reshaped arbitrarily, but I can only imagine that you'll ever need to add or remove axes of size one, in order to make broadcasting work the way you want." ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(1, 2, 3, 4, 5)" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "e = np.expand_dims(c, 0) # add new axis 0 (with size 1)\n", "e.shape" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(2, 3, 4, 5)" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = np.squeeze(e, 0)\n", "f.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, these operations don't create new arrays; they create views." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Vector and matrix multiplication" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To do matrix multiplication, don't use `*`; instead use:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a = np.random.uniform(0, 1, (2,3))\n", "b = np.random.uniform(0, 1, (3,4))" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.35177723, 0.29972786, 0.50640218, 0.4781246 ],\n", " [ 0.77974407, 0.44074346, 0.57848922, 0.84393576]])" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a @ b # Python 3.5 and NumPy 1.10" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.35177723, 0.29972786, 0.50640218, 0.4781246 ],\n", " [ 0.77974407, 0.44074346, 0.57848922, 0.84393576]])" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.dot(a, b) # All versions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These two functions (`@` calls `np.matmul`) behave differently for arrays with more than 2 axes. Hopefully, you will not need to know the difference for this class." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The same operator/function works for vector-vector dot (inner) products, matrix-vector products, and (row) vector-matrix products." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 1 }