{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Notebook 3 - NumPy\n",
    "[NumPy](http://numpy.org) short for Numerical Python, has long been a cornerstone of numerical computing on Python. It provides the data structures, algorithms and the glue needed for most scientific applications involving numerical data in Python. All computation is done in vectorised form - using vectors of several values at once instead of singular values at a time. NumPy contains, among other thigs:\n",
    "* A fast and efficient multidimensional array object `ndarray`.\n",
    "* Mathematical functions for performing element-wise computations with arrays or mathematical operations between arrays.\n",
    "* Tools for reading and manipulating large array data to disk and working with memory-mapped files.\n",
    "* Linear algebra, random number generation and Fourier transform capabilities.\n",
    "\n",
    "For the rest of the course, whenever array is mentioned it refers to the NumPy ndarray.\n",
    "<br>\n",
    "\n",
    "## Table of contents\n",
    "- [The ndarray](#ndarray)\n",
    "    - [Creating arrays](#creating)\n",
    "    - [Data Types](#data)\n",
    "    - [Arithmetic Operations](#arithmetic)\n",
    "    - [Indexing and Slicing](#indexing)\n",
    "    - [Transposing and Swapping Axis](#transposing)\n",
    "- [Universal Functinos](#universal)\n",
    "- [Other useful operations](#other)\n",
    "- [File IO](#file)\n",
    "- [Liear algebra](#linear)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Why NumPy?\n",
    "Is the first question that anybody asks when they find out about it. \n",
    "\n",
    "Some people might say: *I don't care about speed, I want to spend my time researching how to cure cancer, not optimise coputer code!*\n",
    "\n",
    "That's perfectly reasonable, but are you willing to wait a lot longer for your experiment to finish? I definitely don't want to do that. Let's see how much faster NumPy really is!\n",
    "\n",
    "to show that we'll be using the magic command `%timeit` which you can read more about [here](https://ipython.readthedocs.io/en/stable/interactive/magics.html) and don't worry about the details now, they will clear up later.\n",
    "\n",
    "Let's have a look at generating a vector of 10M random values and then summing them all up using the Python way and using the NumPy way!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Running normal python sum()\n",
      "954 ms ± 36.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "Running numpy sum()\n",
      "9.97 ms ± 705 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "x = np.random.randn(10000000) # generate random numbers\n",
    "\n",
    "print(\"Running normal python sum()\")\n",
    "%timeit sum(x)\n",
    "\n",
    "print(\"Running numpy sum()\")\n",
    "%timeit np.sum(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**WOW** that was a difference of more than a **100 times** and that was just for a single summing operation. Imagine if you had several of those running all the time!\n",
    "\n",
    "Are you onboard with Numpy then? Let's proceed..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# The ndarray <a name=\"ndarray\"></a>\n",
    "The ndarray is a backbone on Numpy. It's a fast and flexible container for N-dimensional array objects, usually used for large datasets in Python. Arrays enable you to perform mathematical operations on whole blocks of data using similar syntax to the equivalent operations between scalar elements.\n",
    "\n",
    "Here is a quick example of its capabilities:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-2.3091445 ,  0.78209531,  0.01356692],\n",
       "       [-0.16218064,  0.4900275 , -0.20505824]])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "# create a 2x3 array of random values\n",
    "data = np.random.randn(2,3)\n",
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-23.091445  ,   7.82095311,   0.1356692 ],\n",
       "       [ -1.62180638,   4.90027497,  -2.05058238]])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data * 10 #multiply all numbers by 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-4.618289  ,  1.56419062,  0.02713384],\n",
       "       [-0.32436128,  0.98005499, -0.41011648]])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data + data #element-wise addition"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Every array has a shape, a tuple indicating the size of each dimnesion and a dtype. You can obtain these via the respective methods:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# number of dimensions of the array\n",
    "data.ndim"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 3)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# the size of the array\n",
    "data.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('float64')"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# the type of values store in the array\n",
    "data.dtype"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating arrays <a name=\"creating\"></a>\n",
    "The easiest and quickest way to create an array is from a normal Python list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1.2, 5.2, 5. , 7.8, 0.3])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = [1.2, 5.2, 5, 7.8, 0.3]\n",
    "arr = np.array(data)\n",
    "\n",
    "arr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is also possible to create multidimensional arrays in a similar fashion. An example would be:\n",
    "```python\n",
    "data = [[1.2, 5.2, 5, 7.8, 0.3],\n",
    "        [4.1, 7.2, 4.8, 0.1, 7.7]]\n",
    "```\n",
    "Try creating an multidimensional array below and verify its number of dimensions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ident = [[1, 0], [0, 1]]\n",
    "idarray = np.array(ident)\n",
    "\n",
    "idarray.ndim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also create an array filled with zeros"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.zeros(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again, it is also possible to create a multidimensional array by passing a tuple as an argument"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0., 0., 0., 0., 0., 0.],\n",
       "       [0., 0., 0., 0., 0., 0.],\n",
       "       [0., 0., 0., 0., 0., 0.],\n",
       "       [0., 0., 0., 0., 0., 0.]])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.zeros((4,6))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NumPy also has an equivalent to the built-in Python function `range()` but it's called `arange()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.arange(0, 10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is a summary of the most often used methods to create arrays. Use it as a future reference!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Function | Description |\n",
    "|----|:--|\n",
    "| array  | Convert input data to an ndarray either by inferring a dtype<br>or explicitly specifying a dtype; copies the input data by default. |\n",
    "| arange | Similar to the built-in `range` function but returns an ndarray. |\n",
    "| ones | Produces an array of all 1s with the given shape and dtype. |\n",
    "| ones_like | Similar to `ones` but takes another array and produces a ones array<br>of the same shape and dtype |\n",
    "| zeros, zeros_like | Similar to `ones` but produces an array of 0s. |\n",
    "| eye | Create a square NxN identity matrix (1s on the diagonal and 0s elsewhere). |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data Types <a name=\"data\"></a>\n",
    "The data type or `dtype` is a special object containing the information the array needs to interpret a chunk of memory. We can specify it during the creation of an array "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "arr = np.array([1, 2, 3], dtype=np.float64)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('float64')"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# you can check the type of an array with\n",
    "arr.dtype"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similar to normal Python, NumPy has several types of data like int, float and bool. However, it also extends these by specifying the number of bits used per variable like 16, 32, 64 or 128.\n",
    "\n",
    "To keep things simpe, you can use:\n",
    "- `np.int64` to store integer numbers\n",
    "- `np.float64` to store numbers with a fraction value\n",
    "- `np.bool` to store `True` and `False` values\n",
    "\n",
    "When creating arrays in NumPy the type is inferred (guessed) so you don't need to explicitly specify it.\n",
    "\n",
    "It is not necessary for this course but if you want to learn more about datatypes in NumPy you can go to https://jakevdp.github.io/PythonDataScienceHandbook/02.01-understanding-data-types.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similar to normal Python, you can cast(convert) an array from one dtype to another using the `astype` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('int64')"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr = np.array([1, 2, 3])\n",
    "arr.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('float64')"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "float_arr = arr.astype(np.float64)\n",
    "float_arr.dtype"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The normal limitations to casting apply here as well. You can try creating a `float64` array and then converting it to an `int64` array below "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 1\n",
    "Create a 5x5 [identity matrix](https://en.wikipedia.org/wiki/Identity_matrix). Then convert it to float64 dtype. At the end confirm its properties using the appropriate attributes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 0., 0., 0., 0.],\n",
       "       [0., 1., 0., 0., 0.],\n",
       "       [0., 0., 1., 0., 0.],\n",
       "       [0., 0., 0., 1., 0.],\n",
       "       [0., 0., 0., 0., 1.]])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M = np.eye(5)\n",
    "M = M.astype(\"float64\")\n",
    "M"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Arithmetic operations <a name=\"arithmetic\"></a>\n",
    "You have already gotten a taste of this in the examples above but let's try to extend that.\n",
    "\n",
    "Arrays are important because they enable you to express batch operations on data without having to write for loops - this is called **vectorisation**.\n",
    "\n",
    "Any arithmetic operations between equal-size arrays applies the operation element-wise:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [4, 5, 6]])"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.array([[1, 2, 3], [4, 5, 6]])\n",
    "A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1,  4,  9],\n",
       "       [16, 25, 36]])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A * A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0, 0, 0],\n",
       "       [0, 0, 0]])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A - A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Arithmetic operations with scalars propogate the scalar argument to each element in the array:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 5, 10, 15],\n",
       "       [20, 25, 30]])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A * 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1.        , 1.41421356, 1.73205081],\n",
       "       [2.        , 2.23606798, 2.44948974]])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A ** 0.5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Comparisons between arrays of the same size yield boolean arrays:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1,  7,  4],\n",
       "       [ 4, 12,  2]])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B = np.array([[1, 7, 4],[4, 12, 2]])\n",
    "B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[False, False, False],\n",
       "       [False, False,  True]])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A > B"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Arithmetic operations with differently sized arrays is called **broadcasting** but will not be covered in this course due to the limited time."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 2\n",
    "Generate a vector of size 10 with values ranging from 0 to 1, both included."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "G = np.arange(11)\n",
    "G/10"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Indexing and slicing <a name=\"indexing\"></a>\n",
    "NumPy offers many options for indexing and slicing. Coincidentally, they are very similar to Python.\n",
    "\n",
    "Let's see how this is done in 1D:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.arange(10)\n",
    "A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([5, 6, 7])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[5:8]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 0, 0, 0, 8, 9])"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[5:8] = 0\n",
    "A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Important:** Unlike vanilla Python, NumPy array slices are views on the original array. This means that the data is not copied, and any modifications to the view will be reflected in the source array."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 0, 0])"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A_slice = A[5:8]\n",
    "A_slice"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([12, 17, 24])"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A_slice[:] = [12, 17, 24]\n",
    "A_slice"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we used the [:] operator which assings to all values in the array.\n",
    "Let's now have a look at higher dimensional arrays:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [4, 5, 6],\n",
       "       [7, 8, 9]])"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
    "C"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have 2 dimensions, we need to input 2 indices to get a specific element of the array. Alternatively, if we input only one index, then we obtain the whole row of the array:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([7, 8, 9])"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C[2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C[2][1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C[2, 1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is a picture to better explain indexing in 2D:\n",
    "<img src=\"img/ndarray.png\" alt=\"drawing\" width=\"300\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The same concepts and techniques are extended into multidimensional arrays:\n",
    "if you omit later indices, the returned object will be a lower dimensional ndarray consisting of all data along the higher dimensions."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's look into **slicing**. You already saw above that slicing in 1D is done the same way as in standard Python data structures. So how do we do that in 2D? Well, it is fairly intuitive:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [4, 5, 6],\n",
       "       [7, 8, 9]])"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
    "C"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [4, 5, 6]])"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C[:2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This can be read as *select the first 2 rows of C*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([4, 5])"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C[1, :2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1],\n",
       "       [4],\n",
       "       [7]])"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C[:, :1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is some visual aid for what happened above:\n",
    "<img src=\"img/indexing.png\" alt=\"drawing\" width=\"400\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is also possible to index arrays via booleans.\n",
    "\n",
    "Say we have an 1D array of 0s and 1s and then a 2D array of randomly generated data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['banana', 'orange', 'mango', 'banana', 'tomato', 'passionfruit',\n",
       "       'cherry'], dtype='<U12')"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fruits = np.array([\"banana\", \"orange\", \"mango\", \"banana\", \"tomato\", \"passionfruit\", \"cherry\"])\n",
    "fruits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.90027628, -0.07910361, -0.23354025, -1.29243882],\n",
       "       [-1.49492432,  0.72150455, -0.64116559,  0.34059335],\n",
       "       [ 0.29217836,  0.91614634,  0.12437945, -1.23462564],\n",
       "       [-1.25269389,  0.83684163,  1.38452885, -1.15131822],\n",
       "       [ 1.36073083, -0.46846848,  0.80826957,  0.69214677],\n",
       "       [-0.08169566, -0.84440752, -0.18354753, -0.53533869],\n",
       "       [-0.60946636,  1.64621741, -0.42900154,  1.21657206]])"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = np.random.randn(7,4)\n",
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ True, False, False,  True, False, False, False])"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fruits == \"banana\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.90027628, -0.07910361, -0.23354025, -1.29243882],\n",
       "       [-1.25269389,  0.83684163,  1.38452885, -1.15131822]])"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data[fruits == \"banana\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Powerful right? The only caveat is that the boolean array must be of the same length as the array axis it's indexing. Caution must be taken here as the boolean selection will not fail even if the boolean array is not the correct length!\n",
    "\n",
    "You can also mix and match boolean arrays but there is one small difference compared to Python - the typical boolean operators (`and` and `or`) do not work instead you must use `&`(and) and `|`(or)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.90027628, -0.07910361, -0.23354025, -1.29243882],\n",
       "       [-1.25269389,  0.83684163,  1.38452885, -1.15131822],\n",
       "       [-0.60946636,  1.64621741, -0.42900154,  1.21657206]])"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mask = (fruits == \"banana\") | (fruits == \"cherry\")\n",
    "data[mask]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 3\n",
    "Create a 5x5 matrix of random values. Square all positive values of the matrix and set all else to 0. Attempt to do this in place - ie. without copying the matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[2.33989427, 0.        , 0.        , 0.11422694, 1.18234573],\n",
       "       [0.43102216, 0.        , 0.        , 0.        , 0.        ],\n",
       "       [0.65289885, 1.24975153, 0.        , 0.51836678, 0.03025122],\n",
       "       [2.69460842, 1.56334037, 0.        , 0.        , 0.        ],\n",
       "       [0.95543507, 1.85459418, 0.        , 0.61200025, 0.        ]])"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.random.randn(5,5)\n",
    "np.square(A[A > 0])\n",
    "A[A < 0] = 0\n",
    "A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 4\n",
    "Create a 2D array with 1s on the border and 0s inside"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 1., 1., 1., 1.],\n",
       "       [1., 0., 0., 0., 1.],\n",
       "       [1., 0., 0., 0., 1.],\n",
       "       [1., 0., 0., 0., 1.],\n",
       "       [1., 1., 1., 1., 1.]])"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.zeros((5,5))\n",
    "A[:1] = 1\n",
    "A[:, :1] = 1\n",
    "A[-1:] = 1\n",
    "A[:, -1:] = 1\n",
    "A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Transposing Arrays and Swapping Axes <a name=\"transposing\"></a>\n",
    "We can use the method `reshape()` to convert the data from one shape into another. Later we can use the `T` attribute to obtain the transpose of the array."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.arange(15)\n",
    "A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1,  2,  3,  4],\n",
       "       [ 5,  6,  7,  8,  9],\n",
       "       [10, 11, 12, 13, 14]])"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B = A.reshape((3,5))\n",
    "B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  5, 10],\n",
       "       [ 1,  6, 11],\n",
       "       [ 2,  7, 12],\n",
       "       [ 3,  8, 13],\n",
       "       [ 4,  9, 14]])"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B.T"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also reshape 3D arrays but how would `T` work then? Luckily, we can use the `tranpose()` method which allows us to chose the axes we want to swap:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 2, 4)"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.arange(16)\n",
    "C = A.reshape((2, 2, 4))\n",
    "C.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 2, 4)"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C.transpose((1, 0, 2))\n",
    "C.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plotting\n",
    "You can easily plot and show images in Python via the package `matplotlib` which can be used to plot an array. \n",
    "\n",
    "First we have to set up our environment. Read and run the code below to do just that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "# Import NumPy and matplotlib\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Set a greyscale colourmap (we want white for 0 and black for 1)\n",
    "plt.set_cmap('Greys')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can create an array of values and plot in a canvas. The easiest way to do this is to pick values between 0 and 1 and plot grayscale images where 1 corresponds to black and 0 corresponds to white. Let's see how we can do this by creating an array of 0s:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f3839556ba8>"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "canvas = np.zeros((100,50))\n",
    "plt.imshow(canvas, interpolation=\"none\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All is white right? let's add some black to it!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f38394d48d0>"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "canvas[50, 25] = 1\n",
    "plt.imshow(canvas, interpolation=\"none\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 5\n",
    "Use the canvas template above and create an image where the top right and bottom left pixels are set to black.\n",
    "\n",
    "*Note: Remember to first reset your canvas to only 0s*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f38394ae978>"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "canvas = np.zeros((100,50))\n",
    "canvas[0, 0] = 1\n",
    "canvas[-1, -1] = 1\n",
    "plt.imshow(canvas, interpolation=\"none\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 6\n",
    "Draw a horizontal and verticle line across the canvas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f3839405390>"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "canvas = np.zeros((100, 50))\n",
    "canvas[50] = 1\n",
    "plt.imshow(canvas, interpolation=\"none\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 7\n",
    "Make the top left corner of the image black.\n",
    "\n",
    "*Extra challenge: do this wihtout using numbers for indexing*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f383ea88198>"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "canvas = np.zeros((100,50))\n",
    "x, y = canvas.shape\n",
    "x = int(x/2)\n",
    "y = int(y/2)\n",
    "canvas[:x, :y] = 1\n",
    "plt.imshow(canvas, interpolation=\"none\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Universal Functions <a name=\"universal\"></a>\n",
    "or *ufunc* are functions that perform element-wise operations on data in ndarrays. You can think of them as fast vectorised wrappers for simply functions. Here is an example of `sqrt` and `exp`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.arange(10)\n",
    "A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.        , 1.        , 1.41421356, 1.73205081, 2.        ,\n",
       "       2.23606798, 2.44948974, 2.64575131, 2.82842712, 3.        ])"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sqrt(A)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,\n",
       "       5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,\n",
       "       2.98095799e+03, 8.10308393e+03])"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.exp(A)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Other universal functions take 2 arrays as input. These are called *binary* functions.\n",
    "\n",
    "For example `maximum()` selects the biggest values from two input arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1.27560188e+00, -1.31005829e+00, -1.41723814e-05, -4.54739170e-02,\n",
       "        8.51535984e-01,  4.77467731e-01, -2.60056065e-01,  5.16083793e-01,\n",
       "        7.58027650e-01, -8.65333201e-01])"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.random.randn(10)\n",
    "y = np.random.randn(10)\n",
    "np.maximum(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is a list of useful *ufuncs* in NumyPy:\n",
    "\n",
    "*Again, you don't need to memorise them. This is just a reference*\n",
    "### Unary functions (accept one argument)\n",
    "\n",
    "| Function | Description |\n",
    "|----|----|\n",
    "| abs, fabs | Compute the absolute value element-wise for integer, floating point, or complex values.<br>Use fabs as a faster alternative for non-complex-valued data |\n",
    "| sqrt | Compute the square root of each element. Equivalent to arr ** 0.5 |\n",
    "| exp | Compute the exponent ex of each element |\n",
    "| log, log10, log2, log1p | Natural logarithm (base e), log base 10, log base 2, and log(1 + x), respectively |\n",
    "| cos, cosh, sin, sinh, tan, tanh | Regular and hyperbolic trigonometric functions |\n",
    "\n",
    "### Binary functions (accept 2 arguments)\n",
    "| Functions | Description |\n",
    "| ---- | ---- |\n",
    "| add | Add corresponding elements in arrays |\n",
    "| subtract | Subtract elements in second array from first array |\n",
    "| multiply | Multiply array elements |\n",
    "| divide, floor_divide | Divide or floor divide (truncating the remainder) |\n",
    "| mod | Element-wise modulus (remainder of division) |\n",
    "| power | Raise elements in first array to powers indicated in second array |\n",
    "| maximum | Element-wise maximum. fmax ignores NaN |\n",
    "| minimum | Element-wise minimum. fmin ignores NaN |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Other useful operations <a name=\"other\"></a>\n",
    "NumPy offers a set of mathematical functions that compute statistics about an entire array:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-1.57851313,  0.38194173,  0.8770879 ,  1.83792953],\n",
       "       [-0.43276897,  0.77984742,  1.16567673,  1.04061525],\n",
       "       [-2.59697226,  1.25309649,  1.42768104, -0.75279782],\n",
       "       [-0.97393348, -0.25629383, -0.70739292, -1.43667416],\n",
       "       [-0.01879865, -1.95032899, -0.05164039,  0.82973582]])"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B = np.random.randn(5, 4)\n",
    "B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.05812513387019911"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.05812513387019911"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.mean(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-1.1625026774039822"
      ]
     },
     "execution_count": 65,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B.sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.37961151,  0.63834261, -0.16724814, -0.8435736 , -0.29775805])"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B.mean(axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here `mean(axis=1)` means compute the mean across the columns (axis 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is a set of other similar functions:\n",
    "\n",
    "| Function | Description|\n",
    "| --- | --- |\n",
    "|sum | Sum of all the elements in the array or along an axis. Zero-length arrays have sum 0. |\n",
    "| mean | Arithmetic mean. Zero-length arrays have NaN mean. |\n",
    "| std, var | Standard deviation and variance, respectively, with optional<br>degrees of freedom adjustment (default denominator n). |\n",
    "|min, max | Minimum and maximum. |\n",
    "| argmin, argmax | Indices of minimum and maximum elements, respectively. |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There also are some boolean operations. `any` test where one or more values in an array is `True` and `all` test where all values are `True`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ True,  True,  True, False, False, False, False,  True, False,\n",
       "       False,  True,  True,  True,  True,  True,  True, False, False,\n",
       "        True,  True,  True,  True,  True, False,  True, False, False,\n",
       "        True,  True, False,  True,  True, False,  True, False, False,\n",
       "       False,  True,  True, False,  True, False,  True,  True, False,\n",
       "        True,  True, False, False, False, False, False, False, False,\n",
       "       False,  True, False, False,  True, False,  True, False, False,\n",
       "        True, False, False,  True,  True,  True,  True,  True,  True,\n",
       "       False,  True, False, False,  True,  True, False,  True,  True,\n",
       "       False, False,  True, False,  True, False,  True, False, False,\n",
       "       False,  True,  True, False, False,  True, False,  True,  True,\n",
       "       False])"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.random.randn(100)\n",
    "A_bool = A > 0\n",
    "A_bool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A_bool.any()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A_bool.all()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 8\n",
    "Generate and normalise a random 5x5x5 matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[0.        , 0.00806452, 0.01612903, 0.02419355, 0.03225806],\n",
       "        [0.04032258, 0.0483871 , 0.05645161, 0.06451613, 0.07258065],\n",
       "        [0.08064516, 0.08870968, 0.09677419, 0.10483871, 0.11290323],\n",
       "        [0.12096774, 0.12903226, 0.13709677, 0.14516129, 0.15322581],\n",
       "        [0.16129032, 0.16935484, 0.17741935, 0.18548387, 0.19354839]],\n",
       "\n",
       "       [[0.2016129 , 0.20967742, 0.21774194, 0.22580645, 0.23387097],\n",
       "        [0.24193548, 0.25      , 0.25806452, 0.26612903, 0.27419355],\n",
       "        [0.28225806, 0.29032258, 0.2983871 , 0.30645161, 0.31451613],\n",
       "        [0.32258065, 0.33064516, 0.33870968, 0.34677419, 0.35483871],\n",
       "        [0.36290323, 0.37096774, 0.37903226, 0.38709677, 0.39516129]],\n",
       "\n",
       "       [[0.40322581, 0.41129032, 0.41935484, 0.42741935, 0.43548387],\n",
       "        [0.44354839, 0.4516129 , 0.45967742, 0.46774194, 0.47580645],\n",
       "        [0.48387097, 0.49193548, 0.5       , 0.50806452, 0.51612903],\n",
       "        [0.52419355, 0.53225806, 0.54032258, 0.5483871 , 0.55645161],\n",
       "        [0.56451613, 0.57258065, 0.58064516, 0.58870968, 0.59677419]],\n",
       "\n",
       "       [[0.60483871, 0.61290323, 0.62096774, 0.62903226, 0.63709677],\n",
       "        [0.64516129, 0.65322581, 0.66129032, 0.66935484, 0.67741935],\n",
       "        [0.68548387, 0.69354839, 0.7016129 , 0.70967742, 0.71774194],\n",
       "        [0.72580645, 0.73387097, 0.74193548, 0.75      , 0.75806452],\n",
       "        [0.76612903, 0.77419355, 0.78225806, 0.79032258, 0.7983871 ]],\n",
       "\n",
       "       [[0.80645161, 0.81451613, 0.82258065, 0.83064516, 0.83870968],\n",
       "        [0.84677419, 0.85483871, 0.86290323, 0.87096774, 0.87903226],\n",
       "        [0.88709677, 0.89516129, 0.90322581, 0.91129032, 0.91935484],\n",
       "        [0.92741935, 0.93548387, 0.94354839, 0.9516129 , 0.95967742],\n",
       "        [0.96774194, 0.97580645, 0.98387097, 0.99193548, 1.        ]]])"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M = np.arange(5*5*5)\n",
    "M = M.reshape(5,5,5)\n",
    "\n",
    "M = (M - M.min())/(M.max() - M.min())\n",
    "M"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 9\n",
    "Create a random vector of size 30 and find its mean value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.623688802625794"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.random.rand(30)\n",
    "np.mean(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 10\n",
    "\n",
    "Subrtract the mean of each row of a randomly generated matrix:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.74543641,  0.22202193,  0.52370205,  1.87712214,  1.86512697],\n",
       "       [-0.41672625, -0.02944647,  0.17001214, -0.83433252,  2.63141455],\n",
       "       [-1.021981  , -0.17505632, -0.07618986, -0.07461297, -0.7511966 ],\n",
       "       [-2.26270537,  0.74184188,  0.39589936,  0.28847449,  0.17428801],\n",
       "       [-1.64970934, -1.64178761,  0.31676457,  0.73482004, -0.2623074 ]])"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.random.randn(5,5)\n",
    "x = x - x.mean(axis=1)\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sorting <a name=\"sorting\"></a>\n",
    "Similar to Python's built-in list type, NumyPy arrays can be sorted in place:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1.6331158 , -0.9584933 ,  0.38867611, -1.57654646,  0.67797536,\n",
       "       -0.52135612,  0.02151883,  0.05561592, -0.31669069,  1.4695012 ])"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.random.randn(10)\n",
    "A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-1.57654646, -0.9584933 , -0.52135612, -0.31669069,  0.02151883,\n",
       "        0.05561592,  0.38867611,  0.67797536,  1.4695012 ,  1.6331158 ])"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.sort()\n",
    "A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another option is `unique()` which returns the sorted unique values in an array."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Linear Algebra <a name=\"linear\"></a>\n",
    "Similar to other languages like MATLAB, NumyPy offers a set of standard linear algebra operations, like matrix multiplication, decompositions, determinants and etc.. Unlike some other languages though, the default operations like `*` peform element-wise operations. To perform matrix-wise operartions we need to use special functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [],
   "source": [
    "temp = np.arange(16)\n",
    "A = temp[:8]\n",
    "B = temp[8:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "364"
      ]
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.dot(B)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also extend this with the `numpy.linalg` package:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 6.33842154,  1.27606607, -0.71658047, -0.61896202,  1.21103202],\n",
       "       [ 1.27606607,  1.98671195, -0.52193113, -1.76000421, -1.35723627],\n",
       "       [-0.71658047, -0.52193113,  1.36414106,  1.11827657,  2.05075293],\n",
       "       [-0.61896202, -1.76000421,  1.11827657,  2.31861401,  2.55103779],\n",
       "       [ 1.21103202, -1.35723627,  2.05075293,  2.55103779,  4.65956589]])"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from numpy.linalg import inv, qr\n",
    "A = np.random.randn(5, 5)\n",
    "mat = A.T.dot(A)\n",
    "mat"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[  2.15448337,  -1.90845394,   6.15341037,   0.92062735,\n",
       "         -4.32809649],\n",
       "       [ -1.90845394,   3.86956814,  -5.78343753,   1.58641385,\n",
       "          3.29998914],\n",
       "       [  6.15341037,  -5.78343753,  19.79214728,   2.27318392,\n",
       "        -13.23926987],\n",
       "       [  0.92062735,   1.58641385,   2.27318392,   4.12565275,\n",
       "         -3.03637844],\n",
       "       [ -4.32809649,   3.29998914, -13.23926987,  -3.03637844,\n",
       "          9.78990685]])"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inv(mat)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.00000000e+00, -3.38911408e-16, -2.95287182e-15,\n",
       "         3.93818578e-16, -1.23910385e-15],\n",
       "       [-1.41601726e-15,  1.00000000e+00, -2.57502274e-15,\n",
       "        -1.01003533e-16,  2.53975941e-16],\n",
       "       [ 1.59331815e-15, -2.35551750e-16,  1.00000000e+00,\n",
       "        -3.90688727e-16, -2.40095760e-15],\n",
       "       [-2.79801471e-15,  1.21798097e-15, -4.40189198e-15,\n",
       "         1.00000000e+00,  2.53615120e-15],\n",
       "       [-1.28888178e-15, -5.44621316e-17, -3.76744772e-15,\n",
       "        -2.06240809e-15,  1.00000000e+00]])"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mat.dot(inv(mat))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is a set of commonly used `numpy.linalg` functions\n",
    "\n",
    "| Function | Description |\n",
    "| --- | --- |\n",
    "| diag | Return the diagonal (or off-diagonal) elements of a square matrix as a 1D array,<br>or convert a 1D array into a square matrix with zeros on the off-diagonal |\n",
    "| dot | Matrix multiplication |\n",
    "| trace | Compute the sum of the diagonal elements |\n",
    "| det | Compute the matrix determinant |\n",
    "| eig | Compute the eigenvalues and eigenvectors of a square matrix |\n",
    "| inv | Compute the inverse of a square matrix |\n",
    "| pinv | Compute the Moore-Penrose pseudo-inverse inverse of a square matrix |\n",
    "| qr | Compute the QR decomposition |\n",
    "| svd | Compute the singular value decomposition (SVD) |\n",
    "| solve | Solve the linear system Ax = b for x, where A is a square matrix |\n",
    "| lstsq | Compute the least-squares solution to y = Xb |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 11\n",
    "Obtain the digonal of a dot product of 2 random matrices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 2.4094936 , -0.76347609, -0.78950568, -0.38601468, -0.30973188])"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.random.randn(5, 5)\n",
    "y = np.random.randn(5, 5)\n",
    "\n",
    "prod = np.dot(x, y)\n",
    "\n",
    "np.diag(prod)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## File IO <a name=\"file\"></a>\n",
    "NumPy offers its own set of File IO functions.\n",
    "\n",
    "The most common one is `genfromtxt()` which can load the common `.csv` and `.tsv` files.\n",
    "\n",
    "Now let us analyse temperature data from Stockholm over the years.\n",
    "\n",
    "First we have to load the file:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(77431, 7)"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = np.genfromtxt(\"./data/stockholm_td_adj.dat\")\n",
    "data.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first column of this array gives years, and the 6th gives temperature readings. We can extract these."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [],
   "source": [
    "yrs = data[:, 0]\n",
    "temps = data[:, 5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Having read in our data, we can now work with it - for example, we could produce a plot.\n",
    "We will cover plotting in more depth in notebook 4, so there's no need to get too caught up in the details right now - this is just an examle of something we might do having read in some data. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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TpNSTktLS7NvgeSKr7KLUaW04zfr1jfWY8YczoslCsWS3SUorDLWhffTrAKkZ2Sirjn82drtnZ5g066gc/bmwq51frD+EOZuLcYrK3Jseax4dNMr61xt+kVk1vyd7PxPFKtcDXCFEN3gCW4/lFLOVt5dIoQYqnx8KAD/veYp4mQXWNPCvqGlHX+YtQU3v7rOkvuLFu+uzbPsnCxRqH79bhbO//cyQ7f9aL1nLuP+CC85O1LTjHs/zcZv3ssCcLQDcr0NTXOIiIjIXW52URYA3gSwW0r5jOZDXwG4Xfn37QDmOr02Mu/TjQW4fuYaLLBgR1TthFgaYtOJaPVEpif70dYR3QHugp1HMO2JRW4vgyzS1tEZ9ZsuakAbb88pRERE8cjNDO55AG4DcKkQYqvy52oATwL4nhBiP4DvKW9ThFMbDh0s99/kiOLHPbO2oLy+Fc1tzI5Fu8qGVoybMQ8zvoz8kv8XluzHmzwDSkREFPeS3HpgKeVqACLAhy9zci1ERHQsdXRHVl6lyysJ7ulF+wAAvz5/jMsrISIiIje53mSKiIiIiChaSSnxwXec3kAUKRjgEhERERGF6PVVuXjwyx34KrvY7aWQw6SU2FFU4/YyyAcDXCIiIiKiEJXUeo5zlNY2u7wST8AV7Y0B7fbh+kO45vlVltzXM4v24QcvrMa2QmsmiZA1GOASERFR1Ji1IR9PLdjj9jKIItKY+7/FmPu/dXsZEW3GnB3YWVxryX0dUMbkFVY1WXJ/ZA0GuERERBQ17p+9HTOX5bi9DCIiilAMcIkIu4prsbWA5TVazy7ah/SMTHR2stSLiCjebDpUiX0ldW4vg4hCwACXosLuw9aUkthtymML8cCc7W4vw7Srn1+FH85c4/YyIsrrq3IBAE2c50tEFHdufHkdrnh2pdvLIKIQMMCliLcxrxJXPbcKr66I/JK06sY2fLQ+3+1lEBERERHFJQa4FPHyyhsAAPtK6l1eCRERERERRTIGuERERERERBQTGOASERERERFRTGCAS0RERERERDGBAS4RERERERHFBAa4REREREREFBMY4BIREREREVFMYIBLREREREREMYEBLhEREREREcUEBrhEREREREQUExjgEhERERERUUxggEtEREREREQxgQEuERERERERxQQGuERERERERBQTGOASERERERFRTGCAS0RERERERDGBAS4RERERERHFBAa4REREREREFBMY4BIREREREVFMYIBLREREREREMYEBLhEREREREcUEBrhEREREREQUExjgEhERERERUUxggEtEREREREQxgQEuERERERERxQQGuERERERERBQTGOASERERERFRTGCAS0RERERERDGBAS4RERERERHFBAa4REREREREFBMY4BIREREREVFMYIBLREREREREMYEBLhEREREREcUEBrhEREREREQUExjgEhERERERUUxggEtEREREREQxgQEuERERERERxQQGuERERERERBQTXA1whRBvCSFKhRAlMxI3AAAgAElEQVQ7NO8bKIRYJITYr/w9wM01EhERERERUXRwO4P7DoArfd6XAWCJlHIcgCXK20RERERERES6XA1wpZQrAVT6vPt6AO8q/34XwA8dXRQRERERERFFJbczuP4cJ6U8DADK34NdXg8RERERERFFgUgMcA0RQtwphMgSQmSVlZW5vRwiIiIiIiJyWSQGuCVCiKEAoPxd6u9GUsrXpJTTpJTT0tLSHF0gERERERERRZ5IDHC/AnC78u/bAcx1cS1EREREREQUJdweEzQLwDoAJwkhCoUQvwbwJIDvCSH2A/ie8jYRERERERGRriQ3H1xK+bMAH7rM0YUQERERERFR1IvEEmUiIiIiIiIi0xjgEhERERERUUxggEtEREREREQxgQEuERERERERxQQGuERERERERBQTGOASERERERFRTGCAS0RERERERDFBdw6uEGIogJ8AuADAMABNAHYAyASwUEopbV8hERERERERkQEBM7hCiNcBfKDc5jkA0wHcC2A1gB8CWCOEON+JRRIREREREREFo5fBfVFKme3n/VsBfCqE6AlglD3LIiIiIiIiIjJH7wxukRDiJN93CiHGCyEGSSmbpZT7bFwbERERERERkWF6Ae7z8Jy79TUGnpJlIiIiIiIiooihF+BOllIu832nlHIegCn2LYmIiIiIiIjIPL0AV+98bjerF0JEREREREQUDr0AN0cI8X3fdwohrgBw0L4lEREREREREZmnl6W9F8DXQogVADYp75sG4EIA19q9MCIiIiIiIiIzAmZwpZR7AEwEsB7AeOXPegCTlI8RERERERERRQy9DC6klM0AXndoLUREREREREQhC5jBFUIsE0L8nxBimM/7k4QQFwoh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\n",
      "text/plain": [
       "<Figure size 1152x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(16, 6)) # Create a 16x6 figure\n",
    "plt.plot(yrs, temps) # Plot temps vs yrs\n",
    "\n",
    "#Set some labels\n",
    "plt.title(\"Temperatures in Stockholm\")\n",
    "plt.xlabel(\"year\")\n",
    "plt.ylabel(\"Temperature (C)\")\n",
    "\n",
    "plt.show() # Show the plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 12\n",
    "Read in the file `daily_gas_price.csv`, which lists the daily price of natural gas since 1997. Each row contains a date and a price, separated by a comma. Find the minimum, maximum, and mean gas price over the dataset.\n",
    "\n",
    "(Hint: you will need to use the delimiter option in `np.genfromtxt` to specify that data is separated by commas. Also, NumPy will interpret the data in float format by default - we may need to set the dtype to a string format at first, then discard the dates, before turning the gas prices back into floats to process them! Otherwise, NumPy may find it confusing to try and interpret dates formatted as YYYY-MM-DD as floats and will probably complain.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Mean:  4.331772908366535\n",
      "Std :  2.2031375976175553\n",
      "Max :  18.48\n",
      "Min :  1.05\n",
      "[]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "5522"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = np.genfromtxt(\"./data/daily_gas_price.csv\", delimiter=\",\")\n",
    "\n",
    "# filter NaNs\n",
    "data = data[1:,1]\n",
    "data = data[~np.isnan(data)]\n",
    "\n",
    "# print stats\n",
    "print(\"Mean: \", data.mean())\n",
    "print(\"Std : \", data.std())\n",
    "print(\"Max : \", data.max())\n",
    "print(\"Min : \", data.min())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}