📄 C1_W1_Lab_1_introduction_to_numpy_arrays.ipynb
/home/palash/git/misc/maths/Mathematics for Machine Learning and Data Science by DeepLearning.ai/Linear Algebra/C1_W1_Lab_1_introduction_to_numpy_arrays.ipynb
Language: ipynb • Lines: 838
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "existing-japanese",
   "metadata": {},
   "source": [
    "# Introduction to Python Matrices and NumPy #\n",
    "\n",
    "Welcome to your first notebook of this specialization! In this notebook, you will use NumPy to create 2-D arrays and easily compute mathematical operations. NumPy (Numerical Python) is an open-source package that is widely used in science and engineering. Feel free to skip this notebook if you are already fluent with NumPy.\n",
    "\n",
    "**After this assignment you will be able to:**\n",
    "- Use Jupyter Notebook and its features. \n",
    "- Use NumPy functions to create arrays and NumPy array operations. \n",
    "- Use indexing and slicing for 2-D arrays.\n",
    "- Find the shape of an array, reshape it and stack it horizontally and vertically.\n",
    "\n",
    "**Instructions**\n",
    "- You will be using Python 3.\n",
    "- Follow along the cells using `Shift`+`Enter`. Alternatively, you can press `Run` in the menu. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "indie-lithuania",
   "metadata": {},
   "source": [
    "## Table of Contents ##\n",
    "[About Jupyter Notebooks](#0)\n",
    "- [1 - Basics of NumPy](#1)\n",
    "    - [1.1 - Packages](#1-1)\n",
    "    - [1.2 - Advantages of using NumPy arrays](#1-2)\n",
    "    - [1.3 - How to create NumPy arrays](#1-3)\n",
    "    - [1.4 - More on NumPy arrays](#1-4)\n",
    "- [2 - Multidimensional arrays](#2)\n",
    "    - [2.1 - Finding size, shape and dimension](#2-1)\n",
    "- [3 - Array math operations](#3)\n",
    "    - [3.1 - Multiplying vector with a scalar (broadcasting)](#3-1)\n",
    "- [4 - Indexing and slicing](#4)\n",
    "    - [4.1 - Indexing](#4-1)\n",
    "    - [4.2 - Slicing](#4-2)\n",
    "- [5 - Stacking](#5)\n",
    "- [6 - Exercises](#6)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "welsh-generic",
   "metadata": {},
   "source": [
    "<a name='0'></a>\n",
    "## About Jupyter Notebooks ##\n",
    "\n",
    "\n",
    "Jupyter Notebooks are interactive coding journals that integrate live code, explanatory text, equations, visualizations and other multimedia resources, all in a single document. As a first exercise, run the test snippet below and the print statement cell for \"Hello World\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "expired-interface",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Run the \"Hello World\" in the cell below to print \"Hello World\". \n",
    "test = \"Hello World\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "instrumental-macro",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "print(test)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "whole-fundamental",
   "metadata": {},
   "source": [
    "<a name='1'></a>\n",
    "# 1 - Basics of NumPy #\n",
    "\n",
    "NumPy is the main package for scientific computing in Python. It performs a wide variety of advanced mathematical operations with high efficiency. In this practice lab you will learn several key NumPy functions that will help you in future assignments, such as creating arrays, slicing, indexing, reshaping and stacking."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "neutral-concern",
   "metadata": {},
   "source": [
    "<a name='1-1'></a>\n",
    "## 1.1 - Packages ##"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "warming-hypothesis",
   "metadata": {},
   "source": [
    "Before you get started, you have to import NumPy to load its functions. As you may notice, even though there is no expected output, when you run this cell, the Jupyter Notebook imports the package (often referred to as the library) and its functions. Try it for yourself and run the following cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "under-hardware",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "entire-entertainment",
   "metadata": {},
   "source": [
    "<a name='1-2'></a>\n",
    "## 1.2 - Advantages of using NumPy arrays ##"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "skilled-action",
   "metadata": {},
   "source": [
    "Arrays are one of the core data structures of the NumPy library, essential for organizing your data. You can think of them as a grid of values, all of the same type. If you have used Python lists before, you may remember that they are convenient, as you can store different data types. However, Python lists are limited in functions and take up more space and time to process than NumPy arrays.\n",
    "\n",
    "NumPy provides an array object that is much faster and more compact than Python lists. Through its extensive API integration, the library offers many built-in functions that make computing much easier with only a few lines of code. This can be a huge advantage when performing math operations on large datasets. \n",
    "\n",
    "The array object in NumPy is called `ndarray` meaning 'n-dimensional array'. To begin with, you will use one of the most common array types: the one-dimensional array ('1-D'). A 1-D array represents a standard list of values entirely in one dimension. Remember that in NumPy, all of the elements within the array are of the same type."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "rural-ceiling",
   "metadata": {},
   "outputs": [],
   "source": [
    "one_dimensional_arr = np.array([10, 12])\n",
    "print(one_dimensional_arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "planned-billy",
   "metadata": {},
   "source": [
    "<a name='1-3'></a>\n",
    "## 1.3 - How to create NumPy arrays ##"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dynamic-benjamin",
   "metadata": {},
   "source": [
    "There are several ways to create an array in NumPy. You can create a 1-D array by simply using the function `array()` which takes in a list of values as an argument and returns a 1-D array."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "single-amplifier",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create and print a NumPy array 'a' containing the elements 1, 2, 3.\n",
    "a = np.array([1, 2, 3])\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "flexible-palace",
   "metadata": {},
   "source": [
    "Another way to implement an array is using `np.arange()`. This function will return an array of evenly spaced values within a given interval. To learn more about the arguments that this function takes, there is a powerful feature in Jupyter Notebook that allows you to access the documentation of any function by simply pressing `shift+tab` on your keyboard when clicking on the function. Give it a try for the built-in documentation of `np.arange()`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aggressive-beginning",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create an array with 3 integers, starting from the default integer 0.\n",
    "b = np.arange(3)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "mineral-assets",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create an array that starts from the integer 1, ends at 20, incremented by 3.\n",
    "c = np.arange(1, 20, 3)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "little-entrance",
   "metadata": {},
   "source": [
    "What if you wanted to create an array with five evenly spaced values in the interval from 0 to 100? As you may notice, you have 3 parameters that a function must take. One paremeter is the starting number, in  this case 0, the final number 100 and the number of elements in the array, in this case, 5. NumPy has a function that allows you to do specifically this by using `np.linspace()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "hispanic-offset",
   "metadata": {},
   "outputs": [],
   "source": [
    "lin_spaced_arr = np.linspace(0, 100, 5)\n",
    "print(lin_spaced_arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "victorian-norway",
   "metadata": {},
   "source": [
    "Did you notice that the output of the function is presented in the float value form (e.g. \"... 25. 50. ...\")? The reason is that the default type for values in the NumPy function `np.linspace` is a floating point (`np.float64`). You can easily specify your data type using `dtype`. If you access the built-in documentation of the functions, you may notice that most functions take in an optional parameter `dtype`. In addition to float, NumPy has several other data types such as `int`, and `char`. \n",
    "\n",
    "To change the type to integers, you need to set the dtype to `int`. You can do so, even in the previous functions. Feel free to try it out and modify the cells to output your desired data type. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "black-worker",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "lin_spaced_arr_int = np.linspace(0, 100, 5, dtype=int)\n",
    "print(lin_spaced_arr_int)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "specialized-intellectual",
   "metadata": {},
   "outputs": [],
   "source": [
    "c_int = np.arange(1, 20, 3, dtype=int)\n",
    "print(c_int)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "irish-greeting",
   "metadata": {},
   "outputs": [],
   "source": [
    "b_float = np.arange(3, dtype=float)\n",
    "print(b_float)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "visible-logic",
   "metadata": {},
   "outputs": [],
   "source": [
    "char_arr = np.array(['Welcome to Math for ML!'])\n",
    "print(char_arr)\n",
    "print(char_arr.dtype) # Prints the data type of the array"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "korean-identity",
   "metadata": {},
   "source": [
    "Did you notice that the output of the data type of the `char_arr` array is `<U23`? \n",
    "This means that the string (`'Welcome to Math for ML!'`) is a 23-character (23) unicode string (`U`) on a little-endian architecture (`<`). You can learn more about data types [here](https://numpy.org/doc/stable/user/basics.types.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ready-backup",
   "metadata": {},
   "source": [
    "<a name='1-4'></a>\n",
    "## 1.4 - More on NumPy arrays ##\n",
    "\n",
    "One of the advantages of using NumPy is that you can easily create arrays with built-in functions such as: \n",
    "- `np.ones()` - Returns a new array setting values to one.\n",
    "- `np.zeros()` - Returns a new array setting values to zero.\n",
    "- `np.empty()` - Returns a new uninitialized array. \n",
    "- `np.random.rand()` - Returns a new array with values chosen at random."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "prime-gamma",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Return a new array of shape 3, filled with ones. \n",
    "ones_arr = np.ones(3)\n",
    "print(ones_arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "altered-timer",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Return a new array of shape 3, filled with zeroes.\n",
    "zeros_arr = np.zeros(3)\n",
    "print(zeros_arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "loaded-kansas",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Return a new array of shape 3, without initializing entries.\n",
    "empt_arr = np.empty(3)\n",
    "print(empt_arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "color-steps",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Return a new array of shape 3 with random numbers between 0 and 1.\n",
    "rand_arr = np.random.rand(3)\n",
    "print(rand_arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "angry-engagement",
   "metadata": {},
   "source": [
    "<a name='2'></a>\n",
    "# 2 - Multidimensional Arrays #\n",
    "With NumPy you can also create arrays with more than one dimension. In the above examples, you dealt with 1-D arrays, where you can access their elements using a single index. A multidimensional array has more than one column. Think of a multidimensional array as an excel sheet where each row/column represents a dimension."
   ]
  },
  {
   "attachments": {
    "0_Vh-pKXTJsdL-9FT0.png": {
     "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmkAAADDCAIAAABWCxEkAAALKUlEQVR42uzdX2id9f3A8SchJuovSlWsR/jxK5ZEfr/SC/VCMLVgoTdpbuqPzQgDxQ6jTCSh0Imj7CQMJsOLpoVeJGy4KQO1F+1N05u6DkbiH9AxkQ5MWFuxpM6qzWbXWa1ntNHmOen5m/N8n5w/r9fV8DTP9+Scz5738zznTzpyuVwEAFSs3UMAANoJANoJANoJANoJANoJAGgnAGgnAGgnAGgnAGgnAGgnAKCdAKCdAKCdAKCdAKCdAIB2AoB2AoB2AoB2AoB2hnfkqbbvbdo7V93Pzu3d1FZU1Vuj5cWG8XtPHTGTJKrQiFQ7Gcastdt5ZU+1bXLlG5g9PlP8xpmR3pXs+2jh/VmBYZzcVt0QmUnK7PJ6R2YKT4Yx084Kd1W1ZLNik9schlHbviia3JbofshMYsy0c2XlfPy7Q6+hqdnxviQ22Tc+m4uZGso7DHvcCFHdEC2by8lDR8wkUfL7qryx+ODDOWOmnWUNTeVyE/1htt0/kTdE5Uaoolcgrv6jK7cs/cjiPyx9a7klYi+0XXvouXSjw8hQLg9Mbnq45+p/6Bn+XV49V7JXM5OUGbPL/yk+FMdn7frqYczqtp09w9O5cN1cGqLYucPM64fnis9OsVcgij1hs3s3Lf3INfO+/NYKlujd0Ff0DOfIoavXtjfe3WP/k9qU3r3RTJrJ8FfhPvxg6RxyQ68xq4sxyzWA2JO87OJDebHDq8I/G7/wdvk0t/Q2ylwQKXZ1efGflLi1kiWK39PYTxf5FQg9mVU89maSFe7DqnkwjVlYPt8ZP3UoctFt8bCn6GsGRQ/aln4k/xJMgVsrWaJn4JHCh1+xQ6+h7f2O0tM7HTj8+kzs6dzVbybNZBLyPwi19IbJoakkL8UZs+a8Zpue2AWBktePlw1B//ahMj82NFVwbgreWtESRSbIbmqVynn17WxXHvrdwz1m0kyGlPRDacxau50FPwEc5FXjZZ+KL/PxmaGpUkeIRW4ts0TP8O6hayYo9lqI3VSapwbx12jyn08zaSZDmNyWN0bGbFXHrEnPO6t5L1rpj+3FhjToZ00rXCJ2PPbdBMWuG9pNpXjAFnuq+sZnK7iSZiap1JX32hZ+VX1mpLfkJzyNmXbWpIr3osXfwlb4jVr51+YCjU+lSyyfILup1Qhn7JSzb3x2uqKrtWaSlekZns7LZ8kPEhsz7axisK59C9R0xa89xd/uUXjs4v8i/lauqaHk5qeKJfInyG4qbXnXaouE00yayYT3crHX+66+sceYreqYtdsRLj11hd/tsXRhI8F3UtawRP+uq8egk9uW7r7dVCrzEru2VPEZp5kkuacjoQ8yGjPtrGU/2Jb3olXZpy72juy8nWiCKlki/xjUbioteS9yhgmnmTRkm6799pxlD3vNj6sxS0RjfOq8kMo+Clvh5YVSGyu7iaXPJsXudYEtFr+1iiUKPzg+fp7uJ9QreprMJMnv9yr+ehhj5rsRiqj920NjT06p90nmfZtkGFUucc3hlyP8JmMmW1SZb3lM+HKHMWvNa7aVXfQv+fHfxUOW8sPYP7H8eOfy0CX0511WssSyCbKbaihmklIPeuGTsSuDUU04jVlYbblczrw2oPgHJZL9pi4wkxiz5j3vbO35ib+12xE+ZhJjpp2Uc+TFkeDvHgcziTHTzmaan6WvQO57ZMDfRsRMYsy0E7spzCTU85h5rxAAOO8EAO0EAO0EAO0EAO0EALQTALQTALQTALQTALQTALQTANBOANBOANBOAGgsHR4CgFU3f3bhmV++Nn92wUORpjdf3hW2nVu3bn3jjTc80JTV1dV18uTJTCaT8rp79uzZuXOnx79FPPvss/v27RPOCl13w81t7e0Xz58LsfH2js6u7tsunJtvrLud0nnnpUuXjh079tBDD6Vzt7Zs2ZLNZi3XoL9a+uGMomhhYSGbzY6Ojqa/9OKilk556eYL593r1u5//tHuG7uS3f67s58deff0T3+wsbMj+RfpPjl3YfzQ8ZHtG+5Yc0ND3O2LX3/z3N5Db71/otYjBgewAMLZUuG8qebHWTsBhLO1wrn/+Ue1E0A4hbOKcPauW6udAMIpnOmFUzsBhFM4tRNAOIUzZDi1E0A4hVM7AYRTOEOG03fyAaQXzvPXrb31rvVno2jwV38KtNDDv/hDuN9ix57pBrrb5z9dWHPn+n0/2Zx4OLUTIM0zzq79z/U740ztjLP9+nUhwumaLUBq4XSpNtVw1v4FCNoJIJytFc5AZ5zaCSCcwqmdAMIpnIHDqZ0Awimc2gkgnMKpnQDCKZz1E07tBBBO4dROAOEUTu0EEE7hrJ9waieAcAqndgIIp3BqJ4BwCmf9hNPfUQGoNZw33dj14+19H576e7Lb/+0fP+7saH/kgTs/mD2d+J3/4vzXB9858/D9mdPzZ0/PN8bdfuXw23USziiK2nK5XCX/rre3d25uzv9bKKuzs/PUqVOZTCbldZ9++umJiQmPf4sYHBx89dVXV/c+/P/OyfmzC+G2f+td935+8i9R7tvEt9ze0dl9+7ovPz317TcXG+huR1FUbTgHskcPj20teNMDj70YRdGbL+8Ke9754IMPHjhw4J577klnKJ944onh4WHLNeivln44oyjKZDLZbHZ0dDT9pRcXtXTKS9fDeWcURff93/8E2v7Jf0f3/e9/h9jyxxdvyVy30LEm00B3e/bUJ//811e7n+xf9TPO6tr50ksvpXm3LOdXg4aw//nBQFseyB4NtPGB7NGJn/2wse72My+89t5fP+q+8fo6ed69VwgAtBMAtBMAtBMAtBMAtBMA0E4A0E4A0E4A0E4A0E4AaFX+BhkArXfi2NHZ1X2b804AqMgn5y50377uqy8/004AqCic44eO1/jnS7UTgNYK58j2DTX+3W/tBKC1wnnHmhtq3JR2AiCc2gkAwcKpnQAIp3YCQMhwaicAzSxEOLUTgKY944yiKEQ4tROA5gzn+KHjURSFCKfvs6V5nDlzZmJiYmxsbLXugKXTNDg4aOYpHc6R7Rt27JkOtIR20iQymUw2mx0dHU1/6cVFLZ3y0lA6nIHOOBe5ZguAcGonAMKpnQBQJ+HUTgCEUzsBEE7tBID6Cad2AiCc2gmAcGonANRPOLUTAOHUTgCEMzDfZwtAY/ji/NcH6iCczjsBaAztHZ0H3zlTD+F03gmwcjff2TuQPRpu++E23oB3+7butdc/fH+mHsKpnQAr94/52Tdf3hWuQIfHtjbWlsNt/JkXXnvvxEe3/Nd99XISbPoBQDsBQDsBQDsBQDsBQDsBAO0EAO0EAO0EgDrme4VolsPA9vZsNjs2NrZad8DSadqxY4eZRzuhVj+/wuMApHGw7iEAoKX8+uC0dgJAFeH8zcGZKIp2P9mvnQBQRTgHNm/UTgBII5zaCYBwaicAhAyndgIgnNoJACHDqZ0ANLMQ4dROAJr2jHPxfyQeTu0EoDnDGeiMUzsBaOZwRlEUIpzaCUDTnnGuz9wUaBV/RwWAJgznwOaNA5tDLeS8E4AmDGfQtbQTAOHUTgCEUzsBoE7CqZ0ACKd2AiCc2gkA9RNO7QRAOLUTAOHUTgCon3D6Tj4AGsYrh99+6/0Tqx5O550ANIw6CWcURW25XM7zAVCtBx57Mej2b73r3s9P/Lmxthx643USTuedAHXq0sUL7nZ9htN5J0DL+f2xv/1oy3qPg3YCQHpcswUA7QQA7QQA7QQA7QQA7QQAtBMAtBMAtBMAtBMAtBMAtBMA0E4A0E4A0E4A0E4A0E4AQDsBQDsBQDsBQDsBQDsBQDsBAO0EgKT8JwAA//+cN2wwahmtzAAAAABJRU5ErkJggg=="
    }
   },
   "cell_type": "markdown",
   "id": "editorial-adobe",
   "metadata": {},
   "source": [
    "![0_Vh-pKXTJsdL-9FT0.png](attachment:0_Vh-pKXTJsdL-9FT0.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "turned-nevada",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a 2 dimensional array (2-D)\n",
    "two_dim_arr = np.array([[1,2,3], [4,5,6]])\n",
    "print(two_dim_arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "greater-rescue",
   "metadata": {},
   "source": [
    "An alternative way to create a multidimensional array is by reshaping the initial 1-D array. Using `np.reshape()` you can rearrange elements of the previous array into a new shape. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "assisted-traveler",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1-D array \n",
    "one_dim_arr = np.array([1, 2, 3, 4, 5, 6])\n",
    "\n",
    "# Multidimensional array using reshape()\n",
    "multi_dim_arr = np.reshape(\n",
    "                one_dim_arr, # the array to be reshaped\n",
    "               (2,3) # dimensions of the new array\n",
    "              )\n",
    "# Print the new 2-D array with two rows and three columns\n",
    "print(multi_dim_arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "threaded-continent",
   "metadata": {},
   "source": [
    "<!-- exercise: can you create an array with three rows and 4 columns using np.ones . Whats the dimension, size and shape of the array? -->"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "loving-accordance",
   "metadata": {},
   "source": [
    "<a name='2-1'></a>\n",
    "## 2.1 - Finding size, shape and dimension. ##"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "suitable-russian",
   "metadata": {},
   "source": [
    "In future assignments, you will need to know how to find the size, dimension and shape of an array. These are all atrributes of a `ndarray` and can be accessed as follows:\n",
    "- `ndarray.ndim` - Stores the number dimensions of the array. \n",
    "- `ndarray.shape` - Stores the shape of the array. Each number in the tuple denotes the lengths of each corresponding dimension.\n",
    "- `ndarray.size` - Stores the number of elements in the array.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "caring-julian",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Dimension of the 2-D array multi_dim_arr\n",
    "multi_dim_arr.ndim"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "canadian-ribbon",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Shape of the 2-D array multi_dim_arr\n",
    "# Returns shape of 2 rows and 3 columns\n",
    "multi_dim_arr.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "infectious-peeing",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Size of the array multi_dim_arr\n",
    "# Returns total number of elements\n",
    "multi_dim_arr.size"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "mighty-berry",
   "metadata": {},
   "source": [
    "<a name='3'></a>\n",
    "# 3 - Array math operations #\n",
    "In this section, you will see that NumPy allows you to quickly perform elementwise addition, substraction, multiplication and division for both 1-D and multidimensional arrays. The operations are performed using the math symbol for each '+', '-' and '*'. Recall that addition of Python lists works completely differently as it would append the lists, thus making a longer list, in addition, subtraction and multiplication of Python lists do not work. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "exposed-shower",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr_1 = np.array([2, 4, 6])\n",
    "arr_2 = np.array([1, 3, 5])\n",
    "\n",
    "# Adding two 1-D arrays\n",
    "addition = arr_1 + arr_2\n",
    "print(addition)\n",
    "\n",
    "# Subtracting two 1-D arrays\n",
    "subtraction = arr_1 - arr_2\n",
    "print(subtraction)\n",
    "\n",
    "# Multiplying two 1-D arrays elementwise\n",
    "multiplication = arr_1 * arr_2\n",
    "print(multiplication)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "latest-slovenia",
   "metadata": {},
   "source": [
    "<a name='3-1'></a>\n",
    "## 3.1 - Multiplying vector with a scalar (broadcasting) ##\n",
    "Suppose you need to convert miles to kilometers. To do so, you can use the NumPy array functions that you've learned so far. You can do this by carrying out an operation between an array (miles) and a single number (the conversion rate which is a scalar). Since, 1 mile = 1.6 km, NumPy computes each multiplication within each cell. \n",
    "\n",
    "This concept is called **broadcasting**, which allows you to perform operations specifically on arrays of different shapes. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eligible-grant",
   "metadata": {},
   "outputs": [],
   "source": [
    "vector = np.array([1, 2])\n",
    "vector * 1.6"
   ]
  },
  {
   "attachments": {
    "Unknown-2.png": {
     "image/png": "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"
    }
   },
   "cell_type": "markdown",
   "id": "seasonal-milwaukee",
   "metadata": {},
   "source": [
    "![Unknown-2.png](attachment:Unknown-2.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "plain-thumbnail",
   "metadata": {},
   "source": [
    "<a name='4'></a>\n",
    "# 4 - Indexing and slicing #\n",
    "Indexing is very useful as it allows you to select specific elements from an array. It also lets you select entire rows/columns or planes as you'll see in future assignments for multidimensional arrays. \n",
    "\n",
    "## 4.1 - Indexing ##\n",
    "Let us select specific elements from the arrays as given. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "sought-amazon",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Select the third element of the array. Remember the counting starts from 0.\n",
    "a = ([1, 2, 3, 4, 5])\n",
    "print(a[2])\n",
    "\n",
    "# Select the first element of the array.\n",
    "print(a[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "australian-pharmacy",
   "metadata": {},
   "source": [
    "For multidimensional arrays of shape `n`, to index a specific element, you must input `n` indices, one for each dimension."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "characteristic-positive",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Indexing on a 2-D array\n",
    "two_dim = np.array(([1, 2, 3],\n",
    "          [4, 5, 6], \n",
    "          [7, 8, 9]))\n",
    "\n",
    "# Select element number 8 from the 2-D array using indices i, j.\n",
    "print(two_dim[2][1])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "southwest-warrant",
   "metadata": {},
   "source": [
    "<a name='4-2'></a>\n",
    "## 4.2 - Slicing ##\n",
    "Slicing gives you a sublist of elements that you specify from the array. The slice notation specifies a start and end value, and copies the list from start up to but not including the end (end-exclusive). \n",
    "\n",
    "The syntax is:\n",
    "\n",
    "`array[start:end:step]`\n",
    "\n",
    "If no value is passed to start, it is assumed `start = 0`, if no value is passed to end, it is assumed that `end = length of array - 1` and if no value is passed to step, it is assumed `step = 1`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "moral-police",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Slice the array a to get the array [2,3,4]\n",
    "sliced_arr = a[1:4]\n",
    "print(sliced_arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "181bce55",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Slice the array a to get the array [1,2,3]\n",
    "sliced_arr = a[:3]\n",
    "print(sliced_arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63bfcd06",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Slice the array a to get the array [3,4,5]\n",
    "sliced_arr = a[2:]\n",
    "print(sliced_arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0469a119",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Slice the array a to get the array [1,3,5]\n",
    "sliced_arr = a[::2]\n",
    "print(sliced_arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2ef18ad",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Note that a == a[:] == a[::]\n",
    "print(a == a[:] == a[::])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "norwegian-fence",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Slice the two_dim array to get the first two rows\n",
    "sliced_arr_1 = two_dim[0:2]\n",
    "sliced_arr_1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "structured-lease",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Similarily, slice the two_dim array to get the last two rows\n",
    "sliced_two_dim_rows = two_dim[1:3]\n",
    "print(sliced_two_dim_rows)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fourth-center",
   "metadata": {},
   "outputs": [],
   "source": [
    "sliced_two_dim_cols = two_dim[:,1]\n",
    "print(sliced_two_dim_cols)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "appointed-worry",
   "metadata": {},
   "source": [
    "<a name='5'></a>\n",
    "# 5 - Stacking #\n",
    "Finally, stacking is a feature of NumPy that leads to increased customization of arrays. It means to join two or more arrays, either horizontally or vertically, meaning that it is done along a new axis. \n",
    "\n",
    "- `np.vstack()` - stacks vertically\n",
    "- `np.hstack()` - stacks horizontally\n",
    "- `np.hsplit()` - splits an array into several smaller arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "casual-sacramento",
   "metadata": {},
   "outputs": [],
   "source": [
    "a1 = np.array([[1,1], \n",
    "               [2,2]])\n",
    "a2 = np.array([[3,3],\n",
    "              [4,4]])\n",
    "print(f'a1:\\n{a1}')\n",
    "print(f'a2:\\n{a2}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bizarre-welsh",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Stack the arrays vertically\n",
    "vert_stack = np.vstack((a1, a2))\n",
    "print(vert_stack)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "applicable-tampa",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Stack the arrays horizontally\n",
    "horz_stack = np.hstack((a1, a2))\n",
    "print(horz_stack)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "wanted-society",
   "metadata": {},
   "source": [
    "<a name='6'></a>\n",
    "## Exercises ##\n",
    "Well done! Now, from what you learned today, try to answer the following question. \n",
    "\n",
    "### Question 1 ###\n",
    "Is there a difference between `np.zeros()` and `np.empty()`? Select one of the options given:\n",
    "- A. No difference, they both output arrays of zeros. \n",
    "- B. `np.zeros()` is not initialized, but gives an output of zeros. \n",
    "- C. `np.zeros()` is faster to execute than `np.empty()`.\n",
    "- D. `np.empty()` outputs an uninitialized array, but `np.zeros()` outputs an initialized array of value zero."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fitted-architect",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Run this cell to select your answer\n",
    "import quiz\n",
    "import ipywidgets as widgets\n",
    "q1 = quiz.mcq(quiz.question1, quiz.solution1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "romance-mining",
   "metadata": {},
   "source": [
    "Congratulations on finishing your first notebook of this specialization!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  },
  "vscode": {
   "interpreter": {
    "hash": "56d44d6a8424451b5ce45d1ae0b0b7865dc60710e7f74571dd51dd80d7829ee9"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}