{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Numpy - multidimensional data arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "J.R. Johansson (jrjohansson at gmail.com)\n", "\n", "The latest version of this [IPython notebook](http://ipython.org/notebook.html) lecture is available at [http://github.com/jrjohansson/scientific-python-lectures](http://github.com/jrjohansson/scientific-python-lectures).\n", "\n", "The other notebooks in this lecture series are indexed at [http://jrjohansson.github.io](http://jrjohansson.github.io)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# what is this line all about?!? Answer in lecture 4\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `numpy` package (module) is used in almost all numerical computation using Python. It is a package that provide high-performance vector, matrix and higher-dimensional data structures for Python. It is implemented in C and Fortran so when calculations are vectorized (formulated with vectors and matrices), performance is very good. \n", "\n", "To use `numpy` you need to import the module, using for example:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from numpy import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the `numpy` package the terminology used for vectors, matrices and higher-dimensional data sets is *array*. \n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating `numpy` arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are a number of ways to initialize new numpy arrays, for example from\n", "\n", "* a Python list or tuples\n", "* using functions that are dedicated to generating numpy arrays, such as `arange`, `linspace`, etc.\n", "* reading data from files" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### From lists" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For example, to create new vector and matrix arrays from Python lists we can use the `numpy.array` function." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([1, 2, 3, 4])" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# a vector: the argument to the array function is a Python list\n", "v = array([1,2,3,4])\n", "\n", "v" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4]])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# a matrix: the argument to the array function is a nested Python list\n", "M = array([[1, 2], [3, 4]])\n", "\n", "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `v` and `M` objects are both of the type `ndarray` that the `numpy` module provides." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(numpy.ndarray, numpy.ndarray)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(v), type(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The difference between the `v` and `M` arrays is only their shapes. We can get information about the shape of an array by using the `ndarray.shape` property." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(4,)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v.shape" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(2, 2)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The number of elements in the array is available through the `ndarray.size` property:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.size" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Equivalently, we could use the function `numpy.shape` and `numpy.size`" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(2, 2)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "shape(M)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "size(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far the `numpy.ndarray` looks awfully much like a Python list (or nested list). Why not simply use Python lists for computations instead of creating a new array type? \n", "\n", "There are several reasons:\n", "\n", "* Python lists are very general. They can contain any kind of object. They are dynamically typed. They do not support mathematical functions such as matrix and dot multiplications, etc. Implementing such functions for Python lists would not be very efficient because of the dynamic typing.\n", "* Numpy arrays are **statically typed** and **homogeneous**. The type of the elements is determined when the array is created.\n", "* Numpy arrays are memory efficient.\n", "* Because of the static typing, fast implementation of mathematical functions such as multiplication and addition of `numpy` arrays can be implemented in a compiled language (C and Fortran is used).\n", "\n", "Using the `dtype` (data type) property of an `ndarray`, we can see what type the data of an array has:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "dtype('int64')" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.dtype" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We get an error if we try to assign a value of the wrong type to an element in a numpy array:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "ename": "ValueError", "evalue": "invalid literal for long() with base 10: 'hello'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mM\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"hello\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: invalid literal for long() with base 10: 'hello'" ] } ], "source": [ "M[0,0] = \"hello\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want, we can explicitly define the type of the array data when we create it, using the `dtype` keyword argument: " ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1.+0.j, 2.+0.j],\n", " [ 3.+0.j, 4.+0.j]])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = array([[1, 2], [3, 4]], dtype=complex)\n", "\n", "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Common data types that can be used with `dtype` are: `int`, `float`, `complex`, `bool`, `object`, etc.\n", "\n", "We can also explicitly define the bit size of the data types, for example: `int64`, `int16`, `float128`, `complex128`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using array-generating functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For larger arrays it is impractical to initialize the data manually, using explicit python lists. Instead we can use one of the many functions in `numpy` that generate arrays of different forms. Some of the more common are:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### arange" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# create a range\n", "\n", "x = arange(0, 10, 1) # arguments: start, stop, step\n", "\n", "x" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ -1.00000000e+00, -9.00000000e-01, -8.00000000e-01,\n", " -7.00000000e-01, -6.00000000e-01, -5.00000000e-01,\n", " -4.00000000e-01, -3.00000000e-01, -2.00000000e-01,\n", " -1.00000000e-01, -2.22044605e-16, 1.00000000e-01,\n", " 2.00000000e-01, 3.00000000e-01, 4.00000000e-01,\n", " 5.00000000e-01, 6.00000000e-01, 7.00000000e-01,\n", " 8.00000000e-01, 9.00000000e-01])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = arange(-1, 1, 0.1)\n", "\n", "x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### linspace and logspace" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0. , 0.41666667, 0.83333333, 1.25 ,\n", " 1.66666667, 2.08333333, 2.5 , 2.91666667,\n", " 3.33333333, 3.75 , 4.16666667, 4.58333333,\n", " 5. , 5.41666667, 5.83333333, 6.25 ,\n", " 6.66666667, 7.08333333, 7.5 , 7.91666667,\n", " 8.33333333, 8.75 , 9.16666667, 9.58333333, 10. ])" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# using linspace, both end points ARE included\n", "linspace(0, 10, 25)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 1.00000000e+00, 3.03773178e+00, 9.22781435e+00,\n", " 2.80316249e+01, 8.51525577e+01, 2.58670631e+02,\n", " 7.85771994e+02, 2.38696456e+03, 7.25095809e+03,\n", " 2.20264658e+04])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "logspace(0, 10, 10, base=e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### mgrid" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [], "source": [ "x, y = mgrid[0:5, 0:5] # similar to meshgrid in MATLAB" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[0, 0, 0, 0, 0],\n", " [1, 1, 1, 1, 1],\n", " [2, 2, 2, 2, 2],\n", " [3, 3, 3, 3, 3],\n", " [4, 4, 4, 4, 4]])" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[0, 1, 2, 3, 4],\n", " [0, 1, 2, 3, 4],\n", " [0, 1, 2, 3, 4],\n", " [0, 1, 2, 3, 4],\n", " [0, 1, 2, 3, 4]])" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### random data" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from numpy import random" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.92932506, 0.19684255, 0.736434 , 0.18125714, 0.70905038],\n", " [ 0.18803573, 0.9312815 , 0.1284532 , 0.38138008, 0.36646481],\n", " [ 0.53700462, 0.02361381, 0.97760688, 0.73296701, 0.23042324],\n", " [ 0.9024635 , 0.20860922, 0.67729644, 0.68386687, 0.49385729],\n", " [ 0.95876515, 0.29341553, 0.37520629, 0.29194432, 0.64102804]])" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# uniform random numbers in [0,1]\n", "random.rand(5,5)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.117907 , -1.57016164, 0.78256246, 1.45386709, 0.54744436],\n", " [ 2.30356897, -0.28352021, -0.9087325 , 1.2285279 , -1.00760167],\n", " [ 0.72216801, 0.77507299, -0.37793178, -0.31852241, 0.84493629],\n", " [-0.10682252, 1.15930142, -0.47291444, -0.69496967, -0.58912034],\n", " [ 0.34513487, -0.92389516, -0.216978 , 0.42153272, 0.86650101]])" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# standard normal distributed random numbers\n", "random.randn(5,5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### diag" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[1, 0, 0],\n", " [0, 2, 0],\n", " [0, 0, 3]])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# a diagonal matrix\n", "diag([1,2,3])" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[0, 1, 0, 0],\n", " [0, 0, 2, 0],\n", " [0, 0, 0, 3],\n", " [0, 0, 0, 0]])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# diagonal with offset from the main diagonal\n", "diag([1,2,3], k=1) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### zeros and ones" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0., 0., 0.],\n", " [ 0., 0., 0.],\n", " [ 0., 0., 0.]])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "zeros((3,3))" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1., 1., 1.],\n", " [ 1., 1., 1.],\n", " [ 1., 1., 1.]])" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ones((3,3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## File I/O" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Comma-separated values (CSV)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A very common file format for data files is comma-separated values (CSV), or related formats such as TSV (tab-separated values). To read data from such files into Numpy arrays we can use the `numpy.genfromtxt` function. For example, " ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1800 1 1 -6.1 -6.1 -6.1 1\r\n", "1800 1 2 -15.4 -15.4 -15.4 1\r\n", "1800 1 3 -15.0 -15.0 -15.0 1\r\n", "1800 1 4 -19.3 -19.3 -19.3 1\r\n", "1800 1 5 -16.8 -16.8 -16.8 1\r\n", "1800 1 6 -11.4 -11.4 -11.4 1\r\n", "1800 1 7 -7.6 -7.6 -7.6 1\r\n", "1800 1 8 -7.1 -7.1 -7.1 1\r\n", "1800 1 9 -10.1 -10.1 -10.1 1\r\n", "1800 1 10 -9.5 -9.5 -9.5 1\r\n" ] } ], "source": [ "!head stockholm_td_adj.dat" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [], "source": [ "data = genfromtxt('stockholm_td_adj.dat')" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(77431, 7)" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data.shape" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(figsize=(14,4))\n", "ax.plot(data[:,0]+data[:,1]/12.0+data[:,2]/365, data[:,5])\n", "ax.axis('tight')\n", "ax.set_title('tempeatures in Stockholm')\n", "ax.set_xlabel('year')\n", "ax.set_ylabel('temperature (C)');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using `numpy.savetxt` we can store a Numpy array to a file in CSV format:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.77872576, 0.40043577, 0.66254019],\n", " [ 0.60410063, 0.4791374 , 0.8237106 ],\n", " [ 0.96856318, 0.15459644, 0.96082399]])" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = random.rand(3,3)\n", "\n", "M" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [], "source": [ "savetxt(\"random-matrix.csv\", M)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7.787257639287014088e-01 4.004357670697732408e-01 6.625401863466899854e-01\r\n", "6.041006328761111543e-01 4.791373994963619154e-01 8.237105968088237473e-01\r\n", "9.685631757740569281e-01 1.545964379103705877e-01 9.608239852111523094e-01\r\n" ] } ], "source": [ "!cat random-matrix.csv" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.77873 0.40044 0.66254\r\n", "0.60410 0.47914 0.82371\r\n", "0.96856 0.15460 0.96082\r\n" ] } ], "source": [ "savetxt(\"random-matrix.csv\", M, fmt='%.5f') # fmt specifies the format\n", "\n", "!cat random-matrix.csv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Numpy's native file format" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Useful when storing and reading back numpy array data. Use the functions `numpy.save` and `numpy.load`:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "random-matrix.npy: data\r\n" ] } ], "source": [ "save(\"random-matrix.npy\", M)\n", "\n", "!file random-matrix.npy" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.77872576, 0.40043577, 0.66254019],\n", " [ 0.60410063, 0.4791374 , 0.8237106 ],\n", " [ 0.96856318, 0.15459644, 0.96082399]])" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "load(\"random-matrix.npy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More properties of the numpy arrays" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.itemsize # bytes per element" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "72" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.nbytes # number of bytes" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.ndim # number of dimensions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Manipulating arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Indexing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can index elements in an array using square brackets and indices:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# v is a vector, and has only one dimension, taking one index\n", "v[0]" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.47913739949636192" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# M is a matrix, or a 2 dimensional array, taking two indices \n", "M[1,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we omit an index of a multidimensional array it returns the whole row (or, in general, a N-1 dimensional array) " ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.77872576, 0.40043577, 0.66254019],\n", " [ 0.60410063, 0.4791374 , 0.8237106 ],\n", " [ 0.96856318, 0.15459644, 0.96082399]])" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0.60410063, 0.4791374 , 0.8237106 ])" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The same thing can be achieved with using `:` instead of an index: " ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0.60410063, 0.4791374 , 0.8237106 ])" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M[1,:] # row 1" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0.40043577, 0.4791374 , 0.15459644])" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M[:,1] # column 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can assign new values to elements in an array using indexing:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "collapsed": false }, "outputs": [], "source": [ "M[0,0] = 1" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1. , 0.40043577, 0.66254019],\n", " [ 0.60410063, 0.4791374 , 0.8237106 ],\n", " [ 0.96856318, 0.15459644, 0.96082399]])" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# also works for rows and columns\n", "M[1,:] = 0\n", "M[:,2] = -1" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1. , 0.40043577, -1. ],\n", " [ 0. , 0. , -1. ],\n", " [ 0.96856318, 0.15459644, -1. ]])" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Index slicing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Index slicing is the technical name for the syntax `M[lower:upper:step]` to extract part of an array:" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([1, 2, 3, 4, 5])" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = array([1,2,3,4,5])\n", "A" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([2, 3])" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[1:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Array slices are *mutable*: if they are assigned a new value the original array from which the slice was extracted is modified:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 1, -2, -3, 4, 5])" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[1:3] = [-2,-3]\n", "\n", "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can omit any of the three parameters in `M[lower:upper:step]`:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 1, -2, -3, 4, 5])" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[::] # lower, upper, step all take the default values" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 1, -3, 5])" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[::2] # step is 2, lower and upper defaults to the beginning and end of the array" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 1, -2, -3])" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[:3] # first three elements" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([4, 5])" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[3:] # elements from index 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Negative indices counts from the end of the array (positive index from the beginning):" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "collapsed": false }, "outputs": [], "source": [ "A = array([1,2,3,4,5])" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[-1] # the last element in the array" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([3, 4, 5])" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[-3:] # the last three elements" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Index slicing works exactly the same way for multidimensional arrays:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 1, 2, 3, 4],\n", " [10, 11, 12, 13, 14],\n", " [20, 21, 22, 23, 24],\n", " [30, 31, 32, 33, 34],\n", " [40, 41, 42, 43, 44]])" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = array([[n+m*10 for n in range(5)] for m in range(5)])\n", "\n", "A" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[11, 12, 13],\n", " [21, 22, 23],\n", " [31, 32, 33]])" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# a block from the original array\n", "A[1:4, 1:4]" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 2, 4],\n", " [20, 22, 24],\n", " [40, 42, 44]])" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# strides\n", "A[::2, ::2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Fancy indexing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fancy indexing is the name for when an array or list is used in-place of an index: " ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[10, 11, 12, 13, 14],\n", " [20, 21, 22, 23, 24],\n", " [30, 31, 32, 33, 34]])" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "row_indices = [1, 2, 3]\n", "A[row_indices]" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([11, 22, 34])" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "col_indices = [1, 2, -1] # remember, index -1 means the last element\n", "A[row_indices, col_indices]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also use index masks: If the index mask is an Numpy array of data type `bool`, then an element is selected (True) or not (False) depending on the value of the index mask at the position of each element: " ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4])" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = array([n for n in range(5)])\n", "B" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 2])" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "row_mask = array([True, False, True, False, False])\n", "B[row_mask]" ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 2])" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# same thing\n", "row_mask = array([1,0,1,0,0], dtype=bool)\n", "B[row_mask]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This feature is very useful to conditionally select elements from an array, using for example comparison operators:" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. ,\n", " 5.5, 6. , 6.5, 7. , 7.5, 8. , 8.5, 9. , 9.5])" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = arange(0, 10, 0.5)\n", "x" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([False, False, False, False, False, False, False, False, False,\n", " False, False, True, True, True, True, False, False, False,\n", " False, False], dtype=bool)" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mask = (5 < x) * (x < 7.5)\n", "\n", "mask" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 5.5, 6. , 6.5, 7. ])" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x[mask]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Functions for extracting data from arrays and creating arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### where" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The index mask can be converted to position index using the `where` function" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(array([11, 12, 13, 14]),)" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "indices = where(mask)\n", "\n", "indices" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 5.5, 6. , 6.5, 7. ])" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x[indices] # this indexing is equivalent to the fancy indexing x[mask]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### diag" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the diag function we can also extract the diagonal and subdiagonals of an array:" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0, 11, 22, 33, 44])" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diag(A)" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([10, 21, 32, 43])" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diag(A, -1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### take" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `take` function is similar to fancy indexing described above:" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([-3, -2, -1, 0, 1, 2])" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v2 = arange(-3,3)\n", "v2" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([-2, 0, 2])" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "row_indices = [1, 3, 5]\n", "v2[row_indices] # fancy indexing" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([-2, 0, 2])" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v2.take(row_indices)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But `take` also works on lists and other objects:" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([-2, 0, 2])" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "take([-3, -2, -1, 0, 1, 2], row_indices)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### choose" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Constructs an array by picking elements from several arrays:" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 5, -2, 5, -2])" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "which = [1, 0, 1, 0]\n", "choices = [[-2,-2,-2,-2], [5,5,5,5]]\n", "\n", "choose(which, choices)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Linear algebra" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vectorizing code is the key to writing efficient numerical calculation with Python/Numpy. That means that as much as possible of a program should be formulated in terms of matrix and vector operations, like matrix-matrix multiplication." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Scalar-array operations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use the usual arithmetic operators to multiply, add, subtract, and divide arrays with scalar numbers." ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "collapsed": false }, "outputs": [], "source": [ "v1 = arange(0, 5)" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 2, 4, 6, 8])" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v1 * 2" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([2, 3, 4, 5, 6])" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v1 + 2" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(array([[ 0, 2, 4, 6, 8],\n", " [20, 22, 24, 26, 28],\n", " [40, 42, 44, 46, 48],\n", " [60, 62, 64, 66, 68],\n", " [80, 82, 84, 86, 88]]), array([[ 2, 3, 4, 5, 6],\n", " [12, 13, 14, 15, 16],\n", " [22, 23, 24, 25, 26],\n", " [32, 33, 34, 35, 36],\n", " [42, 43, 44, 45, 46]]))" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A * 2, A + 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Element-wise array-array operations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we add, subtract, multiply and divide arrays with each other, the default behaviour is **element-wise** operations:" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 1, 4, 9, 16],\n", " [ 100, 121, 144, 169, 196],\n", " [ 400, 441, 484, 529, 576],\n", " [ 900, 961, 1024, 1089, 1156],\n", " [1600, 1681, 1764, 1849, 1936]])" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A * A # element-wise multiplication" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0, 1, 4, 9, 16])" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v1 * v1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we multiply arrays with compatible shapes, we get an element-wise multiplication of each row:" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "((5, 5), (5,))" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A.shape, v1.shape" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 1, 4, 9, 16],\n", " [ 0, 11, 24, 39, 56],\n", " [ 0, 21, 44, 69, 96],\n", " [ 0, 31, 64, 99, 136],\n", " [ 0, 41, 84, 129, 176]])" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A * v1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Matrix algebra" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What about matrix multiplication? There are two ways. We can either use the `dot` function, which applies a matrix-matrix, matrix-vector, or inner vector multiplication to its two arguments: " ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 300, 310, 320, 330, 340],\n", " [1300, 1360, 1420, 1480, 1540],\n", " [2300, 2410, 2520, 2630, 2740],\n", " [3300, 3460, 3620, 3780, 3940],\n", " [4300, 4510, 4720, 4930, 5140]])" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dot(A, A)" ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 30, 130, 230, 330, 430])" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dot(A, v1)" ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "30" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dot(v1, v1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, we can cast the array objects to the type `matrix`. This changes the behavior of the standard arithmetic operators `+, -, *` to use matrix algebra." ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "collapsed": false }, "outputs": [], "source": [ "M = matrix(A)\n", "v = matrix(v1).T # make it a column vector" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[0],\n", " [1],\n", " [2],\n", " [3],\n", " [4]])" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 300, 310, 320, 330, 340],\n", " [1300, 1360, 1420, 1480, 1540],\n", " [2300, 2410, 2520, 2630, 2740],\n", " [3300, 3460, 3620, 3780, 3940],\n", " [4300, 4510, 4720, 4930, 5140]])" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M * M" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 30],\n", " [130],\n", " [230],\n", " [330],\n", " [430]])" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M * v" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[30]])" ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# inner product\n", "v.T * v" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 30],\n", " [131],\n", " [232],\n", " [333],\n", " [434]])" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# with matrix objects, standard matrix algebra applies\n", "v + M*v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we try to add, subtract or multiply objects with incompatible shapes we get an error:" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "collapsed": false }, "outputs": [], "source": [ "v = matrix([1,2,3,4,5,6]).T" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "((5, 5), (6, 1))" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "shape(M), shape(v)" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "collapsed": false }, "outputs": [ { "ename": "ValueError", "evalue": "shapes (5,5) and (6,1) not aligned: 5 (dim 1) != 6 (dim 0)", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mM\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m/Users/rob/miniconda/envs/py27-spl/lib/python2.7/site-packages/numpy/matrixlib/defmatrix.pyc\u001b[0m in \u001b[0;36m__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 339\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mN\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 340\u001b[0m \u001b[0;31m# This promotes 1-D vectors to row vectors\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 341\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mN\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0masmatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 342\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misscalar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'__rmul__'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 343\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mN\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m: shapes (5,5) and (6,1) not aligned: 5 (dim 1) != 6 (dim 0)" ] } ], "source": [ "M * v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See also the related functions: `inner`, `outer`, `cross`, `kron`, `tensordot`. Try for example `help(kron)`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Array/Matrix transformations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Above we have used the `.T` to transpose the matrix object `v`. We could also have used the `transpose` function to accomplish the same thing. \n", "\n", "Other mathematical functions that transform matrix objects are:" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 0.+1.j, 0.+2.j],\n", " [ 0.+3.j, 0.+4.j]])" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C = matrix([[1j, 2j], [3j, 4j]])\n", "C" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 0.-1.j, 0.-2.j],\n", " [ 0.-3.j, 0.-4.j]])" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "conjugate(C)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hermitian conjugate: transpose + conjugate" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 0.-1.j, 0.-3.j],\n", " [ 0.-2.j, 0.-4.j]])" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C.H" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can extract the real and imaginary parts of complex-valued arrays using `real` and `imag`:" ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 0., 0.],\n", " [ 0., 0.]])" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "real(C) # same as: C.real" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 1., 2.],\n", " [ 3., 4.]])" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "imag(C) # same as: C.imag" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or the complex argument and absolute value" ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.78539816, 1.10714872],\n", " [ 1.24904577, 1.32581766]])" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "angle(C+1) # heads up MATLAB Users, angle is used instead of arg" ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 1., 2.],\n", " [ 3., 4.]])" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abs(C)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Matrix computations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Inverse" ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 0.+2.j , 0.-1.j ],\n", " [ 0.-1.5j, 0.+0.5j]])" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "linalg.inv(C) # equivalent to C.I " ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "matrix([[ 1.00000000e+00+0.j, 4.44089210e-16+0.j],\n", " [ 0.00000000e+00+0.j, 1.00000000e+00+0.j]])" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C.I * C" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Determinant" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(2.0000000000000004+0j)" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" } ], "source": [ "linalg.det(C)" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(0.50000000000000011+0j)" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "linalg.det(C.I)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Data processing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Often it is useful to store datasets in Numpy arrays. Numpy provides a number of functions to calculate statistics of datasets in arrays. \n", "\n", "For example, let's calculate some properties from the Stockholm temperature dataset used above." ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(77431, 7)" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# reminder, the temperature dataset is stored in the data variable:\n", "shape(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### mean" ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "6.1971096847515854" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# the temperature data is in column 3\n", "mean(data[:,3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The daily mean temperature in Stockholm over the last 200 years has been about 6.2 C." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### standard deviations and variance" ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(8.2822716213405734, 68.596023209663414)" ] }, "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ "std(data[:,3]), var(data[:,3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### min and max" ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "-25.800000000000001" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# lowest daily average temperature\n", "data[:,3].min()" ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "28.300000000000001" ] }, "execution_count": 116, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# highest daily average temperature\n", "data[:,3].max()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### sum, prod, and trace" ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] }, "execution_count": 117, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d = arange(0, 10)\n", "d" ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "45" ] }, "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# sum up all elements\n", "sum(d)" ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "3628800" ] }, "execution_count": 119, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# product of all elements\n", "prod(d+1)" ] }, { "cell_type": "code", "execution_count": 120, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45])" ] }, "execution_count": 120, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# cumulative sum\n", "cumsum(d)" ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 1, 2, 6, 24, 120, 720, 5040,\n", " 40320, 362880, 3628800])" ] }, "execution_count": 121, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# cumulative product\n", "cumprod(d+1)" ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "110" ] }, "execution_count": 122, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# same as: diag(A).sum()\n", "trace(A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Computations on subsets of arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can compute with subsets of the data in an array using indexing, fancy indexing, and the other methods of extracting data from an array (described above).\n", "\n", "For example, let's go back to the temperature dataset:" ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1800 1 1 -6.1 -6.1 -6.1 1\r\n", "1800 1 2 -15.4 -15.4 -15.4 1\r\n", "1800 1 3 -15.0 -15.0 -15.0 1\r\n" ] } ], "source": [ "!head -n 3 stockholm_td_adj.dat" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The dataformat is: year, month, day, daily average temperature, low, high, location.\n", "\n", "If we are interested in the average temperature only in a particular month, say February, then we can create a index mask and use it to select only the data for that month using:" ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11.,\n", " 12.])" ] }, "execution_count": 124, "metadata": {}, "output_type": "execute_result" } ], "source": [ "unique(data[:,1]) # the month column takes values from 1 to 12" ] }, { "cell_type": "code", "execution_count": 125, "metadata": { "collapsed": false }, "outputs": [], "source": [ "mask_feb = data[:,1] == 2" ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "-3.2121095707365961" ] }, "execution_count": 126, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# the temperature data is in column 3\n", "mean(data[mask_feb,3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With these tools we have very powerful data processing capabilities at our disposal. For example, to extract the average monthly average temperatures for each month of the year only takes a few lines of code: " ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "months = arange(1,13)\n", "monthly_mean = [mean(data[data[:,1] == month, 3]) for month in months]\n", "\n", "fig, ax = plt.subplots()\n", "ax.bar(months, monthly_mean)\n", "ax.set_xlabel(\"Month\")\n", "ax.set_ylabel(\"Monthly avg. temp.\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Calculations with higher-dimensional data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When functions such as `min`, `max`, etc. are applied to a multidimensional arrays, it is sometimes useful to apply the calculation to the entire array, and sometimes only on a row or column basis. Using the `axis` argument we can specify how these functions should behave: " ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.2850926 , 0.17302017, 0.17748378],\n", " [ 0.80070487, 0.45527067, 0.61277451],\n", " [ 0.11372793, 0.43608703, 0.87010206]])" ] }, "execution_count": 128, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = random.rand(3,3)\n", "m" ] }, { "cell_type": "code", "execution_count": 129, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.87010206156754955" ] }, "execution_count": 129, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# global max\n", "m.max()" ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0.80070487, 0.45527067, 0.87010206])" ] }, "execution_count": 130, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# max in each column\n", "m.max(axis=0)" ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0.2850926 , 0.80070487, 0.87010206])" ] }, "execution_count": 131, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# max in each row\n", "m.max(axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Many other functions and methods in the `array` and `matrix` classes accept the same (optional) `axis` keyword argument." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Reshaping, resizing and stacking arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The shape of an Numpy array can be modified without copying the underlying data, which makes it a fast operation even for large arrays." ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 1, 2, 3, 4],\n", " [10, 11, 12, 13, 14],\n", " [20, 21, 22, 23, 24],\n", " [30, 31, 32, 33, 34],\n", " [40, 41, 42, 43, 44]])" ] }, "execution_count": 132, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A" ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "collapsed": false }, "outputs": [], "source": [ "n, m = A.shape" ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 20, 21, 22, 23, 24, 30, 31,\n", " 32, 33, 34, 40, 41, 42, 43, 44]])" ] }, "execution_count": 134, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = A.reshape((1,n*m))\n", "B" ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 5, 5, 5, 5, 5, 10, 11, 12, 13, 14, 20, 21, 22, 23, 24, 30, 31,\n", " 32, 33, 34, 40, 41, 42, 43, 44]])" ] }, "execution_count": 135, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B[0,0:5] = 5 # modify the array\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 5, 5, 5, 5, 5],\n", " [10, 11, 12, 13, 14],\n", " [20, 21, 22, 23, 24],\n", " [30, 31, 32, 33, 34],\n", " [40, 41, 42, 43, 44]])" ] }, "execution_count": 136, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A # and the original variable is also changed. B is only a different view of the same data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also use the function `flatten` to make a higher-dimensional array into a vector. But this function creates a copy of the data." ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 5, 5, 5, 5, 5, 10, 11, 12, 13, 14, 20, 21, 22, 23, 24, 30, 31,\n", " 32, 33, 34, 40, 41, 42, 43, 44])" ] }, "execution_count": 137, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = A.flatten()\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 138, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([10, 10, 10, 10, 10, 10, 11, 12, 13, 14, 20, 21, 22, 23, 24, 30, 31,\n", " 32, 33, 34, 40, 41, 42, 43, 44])" ] }, "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B[0:5] = 10\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 139, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 5, 5, 5, 5, 5],\n", " [10, 11, 12, 13, 14],\n", " [20, 21, 22, 23, 24],\n", " [30, 31, 32, 33, 34],\n", " [40, 41, 42, 43, 44]])" ] }, "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A # now A has not changed, because B's data is a copy of A's, not referring to the same data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding a new dimension: newaxis" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With `newaxis`, we can insert new dimensions in an array, for example converting a vector to a column or row matrix:" ] }, { "cell_type": "code", "execution_count": 140, "metadata": { "collapsed": false }, "outputs": [], "source": [ "v = array([1,2,3])" ] }, { "cell_type": "code", "execution_count": 141, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(3,)" ] }, "execution_count": 141, "metadata": {}, "output_type": "execute_result" } ], "source": [ "shape(v)" ] }, { "cell_type": "code", "execution_count": 142, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[1],\n", " [2],\n", " [3]])" ] }, "execution_count": 142, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# make a column matrix of the vector v\n", "v[:, newaxis]" ] }, { "cell_type": "code", "execution_count": 143, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(3, 1)" ] }, "execution_count": 143, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# column matrix\n", "v[:,newaxis].shape" ] }, { "cell_type": "code", "execution_count": 144, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(1, 3)" ] }, "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# row matrix\n", "v[newaxis,:].shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Stacking and repeating arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using function `repeat`, `tile`, `vstack`, `hstack`, and `concatenate` we can create larger vectors and matrices from smaller ones:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### tile and repeat" ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a = array([[1, 2], [3, 4]])" ] }, { "cell_type": "code", "execution_count": 146, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4])" ] }, "execution_count": 146, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# repeat each element 3 times\n", "repeat(a, 3)" ] }, { "cell_type": "code", "execution_count": 147, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 1, 2, 1, 2],\n", " [3, 4, 3, 4, 3, 4]])" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# tile the matrix 3 times \n", "tile(a, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### concatenate" ] }, { "cell_type": "code", "execution_count": 148, "metadata": { "collapsed": false }, "outputs": [], "source": [ "b = array([[5, 6]])" ] }, { "cell_type": "code", "execution_count": 149, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] }, "execution_count": 149, "metadata": {}, "output_type": "execute_result" } ], "source": [ "concatenate((a, b), axis=0)" ] }, { "cell_type": "code", "execution_count": 150, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 5],\n", " [3, 4, 6]])" ] }, "execution_count": 150, "metadata": {}, "output_type": "execute_result" } ], "source": [ "concatenate((a, b.T), axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### hstack and vstack" ] }, { "cell_type": "code", "execution_count": 151, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] }, "execution_count": 151, "metadata": {}, "output_type": "execute_result" } ], "source": [ "vstack((a,b))" ] }, { "cell_type": "code", "execution_count": 152, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 5],\n", " [3, 4, 6]])" ] }, "execution_count": 152, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hstack((a,b.T))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Copy and \"deep copy\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To achieve high performance, assignments in Python usually do not copy the underlying objects. This is important for example when objects are passed between functions, to avoid an excessive amount of memory copying when it is not necessary (technical term: pass by reference). " ] }, { "cell_type": "code", "execution_count": 153, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4]])" ] }, "execution_count": 153, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = array([[1, 2], [3, 4]])\n", "\n", "A" ] }, { "cell_type": "code", "execution_count": 154, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# now B is referring to the same array data as A \n", "B = A " ] }, { "cell_type": "code", "execution_count": 155, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[10, 2],\n", " [ 3, 4]])" ] }, "execution_count": 155, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# changing B affects A\n", "B[0,0] = 10\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 156, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[10, 2],\n", " [ 3, 4]])" ] }, "execution_count": 156, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to avoid this behavior, so that when we get a new completely independent object `B` copied from `A`, then we need to do a so-called \"deep copy\" using the function `copy`:" ] }, { "cell_type": "code", "execution_count": 157, "metadata": { "collapsed": false }, "outputs": [], "source": [ "B = copy(A)" ] }, { "cell_type": "code", "execution_count": 158, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[-5, 2],\n", " [ 3, 4]])" ] }, "execution_count": 158, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# now, if we modify B, A is not affected\n", "B[0,0] = -5\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 159, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[10, 2],\n", " [ 3, 4]])" ] }, "execution_count": 159, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Iterating over array elements" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Generally, we want to avoid iterating over the elements of arrays whenever we can (at all costs). The reason is that in a interpreted language like Python (or MATLAB), iterations are really slow compared to vectorized operations. \n", "\n", "However, sometimes iterations are unavoidable. For such cases, the Python `for` loop is the most convenient way to iterate over an array:" ] }, { "cell_type": "code", "execution_count": 160, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n", "3\n", "4\n" ] } ], "source": [ "v = array([1,2,3,4])\n", "\n", "for element in v:\n", " print(element)" ] }, { "cell_type": "code", "execution_count": 161, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('row', array([1, 2]))\n", "1\n", "2\n", "('row', array([3, 4]))\n", "3\n", "4\n" ] } ], "source": [ "M = array([[1,2], [3,4]])\n", "\n", "for row in M:\n", " print(\"row\", row)\n", " \n", " for element in row:\n", " print(element)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we need to iterate over each element of an array and modify its elements, it is convenient to use the `enumerate` function to obtain both the element and its index in the `for` loop: " ] }, { "cell_type": "code", "execution_count": 162, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('row_idx', 0, 'row', array([1, 2]))\n", "('col_idx', 0, 'element', 1)\n", "('col_idx', 1, 'element', 2)\n", "('row_idx', 1, 'row', array([3, 4]))\n", "('col_idx', 0, 'element', 3)\n", "('col_idx', 1, 'element', 4)\n" ] } ], "source": [ "for row_idx, row in enumerate(M):\n", " print(\"row_idx\", row_idx, \"row\", row)\n", " \n", " for col_idx, element in enumerate(row):\n", " print(\"col_idx\", col_idx, \"element\", element)\n", " \n", " # update the matrix M: square each element\n", " M[row_idx, col_idx] = element ** 2" ] }, { "cell_type": "code", "execution_count": 163, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1, 4],\n", " [ 9, 16]])" ] }, "execution_count": 163, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# each element in M is now squared\n", "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Vectorizing functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As mentioned several times by now, to get good performance we should try to avoid looping over elements in our vectors and matrices, and instead use vectorized algorithms. The first step in converting a scalar algorithm to a vectorized algorithm is to make sure that the functions we write work with vector inputs." ] }, { "cell_type": "code", "execution_count": 164, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def Theta(x):\n", " \"\"\"\n", " Scalar implementation of the Heaviside step function.\n", " \"\"\"\n", " if x >= 0:\n", " return 1\n", " else:\n", " return 0" ] }, { "cell_type": "code", "execution_count": 165, "metadata": { "collapsed": false }, "outputs": [ { "ename": "ValueError", "evalue": "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mTheta\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mTheta\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mScalar\u001b[0m \u001b[0mimplemenation\u001b[0m \u001b[0mof\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mHeaviside\u001b[0m \u001b[0mstep\u001b[0m \u001b[0mfunction\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \"\"\"\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()" ] } ], "source": [ "Theta(array([-3,-2,-1,0,1,2,3]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "OK, that didn't work because we didn't write the `Theta` function so that it can handle a vector input... \n", "\n", "To get a vectorized version of Theta we can use the Numpy function `vectorize`. In many cases it can automatically vectorize a function:" ] }, { "cell_type": "code", "execution_count": 166, "metadata": { "collapsed": false }, "outputs": [], "source": [ "Theta_vec = vectorize(Theta)" ] }, { "cell_type": "code", "execution_count": 167, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 0, 0, 1, 1, 1, 1])" ] }, "execution_count": 167, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Theta_vec(array([-3,-2,-1,0,1,2,3]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also implement the function to accept a vector input from the beginning (requires more effort but might give better performance):" ] }, { "cell_type": "code", "execution_count": 168, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def Theta(x):\n", " \"\"\"\n", " Vector-aware implementation of the Heaviside step function.\n", " \"\"\"\n", " return 1 * (x >= 0)" ] }, { "cell_type": "code", "execution_count": 169, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([0, 0, 0, 1, 1, 1, 1])" ] }, "execution_count": 169, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Theta(array([-3,-2,-1,0,1,2,3]))" ] }, { "cell_type": "code", "execution_count": 170, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(0, 1)" ] }, "execution_count": 170, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# still works for scalars as well\n", "Theta(-1.2), Theta(2.6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using arrays in conditions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When using arrays in conditions,for example `if` statements and other boolean expressions, one needs to use `any` or `all`, which requires that any or all elements in the array evaluates to `True`:" ] }, { "cell_type": "code", "execution_count": 171, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1, 4],\n", " [ 9, 16]])" ] }, "execution_count": 171, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "code", "execution_count": 172, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "at least one element in M is larger than 5\n" ] } ], "source": [ "if (M > 5).any():\n", " print(\"at least one element in M is larger than 5\")\n", "else:\n", " print(\"no element in M is larger than 5\")" ] }, { "cell_type": "code", "execution_count": 173, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "all elements in M are not larger than 5\n" ] } ], "source": [ "if (M > 5).all():\n", " print(\"all elements in M are larger than 5\")\n", "else:\n", " print(\"all elements in M are not larger than 5\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Type casting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since Numpy arrays are *statically typed*, the type of an array does not change once created. But we can explicitly cast an array of some type to another using the `astype` functions (see also the similar `asarray` function). This always creates a new array of new type:" ] }, { "cell_type": "code", "execution_count": 174, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "dtype('int64')" ] }, "execution_count": 174, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.dtype" ] }, { "cell_type": "code", "execution_count": 175, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ 1., 4.],\n", " [ 9., 16.]])" ] }, "execution_count": 175, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M2 = M.astype(float)\n", "\n", "M2" ] }, { "cell_type": "code", "execution_count": 176, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "dtype('float64')" ] }, "execution_count": 176, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M2.dtype" ] }, { "cell_type": "code", "execution_count": 177, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[ True, True],\n", " [ True, True]], dtype=bool)" ] }, "execution_count": 177, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M3 = M.astype(bool)\n", "\n", "M3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Further reading" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* http://numpy.scipy.org\n", "* http://scipy.org/Tentative_NumPy_Tutorial\n", "* http://scipy.org/NumPy_for_Matlab_Users - A Numpy guide for MATLAB users." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Versions" ] }, { "cell_type": "code", "execution_count": 178, "metadata": { "collapsed": false }, "outputs": [ { "data": { "application/json": { "Software versions": [ { "module": "Python", "version": "2.7.10 64bit [GCC 4.2.1 (Apple Inc. build 5577)]" }, { "module": "IPython", "version": "3.2.1" }, { "module": "OS", "version": "Darwin 14.1.0 x86_64 i386 64bit" }, { "module": "numpy", "version": "1.9.2" } ] }, "text/html": [ "
SoftwareVersion
Python2.7.10 64bit [GCC 4.2.1 (Apple Inc. build 5577)]
IPython3.2.1
OSDarwin 14.1.0 x86_64 i386 64bit
numpy1.9.2
Sat Aug 15 11:02:09 2015 JST
" ], "text/latex": [ "\\begin{tabular}{|l|l|}\\hline\n", "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", "Python & 2.7.10 64bit [GCC 4.2.1 (Apple Inc. build 5577)] \\\\ \\hline\n", "IPython & 3.2.1 \\\\ \\hline\n", "OS & Darwin 14.1.0 x86\\_64 i386 64bit \\\\ \\hline\n", "numpy & 1.9.2 \\\\ \\hline\n", "\\hline \\multicolumn{2}{|l|}{Sat Aug 15 11:02:09 2015 JST} \\\\ \\hline\n", "\\end{tabular}\n" ], "text/plain": [ "Software versions\n", "Python 2.7.10 64bit [GCC 4.2.1 (Apple Inc. build 5577)]\n", "IPython 3.2.1\n", "OS Darwin 14.1.0 x86_64 i386 64bit\n", "numpy 1.9.2\n", "Sat Aug 15 11:02:09 2015 JST" ] }, "execution_count": 178, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%reload_ext version_information\n", "\n", "%version_information numpy" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.10" } }, "nbformat": 4, "nbformat_minor": 0 }