{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "807f94ad",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa4ee33f",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Avoid for-loops (element-after-element operations)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c4290b53",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "def compute_reciprocals(values):\n",
    "    output = np.empty(len(values))\n",
    "    for i in range(len(values)):\n",
    "        output[i] = 1.0 / values[i]\n",
    "    return output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b6e6dca6",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "18.4 µs ± 155 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n"
     ]
    }
   ],
   "source": [
    "%%timeit\n",
    "\n",
    "np.random.seed(0)\n",
    "        \n",
    "values = np.random.randint(1, 10, size=5)\n",
    "compute_reciprocals(values)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd70d5d3",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Very slow! Each time the reciprocal is computed, Python first examines the object's **type** and does a dynamic lookup of the **correct function** to use for that type"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cfd3f5d5",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Basic functions\n",
    "\n",
    "In NumPy - convenient interface for just this kind of statically typed, compiled routine. --> vectorized operations\n",
    "- performing an operation on the array, which will then be applied to each element\n",
    "- pushes the loop into the compiled layer that underlies NumPy --> much faster execution\n",
    "\n",
    "- basic funcs quickly execute repeated operations on values in NumPy arrays --> extremely flexible"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "28eb2782",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8.81 µs ± 173 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n"
     ]
    }
   ],
   "source": [
    "%%timeit\n",
    "values = np.random.randint(1, 10, size=5)\n",
    "#compute_reciprocals(values)\n",
    "1.0 / values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8fa81fb7",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x     = [0 1 2 3]\n",
      "x + 5 = [5 6 7 8]\n",
      "x - 5 = [-5 -4 -3 -2]\n",
      "x * 2 = [0 2 4 6]\n"
     ]
    }
   ],
   "source": [
    "# simple vectorized functions \n",
    "x = np.arange(4)\n",
    "print(\"x     =\", x)\n",
    "print(\"x + 5 =\", x + 5)\n",
    "print(\"x - 5 =\", x - 5)\n",
    "print(\"x * 2 =\", x * 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "84b8ac14",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x     = [1, 2, 3]\n",
      "e^x   = [ 2.71828183  7.3890561  20.08553692]\n",
      "2^x   = [2. 4. 8.]\n",
      "3^x   = [ 3  9 27]\n"
     ]
    }
   ],
   "source": [
    "# exponents and logarithms\n",
    "x = [1, 2, 3]\n",
    "print(\"x     =\", x)\n",
    "print(\"e^x   =\", np.exp(x))\n",
    "print(\"2^x   =\", np.exp2(x))\n",
    "print(\"3^x   =\", np.power(3, x))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10ec3b82",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Mgrid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "289495f1",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "x = np.arange(1,10) # timepoints\n",
    "y = np.arange(-70, 100, 10) # voltage change\n",
    "\n",
    "# for loop solution\n",
    "xx, yy = np.zeros((len(x), len(y))), np.zeros((len(y), len(x)))\n",
    "for a, i in enumerate(x):\n",
    "    xx[a] = np.repeat(i, len(y))\n",
    "\n",
    "for b, j in enumerate(y):\n",
    "    yy[b] = np.repeat(j, len(x))\n",
    "yy = yy.T\n",
    "\n",
    "# 1-line mgrid solution\n",
    "# works like broadcasting\n",
    "XX, YY = np.mgrid[1:10:1, -70:100:10] # two arrays with shape (9,17); (len(x), len(y))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "25bce1f0",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 7748 characters]",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure()\n",
    "ax = fig.add_subplot()\n",
    "ax.scatter(xx, yy)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b8a8d1e",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Hands on exercises\n",
    "\n",
    "- Open notebook exercises/numpy_vectorize.ipynb\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1fb78b62",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "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.11.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
