{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "toc": true }, "source": [ "

Table of Contents

\n", "
" ] }, { "cell_type": "markdown", "metadata": { "id": "0vJLt3JRL9eR", "slideshow": { "slide_type": "slide" } }, "source": [ "NUMPY tutorial" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "Several numpy tutorials can be found [here](https://numpy.org/learn/)" ] }, { "cell_type": "markdown", "metadata": { "id": "3cfrOV4dL9hW", "slideshow": { "slide_type": "slide" } }, "source": [ "# Numpy" ] }, { "cell_type": "markdown", "metadata": { "id": "fY12nHhyL9hX" }, "source": [ "Numpy is the core library for scientific computing in Python. It provides a high-performance multidimensional array object, and tools for working with these arrays. \n", "\n", "For MATLAB users, consider consulting the Numpy's [NumPy for Matlab Users](http://wiki.scipy.org/NumPy_for_Matlab_Users) page." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Basics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Numpy’s main object is the multidimensional `numpy array`:
\n", " => it is a table of elements (usually numbers), all of the same type, indexed by a tuple of non-negative integers
\n", " => array dimensions are called `axes`
\n", " => the number of dimensions is called the array `rank`
\n", " => numpy's array class is called `ndarray` " ] }, { "cell_type": "markdown", "metadata": { "id": "lZMyAdqhL9hY", "slideshow": { "slide_type": "fragment" } }, "source": [ "Import the `numpy` package as follows:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "58QdX8BLL9hZ" }, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "markdown", "metadata": { "id": "DDx6v1EdL9hb", "slideshow": { "slide_type": "slide" } }, "source": [ "# Arrays" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Creation" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### from list/tuple\n", "Create arrays from a sequence of values (Python list or tuple)." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] }, { "data": { "text/plain": [ "array([0, 1, 2])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# create from a sequence of values (Python list or tuple):\n", "a = np.array([0, 1, 2]) #>> create from list\n", "a = np.array((0, 1, 2)) #>> create from tuple\n", "\n", "print(type(a))\n", "a" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 3],\n", " [4, 5, 6]])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# create multi-dimentional arrays from nested lists\n", "nested_list = [[1,2,3],[4,5,6]]\n", "a = np.array(nested_list)\n", "\n", "a" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "uint8\n", "float32\n", "but returns an array." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.arange(0, 10)\n", "a" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0 1 2 3 4 5]\n" ] } ], "source": [ "a = np.arange(6) # 1d array\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**Arange can be combined with the `reshape()` function to create multidimensional arrays:**" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 0 1 2]\n", " [ 3 4 5]\n", " [ 6 7 8]\n", " [ 9 10 11]]\n" ] } ], "source": [ "b = np.arange(12).reshape(4, 3) # 2d array (12 elements, arranged as 4 rows x 3 columns)\n", "print(b)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[[ 0 1 2 3]\n", " [ 4 5 6 7]\n", " [ 8 9 10 11]]\n", "\n", " [[12 13 14 15]\n", " [16 17 18 19]\n", " [20 21 22 23]]]\n", "[[ 0 4 8]\n", " [12 16 20]]\n" ] } ], "source": [ "# WARNING: in the following example, the 3D array is filled sequentially, parsing the values through the various channels \n", "# => does NOT fill a channel before going to the next) \n", "# => in this case the first channel has values 0, 4, 8, 12, 16, 20 (since there are 4 channels in array)\n", "\n", "c = np.arange(24).reshape(2, 3, 4) # 3d array (24 elements, arranged as 4 matrices of 2 rows x 3 columns)\n", "print(c)\n", "\n", "print(c[:,:,0])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### zeros()\n", "Create an array of zeros, specifying the array shape as a tuple." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0., 0., 0., 0.],\n", " [0., 0., 0., 0.],\n", " [0., 0., 0., 0.]])" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.zeros((3, 4))\n", "a" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### ones()\n", "Create an array of ones, specifying the array shape as a tuple." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1., 1., 1., 1.],\n", " [1., 1., 1., 1.],\n", " [1., 1., 1., 1.]])" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.ones((3, 4))\n", "a" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### full()\n", "Create an array of unique values, specifying the array shape as a tuple." ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "HtFsr03bL9h7", "outputId": "2688b157-2fad-4fc6-f20b-8633207f0326" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[7 7]\n", " [7 7]]\n" ] } ], "source": [ "a = np.full((2,2), 7)\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "-QcALHvkL9h9", "outputId": "5035d6fe-cb7e-4222-c972-55fe23c9d4c0", "slideshow": { "slide_type": "subslide" } }, "source": [ "### eye()\n", "Create the identity matrix, specifying the array shape as a tuple." ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "-QcALHvkL9h9", "outputId": "5035d6fe-cb7e-4222-c972-55fe23c9d4c0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1. 0.]\n", " [0. 1.]]\n" ] } ], "source": [ "a = np.eye(2)\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### random()\n", "Create an array of random floats in the range [0-1], specifying the array shape as a tuple." ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.43048459 0.06487667]\n", " [0.87162144 0.11032227]]\n" ] } ], "source": [ "a = np.random.random((2,2)) # Create an array filled with random values\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### empty()\n", "Creates an array whose initial content is random and depends on the state of the memory. " ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.51020597, 0.58265003],\n", " [0.49145982, 0.48806746]])" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.empty((2,2))\n", "a" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### empty_like()\n", "Create an empty array with the same shape as a." ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.51020597, 0.58265003],\n", " [0.49145982, 0.48806746]])" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.ones((2,2))\n", "\n", "b = np.empty_like(a) # Create an empty matrix with the same shape as x\n", "b" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Attributes\n", "The most important attributes are `shape` and `dtype`. (See [here](https://numpy.org/devdocs/user/quickstart.html) for a more complete list)." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 3],\n", " [4, 5, 6]])" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# create 2D array\n", "a = np.array([[1, 2, 3], [4, 5, 6]])\n", "a" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### shape\n", "Returns the dimensions of the array as a tuple of integers:
\n", "`(nb_rows, nb_columns)` for 2D arrays, `(nb_rows, nb_columns, nb_channels)` for 3D arrays" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2, 3)" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.shape" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### ndim\n", "Returns the number of `dimensions` of the array. " ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.ndim" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(a.shape) # >> equivalent to asking for the length of the array shape" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### dtype\n", "Returns the type of the elements in the array. " ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dtype('int64')" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.dtype" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Indexing / slicing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Numpy offers `indexing` and `slicing`, similar to Python lists:
\n", "- access elements using square brackets\n", "- Python is zero-based, meaning the first element is accessed with the index 0
\n", "- the result of the slicing includes the start index, but excludes the end index\n", "\n", "However because arrays may be multidimensional, you must specify a slice for each dimension of the array:\n", "- `array[rows, cols, channels]`" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "# --- REMINDER: indexing/slicing Python lists\n", "l = [1, 2, 3, 4, 5] # create list\n", "\n", "# - access single element\n", "l[0] # access first element\n", "l[-1] # access last element\n", "l[-2] # access second to last element\n", "\n", "# - slice (access multiple elements)\n", "# Important: the result includes the start index, but excludes the end index\n", "l[1:3] # access 2nd & 3rd elements\n", "l[1:-2] # access 2nd until 2nd to last element\n", "l[:3] # access all elements from start until 4th element\n", "l[3:] # access all elements from 4th element until end\n", "l[::2] # access every nth element\n", "\n", "# - assign element\n", "l[0] = 0 # replace element\n", "l[1:2] = [-1, -2] # assign a sublist to a slice" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0 1 2 3 4 5]\n", "0\n", "[1 2]\n" ] } ], "source": [ "# --- 1D numpy array\n", "# => index/slice array just like a list\n", "\n", "a = np.arange(6) # create 1D array\n", "print(a)\n", "print(a[0]) # access first element\n", "print(a[1:3]) # access 2nd & 4th elements" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 0 1 2]\n", " [ 3 4 5]\n", " [ 6 7 8]\n", " [ 9 10 11]]\n" ] }, { "data": { "text/plain": [ "array([10, 11])" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# --- 2D numpy array\n", "# => specify a slice for each dimension of the array\n", "\n", "b = np.arange(12).reshape(4, 3) # create 2D array (12 elements, arranged as 4 rows x 3 columns)\n", "print(b)\n", "\n", "b[:, 0] # access all row elements from the first column\n", "b[-1, -2:] # access the last 2 elements from the last row" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[[ 0 1 2 3]\n", " [ 4 5 6 7]\n", " [ 8 9 10 11]]\n", "\n", " [[12 13 14 15]\n", " [16 17 18 19]\n", " [20 21 22 23]]]\n" ] }, { "data": { "text/plain": [ "array([[ 0, 4, 8],\n", " [12, 16, 20]])" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# --- 3D numpy array\n", "\n", "# WARNING: in the following example, the 3D array is filled sequentially, parsing the values through the various channels \n", "# => does NOT fill a channel before going to the next) \n", "# => in this case the first channel has values 0, 4, 8, 12, 16, 20 (since there are 4 channels in array)\n", "\n", "c = np.arange(24).reshape(2, 3, 4) # create 3d array (24 elements, arranged as 4 matrices of 2 rows x 3 columns)\n", "print(c)\n", "\n", "c[:, :, 0] # access all rows and columns from the first channel\n", "c[..., 0] # (equivalent to above command)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**Boolean indexing**" ] }, { "cell_type": "markdown", "metadata": { "id": "kaE8dBGgL9id" }, "source": [ "Boolean array indexing: Boolean array indexing lets you pick out arbitrary elements of an array. Frequently this type of indexing is used to select the elements of an array that satisfy some condition. Here is an example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 69 }, "id": "32PusjtKL9id", "outputId": "8782e8ec-b78d-44d7-8141-23e39750b854", "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[False False]\n", " [ True True]\n", " [ True True]]\n" ] } ], "source": [ "import numpy as np\n", "\n", "a = np.array([[1,2], [3, 4], [5, 6]])\n", "\n", "bool_idx = (a > 2) # Find the elements of a that are bigger than 2;\n", " # this returns a numpy array of Booleans of the same\n", " # shape as a, where each slot of bool_idx tells\n", " # whether that element of a is > 2.\n", "\n", "print(bool_idx)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "cb2IRMXaL9if", "outputId": "5983f208-3738-472d-d6ab-11fe85b36c95", "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[3 4 5 6]\n", "[3 4 5 6]\n" ] } ], "source": [ "# We use boolean array indexing to construct a rank 1 array\n", "# consisting of the elements of a corresponding to the True values\n", "# of bool_idx\n", "print(a[bool_idx])\n", "\n", "# We can do all of the above in a single concise statement:\n", "print(a[a > 2])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Shape manipulation" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### ravel()\n", "Returns the array, flattened." ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 2 3]\n", " [4 5 6]]\n" ] }, { "data": { "text/plain": [ "array([1, 2, 3, 4, 5, 6])" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.array([[1, 2, 3], [4, 5, 6]])\n", "print(a)\n", "\n", "a.ravel()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### transpose" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 2 3]\n", " [4 5 6]]\n" ] }, { "data": { "text/plain": [ "array([[1, 4],\n", " [2, 5],\n", " [3, 6]])" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.array([[1, 2, 3], [4, 5, 6]])\n", "print(a)\n", "\n", "a.T" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### stack" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "#### hstack()\n", "Stack arrays horizontally." ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 2, 3, 4, 5, 6])" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.array([1, 2, 3])\n", "b = np.array([4, 5, 6])\n", "\n", "c = np.hstack((a, b))\n", "c" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "#### vstack()\n", "Stack arrays vertically." ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 3],\n", " [4, 5, 6]])" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.array([1, 2, 3])\n", "b = np.array([4, 5, 6])\n", "\n", "c = np.vstack((a, b))\n", "c" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "#### dstack()\n", "Stack arrays depth-wise.
\n", "(Useful to create RGB arrays from distinct R, G, B channels.)" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [], "source": [ "R = np.zeros((3, 3)) # create 1st channel with 0\n", "G = np.ones((3, 3)) # create 2nd channel with 1\n", "B = np.full((3, 3), 10) # create 3rd channel with 10" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[[ 0., 1., 10.],\n", " [ 0., 1., 10.],\n", " [ 0., 1., 10.]],\n", "\n", " [[ 0., 1., 10.],\n", " [ 0., 1., 10.],\n", " [ 0., 1., 10.]],\n", "\n", " [[ 0., 1., 10.],\n", " [ 0., 1., 10.],\n", " [ 0., 1., 10.]]])" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RGB = np.dstack((R, G, B))\n", "RGB" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### reshape()" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 2 3]\n", " [4 5 6]]\n" ] }, { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.array([[1, 2, 3], [4, 5, 6]])\n", "print(a)\n", "\n", "a.reshape(3,2)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### resize()" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 2 3]\n", " [4 5 6]]\n" ] }, { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.array([[1, 2, 3], [4, 5, 6]])\n", "print(a)\n", "\n", "a.resize((3, 2))\n", "a" ] }, { "cell_type": "markdown", "metadata": { "id": "jTctwqdQL9ih", "slideshow": { "slide_type": "slide" } }, "source": [ "## Datatypes" ] }, { "cell_type": "markdown", "metadata": { "id": "kSZQ1WkIL9ih" }, "source": [ "Every numpy array is a grid of elements of the same type.
\n", "You can explicitly specify the datatype when creating an array.
\n", "\n", "See numpy documentation about all numpy datatypes [here](http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html)." ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, "id": "4za4O0m5L9ih", "outputId": "2ea4fb80-a4df-43f9-c162-5665895c13ae", "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "int64 float64 uint8\n" ] } ], "source": [ "x = np.array([1, 2]) # dtype not specified => guessed by numpy => int\n", "y = np.array([1.0, 2.0]) # dtype not specified => guessed by numpy => float\n", "z = np.array([1, 2], dtype=np.uint8) # dtype specified\n", "\n", "print(x.dtype, y.dtype, z.dtype)" ] }, { "cell_type": "markdown", "metadata": { "id": "TuB-fdhIL9ik", "slideshow": { "slide_type": "slide" } }, "source": [ "## Array math" ] }, { "cell_type": "markdown", "metadata": { "id": "18e8V8elL9ik" }, "source": [ "Arithmetic operators on arrays apply `elementwise`. A new array is created and filled with the result.
\n", "Basic mathematical functions are alse available as functions in the numpy module (ex: `np.add()`, etc.). See the full list of mathematical functions provided by numpy in the [documentation](http://docs.scipy.org/doc/numpy/reference/routines.math.html).\n", "\n", "Note: unlike MATLAB, `*` is elementwise multiplication, not matrix multiplication. Numpy uses instead the `dot` function to compute inner products of vectors, to multiply a vector by a matrix, and to multiply matrices." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### element-wise operations" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# --- create arrays\n", "x = np.array([[1,2],[3,4]], dtype=np.float64)\n", "y = np.array([[5,6],[7,8]], dtype=np.float64)" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 86 }, "id": "gHKvBrSKL9il", "outputId": "a8a924b1-9d60-4b68-8fd3-e4657ae3f08b", "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 6 8]\n", " [10 12]]\n", "[[ 6 8]\n", " [10 12]]\n" ] } ], "source": [ "# --- elementwise sum\n", "print(x + y)\n", "print(np.add(x, y)) #>> equivalent result" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 86 }, "id": "1fZtIAMxL9in", "outputId": "122f1380-6144-4d6c-9d31-f62d839889a2", "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[-4. -4.]\n", " [-4. -4.]]\n", "[[-4. -4.]\n", " [-4. -4.]]\n" ] } ], "source": [ "# --- elementwise difference\n", "print(x - y)\n", "print(np.subtract(x, y)) #>> equivalent result" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 86 }, "id": "nil4AScML9io", "outputId": "038c8bb2-122b-4e59-c0a8-a091014fe68e", "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 5. 12.]\n", " [21. 32.]]\n", "[[ 5. 12.]\n", " [21. 32.]]\n" ] } ], "source": [ "# --- elementwise product\n", "print(x * y)\n", "print(np.multiply(x, y)) #>> equivalent result" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 86 }, "id": "0JoA4lH6L9ip", "outputId": "12351a74-7871-4bc2-97ce-a508bf4810da", "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.2 0.33333333]\n", " [0.42857143 0.5 ]]\n", "[[0.2 0.33333333]\n", " [0.42857143 0.5 ]]\n" ] } ], "source": [ "# --- elementwise division\n", "print(x / y)\n", "print(np.divide(x, y)) #>> equivalent result" ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "g0iZuA6bL9ir", "outputId": "29927dda-4167-4aa8-fbda-9008b09e4356", "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1. 1.41421356]\n", " [1.73205081 2. ]]\n" ] } ], "source": [ "# --- elementwise square root\n", "print(np.sqrt(x))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### dot product" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "I3FnmoSeL9iu", "outputId": "46f4575a-2e5e-4347-a34e-0cc5bd280110", "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "219\n", "219\n" ] } ], "source": [ "# --- dot product \n", "# NB: unlike MATLAB, `*` is elementwise multiplication, not matrix multiplication. Numpy uses instead the `dot` function to compute inner products of vectors, to multiply a vector by a matrix, and to multiply matrices.\n", "\n", "x = np.array([[1,2],[3,4]])\n", "y = np.array([[5,6],[7,8]])\n", "\n", "v = np.array([9,10])\n", "w = np.array([11, 12])\n", "\n", "# Inner product of vectors; both produce 219\n", "print(v.dot(w))\n", "print(np.dot(v, w))" ] }, { "cell_type": "markdown", "metadata": { "id": "vmxPbrHASVeA", "slideshow": { "slide_type": "fragment" } }, "source": [ "You can also use the `@` operator which is equivalent to numpy's `dot` operator." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, "id": "vyrWA-mXSdtt", "outputId": "a9aae545-2c93-4649-b220-b097655955f6" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "219\n" ] } ], "source": [ "print(v @ w)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 69 }, "id": "zvUODeTxL9iw", "outputId": "4093fc76-094f-4453-a421-a212b5226968", "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[29 67]\n", "[29 67]\n", "[29 67]\n" ] } ], "source": [ "# Matrix / vector product; both produce the rank 1 array [29 67]\n", "print(x.dot(v))\n", "print(np.dot(x, v))\n", "print(x @ v)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 121 }, "id": "3V_3NzNEL9iy", "outputId": "af2a89f9-af5d-47a6-9ad2-06a84b521b94", "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[19 22]\n", " [43 50]]\n", "[[19 22]\n", " [43 50]]\n", "[[19 22]\n", " [43 50]]\n" ] } ], "source": [ "# Matrix / matrix product; both produce the rank 2 array\n", "print(x.dot(y))\n", "print(np.dot(x, y))\n", "print(x @ y)" ] }, { "cell_type": "markdown", "metadata": { "id": "REfLrUTcL9i7", "slideshow": { "slide_type": "subslide" } }, "source": [ "### broadcasting" ] }, { "cell_type": "markdown", "metadata": { "id": "EygGAMWqL9i7" }, "source": [ "Broadcasting is a powerful mechanism that allows numpy to work with arrays of different shapes when performing arithmetic operations. Frequently we have a smaller array and a larger array, and we want to use the smaller array multiple times to perform some operation on the larger array." ] }, { "cell_type": "markdown", "metadata": { "id": "EygGAMWqL9i7" }, "source": [ "**Example**: add a constant vector to each row of a matrix" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 86 }, "id": "WEEvkV1ZL9i7", "outputId": "3896d03c-3ece-4aa8-f675-aef3a220574d", "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 2 2 4]\n", " [ 5 5 7]\n", " [ 8 8 10]\n", " [11 11 13]]\n" ] } ], "source": [ "# => add vector v to each row of the matrix x, storing the result in the matrix y\n", "x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])\n", "v = np.array([1, 0, 1])\n", "y = np.empty_like(x) # Create an empty matrix with the same shape as x\n", "\n", "# Add the vector v to each row of the matrix x with an explicit loop\n", "for i in range(4):\n", " y[i, :] = x[i, :] + v\n", "\n", "print(y)" ] }, { "cell_type": "markdown", "metadata": { "id": "2OlXXupEL9i-", "slideshow": { "slide_type": "subslide" } }, "source": [ "This works; however when the matrix `x` is very large, computing an explicit loop in Python could be slow. Note that adding the vector v to each row of the matrix `x` is equivalent to forming a matrix `vv` by stacking multiple copies of `v` vertically, then performing elementwise summation of `x` and `vv`. We could implement this approach like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 86 }, "id": "vS7UwAQQL9i-", "outputId": "8621e502-c25d-4a18-c973-886dbfd1df36" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 0 1]\n", " [1 0 1]\n", " [1 0 1]\n", " [1 0 1]]\n" ] } ], "source": [ "vv = np.tile(v, (4, 1)) # Stack 4 copies of v on top of each other\n", "print(vv) # Prints \"[[1 0 1]\n", " # [1 0 1]\n", " # [1 0 1]\n", " # [1 0 1]]\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 86 }, "id": "N0hJphSIL9jA", "outputId": "def6a757-170c-43bf-8728-732dfb133273" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 2 2 4]\n", " [ 5 5 7]\n", " [ 8 8 10]\n", " [11 11 13]]\n" ] } ], "source": [ "y = x + vv # Add x and vv elementwise\n", "print(y)" ] }, { "cell_type": "markdown", "metadata": { "id": "zHos6RJnL9jB", "slideshow": { "slide_type": "skip" } }, "source": [ "Numpy broadcasting allows us to perform this computation without actually creating multiple copies of v. Consider this version, using broadcasting:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 86 }, "id": "vnYFb-gYL9jC", "outputId": "df3bea8a-ad72-4a83-90bb-306b55c6fb93", "slideshow": { "slide_type": "skip" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 2 2 4]\n", " [ 5 5 7]\n", " [ 8 8 10]\n", " [11 11 13]]\n" ] } ], "source": [ "import numpy as np\n", "\n", "# We will add the vector v to each row of the matrix x,\n", "# storing the result in the matrix y\n", "x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])\n", "v = np.array([1, 0, 1])\n", "y = x + v # Add v to each row of x using broadcasting\n", "print(y)" ] }, { "cell_type": "markdown", "metadata": { "id": "08YyIURKL9jH", "slideshow": { "slide_type": "skip" } }, "source": [ "The line `y = x + v` works even though `x` has shape `(4, 3)` and `v` has shape `(3,)` due to broadcasting; this line works as if v actually had shape `(4, 3)`, where each row was a copy of `v`, and the sum was performed elementwise.\n", "\n", "Broadcasting two arrays together follows these rules:\n", "\n", "1. If the arrays do not have the same rank, prepend the shape of the lower rank array with 1s until both shapes have the same length.\n", "2. The two arrays are said to be compatible in a dimension if they have the same size in the dimension, or if one of the arrays has size 1 in that dimension.\n", "3. The arrays can be broadcast together if they are compatible in all dimensions.\n", "4. After broadcasting, each array behaves as if it had shape equal to the elementwise maximum of shapes of the two input arrays.\n", "5. In any dimension where one array had size 1 and the other array had size greater than 1, the first array behaves as if it were copied along that dimension\n", "\n", "If this explanation does not make sense, try reading the explanation from the [documentation](http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html) or this [explanation](http://wiki.scipy.org/EricsBroadcastingDoc).\n", "\n", "Functions that support broadcasting are known as universal functions. You can find the list of all universal functions in the [documentation](http://docs.scipy.org/doc/numpy/reference/ufuncs.html#available-ufuncs).\n", "\n", "Here are some applications of broadcasting:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 69 }, "id": "EmQnwoM9L9jH", "outputId": "f59e181e-e2d4-416c-d094-c4d003ce8509", "slideshow": { "slide_type": "skip" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 4 5]\n", " [ 8 10]\n", " [12 15]]\n" ] } ], "source": [ "# Compute outer product of vectors\n", "v = np.array([1,2,3]) # v has shape (3,)\n", "w = np.array([4,5]) # w has shape (2,)\n", "# To compute an outer product, we first reshape v to be a column\n", "# vector of shape (3, 1); we can then broadcast it against w to yield\n", "# an output of shape (3, 2), which is the outer product of v and w:\n", "\n", "print(np.reshape(v, (3, 1)) * w)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "PgotmpcnL9jK", "outputId": "567763d3-073a-4e3c-9ebe-6c7d2b6d3446", "slideshow": { "slide_type": "skip" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[2 4 6]\n", " [5 7 9]]\n" ] } ], "source": [ "# Add a vector to each row of a matrix\n", "x = np.array([[1,2,3], [4,5,6]])\n", "# x has shape (2, 3) and v has shape (3,) so they broadcast to (2, 3),\n", "# giving the following matrix:\n", "\n", "print(x + v)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "T5hKS1QaL9jK", "outputId": "5f14ac5c-7a21-4216-e91d-cfce5720a804", "slideshow": { "slide_type": "skip" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 5 6 7]\n", " [ 9 10 11]]\n" ] } ], "source": [ "# Add a vector to each column of a matrix\n", "# x has shape (2, 3) and w has shape (2,).\n", "# If we transpose x then it has shape (3, 2) and can be broadcast\n", "# against w to yield a result of shape (3, 2); transposing this result\n", "# yields the final result of shape (2, 3) which is the matrix x with\n", "# the vector w added to each column. Gives the following matrix:\n", "\n", "print((x.T + w).T)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "JDUrZUl6L9jN", "outputId": "53e99a89-c599-406d-9fe3-7aa35ae5fb90", "slideshow": { "slide_type": "skip" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 5 6 7]\n", " [ 9 10 11]]\n" ] } ], "source": [ "# Another solution is to reshape w to be a row vector of shape (2, 1);\n", "# we can then broadcast it directly against x to produce the same\n", "# output.\n", "print(x + np.reshape(w, (2, 1)))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "VzrEo4KGL9jP", "outputId": "53c9d4cc-32d5-46b0-d090-53c7db57fb32", "slideshow": { "slide_type": "skip" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 2 4 6]\n", " [ 8 10 12]]\n" ] } ], "source": [ "# Multiply a matrix by a constant:\n", "# x has shape (2, 3). Numpy treats scalars as arrays of shape ();\n", "# these can be broadcast together to shape (2, 3), producing the\n", "# following array:\n", "print(x * 2)" ] }, { "cell_type": "markdown", "metadata": { "id": "89e2FXxFL9jQ", "slideshow": { "slide_type": "skip" } }, "source": [ "Broadcasting typically makes your code more concise and faster, so you should strive to use it where possible." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Copies and Views" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When operating and manipulating arrays, their data is sometimes copied into a new array and sometimes not.
\n", "\n", "There are three cases." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### no copy\n", "Simple assignments make no copy of objects or their data." ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 132, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.ones((5, 5), dtype='uint8')\n", "\n", "b = a # no new object is created\n", "b is a # a and b are two names for the same ndarray object" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### shallow copy: view()\n", "Different array objects can share the same data. The `view` method creates a new array object that looks at the same data." ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "False\n", "True\n" ] } ], "source": [ "a = np.ones((5, 5), dtype='uint8')\n", "a_shallow = a.view() # make shallow copy: ``a_deep`` is a 'view' of the data owned by a\n", "\n", "print(a_shallow is a)\n", "print(a_shallow.base is a )" ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "array([[100, 1, 1, 1, 1],\n", " [ 1, 1, 1, 1, 1],\n", " [ 1, 1, 1, 1, 1],\n", " [ 1, 1, 1, 1, 1],\n", " [ 1, 1, 1, 1, 1]], dtype=uint8)" ] }, "execution_count": 137, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a_shallow[0, 0] = 100 # a's data changes\n", "a" ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "(5, 5)" ] }, "execution_count": 133, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a_shallow = a_shallow.reshape((1, 25)) # a's shape doesn't change\n", "a.shape" ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "array([[ 1, 10, 10, 1, 1],\n", " [ 1, 10, 10, 1, 1],\n", " [ 1, 10, 10, 1, 1],\n", " [ 1, 10, 10, 1, 1],\n", " [ 1, 10, 10, 1, 1]], dtype=uint8)" ] }, "execution_count": 134, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Slicing an array returns a view of it:\n", "\n", "s = a[:, 1:3]\n", "s[:] = 10 # s[:] is a view of s. Note the difference between s = 10 and s[:] = 10\n", "a" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### deep copy: copy()\n", "The copy method makes a complete copy of the array and its data." ] }, { "cell_type": "code", "execution_count": 138, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "False\n", "False\n" ] }, { "data": { "text/plain": [ "array([[1, 1, 1, 1, 1],\n", " [1, 1, 1, 1, 1],\n", " [1, 1, 1, 1, 1],\n", " [1, 1, 1, 1, 1],\n", " [1, 1, 1, 1, 1]], dtype=uint8)" ] }, "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.ones((5, 5), dtype='uint8')\n", "a_deep = a.copy() # a new array ``a_deep`` with new data is created\n", "\n", "print(a_deep is a)\n", "print(a_deep.base is a) # a_deep doesn't share anything with a\n", "\n", "a_deep[0, 0] = 9999 # changing the deep copy does not change the original variable\n", "a" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "del a # the memory of ``a`` can be released" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Printing\n", "When you print an array, NumPy displays it in a similar way to nested lists. \n", "(In the case of large arrays, it prints the first and last elements only.)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0 1 2 3 4 5]\n" ] } ], "source": [ "a = np.arange(6) # 1d array\n", "print(a)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 0 1 2]\n", " [ 3 4 5]\n", " [ 6 7 8]\n", " [ 9 10 11]]\n" ] } ], "source": [ "b = np.arange(12).reshape(4, 3) # 2d array (12 elements, arranged as 4 rows, 3 columns)\n", "print(b)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[[ 0 1 2 3]\n", " [ 4 5 6 7]\n", " [ 8 9 10 11]]\n", "\n", " [[12 13 14 15]\n", " [16 17 18 19]\n", " [20 21 22 23]]]\n" ] } ], "source": [ "c = np.arange(24).reshape(2, 3, 4) # 3d array (24 elements, arranged as 4 matrices of 2 rows and 3 columns)\n", "print(c)" ] } ], "metadata": { "celltoolbar": "Slideshow", "colab": { "collapsed_sections": [], "name": "colab-tutorial.ipynb", "provenance": [] }, "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.8.5" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": true, "toc_position": {}, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 1 }