{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Numpy - multidimensional data arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "J.R. Johansson (robert@riken.jp) http://dml.riken.jp/~rob/\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.com](http://jrjohansson.github.com)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# what is this line all about?!? Answer in lecture 4\n", "%pylab inline" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Populating the interactive namespace from numpy and matplotlib\n" ] } ], "prompt_number": 1 }, { "cell_type": "heading", "level": 2, "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` need to import the module it using of example:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from numpy import *" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "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": "heading", "level": 2, "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": "heading", "level": 3, "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", "collapsed": false, "input": [ "# a vector: the argument to the array function is a Python list\n", "v = array([1,2,3,4])\n", "\n", "v" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 3, "text": [ "array([1, 2, 3, 4])" ] } ], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "# a matrix: the argument to the array function is a nested Python list\n", "M = array([[1, 2], [3, 4]])\n", "\n", "M" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 4, "text": [ "array([[1, 2],\n", " [3, 4]])" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `v` and `M` objects are both of the type `ndarray` that the `numpy` module provides." ] }, { "cell_type": "code", "collapsed": false, "input": [ "type(v), type(M)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 5, "text": [ "(numpy.ndarray, numpy.ndarray)" ] } ], "prompt_number": 5 }, { "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", "collapsed": false, "input": [ "v.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "(4,)" ] } ], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": [ "M.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ "(2, 2)" ] } ], "prompt_number": 7 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The number of elements in the array is available through the `ndarray.size` property:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "M.size" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 8, "text": [ "4" ] } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Equivalently, we could use the function `numpy.shape` and `numpy.size`" ] }, { "cell_type": "code", "collapsed": false, "input": [ "shape(M)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 9, "text": [ "(2, 2)" ] } ], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": [ "size(M)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 10, "text": [ "4" ] } ], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far the `numpy.ndarray` looks awefully 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. Implementating 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 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", "collapsed": false, "input": [ "M.dtype" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 11, "text": [ "dtype('int64')" ] } ], "prompt_number": 11 }, { "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", "collapsed": false, "input": [ "M[0,0] = \"hello\"" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "invalid literal for int() with base 10: 'hello'", "output_type": "pyerr", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mM\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34m\"hello\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mValueError\u001b[0m: invalid literal for int() with base 10: 'hello'" ] } ], "prompt_number": 12 }, { "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", "collapsed": false, "input": [ "M = array([[1, 2], [3, 4]], dtype=complex)\n", "\n", "M" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 13, "text": [ "array([[ 1.+0.j, 2.+0.j],\n", " [ 3.+0.j, 4.+0.j]])" ] } ], "prompt_number": 13 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Common type 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": "heading", "level": 3, "metadata": {}, "source": [ "Using array-generating functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For larger arrays it is inpractical to initialize the data manually, using explicit python lists. Instead we can use one of the many functions in `numpy` that generates arrays of different forms. Some of the more common are:" ] }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "arange" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# create a range\n", "\n", "x = arange(0, 10, 1) # arguments: start, stop, step\n", "\n", "x" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 14, "text": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] } ], "prompt_number": 14 }, { "cell_type": "code", "collapsed": false, "input": [ "x = arange(-1, 1, 0.1)\n", "\n", "x" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 15, "text": [ "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])" ] } ], "prompt_number": 15 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "linspace and logspace" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# using linspace, both end points ARE included\n", "linspace(0, 10, 25)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 16, "text": [ "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. ])" ] } ], "prompt_number": 16 }, { "cell_type": "code", "collapsed": false, "input": [ "logspace(0, 10, 10, base=e)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 17, "text": [ "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])" ] } ], "prompt_number": 17 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "mgrid" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x, y = mgrid[0:5, 0:5] # similar to meshgrid in MATLAB" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 18 }, { "cell_type": "code", "collapsed": false, "input": [ "x" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 19, "text": [ "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]])" ] } ], "prompt_number": 19 }, { "cell_type": "code", "collapsed": false, "input": [ "y" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 20, "text": [ "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]])" ] } ], "prompt_number": 20 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "random data" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from numpy import random" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 21 }, { "cell_type": "code", "collapsed": false, "input": [ "# uniform random numbers in [0,1]\n", "random.rand(5,5)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 22, "text": [ "array([[ 0.30550798, 0.91803791, 0.93239421, 0.28751598, 0.04860825],\n", " [ 0.45066196, 0.76661561, 0.52674476, 0.8059357 , 0.1117966 ],\n", " [ 0.05369232, 0.48848972, 0.74334693, 0.71935866, 0.35233569],\n", " [ 0.13872424, 0.58346613, 0.37483754, 0.59727255, 0.38859949],\n", " [ 0.29037136, 0.8360109 , 0.63105782, 0.58906755, 0.64758577]])" ] } ], "prompt_number": 22 }, { "cell_type": "code", "collapsed": false, "input": [ "# standard normal distributed random numbers\n", "random.randn(5,5)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 23, "text": [ "array([[ 0.28795069, -0.35938689, -0.31555872, 0.48542156, 0.26751156],\n", " [ 2.13568908, 0.85288911, -0.70587016, 0.98492216, -0.99610179],\n", " [ 0.49670578, -0.08179433, 0.58322716, -0.21797477, -1.16777687],\n", " [-0.3343575 , 0.20369114, -0.31390896, 0.3598063 , 0.36981814],\n", " [ 0.4876012 , 1.9979494 , 0.75177876, -1.80697478, 1.64068423]])" ] } ], "prompt_number": 23 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "diag" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# a diagonal matrix\n", "diag([1,2,3])" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 24, "text": [ "array([[1, 0, 0],\n", " [0, 2, 0],\n", " [0, 0, 3]])" ] } ], "prompt_number": 24 }, { "cell_type": "code", "collapsed": false, "input": [ "# diagonal with offset from the main diagonal\n", "diag([1,2,3], k=1) " ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 25, "text": [ "array([[0, 1, 0, 0],\n", " [0, 0, 2, 0],\n", " [0, 0, 0, 3],\n", " [0, 0, 0, 0]])" ] } ], "prompt_number": 25 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "zeros and ones" ] }, { "cell_type": "code", "collapsed": false, "input": [ "zeros((3,3))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 26, "text": [ "array([[ 0., 0., 0.],\n", " [ 0., 0., 0.],\n", " [ 0., 0., 0.]])" ] } ], "prompt_number": 26 }, { "cell_type": "code", "collapsed": false, "input": [ "ones((3,3))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 27, "text": [ "array([[ 1., 1., 1.],\n", " [ 1., 1., 1.],\n", " [ 1., 1., 1.]])" ] } ], "prompt_number": 27 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "File I/O" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Comma-separated values (CSV)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A very common file format for data files are the comma-separated values (CSV), or related format such as TSV (tab-separated values). To read data from such file into Numpy arrays we can use the `numpy.genfromtxt` function. For example, " ] }, { "cell_type": "code", "collapsed": false, "input": [ "!head stockholm_td_adj.dat" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "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" ] } ], "prompt_number": 28 }, { "cell_type": "code", "collapsed": false, "input": [ "data = genfromtxt('stockholm_td_adj.dat')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 29 }, { "cell_type": "code", "collapsed": false, "input": [ "data.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 30, "text": [ "(77431, 7)" ] } ], "prompt_number": 30 }, { "cell_type": "code", "collapsed": false, "input": [ "fig, ax = 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)');" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAA0EAAAEZCAYAAABRvy5qAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsfXe4FcX5/3soAiIgRbg2QEEs2AgYS1BBxC8qgootKmCJ\nGo0tMahYb+yJArFFDAIaNYgUGyoWBAR/ikERpQhBpSlgFKRLu/v74zic2ffMvDPvzOw551728zw8\n3D07O/Pu7LS3Z6IoiiBFihQpUqRIkSJFihQpdhBUKzYBKVKkSJEiRYoUKVKkSFFIpExQihQpUqRI\nkSJFihQpdiikTFCKFClSpEiRIkWKFCl2KKRMUIoUKVKkSJEiRYoUKXYopExQihQpUqRIkSJFihQp\ndiikTFCKFClSpEiRIkWKFCl2KKRMUIoUKVKkqHI45ZRT4Nlnny02GSRatmwJEyZMYN8zoVq1avD1\n11/7kJYiRYoUVR4pE5QiRYoUJYaWLVvCe++9V2wy2CgvL4fevXsXmwwAAHjjjTecaZk6dSocc8wx\nsOuuu0Ljxo2hY8eOMH36dAAAePrpp+HYY48NQmMmk4FMJsO+lyJFihQp/FGj2ASkSJEiRYo4MpkM\n7Ih5rLdu3Qo1ahR3W1qzZg10794dnnzySTjnnHNg06ZNMGXKFKhVq1ZR6UqRIkWKFGGRaoJSpEiR\nooTQu3dvWLx4MZx22mlQr149eOihhwAA4KOPPoJjjjkGGjZsCIcffjhMnjx5+zOdOnWC22+/HX7z\nm99AvXr1oEePHvDDDz/ABRdcAA0aNIBf//rXsGjRou3lq1WrBo8++ii0atUKdtttN7jxxhtjTNew\nYcPgoIMOgkaNGkG3bt1g8eLF2+9dd9110Lx5c2jQoAF06NABpk6dCgAA48ePh/vvvx9GjhwJ9erV\ng3bt2gFAvlmXrC1auHAhVKtWDYYNGwYtWrSAE0880dj+H//4R2jWrBk0aNAADj30UJg9e7ayHzt1\n6gRDhw4FgKz2pmPHjtCvXz9o1KgR7LvvvjB+/Hjlc/Pnz4dMJgPnnnsuZDIZqF27NnTt2hUOOeQQ\nmDt3Llx55ZXw4YcfQr169aBRo0YAALB69Wro06cPNG3aFFq2bAn33ntvrD+HDBkCBx10ENSvXx/a\ntm0Ln332WV67c+fOhX333RdGjhy5/bcZM2bAYYcdBrvuuiucd955sGnTplid++23HzRu3Bh69uwJ\ny5YtU77PRRddBFdddRWccsopUK9ePTj22GNh+fLlcN1110HDhg3hwAMPVNKTIkWKFFUeUYoUKVKk\nKCm0bNkymjBhwvbrpUuXRo0bN47efPPNKIqi6J133okaN24c/fDDD1EURdHxxx8f7bffftHXX38d\nrV69OjrooIOi1q1bRxMmTIi2bt0a9enTJ7r44ou315fJZKITTjghWrVqVbR48eKoTZs20VNPPRVF\nURS9/PLLUevWraMvv/wy2rZtW3TPPfdExxxzzPZnn3vuuWjlypXRtm3bogEDBkRlZWXRpk2boiiK\novLy8qh3797ku5SXl0cXXnhhFEVR9M0330SZTCbq27dvtGHDhmjjxo1k++PHj4/at28frV69Ooqi\nKPryyy+jZcuWKfuwU6dO0dChQ6MoiqLhw4dHNWvWjJ566qmooqIieuKJJ6I99thD+dyaNWuixo0b\nR3379o3efPPNaOXKlbH7Tz/9dNSxY8fYb717945OP/30aN26ddHChQujNm3abG/7xRdfjPbcc89o\n+vTpURRF0YIFC6JFixbF+uaTTz6JmjdvHr3++uvb62zRokV05JFHRsuWLYtWrlwZHXjggdHgwYOj\nKIqiCRMmRE2aNIlmzJgRbdq0Kbrmmmui4447bvuzmUwm+uqrr6IoiqK+fftGTZo0iT799NPo559/\njk444YSoRYsW0bPPPhtVVFREt912W9S5c2dlX6RIkSJFVUaqCUqRIkWKEsdzzz0Hp5xyCnTr1g0A\nAE488UTo0KEDvP766wCQNZ+7+OKLYZ999oH69evDySefDG3atIETTjgBqlevDmeffTbMmDEjVudN\nN90Eu+66K+y9995w/fXXw4gRIwAAYPDgwdC/f3/Yf//9oVq1atC/f3/47LPPYMmSJQAAcMEFF0DD\nhg2hWrVq8Kc//Qk2bdoE8+bNAwCAKIqMZnyq++Xl5VCnTh2oXbu2tv3FixfDTjvtBGvXroW5c+dC\nRUUF7L///lBWVmbVhy1atIBLL70UMpkM9OnTB5YtWwbff/99Xrl69erB1KlTIZPJwGWXXQZNmzaF\nnj17bi+L6d+2bRuMHDkS7r//fqhbty60aNECbrjhhu1BGZ566im46aaboH379gAA0KpVK2jevPn2\n5ydPngw9e/aEZ599Fk455ZTtv2cyGbj22muhrKwMGjZsCKeddtp2jc3zzz8Pl156KRx++OGw0047\n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\n", "text": [ "" ] } ], "prompt_number": 31 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using `numpy.savetxt` we can store a Numpy array to a file in CSV format:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "M = rand(3,3)\n", "\n", "M" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 32, "text": [ "array([[ 0.70506801, 0.54618952, 0.31039856],\n", " [ 0.26640475, 0.10358152, 0.73231132],\n", " [ 0.07987128, 0.34462854, 0.91114433]])" ] } ], "prompt_number": 32 }, { "cell_type": "code", "collapsed": false, "input": [ "savetxt(\"random-matrix.csv\", M)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 33 }, { "cell_type": "code", "collapsed": false, "input": [ "!cat random-matrix.csv" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "7.050680113576863750e-01 5.461895177867910345e-01 3.103985627238065037e-01\r\n", "2.664047486311884594e-01 1.035815249084012235e-01 7.323113219935466489e-01\r\n", "7.987128326702574999e-02 3.446285401590922781e-01 9.111443300153220237e-01\r\n" ] } ], "prompt_number": 34 }, { "cell_type": "code", "collapsed": false, "input": [ "savetxt(\"random-matrix.csv\", M, fmt='%.5f') # fmt specifies the format\n", "\n", "!cat random-matrix.csv" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "0.70507 0.54619 0.31040\r\n", "0.26640 0.10358 0.73231\r\n", "0.07987 0.34463 0.91114\r\n" ] } ], "prompt_number": 35 }, { "cell_type": "heading", "level": 3, "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", "collapsed": false, "input": [ "save(\"random-matrix.npy\", M)\n", "\n", "!file random-matrix.npy" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "random-matrix.npy: data\r\n" ] } ], "prompt_number": 36 }, { "cell_type": "code", "collapsed": false, "input": [ "load(\"random-matrix.npy\")" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 37, "text": [ "array([[ 0.70506801, 0.54618952, 0.31039856],\n", " [ 0.26640475, 0.10358152, 0.73231132],\n", " [ 0.07987128, 0.34462854, 0.91114433]])" ] } ], "prompt_number": 37 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "More properties of the numpy arrays" ] }, { "cell_type": "code", "collapsed": false, "input": [ "M.itemsize # bytes per element" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 38, "text": [ "8" ] } ], "prompt_number": 38 }, { "cell_type": "code", "collapsed": false, "input": [ "M.nbytes # number of bytes" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 39, "text": [ "72" ] } ], "prompt_number": 39 }, { "cell_type": "code", "collapsed": false, "input": [ "M.ndim # number of dimensions" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 40, "text": [ "2" ] } ], "prompt_number": 40 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Manipulating arrays" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Indexing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can index elements in an array using the square bracket and indices:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# v is a vector, and has only one dimension, taking one index\n", "v[0]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 41, "text": [ "1" ] } ], "prompt_number": 41 }, { "cell_type": "code", "collapsed": false, "input": [ "# M is a matrix, or a 2 dimensional array, taking two indices \n", "M[1,1]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 42, "text": [ "0.10358152490840122" ] } ], "prompt_number": 42 }, { "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", "collapsed": false, "input": [ "M" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 43, "text": [ "array([[ 0.70506801, 0.54618952, 0.31039856],\n", " [ 0.26640475, 0.10358152, 0.73231132],\n", " [ 0.07987128, 0.34462854, 0.91114433]])" ] } ], "prompt_number": 43 }, { "cell_type": "code", "collapsed": false, "input": [ "M[1]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 44, "text": [ "array([ 0.26640475, 0.10358152, 0.73231132])" ] } ], "prompt_number": 44 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The same thing can be achieved with using `:` instead of an index: " ] }, { "cell_type": "code", "collapsed": false, "input": [ "M[1,:] # row 1" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 45, "text": [ "array([ 0.26640475, 0.10358152, 0.73231132])" ] } ], "prompt_number": 45 }, { "cell_type": "code", "collapsed": false, "input": [ "M[:,1] # column 1" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 46, "text": [ "array([ 0.54618952, 0.10358152, 0.34462854])" ] } ], "prompt_number": 46 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can assign new values to elements in an array using indexing:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "M[0,0] = 1" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 47 }, { "cell_type": "code", "collapsed": false, "input": [ "M" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 48, "text": [ "array([[ 1. , 0.54618952, 0.31039856],\n", " [ 0.26640475, 0.10358152, 0.73231132],\n", " [ 0.07987128, 0.34462854, 0.91114433]])" ] } ], "prompt_number": 48 }, { "cell_type": "code", "collapsed": false, "input": [ "# also works for rows and columns\n", "M[1,:] = 0\n", "M[:,2] = -1" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 49 }, { "cell_type": "code", "collapsed": false, "input": [ "M" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 50, "text": [ "array([[ 1. , 0.54618952, -1. ],\n", " [ 0. , 0. , -1. ],\n", " [ 0.07987128, 0.34462854, -1. ]])" ] } ], "prompt_number": 50 }, { "cell_type": "heading", "level": 3, "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", "collapsed": false, "input": [ "A = array([1,2,3,4,5])\n", "A" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 51, "text": [ "array([1, 2, 3, 4, 5])" ] } ], "prompt_number": 51 }, { "cell_type": "code", "collapsed": false, "input": [ "A[1:3]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 52, "text": [ "array([2, 3])" ] } ], "prompt_number": 52 }, { "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", "collapsed": false, "input": [ "A[1:3] = [-2,-3]\n", "\n", "A" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 53, "text": [ "array([ 1, -2, -3, 4, 5])" ] } ], "prompt_number": 53 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can omit any of the three parameters in `M[lower:upper:step]`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "A[::] # lower, upper, step all take the default values" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 54, "text": [ "array([ 1, -2, -3, 4, 5])" ] } ], "prompt_number": 54 }, { "cell_type": "code", "collapsed": false, "input": [ "A[::2] # step is 2, lower and upper defaults to the beginning and end of the array" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 55, "text": [ "array([ 1, -3, 5])" ] } ], "prompt_number": 55 }, { "cell_type": "code", "collapsed": false, "input": [ "A[:3] # first three elements" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 56, "text": [ "array([ 1, -2, -3])" ] } ], "prompt_number": 56 }, { "cell_type": "code", "collapsed": false, "input": [ "A[3:] # elements from index 3" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 57, "text": [ "array([4, 5])" ] } ], "prompt_number": 57 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Negative indices counts from the end of the array (positive index from the begining):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "A = array([1,2,3,4,5])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 58 }, { "cell_type": "code", "collapsed": false, "input": [ "A[-1] # the last element in the array" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 59, "text": [ "5" ] } ], "prompt_number": 59 }, { "cell_type": "code", "collapsed": false, "input": [ "A[-3:] # the last three elements" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 60, "text": [ "array([3, 4, 5])" ] } ], "prompt_number": 60 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Index slicing works exactly the same way for multidimensional arrays:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "A = array([[n+m*10 for n in range(5)] for m in range(5)])\n", "\n", "A" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 61, "text": [ "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]])" ] } ], "prompt_number": 61 }, { "cell_type": "code", "collapsed": false, "input": [ "# a block from the original array\n", "A[1:4, 1:4]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 62, "text": [ "array([[11, 12, 13],\n", " [21, 22, 23],\n", " [31, 32, 33]])" ] } ], "prompt_number": 62 }, { "cell_type": "code", "collapsed": false, "input": [ "# strides\n", "A[::2, ::2]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 63, "text": [ "array([[ 0, 2, 4],\n", " [20, 22, 24],\n", " [40, 42, 44]])" ] } ], "prompt_number": 63 }, { "cell_type": "heading", "level": 3, "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", "collapsed": false, "input": [ "row_indices = [1, 2, 3]\n", "A[row_indices]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 64, "text": [ "array([[10, 11, 12, 13, 14],\n", " [20, 21, 22, 23, 24],\n", " [30, 31, 32, 33, 34]])" ] } ], "prompt_number": 64 }, { "cell_type": "code", "collapsed": false, "input": [ "col_indices = [1, 2, -1] # remember, index -1 means the last element\n", "A[row_indices, col_indices]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 65, "text": [ "array([11, 22, 34])" ] } ], "prompt_number": 65 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also index masks: If the index mask is an Numpy array of with data type `bool`, then an element is selected (True) or not (False) depending on the value of the index mask at the position each element: " ] }, { "cell_type": "code", "collapsed": false, "input": [ "B = array([n for n in range(5)])\n", "B" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 66, "text": [ "array([0, 1, 2, 3, 4])" ] } ], "prompt_number": 66 }, { "cell_type": "code", "collapsed": false, "input": [ "row_mask = array([True, False, True, False, False])\n", "B[row_mask]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 67, "text": [ "array([0, 2])" ] } ], "prompt_number": 67 }, { "cell_type": "code", "collapsed": false, "input": [ "# same thing\n", "row_mask = array([1,0,1,0,0], dtype=bool)\n", "B[row_mask]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 68, "text": [ "array([0, 2])" ] } ], "prompt_number": 68 }, { "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", "collapsed": false, "input": [ "x = arange(0, 10, 0.5)\n", "x" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 69, "text": [ "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])" ] } ], "prompt_number": 69 }, { "cell_type": "code", "collapsed": false, "input": [ "mask = (5 < x) * (x < 7.5)\n", "\n", "mask" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 70, "text": [ "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)" ] } ], "prompt_number": 70 }, { "cell_type": "code", "collapsed": false, "input": [ "x[mask]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 71, "text": [ "array([ 5.5, 6. , 6.5, 7. ])" ] } ], "prompt_number": 71 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Functions for extracting data from arrays and creating arrays" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "where" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The index mask can be converted to position index using the `where` function" ] }, { "cell_type": "code", "collapsed": false, "input": [ "indices = where(mask)\n", "\n", "indices" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 72, "text": [ "(array([11, 12, 13, 14]),)" ] } ], "prompt_number": 72 }, { "cell_type": "code", "collapsed": false, "input": [ "x[indices] # this indexing is equivalent to the fancy indexing x[mask]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 73, "text": [ "array([ 5.5, 6. , 6.5, 7. ])" ] } ], "prompt_number": 73 }, { "cell_type": "heading", "level": 3, "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", "collapsed": false, "input": [ "diag(A)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 74, "text": [ "array([ 0, 11, 22, 33, 44])" ] } ], "prompt_number": 74 }, { "cell_type": "code", "collapsed": false, "input": [ "diag(A, -1)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 75, "text": [ "array([10, 21, 32, 43])" ] } ], "prompt_number": 75 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "take" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `take` function is similar to fancy indexing described above:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "v2 = arange(-3,3)\n", "v2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 76, "text": [ "array([-3, -2, -1, 0, 1, 2])" ] } ], "prompt_number": 76 }, { "cell_type": "code", "collapsed": false, "input": [ "row_indices = [1, 3, 5]\n", "v2[row_indices] # fancy indexing" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 77, "text": [ "array([-2, 0, 2])" ] } ], "prompt_number": 77 }, { "cell_type": "code", "collapsed": false, "input": [ "v2.take(row_indices)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 78, "text": [ "array([-2, 0, 2])" ] } ], "prompt_number": 78 }, { "cell_type": "markdown", "metadata": {}, "source": [ "But `take` also works on lists and other objects:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "take([-3, -2, -1, 0, 1, 2], row_indices)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 79, "text": [ "array([-2, 0, 2])" ] } ], "prompt_number": 79 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "choose" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Constructs and array by picking elements form several arrays:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "which = [1, 0, 1, 0]\n", "choices = [[-2,-2,-2,-2], [5,5,5,5]]\n", "\n", "choose(which, choices)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 80, "text": [ "array([ 5, -2, 5, -2])" ] } ], "prompt_number": 80 }, { "cell_type": "heading", "level": 2, "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": "heading", "level": 3, "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", "collapsed": false, "input": [ "v1 = arange(0, 5)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 81 }, { "cell_type": "code", "collapsed": false, "input": [ "v1 * 2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 82, "text": [ "array([0, 2, 4, 6, 8])" ] } ], "prompt_number": 82 }, { "cell_type": "code", "collapsed": false, "input": [ "v1 + 2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 83, "text": [ "array([2, 3, 4, 5, 6])" ] } ], "prompt_number": 83 }, { "cell_type": "code", "collapsed": false, "input": [ "A * 2, A + 2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 84, "text": [ "(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]]),\n", " 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]]))" ] } ], "prompt_number": 84 }, { "cell_type": "heading", "level": 3, "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", "collapsed": false, "input": [ "A * A # element-wise multiplication" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 85, "text": [ "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]])" ] } ], "prompt_number": 85 }, { "cell_type": "code", "collapsed": false, "input": [ "v1 * v1" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 86, "text": [ "array([ 0, 1, 4, 9, 16])" ] } ], "prompt_number": 86 }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we multiply arrays with compatible shapes, we get an element-wise multiplication of each row:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "A.shape, v1.shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 87, "text": [ "((5, 5), (5,))" ] } ], "prompt_number": 87 }, { "cell_type": "code", "collapsed": false, "input": [ "A * v1" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 88, "text": [ "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]])" ] } ], "prompt_number": 88 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Matrix algebra" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What about matrix mutiplication? 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", "collapsed": false, "input": [ "dot(A, A)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 89, "text": [ "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]])" ] } ], "prompt_number": 89 }, { "cell_type": "code", "collapsed": false, "input": [ "dot(A, v1)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 90, "text": [ "array([ 30, 130, 230, 330, 430])" ] } ], "prompt_number": 90 }, { "cell_type": "code", "collapsed": false, "input": [ "dot(v1, v1)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 91, "text": [ "30" ] } ], "prompt_number": 91 }, { "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", "collapsed": false, "input": [ "M = matrix(A)\n", "v = matrix(v1).T # make it a column vector" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 92 }, { "cell_type": "code", "collapsed": false, "input": [ "v" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 93, "text": [ "matrix([[0],\n", " [1],\n", " [2],\n", " [3],\n", " [4]])" ] } ], "prompt_number": 93 }, { "cell_type": "code", "collapsed": false, "input": [ "M * M" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 94, "text": [ "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]])" ] } ], "prompt_number": 94 }, { "cell_type": "code", "collapsed": false, "input": [ "M * v" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 95, "text": [ "matrix([[ 30],\n", " [130],\n", " [230],\n", " [330],\n", " [430]])" ] } ], "prompt_number": 95 }, { "cell_type": "code", "collapsed": false, "input": [ "# inner product\n", "v.T * v" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 96, "text": [ "matrix([[30]])" ] } ], "prompt_number": 96 }, { "cell_type": "code", "collapsed": false, "input": [ "# with matrix objects, standard matrix algebra applies\n", "v + M*v" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 97, "text": [ "matrix([[ 30],\n", " [131],\n", " [232],\n", " [333],\n", " [434]])" ] } ], "prompt_number": 97 }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we try to add, subtract or multiply objects with incomplatible shapes we get an error:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "v = matrix([1,2,3,4,5,6]).T" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 98 }, { "cell_type": "code", "collapsed": false, "input": [ "shape(M), shape(v)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 99, "text": [ "((5, 5), (6, 1))" ] } ], "prompt_number": 99 }, { "cell_type": "code", "collapsed": false, "input": [ "M * v" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "objects are not aligned", "output_type": "pyerr", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mM\u001b[0m \u001b[1;33m*\u001b[0m \u001b[0mv\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;32m/usr/local/lib/python3.3/dist-packages/numpy/matrixlib/defmatrix.py\u001b[0m in \u001b[0;36m__mul__\u001b[1;34m(self, other)\u001b[0m\n\u001b[0;32m 339\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mother\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mN\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlist\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtuple\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 340\u001b[0m \u001b[1;31m# This promotes 1-D vectors to row vectors\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 341\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mN\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdot\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0masmatrix\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mother\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 342\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0misscalar\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mother\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mor\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mother\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'__rmul__'\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 343\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mN\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdot\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mother\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mValueError\u001b[0m: objects are not aligned" ] } ], "prompt_number": 100 }, { "cell_type": "markdown", "metadata": {}, "source": [ "See also the related functions: `inner`, `outer`, `cross`, `kron`, `tensordot`. Try for example `help(kron)`." ] }, { "cell_type": "heading", "level": 3, "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 transforms matrix objects are:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "C = matrix([[1j, 2j], [3j, 4j]])\n", "C" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 101, "text": [ "matrix([[ 0.+1.j, 0.+2.j],\n", " [ 0.+3.j, 0.+4.j]])" ] } ], "prompt_number": 101 }, { "cell_type": "code", "collapsed": false, "input": [ "conjugate(C)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 102, "text": [ "matrix([[ 0.-1.j, 0.-2.j],\n", " [ 0.-3.j, 0.-4.j]])" ] } ], "prompt_number": 102 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hermitian conjugate: transpose + conjugate" ] }, { "cell_type": "code", "collapsed": false, "input": [ "C.H" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 103, "text": [ "matrix([[ 0.-1.j, 0.-3.j],\n", " [ 0.-2.j, 0.-4.j]])" ] } ], "prompt_number": 103 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can extract the real and imaginary parts of complex-valued arrays using `real` and `imag`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "real(C) # same as: C.real" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 104, "text": [ "matrix([[ 0., 0.],\n", " [ 0., 0.]])" ] } ], "prompt_number": 104 }, { "cell_type": "code", "collapsed": false, "input": [ "imag(C) # same as: C.imag" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 105, "text": [ "matrix([[ 1., 2.],\n", " [ 3., 4.]])" ] } ], "prompt_number": 105 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or the complex argument and absolute value" ] }, { "cell_type": "code", "collapsed": false, "input": [ "angle(C+1) # heads up MATLAB Users, angle is used instead of arg" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 106, "text": [ "array([[ 0.78539816, 1.10714872],\n", " [ 1.24904577, 1.32581766]])" ] } ], "prompt_number": 106 }, { "cell_type": "code", "collapsed": false, "input": [ "abs(C)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 107, "text": [ "matrix([[ 1., 2.],\n", " [ 3., 4.]])" ] } ], "prompt_number": 107 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Matrix computations" ] }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "Inverse" ] }, { "cell_type": "code", "collapsed": false, "input": [ "inv(C) # equivalent to C.I " ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 108, "text": [ "matrix([[ 0.+2.j , 0.-1.j ],\n", " [ 0.-1.5j, 0.+0.5j]])" ] } ], "prompt_number": 108 }, { "cell_type": "code", "collapsed": false, "input": [ "C.I * C" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 109, "text": [ "matrix([[ 1.00000000e+00+0.j, 4.44089210e-16+0.j],\n", " [ 0.00000000e+00+0.j, 1.00000000e+00+0.j]])" ] } ], "prompt_number": 109 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "Determinant" ] }, { "cell_type": "code", "collapsed": false, "input": [ "det(C)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 110, "text": [ "(2.0000000000000004+0j)" ] } ], "prompt_number": 110 }, { "cell_type": "code", "collapsed": false, "input": [ "det(C.I)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 111, "text": [ "(0.50000000000000011+0j)" ] } ], "prompt_number": 111 }, { "cell_type": "heading", "level": 3, "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 data from the Stockholm temperature dataset used above." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# reminder, the tempeature dataset is stored in the data variable:\n", "shape(data)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 112, "text": [ "(77431, 7)" ] } ], "prompt_number": 112 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "mean" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# the temperature data is in column 3\n", "mean(data[:,3])" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 113, "text": [ "6.1971096847515925" ] } ], "prompt_number": 113 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The daily mean temperature in Stockholm over the last 200 year so has been about 6.2 C." ] }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "standard deviations and variance" ] }, { "cell_type": "code", "collapsed": false, "input": [ "std(data[:,3]), var(data[:,3])" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 114, "text": [ "(8.2822716213405663, 68.596023209663286)" ] } ], "prompt_number": 114 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "min and max" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# lowest daily average temperature\n", "data[:,3].min()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 115, "text": [ "-25.800000000000001" ] } ], "prompt_number": 115 }, { "cell_type": "code", "collapsed": false, "input": [ "# highest daily average temperature\n", "data[:,3].max()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 116, "text": [ "28.300000000000001" ] } ], "prompt_number": 116 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "sum, prod, and trace" ] }, { "cell_type": "code", "collapsed": false, "input": [ "d = arange(0, 10)\n", "d" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 117, "text": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] } ], "prompt_number": 117 }, { "cell_type": "code", "collapsed": false, "input": [ "# sum up all elements\n", "sum(d)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 118, "text": [ "45" ] } ], "prompt_number": 118 }, { "cell_type": "code", "collapsed": false, "input": [ "# product of all elements\n", "prod(d+1)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 119, "text": [ "3628800" ] } ], "prompt_number": 119 }, { "cell_type": "code", "collapsed": false, "input": [ "# cummulative sum\n", "cumsum(d)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 120, "text": [ "array([ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45])" ] } ], "prompt_number": 120 }, { "cell_type": "code", "collapsed": false, "input": [ "# cummulative product\n", "cumprod(d+1)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 121, "text": [ "array([ 1, 2, 6, 24, 120, 720, 5040,\n", " 40320, 362880, 3628800])" ] } ], "prompt_number": 121 }, { "cell_type": "code", "collapsed": false, "input": [ "# same as: diag(A).sum()\n", "trace(A)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 122, "text": [ "110" ] } ], "prompt_number": 122 }, { "cell_type": "heading", "level": 3, "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", "collapsed": false, "input": [ "!head -n 3 stockholm_td_adj.dat" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "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" ] } ], "prompt_number": 123 }, { "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 the select out only the data for that month using:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "unique(data[:,1]) # the month column takes values from 1 to 12" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 124, "text": [ "array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11.,\n", " 12.])" ] } ], "prompt_number": 124 }, { "cell_type": "code", "collapsed": false, "input": [ "mask_feb = data[:,1] == 2" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 125 }, { "cell_type": "code", "collapsed": false, "input": [ "# the temperature data is in column 3\n", "mean(data[mask_feb,3])" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 126, "text": [ "-3.2121095707366085" ] } ], "prompt_number": 126 }, { "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", "collapsed": false, "input": [ "months = arange(1,13)\n", "monthly_mean = [mean(data[data[:,1] == month, 3]) for month in months]\n", "\n", "fig, ax = subplots()\n", "ax.bar(months, monthly_mean)\n", "ax.set_xlabel(\"Month\")\n", "ax.set_ylabel(\"Monthly avg. temp.\");" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "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\nv3690tPTZbFYjI7itaVLl6qgoEDHjh3Tf//7X6WlpRkdyWN2u10vvPCCDh06pKqqKrW3t2v79u1G\nx+rSggULrro7wLp165Sfn6/jx4/rlltu0bp16wxK556r/FOmTFF1dbWOHDmi1NRUrV271qB0XXOV\nXbr8D9Ldu3frBz/4gUdzQq4IKioqlJKSoqFDhyomJkZz587Vzp07jY7lsQEDBigrK0uSdO211yot\nLU1nzpwxOJV3Tp06pbfeekv33ntv2F34d/78ee3fv18LFy6UJEVHR+u6664zOJXn4uLiFBMTo9bW\nVrW1tam1tVWDBw82OlaXJk2apD59+nR4raysTMXFxZKk4uJi7dixw4hoHnGVPz8/X1FRl388Tpgw\nQadOnTIimluuskvSsmXL9Pjjj3s8J+SK4PTp0xoyZIjz48TERJ0+fdrARL6z2+2qrKzUhAkTjI7i\nlYcfflhPPPGE8xshnNTW1qp///5asGCBbrjhBi1evFitra1Gx/JY3759tXz5ciUlJWnQoEG6/vrr\ndeuttxody2v19fXOE0ASEhJUX19vcCLfbd68WQUFBUbH8NjOnTuVmJioMWPGePxnQu47PRy3Ilxp\naWnR7NmztX79el177bVGx/HY3//+d8XHxys7OzvsVgOS1NbWpkOHDulnP/uZDh06pN69e4f0tsR3\n1dTU6I9//KPsdrvOnDmjlpYWvfLKK0bH6haLxRK239ePPfaYevbsqbvvvtvoKB5pbW3VmjVrtHr1\naudrnnwfh1wRDB48WHV1dc6P6+rqlJiYaGAi73399deaNWuW7rnnHk2fPt3oOF559913VVZWpmHD\nhqmoqEj/+te/NH/+fKNjeSwxMVGJiYkaN26cJGn27Nk6dOiQwak89+9//1s33nij+vXrp+joaM2c\nOVPvvvuu0bG8lpCQoHPnzkm6fN1QfHy8wYm89+KLL+qtt94KqyKuqamR3W5XZmamhg0bplOnTmns\n2LH65JNPuvxzIVcEOTk5+uijj2S323Xp0iW99tprmjZtmtGxPOZwOLRo0SKlp6froYceMjqO19as\nWaO6ujrV1tZq+/btuvnmm/XSSy8ZHctjAwYM0JAhQ3T8+HFJ0p49e5SRkWFwKs+NGjVKBw8e1Jdf\nfimHw6E9e/YoPT3d6FhemzZtmrZu3SpJ2rp1a9j9g6i8vFxPPPGEdu7cqWuuucboOB6zWq2qr69X\nbW2tamtrlZiYqEOHDrkvYkcIeuuttxypqamO5ORkx5o1a4yO45X9+/c7LBaLIzMz05GVleXIyspy\nvP3220bH8onNZnMUFhYaHcNrhw8fduTk5DjGjBnjmDFjhqOpqcnoSF4pLS11pKenO0aPHu2YP3++\n49KlS0ZH6tLcuXMdAwcOdMTExDgSExMdmzdvdnz++eeOW265xTFixAhHfn6+o7Gx0eiYnfpu/k2b\nNjlSUlIcSUlJzu/hBx54wOiYLl3J3rNnT+d/+28bNmyY4/PPP3c7hwvKAMDkQm5rCAAQXBQBAJgc\nRQAAJkcRAIDJUQQAYHIUAQCYHEUAU4uKitK8efOcH7e1tal///4+3377/Pnzev75550f22y2sLyV\nN8yFIoCp9e7dW9XV1bp48aIkaffu3UpMTPT53jiNjY167rnn/BkRCDiKAKZXUFCgXbt2SZK2bdum\noqIi5426GhoaNH36dGVmZuqHP/yhqqqqJEklJSVauHCh8vLylJycrA0bNkiSVq5cqZqaGmVnZ2vF\nihWyWCxqaWnRnXfeqbS0NN1zzz3G/CWBLlAEML277rpL27dv11dffaWqqqoOtw1ftWqVxo4dqyNH\njmjNmjUdbsB3/Phx/eMf/1BFRYVWr16t9vZ2lZaWKjk5WZWVlXr88cflcDhUWVmp9evX6+jRozpx\n4oQOHDhgxF8T6BRFANOzWq2y2+3atm2bpk6d2uFzBw4ccB5DyMvL0+eff67m5mZZLBZNnTpVMTEx\n6tevn+Lj41VfX+/ylr/jx4/XoEGDZLFYlJWVJbvdHoy/FuCxaKMDAKFg2rRp+uUvf6l9+/bp008/\n7fC5zm7H1bNnT+fve/Tooba2Npdf16tXL4++DjAKKwJAlx8CXlJSctUtqydNmuS8H73NZlP//v0V\nGxvbaTnExsaqubk54HkBf2JFAFO7cnbQ4MGD9eCDDzpfu/L6lYPCmZmZ6t27t/Me+509datfv366\n6aabZLVaVVBQoIKCgqu+Llyf1oXIxW2oAcDk2BoCAJOjCADA5CgCADA5igAATI4iAACTowgAwOQo\nAgAwOYoAAEzu/wFpvCRs/trHHgAAAABJRU5ErkJggg==\n", "text": [ "" ] } ], "prompt_number": 127 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Calculations with higher-dimensional data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When functions such as `min`, `max`, etc., is 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", "collapsed": false, "input": [ "m = rand(3,3)\n", "m" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 128, "text": [ "array([[ 0.09260423, 0.73349712, 0.43306604],\n", " [ 0.65890098, 0.4972126 , 0.83049668],\n", " [ 0.80428551, 0.0817173 , 0.57833117]])" ] } ], "prompt_number": 128 }, { "cell_type": "code", "collapsed": false, "input": [ "# global max\n", "m.max()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 129, "text": [ "0.83049668273782951" ] } ], "prompt_number": 129 }, { "cell_type": "code", "collapsed": false, "input": [ "# max in each column\n", "m.max(axis=0)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 130, "text": [ "array([ 0.80428551, 0.73349712, 0.83049668])" ] } ], "prompt_number": 130 }, { "cell_type": "code", "collapsed": false, "input": [ "# max in each row\n", "m.max(axis=1)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 131, "text": [ "array([ 0.73349712, 0.83049668, 0.80428551])" ] } ], "prompt_number": 131 }, { "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": "heading", "level": 2, "metadata": {}, "source": [ "Reshaping, resizing and stacking arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The shape of an Numpy array can be modified without copying the underlaying data, which makes it a fast operation even for large arrays." ] }, { "cell_type": "code", "collapsed": false, "input": [ "A" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 132, "text": [ "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]])" ] } ], "prompt_number": 132 }, { "cell_type": "code", "collapsed": false, "input": [ "n, m = A.shape" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 133 }, { "cell_type": "code", "collapsed": false, "input": [ "B = A.reshape((1,n*m))\n", "B" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 134, "text": [ "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]])" ] } ], "prompt_number": 134 }, { "cell_type": "code", "collapsed": false, "input": [ "B[0,0:5] = 5 # modify the array\n", "\n", "B" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 135, "text": [ "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]])" ] } ], "prompt_number": 135 }, { "cell_type": "code", "collapsed": false, "input": [ "A # and the original variable is also changed. B is only a different view of the same data" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 136, "text": [ "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]])" ] } ], "prompt_number": 136 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also use the function `flatten` to make a higher-dimensional array into a vector. But this function create a copy of the data." ] }, { "cell_type": "code", "collapsed": false, "input": [ "B = A.flatten()\n", "\n", "B" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 137, "text": [ "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])" ] } ], "prompt_number": 137 }, { "cell_type": "code", "collapsed": false, "input": [ "B[0:5] = 10\n", "\n", "B" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 138, "text": [ "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])" ] } ], "prompt_number": 138 }, { "cell_type": "code", "collapsed": false, "input": [ "A # now A has not changed, because B's data is a copy of A's, not refering to the same data" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 139, "text": [ "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]])" ] } ], "prompt_number": 139 }, { "cell_type": "heading", "level": 2, "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", "collapsed": false, "input": [ "v = array([1,2,3])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 140 }, { "cell_type": "code", "collapsed": false, "input": [ "shape(v)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 141, "text": [ "(3,)" ] } ], "prompt_number": 141 }, { "cell_type": "code", "collapsed": false, "input": [ "# make a column matrix of the vector v\n", "v[:, newaxis]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 142, "text": [ "array([[1],\n", " [2],\n", " [3]])" ] } ], "prompt_number": 142 }, { "cell_type": "code", "collapsed": false, "input": [ "# column matrix\n", "v[:,newaxis].shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 143, "text": [ "(3, 1)" ] } ], "prompt_number": 143 }, { "cell_type": "code", "collapsed": false, "input": [ "# row matrix\n", "v[newaxis,:].shape" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 144, "text": [ "(1, 3)" ] } ], "prompt_number": 144 }, { "cell_type": "heading", "level": 2, "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": "heading", "level": 3, "metadata": {}, "source": [ "tile and repeat" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a = array([[1, 2], [3, 4]])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 145 }, { "cell_type": "code", "collapsed": false, "input": [ "# repeat each element 3 times\n", "repeat(a, 3)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 146, "text": [ "array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4])" ] } ], "prompt_number": 146 }, { "cell_type": "code", "collapsed": false, "input": [ "# tile the matrix 3 times \n", "tile(a, 3)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 147, "text": [ "array([[1, 2, 1, 2, 1, 2],\n", " [3, 4, 3, 4, 3, 4]])" ] } ], "prompt_number": 147 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "concatenate" ] }, { "cell_type": "code", "collapsed": false, "input": [ "b = array([[5, 6]])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 148 }, { "cell_type": "code", "collapsed": false, "input": [ "concatenate((a, b), axis=0)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 149, "text": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] } ], "prompt_number": 149 }, { "cell_type": "code", "collapsed": false, "input": [ "concatenate((a, b.T), axis=1)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 150, "text": [ "array([[1, 2, 5],\n", " [3, 4, 6]])" ] } ], "prompt_number": 150 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "hstack and vstack" ] }, { "cell_type": "code", "collapsed": false, "input": [ "vstack((a,b))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 151, "text": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] } ], "prompt_number": 151 }, { "cell_type": "code", "collapsed": false, "input": [ "hstack((a,b.T))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 152, "text": [ "array([[1, 2, 5],\n", " [3, 4, 6]])" ] } ], "prompt_number": 152 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Copy and \"deep copy\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To achieve high performance, assignments in Python usually do not copy the underlaying 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 (techincal term: pass by reference). " ] }, { "cell_type": "code", "collapsed": false, "input": [ "A = array([[1, 2], [3, 4]])\n", "\n", "A" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 153, "text": [ "array([[1, 2],\n", " [3, 4]])" ] } ], "prompt_number": 153 }, { "cell_type": "code", "collapsed": false, "input": [ "# now B is referring to the same array data as A \n", "B = A " ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 154 }, { "cell_type": "code", "collapsed": false, "input": [ "# changing B affects A\n", "B[0,0] = 10\n", "\n", "B" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 155, "text": [ "array([[10, 2],\n", " [ 3, 4]])" ] } ], "prompt_number": 155 }, { "cell_type": "code", "collapsed": false, "input": [ "A" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 156, "text": [ "array([[10, 2],\n", " [ 3, 4]])" ] } ], "prompt_number": 156 }, { "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", "collapsed": false, "input": [ "B = copy(A)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 157 }, { "cell_type": "code", "collapsed": false, "input": [ "# now, if we modify B, A is not affected\n", "B[0,0] = -5\n", "\n", "B" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 158, "text": [ "array([[-5, 2],\n", " [ 3, 4]])" ] } ], "prompt_number": 158 }, { "cell_type": "code", "collapsed": false, "input": [ "A" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 159, "text": [ "array([[10, 2],\n", " [ 3, 4]])" ] } ], "prompt_number": 159 }, { "cell_type": "heading", "level": 2, "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", "collapsed": false, "input": [ "v = array([1,2,3,4])\n", "\n", "for element in v:\n", " print(element)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1\n", "2\n", "3\n", "4\n" ] } ], "prompt_number": 160 }, { "cell_type": "code", "collapsed": false, "input": [ "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)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "row [1 2]\n", "1\n", "2\n", "row [3 4]\n", "3\n", "4\n" ] } ], "prompt_number": 161 }, { "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", "collapsed": false, "input": [ "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" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "row_idx 0 row [1 2]\n", "col_idx 0 element 1\n", "col_idx 1 element 2\n", "row_idx 1 row [3 4]\n", "col_idx 0 element 3\n", "col_idx 1 element 4\n" ] } ], "prompt_number": 162 }, { "cell_type": "code", "collapsed": false, "input": [ "# each element in M is now squared\n", "M" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 163, "text": [ "array([[ 1, 4],\n", " [ 9, 16]])" ] } ], "prompt_number": 163 }, { "cell_type": "heading", "level": 2, "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", "collapsed": false, "input": [ "def Theta(x):\n", " \"\"\"\n", " Scalar implemenation of the Heaviside step function.\n", " \"\"\"\n", " if x >= 0:\n", " return 1\n", " else:\n", " return 0" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 164 }, { "cell_type": "code", "collapsed": false, "input": [ "Theta(array([-3,-2,-1,0,1,2,3]))" ], "language": "python", "metadata": {}, "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": "pyerr", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mTheta\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0marray\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;32m\u001b[0m in \u001b[0;36mTheta\u001b[1;34m(x)\u001b[0m\n\u001b[0;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[1;33m.\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \"\"\"\n\u001b[1;32m----> 5\u001b[1;33m \u001b[1;32mif\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m>=\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 6\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 7\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mValueError\u001b[0m: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()" ] } ], "prompt_number": 165 }, { "cell_type": "markdown", "metadata": {}, "source": [ "OK, that didn't work because we didn't write the `Theta` function so that it can handle with 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", "collapsed": false, "input": [ "Theta_vec = vectorize(Theta)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 166 }, { "cell_type": "code", "collapsed": false, "input": [ "Theta_vec(array([-3,-2,-1,0,1,2,3]))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 167, "text": [ "array([0, 0, 0, 1, 1, 1, 1])" ] } ], "prompt_number": 167 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also implement the function to accept vector input from the beginning (requires more effort but might give better performance):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def Theta(x):\n", " \"\"\"\n", " Vector-aware implemenation of the Heaviside step function.\n", " \"\"\"\n", " return 1 * (x >= 0)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 168 }, { "cell_type": "code", "collapsed": false, "input": [ "Theta(array([-3,-2,-1,0,1,2,3]))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 169, "text": [ "array([0, 0, 0, 1, 1, 1, 1])" ] } ], "prompt_number": 169 }, { "cell_type": "code", "collapsed": false, "input": [ "# still works for scalars as well\n", "Theta(-1.2), Theta(2.6)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 170, "text": [ "(0, 1)" ] } ], "prompt_number": 170 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Using arrays in conditions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When using arrays in conditions in for example `if` statements and other boolean expressions, one need to use one of `any` or `all`, which requires that any or all elements in the array evalutes to `True`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "M" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 171, "text": [ "array([[ 1, 4],\n", " [ 9, 16]])" ] } ], "prompt_number": 171 }, { "cell_type": "code", "collapsed": false, "input": [ "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\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "at least one element in M is larger than 5\n" ] } ], "prompt_number": 172 }, { "cell_type": "code", "collapsed": false, "input": [ "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\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "all elements in M are not larger than 5\n" ] } ], "prompt_number": 173 }, { "cell_type": "heading", "level": 2, "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 create a new array of new type:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "M.dtype" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 174, "text": [ "dtype('int64')" ] } ], "prompt_number": 174 }, { "cell_type": "code", "collapsed": false, "input": [ "M2 = M.astype(float)\n", "\n", "M2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 175, "text": [ "array([[ 1., 4.],\n", " [ 9., 16.]])" ] } ], "prompt_number": 175 }, { "cell_type": "code", "collapsed": false, "input": [ "M2.dtype" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 176, "text": [ "dtype('float64')" ] } ], "prompt_number": 176 }, { "cell_type": "code", "collapsed": false, "input": [ "M3 = M.astype(bool)\n", "\n", "M3" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 177, "text": [ "array([[ True, True],\n", " [ True, True]], dtype=bool)" ] } ], "prompt_number": 177 }, { "cell_type": "heading", "level": 2, "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": "heading", "level": 2, "metadata": {}, "source": [ "Versions" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%reload_ext version_information\n", "\n", "%version_information numpy" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
SoftwareVersion
Python3.3.2+ (default, Oct 9 2013, 14:50:09) [GCC 4.8.1]
IPython1.1.0
OSposix [linux]
numpy1.8.0
Mon Nov 11 15:06:46 2013 KST
" ], "json": [ "{ \"Software versions\" : [{ \"module\" : \"Python\", \"version\" : \"3.3.2+ (default, Oct 9 2013, 14:50:09) [GCC 4.8.1]\" }, { \"module\" : \"IPython\", \"version\" : \"1.1.0\" }, { \"module\" : \"OS\", \"version\" : \"posix [linux]\" }, { \"module\" : \"numpy\", \"version\" : \"1.8.0\" } ] }" ], "latex": [ "\\begin{tabular}{|l|l|}\\hline\n", "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", "Python & 3.3.2+ (default, Oct 9 2013, 14:50:09) [GCC 4.8.1] \\\\ \\hline\n", "IPython & 1.1.0 \\\\ \\hline\n", "OS & posix [linux] \\\\ \\hline\n", "numpy & 1.8.0 \\\\ \\hline\n", "\\hline \\multicolumn{2}{|l|}{Mon Nov 11 15:06:46 2013 KST} \\\\ \\hline\n", "\\end{tabular}\n" ], "metadata": {}, "output_type": "pyout", "prompt_number": 178, "text": [ "Software versions\n", "Python 3.3.2+ (default, Oct 9 2013, 14:50:09) [GCC 4.8.1]\n", "IPython 1.1.0\n", "OS posix [linux]\n", "numpy 1.8.0\n", "\n", "Mon Nov 11 15:06:46 2013 KST" ] } ], "prompt_number": 178 } ], "metadata": {} } ] }