{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "73703282",
   "metadata": {},
   "source": [
    "## Lecture 17, 15 October 2026"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "44b502e8",
   "metadata": {
    "id": "9XZ7Vgb5PgAJ"
   },
   "source": [
    "# Using numpy\n",
    "\n",
    "- Arrays and lists\n",
    "- Arrays are \"homogenous\" with regular structure\n",
    "- Lists are flexible"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee8a759c",
   "metadata": {
    "id": "7J8WHOI8PgAK"
   },
   "source": [
    "### Load numpy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "9dc84890",
   "metadata": {
    "id": "h320Z7OIPgAL"
   },
   "outputs": [],
   "source": [
    "import numpy as np  # Shortcut, use np for numpy in code"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e7b5431",
   "metadata": {},
   "source": [
    "### Constructing arrays"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d5f6633",
   "metadata": {},
   "source": [
    "`np.array()` constructs an array from an input sequence\n",
    "- Sequence can be a list, tuple, output of a `range()` command ...\n",
    "- Size of the array is fixed by the sequence\n",
    "- Underlying type is also fixed"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "bcf998e6",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Toz_61yQ0cFA",
    "outputId": "dc2017b3-06ea-4043-87d8-f16bb7dd80a0"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b = np.array(range(10))\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b75fe395-91b4-4422-8536-b9851de9ecee",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('int64')"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b.dtype # Underlying datatype of b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e97ded7",
   "metadata": {},
   "source": [
    "Use nested sequences to produce multi-dimensional arrays"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9677ff9",
   "metadata": {},
   "source": [
    "- A 2d array is an array of 1d arrays\n",
    "- Note: can mix and match notation for sequences, but dimensions much match"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "321bae2f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0, 1, 0],\n",
       "       [2, 3, 2]])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c = np.array([(0,1,0),[2,3,2]])\n",
    "c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e8a63051-a542-4dcf-9c6d-5022b403ebe2",
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (2,) + inhomogeneous part.",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mValueError\u001b[39m                                Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m c = np.array([(\u001b[32m0\u001b[39m,\u001b[32m1\u001b[39m,\u001b[32m0\u001b[39m),[\u001b[32m2\u001b[39m,\u001b[32m2\u001b[39m]])\n\u001b[32m      2\u001b[39m c\n",
      "\u001b[31mValueError\u001b[39m: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (2,) + inhomogeneous part."
     ]
    }
   ],
   "source": [
    "c = np.array([(0,1,0),[2,2]])\n",
    "c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "42771b6c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0, 1, 0],\n",
       "       [0, 1, 2]])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d = np.array([(0,1,0),range(3)])\n",
    "d  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67520cf0",
   "metadata": {},
   "source": [
    "- A 3d array is an array of 2d arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4b678167",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "yHc44L_a0pp4",
    "outputId": "60dbf89b-1b67-47f6-ad4d-eae28c884866"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[0, 1, 0],\n",
       "        [2, 3, 2]],\n",
       "\n",
       "       [[9, 5, 4],\n",
       "        [6, 7, 5]]])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d = np.array([[(0,1,0),[2,3,2]],[[9,5,4],[6,7,5]]])\n",
    "d"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "885508e9-b220-4f21-8c6f-6ec3f3d5687c",
   "metadata": {},
   "source": [
    "- An element of a multidimensional array is `a[i,j,k]` not `a[i][j][k]`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d355a443-b1e9-4215-88d7-76dc514743ea",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.int64(4)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d[1,0,2]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e39dd80-78bb-43ee-9efd-3072b3d8012d",
   "metadata": {},
   "source": [
    "- Create a zero array"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "5fd3677e-b375-43fd-8baf-e543c8bc7ec6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0., 0., 0.],\n",
       "       [0., 0., 0.],\n",
       "       [0., 0., 0.],\n",
       "       [0., 0., 0.],\n",
       "       [0., 0., 0.]])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m_zeros = np.zeros((5,3))  # np.zeros(shape), shape = (dim1,dim2,...,dimk)\n",
    "m_zeros"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "4ea6eb83-5f7a-4c08-83a9-6f11ca8fdfa1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('float64')"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m_zeros.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "724d05b7-0fb8-4c4c-a520-60d0a0ffb1de",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "Cannot interpret '3' as a data type",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mTypeError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[11]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m m_zeros = np.zeros(\u001b[32m5\u001b[39m,\u001b[32m3\u001b[39m)    \u001b[38;5;66;03m# The shapce must be a sequence, the argument is a single sequence\u001b[39;00m\n",
      "\u001b[31mTypeError\u001b[39m: Cannot interpret '3' as a data type"
     ]
    }
   ],
   "source": [
    "m_zeros = np.zeros(5,3)    # The shapce must be a sequence, the argument is a single sequence"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "96052377-3cc8-4a49-8437-5d4d100db706",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 0, 0, 0, 0, 0, 0])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m_zero_int = np.zeros(7,dtype='int64')\n",
    "m_zero_int"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36b34d81-71fd-4bec-b516-65e12ec31dc0",
   "metadata": {},
   "source": [
    "- An array of 1's"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "edf7a706-95ff-49cf-a322-92566fcb59cc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[1., 1., 1.],\n",
       "        [1., 1., 1.]],\n",
       "\n",
       "       [[1., 1., 1.],\n",
       "        [1., 1., 1.]],\n",
       "\n",
       "       [[1., 1., 1.],\n",
       "        [1., 1., 1.]],\n",
       "\n",
       "       [[1., 1., 1.],\n",
       "        [1., 1., 1.]],\n",
       "\n",
       "       [[1., 1., 1.],\n",
       "        [1., 1., 1.]],\n",
       "\n",
       "       [[1., 1., 1.],\n",
       "        [1., 1., 1.]],\n",
       "\n",
       "       [[1., 1., 1.],\n",
       "        [1., 1., 1.]],\n",
       "\n",
       "       [[1., 1., 1.],\n",
       "        [1., 1., 1.]]])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m_ones = np.ones((8,2,3))\n",
    "m_ones"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c7e5893-59a6-4f24-9402-3eeb8f97ed36",
   "metadata": {},
   "source": [
    "- Arrays with random numbers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "97f6f810-601a-48ea-bc90-29f6b1f76b82",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.00287431, 0.29674119, 0.24476656, 0.18877922, 0.99492982,\n",
       "        0.44853876],\n",
       "       [0.6345094 , 0.47673303, 0.4519863 , 0.83850117, 0.86138401,\n",
       "        0.70540875],\n",
       "       [0.79674745, 0.46600115, 0.72986825, 0.83545426, 0.78176263,\n",
       "        0.14423092],\n",
       "       [0.89425469, 0.65231874, 0.5992051 , 0.34482752, 0.83451738,\n",
       "        0.04742279]])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m_rand = np.random.random((4,6)) # Implicitly uniform in [0,1)\n",
    "m_rand"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "ac594150-3a0d-4086-b0cc-04925a7e3400",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[34, 72, 62, 40],\n",
       "       [67,  3, 72, 58],\n",
       "       [87,  7, 82, 79]])"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m_rand_int = np.random.randint(1,100,(3,4)) # Integers uniformly from [0,100)\n",
    "m_rand_int"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "d56108bc-c197-4ee8-905b-a79fa27c89f0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.93927631,  0.43362544],\n",
       "       [-0.85580182, -0.22417338],\n",
       "       [ 0.6479333 ,  0.18524646],\n",
       "       [-0.63437082,  1.18033941],\n",
       "       [-0.75870278, -0.24999268]])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m_normal = np.random.normal(0,1,(5,2))   # np.normal(mean, std_dev, shape)\n",
    "m_normal"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "76e1a8f3",
   "metadata": {},
   "source": [
    "### Broadcasting\n",
    "- In simplest form, scalar operations applied pointwise"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "07f73f8e",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "0tbpjYMIPgAN",
    "outputId": "183f21e4-4d1a-42d3-9562-93e2350dc672"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(10)  # arange(n) is same as array(range(n))\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "d0b6942d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a**3  # Replace each element by its cube  --- [ x**3 for x in a ]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "b17c7c55",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 3,  4,  5,  6,  7,  8,  9, 10, 11, 12])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a+3   # Add 3 to each element"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "d6ac7180",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0,  3,  6,  9, 12, 15, 18, 21, 24, 27])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "3*a   # Multiply each element by 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "2d13102d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 3,  4,  5,  6,  7,  8,  9, 10, 11, 12])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "3+a   # Same as a+3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "84bd35fd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([-3, -2, -1,  0,  1,  2,  3,  4,  5,  6]),\n",
       " array([ 3,  2,  1,  0, -1, -2, -3, -4, -5, -6]))"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a-3, 3-a  # Order is important because subtraction is not commutative"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "6633740a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([    1,     3,     9,    27,    81,   243,   729,  2187,  6561,\n",
       "       19683])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "3**a  # Powers of 3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8084dbf3",
   "metadata": {
    "id": "GWB0pizJPgAV"
   },
   "source": [
    "### Stacking arrays"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24b111b5",
   "metadata": {},
   "source": [
    "- `np.random.random(m,n)` creates an $m x n$ array with random numbers drawn uniformly from $[0,1)$\n",
    "- `10*np.random.random()` scales the entries by 10\n",
    "- `np.floor()` removes the fractional part"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "67bf845b",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "S5O3dYahPgAV",
    "outputId": "d37a6b01-d3a6-4611-a5ed-661f7d2df7c5"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[4. 6.]\n",
      " [7. 7.]]\n",
      "[[4. 0.]\n",
      " [0. 2.]\n",
      " [5. 8.]]\n"
     ]
    }
   ],
   "source": [
    "a = np.floor(10*np.random.random((2,2)))\n",
    "b = np.floor(10*np.random.random((3,2)))\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "241006b5",
   "metadata": {},
   "source": [
    "- `vstack()` stacks a sequence of arrays vertically -- should have same number of columns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "fe93dae6",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "plK9_37KPgAV",
    "outputId": "1b437dc4-4d7e-492e-a342-47ea684db598"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[4., 6.],\n",
       "       [7., 7.],\n",
       "       [4., 0.],\n",
       "       [0., 2.],\n",
       "       [5., 8.]])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.vstack((a,b))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7031f063",
   "metadata": {},
   "source": [
    "- Cannot `vstack()` if number of columns don't match"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "8f72029d",
   "metadata": {
    "id": "Yk1_M6ygPgAW"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[7., 2., 5.],\n",
       "       [0., 5., 6.]])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c = np.floor(10*np.random.random((2,3)))\n",
    "c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "ccbe8370",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 374
    },
    "id": "kFT-DWFKPgAW",
    "outputId": "7590a8b7-9ff0-42cf-b68b-1c54cbb22e5c"
   },
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "all the input array dimensions except for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 2 and the array at index 1 has size 3",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mValueError\u001b[39m                                Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[27]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m np.vstack((a,c))\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/python-venv/lib/python3.13/site-packages/numpy/_core/shape_base.py:290\u001b[39m, in \u001b[36mvstack\u001b[39m\u001b[34m(tup, dtype, casting)\u001b[39m\n\u001b[32m    288\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(arrs, \u001b[38;5;28mtuple\u001b[39m):\n\u001b[32m    289\u001b[39m     arrs = (arrs,)\n\u001b[32m--> \u001b[39m\u001b[32m290\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43m_nx\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mconcatenate\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43marrs\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43m0\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mdtype\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mdtype\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mcasting\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mcasting\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n",
      "\u001b[31mValueError\u001b[39m: all the input array dimensions except for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 2 and the array at index 1 has size 3"
     ]
    }
   ],
   "source": [
    "np.vstack((a,c))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d041f06d",
   "metadata": {},
   "source": [
    "- Number of rows does not matter if columns match"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "d3a3340f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0., 7.],\n",
       "       [4., 6.],\n",
       "       [5., 4.]])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d = np.floor(10*np.random.random((3,2)))\n",
    "d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "c8199ea5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[4., 6.],\n",
       "       [7., 7.],\n",
       "       [0., 7.],\n",
       "       [4., 6.],\n",
       "       [5., 4.]])"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.vstack((a,d))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4c89c20",
   "metadata": {},
   "source": [
    "- Can stack any length sequence of arrays, not just two arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "af178539",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[4., 6.],\n",
       "       [7., 7.],\n",
       "       [4., 0.],\n",
       "       [0., 2.],\n",
       "       [5., 8.],\n",
       "       [0., 7.],\n",
       "       [4., 6.],\n",
       "       [5., 4.]])"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.vstack([a,b,d])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "62e2c411-9d7e-4fdc-8979-cee6ca84232e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[4., 6.],\n",
       "       [7., 7.],\n",
       "       [4., 0.],\n",
       "       [0., 2.],\n",
       "       [5., 8.],\n",
       "       [0., 7.],\n",
       "       [4., 6.],\n",
       "       [5., 4.]])"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.vstack((a,b,d))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e43043af",
   "metadata": {},
   "source": [
    "- Likewise, `hstack()` stacks horizontally, number of rows must match"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "2d5ea8bc",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "0wlDpZXFPgAW",
    "outputId": "4cc239ae-06a6-4e8a-ff0f-9f3b156f5ec8"
   },
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "all the input array dimensions except for the concatenation axis must match exactly, but along dimension 0, the array at index 0 has size 2 and the array at index 1 has size 3",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mValueError\u001b[39m                                Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[32]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m np.hstack((a,b))\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/python-venv/lib/python3.13/site-packages/numpy/_core/shape_base.py:365\u001b[39m, in \u001b[36mhstack\u001b[39m\u001b[34m(tup, dtype, casting)\u001b[39m\n\u001b[32m    363\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m _nx.concatenate(arrs, \u001b[32m0\u001b[39m, dtype=dtype, casting=casting)\n\u001b[32m    364\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m365\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43m_nx\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mconcatenate\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43marrs\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43m1\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mdtype\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mdtype\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mcasting\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mcasting\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n",
      "\u001b[31mValueError\u001b[39m: all the input array dimensions except for the concatenation axis must match exactly, but along dimension 0, the array at index 0 has size 2 and the array at index 1 has size 3"
     ]
    }
   ],
   "source": [
    "np.hstack((a,b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "d09e402c",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 374
    },
    "id": "KZaBAqNFPgAW",
    "outputId": "388927a4-6e95-4e41-f7bb-080e841f2225"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[4., 0., 0., 7.],\n",
       "       [0., 2., 4., 6.],\n",
       "       [5., 8., 5., 4.]])"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.hstack((b,d))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9382e062",
   "metadata": {},
   "source": [
    "- For `hstack()`, number of columns does not matter\n",
    "- Like `vstack()`, can combine a sequence of arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "ce09b2d6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[9., 0., 6.],\n",
       "       [7., 8., 3.],\n",
       "       [2., 3., 9.]])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "e = np.floor(10*np.random.random((3,3)))\n",
    "e"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "a0e8cfe1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[4., 0., 0., 7., 9., 0., 6.],\n",
       "       [0., 2., 4., 6., 7., 8., 3.],\n",
       "       [5., 8., 5., 4., 2., 3., 9.]])"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.hstack((b,d,e))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26e2bea0",
   "metadata": {
    "id": "w8kXeA-cPgAX"
   },
   "source": [
    "### Splitting arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "19ece54b",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "IJWoNNByPgAX",
    "outputId": "4524b696-c4c3-4907-dab3-06d0c29c070a"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[4., 7., 4., 7., 6., 2., 9., 9., 0., 5., 0., 9.],\n",
       "       [4., 8., 6., 9., 7., 6., 8., 9., 6., 3., 6., 7.]])"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.floor(10*np.random.random((2,12)))\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10f6b98e",
   "metadata": {},
   "source": [
    "- `hsplit(A,n)` splits array `A` into `n` equal parts horizontally\n",
    "- `n` must be a divisor of the number of columns in `A`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "48d8d067",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Iuub4T1tPgAX",
    "outputId": "cbea1f4b-dd3a-4a01-f194-ce610b325636"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[array([[4., 7., 4., 7.],\n",
       "        [4., 8., 6., 9.]]),\n",
       " array([[6., 2., 9., 9.],\n",
       "        [7., 6., 8., 9.]]),\n",
       " array([[0., 5., 0., 9.],\n",
       "        [6., 3., 6., 7.]])]"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.hsplit(a,3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "25666e7d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[array([[4., 7.],\n",
       "        [4., 8.]]),\n",
       " array([[4., 7.],\n",
       "        [6., 9.]]),\n",
       " array([[6., 2.],\n",
       "        [7., 6.]]),\n",
       " array([[9., 9.],\n",
       "        [8., 9.]]),\n",
       " array([[0., 5.],\n",
       "        [6., 3.]]),\n",
       " array([[0., 9.],\n",
       "        [6., 7.]])]"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.hsplit(a,6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "1a531ee0",
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "array split does not result in an equal division",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mValueError\u001b[39m                                Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[39]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m np.hsplit(a,\u001b[32m5\u001b[39m)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/python-venv/lib/python3.13/site-packages/numpy/lib/_shape_base_impl.py:929\u001b[39m, in \u001b[36mhsplit\u001b[39m\u001b[34m(ary, indices_or_sections)\u001b[39m\n\u001b[32m    927\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m'\u001b[39m\u001b[33mhsplit only works on arrays of 1 or more dimensions\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m    928\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m ary.ndim > \u001b[32m1\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m929\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43msplit\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mary\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mindices_or_sections\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43m1\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m    930\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m    931\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m split(ary, indices_or_sections, \u001b[32m0\u001b[39m)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/python-venv/lib/python3.13/site-packages/numpy/lib/_shape_base_impl.py:854\u001b[39m, in \u001b[36msplit\u001b[39m\u001b[34m(ary, indices_or_sections, axis)\u001b[39m\n\u001b[32m    852\u001b[39m     N = ary.shape[axis]\n\u001b[32m    853\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m N % sections:\n\u001b[32m--> \u001b[39m\u001b[32m854\u001b[39m         \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m    855\u001b[39m             \u001b[33m'\u001b[39m\u001b[33marray split does not result in an equal division\u001b[39m\u001b[33m'\u001b[39m) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m    856\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m array_split(ary, indices_or_sections, axis)\n",
      "\u001b[31mValueError\u001b[39m: array split does not result in an equal division"
     ]
    }
   ],
   "source": [
    "np.hsplit(a,5)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d811e61d",
   "metadata": {},
   "source": [
    "- Can also specify where to split as a list of columns\n",
    "- `hsplit(A,[c1,c2,..,ck])` will split as `A[:c1]`,`A[c1:c2]`,...,`A[ck:]`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "71a09d5f",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "B99x--gCPgAX",
    "outputId": "32298f0d-ee36-439a-de1e-553e6ba40b51"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[array([[4., 7.],\n",
       "        [4., 8.]]),\n",
       " array([[4., 7., 6.],\n",
       "        [6., 9., 7.]]),\n",
       " array([[2., 9.],\n",
       "        [6., 8.]]),\n",
       " array([[9., 0., 5., 0., 9.],\n",
       "        [9., 6., 3., 6., 7.]])]"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.hsplit(a,(2,5,7)) # a[:2], a[2:5], a[5:7], a[7:]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1d9956c",
   "metadata": {},
   "source": [
    "- Similarly, `vsplit` for vertical split"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "382efea1",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "aiczUDZ7PgAX",
    "outputId": "75552879-cd4c-4906-b99c-4c68e28d6a35"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[array([[4., 7., 4., 7., 6., 2., 9., 9., 0., 5., 0., 9.]]),\n",
       " array([[4., 8., 6., 9., 7., 6., 8., 9., 6., 3., 6., 7.]])]"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.vsplit(a,2) # Split a vertically"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "5c76cebe",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[array([[4., 7., 4., 7., 6., 2., 9., 9., 0., 5., 0., 9.]]),\n",
       " array([[4., 8., 6., 9., 7., 6., 8., 9., 6., 3., 6., 7.]])]"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.split(a,2) # behaves like vsplit"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3fa690b1",
   "metadata": {},
   "source": [
    "### Reshaping arrays"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15c6d6e0",
   "metadata": {
    "id": "8MPOa8PnPgAZ"
   },
   "source": [
    "- Can change the *shape* of an array if the dimensions match"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "164312a7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 12)"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "3be27a7b",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_46357/1043616937.py:1: DeprecationWarning: Setting the shape on a NumPy array has been deprecated in NumPy 2.5.\n",
      "As an alternative, you can create a new view using np.reshape (with copy=False if needed).\n",
      "  a.shape = 4,6\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[4., 7., 4., 7., 6., 2.],\n",
       "       [9., 9., 0., 5., 0., 9.],\n",
       "       [4., 8., 6., 9., 7., 6.],\n",
       "       [8., 9., 6., 3., 6., 7.]])"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.shape = 4,6\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "954d26a5-dbfa-44d4-915e-22096ee55fc9",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_46357/2042295483.py:1: DeprecationWarning: Setting the shape on a NumPy array has been deprecated in NumPy 2.5.\n",
      "As an alternative, you can create a new view using np.reshape (with copy=False if needed).\n",
      "  a.shape = 3,8\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[4., 7., 4., 7., 6., 2., 9., 9.],\n",
       "       [0., 5., 0., 9., 4., 8., 6., 9.],\n",
       "       [7., 6., 8., 9., 6., 3., 6., 7.]])"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.shape = 3,8\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "162483a2-dcfc-4165-a120-2c1d6d4cfb99",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_46357/4131849822.py:1: DeprecationWarning: Setting the shape on a NumPy array has been deprecated in NumPy 2.5.\n",
      "As an alternative, you can create a new view using np.reshape (with copy=False if needed).\n",
      "  a.shape = 2,3,4\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[[4., 7., 4., 7.],\n",
       "        [6., 2., 9., 9.],\n",
       "        [0., 5., 0., 9.]],\n",
       "\n",
       "       [[4., 8., 6., 9.],\n",
       "        [7., 6., 8., 9.],\n",
       "        [6., 3., 6., 7.]]])"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.shape = 2,3,4\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38c20710",
   "metadata": {
    "id": "CHDoUz0wPgAY"
   },
   "source": [
    "### Copy and view"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "19b4cef5",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_46357/3929540922.py:1: DeprecationWarning: Setting the shape on a NumPy array has been deprecated in NumPy 2.5.\n",
      "As an alternative, you can create a new view using np.reshape (with copy=False if needed).\n",
      "  a.shape = 2,12\n"
     ]
    }
   ],
   "source": [
    "a.shape = 2,12"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "60c9a999",
   "metadata": {
    "id": "UdEup_87PgAY"
   },
   "outputs": [],
   "source": [
    "c = a.copy()  # Creates a disjoint copy of the array\n",
    "d = a.view()  # Creates another link to the same array\n",
    "e = a         # Aliases e to point to same array as a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "bb9b49f5",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "BOQNN1jmPgAY",
    "outputId": "f03caa0d-3f27-4b48-808c-137cb3ed22c4"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[4., 7., 4., 7., 6., 2., 9., 9., 0., 5., 0., 9.],\n",
       "        [4., 8., 6., 9., 7., 6., 8., 9., 6., 3., 6., 7.]]),\n",
       " array([[4., 7., 4., 7., 6., 2., 9., 9., 0., 5., 0., 9.],\n",
       "        [4., 8., 6., 9., 7., 6., 8., 9., 6., 3., 6., 7.]]),\n",
       " array([[4., 7., 4., 7., 6., 2., 9., 9., 0., 5., 0., 9.],\n",
       "        [4., 8., 6., 9., 7., 6., 8., 9., 6., 3., 6., 7.]]),\n",
       " array([[4., 7., 4., 7., 6., 2., 9., 9., 0., 5., 0., 9.],\n",
       "        [4., 8., 6., 9., 7., 6., 8., 9., 6., 3., 6., 7.]]))"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a, c, d, e"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3f5a566",
   "metadata": {},
   "source": [
    "- Updating `c` has no effect on the others since it is a disjoint copy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "5f7c5ed9",
   "metadata": {
    "id": "0fHh6aqrPgAY"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[4., 7., 4., 7., 6., 2., 9., 9., 0., 5., 0., 9.],\n",
       "        [4., 8., 6., 9., 7., 6., 8., 9., 6., 3., 6., 7.]]),\n",
       " array([[ 4.,  7.,  4.,  7., 88.,  2.,  9.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[4., 7., 4., 7., 6., 2., 9., 9., 0., 5., 0., 9.],\n",
       "        [4., 8., 6., 9., 7., 6., 8., 9., 6., 3., 6., 7.]]),\n",
       " array([[4., 7., 4., 7., 6., 2., 9., 9., 0., 5., 0., 9.],\n",
       "        [4., 8., 6., 9., 7., 6., 8., 9., 6., 3., 6., 7.]]))"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c[0,4] = 88  # Note, not c[0][4]\n",
    "a, c, d, e"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee3c613e",
   "metadata": {},
   "source": [
    "- Updating `d` will indirectly update `a` and `e`, but not `c`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "eb694ac9",
   "metadata": {
    "id": "qAyGiU5gPgAZ"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[ 4.,  7.,  4.,  7.,  6., 66.,  9.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7., 88.,  2.,  9.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66.,  9.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66.,  9.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]))"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d[0,5] = 66\n",
    "a, c, d, e"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85affc3a",
   "metadata": {},
   "source": [
    "- Likewise, updating `e` updates `a` and `d`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "62617b80",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[ 4.,  7.,  4.,  7.,  6., 66., 77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7., 88.,  2.,  9.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66., 77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66., 77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]))"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "e[0,6] = 77\n",
    "a, c, d, e"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2c39372",
   "metadata": {},
   "source": [
    "- We can flatten an array into a sequence"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "5253c9a4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.0 7.0 4.0 7.0 6.0 66.0 77.0 9.0 0.0 5.0 0.0 9.0 4.0 8.0 6.0 9.0 7.0 6.0 8.0 9.0 6.0 3.0 6.0 7.0 "
     ]
    }
   ],
   "source": [
    "for i in a.flat:\n",
    "    print(i,end=\" \")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d43d423",
   "metadata": {
    "id": "KCHfd3tHPgAZ"
   },
   "source": [
    "- `base` tells us if an array shares its storage with another array\n",
    "- For the original array, `base` is `None`\n",
    "- For a view, the `base` points to the \"parent\" array"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "b5f32d56",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(None,\n",
       " None,\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66., 77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " None)"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.base, c.base, d.base, e.base"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "71b4d2a0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d.base is a"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3768d293",
   "metadata": {},
   "source": [
    "- Changing the shape of the base does array not affect the shape of a view"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "a34cbfcb",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_46357/1774912613.py:1: DeprecationWarning: Setting the shape on a NumPy array has been deprecated in NumPy 2.5.\n",
      "As an alternative, you can create a new view using np.reshape (with copy=False if needed).\n",
      "  a.shape = 4,6\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(array([[ 4.,  7.,  4.,  7.,  6., 66.],\n",
       "        [77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.],\n",
       "        [ 8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7., 88.,  2.,  9.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66., 77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66.],\n",
       "        [77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.],\n",
       "        [ 8.,  9.,  6.,  3.,  6.,  7.]]))"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.shape = 4,6\n",
    "a,c,d,e"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81c57a3d",
   "metadata": {},
   "source": [
    "- Changing the shape of the view does not affect the shape of the base array"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "e7a31ce9",
   "metadata": {
    "id": "Z_b9L3OiPgAa"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_46357/2378460131.py:1: DeprecationWarning: Setting the shape on a NumPy array has been deprecated in NumPy 2.5.\n",
      "As an alternative, you can create a new view using np.reshape (with copy=False if needed).\n",
      "  d.shape = 3,8\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(array([[ 4.,  7.,  4.,  7.,  6., 66.],\n",
       "        [77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.],\n",
       "        [ 8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7., 88.,  2.,  9.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66., 77.,  9.],\n",
       "        [ 0.,  5.,  0.,  9.,  4.,  8.,  6.,  9.],\n",
       "        [ 7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66.],\n",
       "        [77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.],\n",
       "        [ 8.,  9.,  6.,  3.,  6.,  7.]]))"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d.shape = 3,8\n",
    "a,c,d,e"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94eb247f",
   "metadata": {},
   "source": [
    "- Since they still have the same base, updating `a` changes the corresponding element in `d`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "23d94010",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[ 4.,  7.,  4.,  7.,  6., 66.],\n",
       "        [77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.],\n",
       "        [99.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7., 88.,  2.,  9.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66., 77.,  9.],\n",
       "        [ 0.,  5.,  0.,  9.,  4.,  8.,  6.,  9.],\n",
       "        [ 7.,  6., 99.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66.],\n",
       "        [77.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.],\n",
       "        [99.,  9.,  6.,  3.,  6.,  7.]]))"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[3,0] = 99\n",
    "a,c,d,e"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f21db46",
   "metadata": {},
   "source": [
    "- Likewise, updating the shape of `e`, which is an alias for `a`, does not affect the shape of the view `d`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "585b516c",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_46357/682286037.py:1: DeprecationWarning: Setting the shape on a NumPy array has been deprecated in NumPy 2.5.\n",
      "As an alternative, you can create a new view using np.reshape (with copy=False if needed).\n",
      "  e.shape = 8,3\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(array([[ 4.,  7.,  4.],\n",
       "        [ 7.,  6., 66.],\n",
       "        [77.,  9.,  0.],\n",
       "        [ 5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.],\n",
       "        [ 9.,  7.,  6.],\n",
       "        [99.,  9.,  6.],\n",
       "        [ 3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7., 88.,  2.,  9.,  9.,  0.,  5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.,  9.,  7.,  6.,  8.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.,  7.,  6., 66., 77.,  9.],\n",
       "        [ 0.,  5.,  0.,  9.,  4.,  8.,  6.,  9.],\n",
       "        [ 7.,  6., 99.,  9.,  6.,  3.,  6.,  7.]]),\n",
       " array([[ 4.,  7.,  4.],\n",
       "        [ 7.,  6., 66.],\n",
       "        [77.,  9.,  0.],\n",
       "        [ 5.,  0.,  9.],\n",
       "        [ 4.,  8.,  6.],\n",
       "        [ 9.,  7.,  6.],\n",
       "        [99.,  9.,  6.],\n",
       "        [ 3.,  6.,  7.]]))"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "e.shape = 8,3\n",
    "a,c,d,e"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ecf831b0",
   "metadata": {},
   "source": [
    "### Matrix operations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "6f6f97c5",
   "metadata": {
    "id": "_tlORJR39JCX"
   },
   "outputs": [],
   "source": [
    "a = np.array([[1,2],[3,4]])\n",
    "b = np.array([[5,6],[7,8]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "66ab56e4",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "bf-JLWzy9WIQ",
    "outputId": "13ca520e-62f3-4c5f-ef98-803fa7980127"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[1, 2],\n",
       "        [3, 4]]),\n",
       " array([[5, 6],\n",
       "        [7, 8]]))"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a,b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80bd3924",
   "metadata": {},
   "source": [
    "- Pointwise addition and multiplication"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "ff0a7f36",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Nt22QeUt9YYx",
    "outputId": "2987cb3e-6b0e-4908-972e-71d8a1482020"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[ 6,  8],\n",
       "        [10, 12]]),\n",
       " array([[ 5, 12],\n",
       "        [21, 32]]))"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a+b, a*b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ba1475e",
   "metadata": {},
   "source": [
    "- Matrix multiplication"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "id": "1cea7471",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "ovbajxvGz7aD",
    "outputId": "f78ff2b2-658b-489a-d6ef-6d1cabe0d10e"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[19, 22],\n",
       "       [43, 50]])"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.matmul(a,b)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dff2f85a",
   "metadata": {},
   "source": [
    "- Transpose and inverse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "id": "c3b3bf57",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "jjQ_aEei0FyO",
    "outputId": "616ca916-cf5b-4eec-812c-e05aa3e8b955"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 3],\n",
       "       [2, 4]])"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.T"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "id": "ed0b0eba",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "6tAyxzKK0SVO",
    "outputId": "1049c3b7-e899-4e81-ed22-66845b88e0a3"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-2. ,  1. ],\n",
       "       [ 1.5, -0.5]])"
      ]
     },
     "execution_count": 65,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.linalg.inv(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d57fca4",
   "metadata": {},
   "source": [
    "- $A A^{-1}$ should give the identity matrix\n",
    "- Note the small imprecision due to round off error"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "id": "9e4a3736",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "DDf8EGtC0Y9w",
    "outputId": "8a51e4b8-bc71-4435-e9e3-65076495bfaa",
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1.0000000e+00, 0.0000000e+00],\n",
       "       [8.8817842e-16, 1.0000000e+00]])"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.matmul(a,np.linalg.inv(a)) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06b4917d",
   "metadata": {},
   "source": [
    "- Fit a function $f$ to a set of data points $\\{(x_1,y_1), (x_2,y_2), \\ldots, (x_n,y_n)\\}$\n",
    "- Compute *mean square error (MSE)*\n",
    "\n",
    "$\\displaystyle MSE = \\frac{1}{n} \\sum_{i=1}^{n} (f(x_i) - y_i)^2$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "9737ec0b",
   "metadata": {},
   "outputs": [],
   "source": [
    "predictions = np.array([1.2,2.3,3.1]) # (f(x1), f(x2), f(x3))\n",
    "values = np.array([1,2.5,3]) # (y1, y2, y3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "799ffa9b",
   "metadata": {},
   "source": [
    "- `predictions - values` creates the array $(f(x_1) - y_1, f(x_2) - y_2, f(x_3)-y_3)$\n",
    "- Applying `np.square` to this squares each element\n",
    "- Applying `np.sum` to the result sums up the squares of the errors"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "6075b2f2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(0.03000000000000002)"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n = len(predictions)\n",
    "mse = 1/n * np.sum(np.square(predictions-values))\n",
    "mse"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58ef2f1f",
   "metadata": {},
   "source": [
    "### Axes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "id": "fb40bf97",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.81918247, 0.66135039, 5.62839188],\n",
       "       [1.01971084, 7.75736105, 8.94883196],\n",
       "       [8.05270082, 5.09213257, 0.0812723 ],\n",
       "       [8.45844436, 5.28911391, 1.0152308 ]])"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.random.random((4,3))*10\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77d305dd",
   "metadata": {},
   "source": [
    "- `np.sum()` adds up all the entries in the array"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "a82a6610",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(52.823723341821086)"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sum(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a948048",
   "metadata": {},
   "source": [
    "- Selecting `axis = 0` applies the operation column by column"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "0e1440e5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([18.3500385 , 18.7999579 , 15.67372694])"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sum(a,axis=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "317949ff",
   "metadata": {},
   "source": [
    "- Selecting `axis = 1` applies the opertion row by row"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "ea8a7dda",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 7.10892474, 17.72590385, 13.22610568, 14.76278907])"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sum(a,axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b81b7b94",
   "metadata": {},
   "source": [
    "- `concatenate()` generalizes `vstack()` and `hstack()`\n",
    "- By default it is `vstack()`, equivalent to setting `axis = 0`\n",
    "- If we set `axis = 1`, we get `hstack()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "5fa97c15",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.76220839, 5.13172146, 8.54228238],\n",
       "       [8.78106453, 0.50656321, 6.77683995],\n",
       "       [7.489932  , 9.45161404, 0.88572984],\n",
       "       [7.89212861, 2.33473777, 1.15055342]])"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b = np.random.random((4,3))*10\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "ab2bd96a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[0.81918247, 0.66135039, 5.62839188],\n",
       "        [1.01971084, 7.75736105, 8.94883196],\n",
       "        [8.05270082, 5.09213257, 0.0812723 ],\n",
       "        [8.45844436, 5.28911391, 1.0152308 ],\n",
       "        [0.76220839, 5.13172146, 8.54228238],\n",
       "        [8.78106453, 0.50656321, 6.77683995],\n",
       "        [7.489932  , 9.45161404, 0.88572984],\n",
       "        [7.89212861, 2.33473777, 1.15055342]]),\n",
       " array([[0.81918247, 0.66135039, 5.62839188, 0.76220839, 5.13172146,\n",
       "         8.54228238],\n",
       "        [1.01971084, 7.75736105, 8.94883196, 8.78106453, 0.50656321,\n",
       "         6.77683995],\n",
       "        [8.05270082, 5.09213257, 0.0812723 , 7.489932  , 9.45161404,\n",
       "         0.88572984],\n",
       "        [8.45844436, 5.28911391, 1.0152308 , 7.89212861, 2.33473777,\n",
       "         1.15055342]]))"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.vstack((a,b)), np.hstack((a,b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "id": "51a6d38f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.81918247, 0.66135039, 5.62839188],\n",
       "       [1.01971084, 7.75736105, 8.94883196],\n",
       "       [8.05270082, 5.09213257, 0.0812723 ],\n",
       "       [8.45844436, 5.28911391, 1.0152308 ],\n",
       "       [0.76220839, 5.13172146, 8.54228238],\n",
       "       [8.78106453, 0.50656321, 6.77683995],\n",
       "       [7.489932  , 9.45161404, 0.88572984],\n",
       "       [7.89212861, 2.33473777, 1.15055342]])"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.concatenate((a,b))  # Implicitly, axis = 0, so vstack()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "51d291fa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.81918247, 0.66135039, 5.62839188, 0.76220839, 5.13172146,\n",
       "        8.54228238],\n",
       "       [1.01971084, 7.75736105, 8.94883196, 8.78106453, 0.50656321,\n",
       "        6.77683995],\n",
       "       [8.05270082, 5.09213257, 0.0812723 , 7.489932  , 9.45161404,\n",
       "        0.88572984],\n",
       "       [8.45844436, 5.28911391, 1.0152308 , 7.89212861, 2.33473777,\n",
       "        1.15055342]])"
      ]
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.concatenate((a,b),axis=1) # Concatenate row-wise, so same as hstack()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "1768c698",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[0.88046795, 0.99392296, 0.27633746, 0.73703386, 0.29114019,\n",
       "         0.66363698, 0.67184984]]),\n",
       " array([[0.00581885, 0.83850564, 0.20357386, 0.05392887, 0.88108961,\n",
       "         0.41618057, 0.26064871]]))"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c = np.random.random((1,7))\n",
    "d = np.random.random((1,7))\n",
    "c,d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "id": "9a6b31cc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.88046795, 0.99392296, 0.27633746, 0.73703386, 0.29114019,\n",
       "        0.66363698, 0.67184984, 0.00581885, 0.83850564, 0.20357386,\n",
       "        0.05392887, 0.88108961, 0.41618057, 0.26064871]])"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.concatenate((c,d),axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51391597",
   "metadata": {},
   "source": [
    "### Broadcasting"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c321b32",
   "metadata": {},
   "source": [
    "- Array with scalar --- map operation to each array element"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "id": "6e667559",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2., 4., 6.])"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([1.0, 2.0, 3.0])\n",
    "b = 2.0\n",
    "a * b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bace1b27",
   "metadata": {},
   "source": [
    "- Array with array/sequence of same length --- pointwise application of operation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "id": "5ac721de",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2., 4., 6.])"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([1.0, 2.0, 3.0])\n",
    "b = np.array([2.0, 2.0, 2.0])\n",
    "a * b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d9af900",
   "metadata": {},
   "source": [
    "- More generally, can broadcast to an array of same dimension as rightmost index\n",
    "\n",
    "*Example*\n",
    "\n",
    "- Store an $m \\times n$ image as 3 layers, Red, Blue and Green\n",
    "- Array dimensions are $(m,n,3)$\n",
    "- Want to scale RGB values by different amounts\n",
    "- Multiply image by $(rscale,bscale,gscale)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "40533355",
   "metadata": {},
   "outputs": [],
   "source": [
    "pic = np.random.random((4,4,3))*10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "ac57c8b3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[9.54339525, 2.04858848, 8.32272994],\n",
       "        [5.80180354, 9.62061036, 7.18816488],\n",
       "        [1.37215031, 9.76922239, 3.1358162 ],\n",
       "        [5.50714082, 5.64770232, 3.69201246]],\n",
       "\n",
       "       [[9.06901035, 7.25147347, 6.80622958],\n",
       "        [4.96124661, 5.98674093, 6.85115847],\n",
       "        [1.96223355, 6.27720441, 4.39818831],\n",
       "        [3.47650489, 9.69669405, 8.54726802]],\n",
       "\n",
       "       [[6.13447243, 7.4298857 , 0.18210238],\n",
       "        [7.28022702, 8.3608639 , 0.55779384],\n",
       "        [3.22308057, 2.4264986 , 0.51528485],\n",
       "        [9.20997866, 3.60708512, 5.33017625]],\n",
       "\n",
       "       [[7.19704387, 8.7503995 , 2.7930906 ],\n",
       "        [2.98163554, 2.92389833, 7.62467763],\n",
       "        [1.3500405 , 9.1557671 , 0.96891741],\n",
       "        [1.14362885, 4.76294657, 9.21407576]]])"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pic"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "d6e11e26",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[9.54339525e+00, 2.04858848e+02, 8.32272994e+03],\n",
       "        [5.80180354e+00, 9.62061036e+02, 7.18816488e+03],\n",
       "        [1.37215031e+00, 9.76922239e+02, 3.13581620e+03],\n",
       "        [5.50714082e+00, 5.64770232e+02, 3.69201246e+03]],\n",
       "\n",
       "       [[9.06901035e+00, 7.25147347e+02, 6.80622958e+03],\n",
       "        [4.96124661e+00, 5.98674093e+02, 6.85115847e+03],\n",
       "        [1.96223355e+00, 6.27720441e+02, 4.39818831e+03],\n",
       "        [3.47650489e+00, 9.69669405e+02, 8.54726802e+03]],\n",
       "\n",
       "       [[6.13447243e+00, 7.42988570e+02, 1.82102382e+02],\n",
       "        [7.28022702e+00, 8.36086390e+02, 5.57793844e+02],\n",
       "        [3.22308057e+00, 2.42649860e+02, 5.15284854e+02],\n",
       "        [9.20997866e+00, 3.60708512e+02, 5.33017625e+03]],\n",
       "\n",
       "       [[7.19704387e+00, 8.75039950e+02, 2.79309060e+03],\n",
       "        [2.98163554e+00, 2.92389833e+02, 7.62467763e+03],\n",
       "        [1.35004050e+00, 9.15576710e+02, 9.68917413e+02],\n",
       "        [1.14362885e+00, 4.76294657e+02, 9.21407576e+03]]])"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pic*[1,100,1000]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4c1fb26",
   "metadata": {},
   "source": [
    "*Note*\n",
    "- In the example above, a shape of `(3,4,4)` would display more intuitively as 3 layers of colour for the base image of shape `(4,4)`\n",
    "- However, for broadcasting, we need the *rightmost* index to match, so we wrote it as `(4,4,3)`, which is laid out less intuitively by `numpy` as 4 layers with shape `(4,3)`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26c11989",
   "metadata": {},
   "source": [
    "*Broadcasting example*\n",
    "\n",
    "- Find the nearest point to a given point in a collection\n",
    "- Given $(x,y)$ and $\\{(x_1,y_1), (x_2,y_2), \\ldots, (x_n,y_n)\\}$, report $j$ such that $(x_j,y_j)$ is the closest point to $(x,y)$\n",
    "- Distance of each point is $\\sqrt{(x_i - x)^2 + (y_i - y)^2}$\n",
    "- $\\arg\\min$ reports index where $\\min$ is achieved"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3754d683",
   "metadata": {},
   "source": [
    "- Let us break this up into steps"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87d1d6f5",
   "metadata": {},
   "source": [
    "1. The setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "89f299ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "observation = np.array([111.0, 188.0])  # (x,y)\n",
    "codes = np.array([[132.0, 193.0],  # [(x1,y1), (x2,y2), (x3,y3), (x4,y4)]\n",
    "               [102.0, 203.0],\n",
    "               [45.0, 155.0],\n",
    "               [57.0, 173.0]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1f7d2c3",
   "metadata": {},
   "source": [
    "2. Use broadcast to subtact $(x,y)$ from each $(x_i,y_i)$ to get the array $[(x_1-x,y_1-y),(x_2-x,y_2-y),(x_3-x,y_3-y),(x_4-x,y_4-y)]$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "id": "d9bc0821",
   "metadata": {},
   "outputs": [],
   "source": [
    "diff = codes - observation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "id": "439413d2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 21.,   5.],\n",
       "       [ -9.,  15.],\n",
       "       [-66., -33.],\n",
       "       [-54., -15.]])"
      ]
     },
     "execution_count": 86,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "diff"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88303d41",
   "metadata": {},
   "source": [
    "3. Use scalar broadcast to map each pair $(x_i-x,y_i-y)$ to $((x_i-x)^2,(y_i-y)^2)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "id": "c9b04bd7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 441.,   25.],\n",
       "       [  81.,  225.],\n",
       "       [4356., 1089.],\n",
       "       [2916.,  225.]])"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "diff**2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7197808",
   "metadata": {},
   "source": [
    "4. Add up each row as $(x_i-x)^2 + (y_i-y)^2$ by computing `np.sum()` along `axis = 1`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "id": "7daa8192",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 466.,  306., 5445., 3141.])"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sum(diff**2,axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7df0438b",
   "metadata": {},
   "source": [
    "5. Apply `np.sqrt()` pointwise to this array of squared differences"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "id": "aa845693",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([21.58703314, 17.49285568, 73.79024326, 56.04462508])"
      ]
     },
     "execution_count": 89,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sqrt(np.sum(diff**2,axis=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a660b0e",
   "metadata": {},
   "source": [
    "6. Find the index where this value is minimum"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "id": "c71bdf9a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.int64(1)"
      ]
     },
     "execution_count": 90,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dist = np.sqrt(np.sum(diff**2,axis=1))\n",
    "np.argmin(dist)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc4ea620",
   "metadata": {},
   "source": [
    "- Or, equivalently, without the intermediate variable `dist`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "3149ba82",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.int64(1)"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.argmin(np.sqrt(np.sum(diff**2,axis=1)))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "606c370e-6276-457b-8d2b-991ab0bac571",
   "metadata": {},
   "source": [
    "- Here is the whole computation in one shot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "id": "7d27093c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([21.58703314, 17.49285568, 73.79024326, 56.04462508]), np.int64(1))"
      ]
     },
     "execution_count": 92,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "observation = np.array([111.0, 188.0])  # (x,y)\n",
    "codes = np.array([[132.0, 193.0],  # [(x1,y1), (x2,y2), (x3,y3), (x4,y4)]\n",
    "               [102.0, 203.0],\n",
    "               [45.0, 155.0],\n",
    "               [57.0, 173.0]])\n",
    "\n",
    "diff = codes - observation\n",
    "dist = np.sqrt(np.sum(diff**2,axis=1))\n",
    "dist, np.argmin(dist)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "55340923",
   "metadata": {},
   "source": [
    "- In fact, the entire computation can be written in one line"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "id": "fa6d6561",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.int64(1)"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.argmin(np.sqrt(np.sum((codes-observation)**2,axis=1)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "id": "d43f7353-b363-476d-acbc-721ccbd6b514",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(17.4928556845359)"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.min(np.sqrt(np.sum((codes-observation)**2,axis=1)))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
