{ "cells": [ { "cell_type": "raw", "metadata": {}, "source": [ "---\n", "reference-location: margin\n", "citation-location: margin\n", "execute:\n", " freeze: auto\n", " cache: true\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# The `numpy` module\n", "\n", "[Jupyter Notebook](https://lancejnelson.github.io/PH135/jupyter/numpy.ipynb)\n", "\n", "\n", "Numpy (short for \"numerical python\" and pronounced \"num\"-\"pie\") is a popular Python library that is heavily used in the scientific/mathematical community. So much so that numpy is typically included as part of the standard bundle of libraries that comes with your Python installation. The functions inside numpy will allow you to solve problems with less effort and will produce faster-executing code. \n", "\n", "\n", "## Numpy Arrays\n", "You are already familiar with Python lists but may not have noticed that they are not suitable for mathematical calculations. For example, attempting to multiply a list by a scalar or evaluating a mathematical function like $\\sin()$ on a list will *not* produce a mathematical result or may produce an error. For example, consider the following code.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "myList = [4,5,7]\n", "\n", "newList = 2 * myList\n", "print(newList)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You probably expected `newList` to be `[8,10,14]` but multiplying a list by a number doesn't do that. Instead it repeats the list and concatenates it to itself. To multiply each element of a list by a number you must use a `for` loop." ] }, { "cell_type": "code", "metadata": {}, "source": [ "myList = [4,5,7]\n", "\n", "newList = []\n", "for i in myList:\n", " newList.append(i* 2)\n", "\n", "print(newList)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "but this seems overly cumbersome for such a simple task. Numpy *ndarrays* (short for n-dimensional arrays) or just *arrays* make this task much simpler. Arrays are similar to lists or nested lists except that mathematical operations and `numpy` functions (but not `math` functions) automatically propagate to each element instead of requiring a `for` loop to iterate over it. Because of their power and convenience, arrays are the default object type for any operation performed with NumPy. \n", "\n", "\n", "## Array Creation\n", "\n", "### Type Conversion from List\n", "\n", "You can create an array from a list using numpy's `array` function. The list that is to be converted is the argument to the `array` function. Mathematical operations can then be performed on the array and that operation will propagate through to all of the elements.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "\n", "myArray = array([4,5,7])\n", "\n", "newArray = 2 * myArray \n", "\n", "print(newArray)\n" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nested lists, or lists that contain lists as their elements, can be converted to multi-dimensional arrays using the `array` function." ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "myArray = array([[1,2,3],[4,5,6]])\n", "\n", "print(myArray)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The `arange` and `linspace` Functions\n", "\n", " Numpy has some sequence-generating functions that generate arrays by specifying start, stop, and step size values similar to the way `range` generates a list. The two most common ones are `arange` and `linspace`. The `arange` function behaves very similar to the native Python `range` function with a few notable exceptions:\n", "\n", "\n", "1. `arange` produces an array whereas `range` produces a list.\n", "2. The step size for `arange` does not need to be an integer.\n", "3. `range` produces an iterator and `arange` generates a sequence of values immediately.\n", "\n", "\n", "The arguments to `arange` are similar to `range`\n", "\n", "```\n", "arange(start,stop,step)\n", "```\n", "\n", "The `linspace` function is related to the `arange` function except that instead of specifying the step size of the sequence, the sequence is generated based on the number of equally-spaced points in the given span of numbers. Additionally, `arange` excludes the stop value while `linspace` includes it. The difference between these two functions is subtle and the use of one over the other often comes down to user preference or convenience.\n", "\n", "```\n", "linspace(start,stop,number of points)\n", "```\n", "\n", "Below is an example that shows the usage of `linspace` and `arange`." ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import linspace,arange\n", "\n", "myArray = linspace(0,10,20)\n", "myArray2 = arange(0,10,0.5)\n", "print(myArray)\n", "print(myArray2)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When using `linspace` you may still want to know what the sequence spacing is. You can request that `linspace` provide this information by adding the optional argument `retstep = True` to the argument list. With this addition, linspace not only returns the sequence to you, but also the stepsize." ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import linspace,arange\n", "\n", "myArray,mydx = linspace(0,10,20,retstep= True)\n", "print(mydx)\n", "print(myArray)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Two other useful functions for generating arrays are `zeros` and `ones` which generate arrays populated with exclusively ones or zeros. The functions require shape arguments as a tuple or list to specify the shape of the array.\n", "\n", "\n", "```\n", "zeros((rows,columns))\n", "```\n", "\n", "\n", "If the array to be created is only one dimensional, the argument can be a single number instead of a tuple.\n", "\n", "```\n", "zeros(n)\n", "```\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import zeros,ones\n", "\n", "myArray = zeros([3,4])\n", "myArray2 = ones(5)\n", "print(myArray)\n", "print(myArray2)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Arrays of any constant (not just one or zero) can then be easily generated by performing the needed math on the original array.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import zeros,ones\n", "\n", "myArray = zeros([3,4]) + 5\n", "myArray2 = ones(5) * 12\n", "print(myArray)\n", "print(myArray2)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Arrays from Functions\n", "\n", "A third approach is to generate an array from a function using the `fromfunction` function which generates an array of values using the array indices as the inputs. Ths function requires two arguments: the name of the function being used and the shape of the array being generated.\n", "\n", "```\n", "fromfunction(function, shape)\n", "```\n", "\n", "Let's make a 3 x 3 array where each element is the product of the row and column indices:" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import fromfunction\n", "\n", "def prod(x,y):\n", " return x * y\n", "\n", "myArray = fromfunction(prod,(3,3))\n", "print(myArray)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The table below gives a summary of useful functions for creating numpy arrays. The required arguments are also described.\n", "\n", "| Method | Description|\n", "|--------|------------|\n", "| `linspace(start,stop,n)`| Returns an array of `n` evenly-spaced points begining at `start` and ending at `stop`.|\n", "| `arange(start,stop,dx)`| Returns an array beginning at `start`,ending at `stop` with a step size of `dx`.|\n", "| `empty(dims)`| Returns an empty array with dimensions `dim`.|\n", "| `zeros(dims)`| Returns an array of zeros with dimensions `dim`.|\n", "| `ones(dims)`| Returns an array of ones with dimensions `dim`.|\n", "| `zeros_like(arr)`| Returns an array of zeros with dimensions that match the dimensions of `arr`.|\n", "| `fromfunction(function,dims)`| Returns an array of numbers generated by evaluating `function` on the indices of an array with dimensions `dims`.|\n", "| `copy(arr)`| Creates a copy of array `arr`.|\n", "| `genfromtext(file)`| Reads `file` and loads the text into an array (file must only contain numbers).|\n", "\n", ": Common functions for generating arrays\n", "\n", "## Accessing and Slicing Arrays\n", "\n", "Accessing and slicing arrays can be done in exactly the same way as is done with lists. However, there is some additional functionality for accessing and slicing arrays that do not apply to lists.\n", "\n", "\n", "### One-dimensional Arrays\n", "\n", "Elements from a one-dimensional array can be extracted using square brackets (`[]`) just like we have done with lists." ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "\n", "myArray = array([3,4,7,8])\n", "print(myArray[2])" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Multi-dimensional Arrays\n", "Multi-dimensional array can be indexed in a similar fashion to nested lists, but because we often encounter multi-dimensional arrays there is a shortcut that makes the syntax simpler and more convenient. Let's consider a two-dimensional array as an example. To access the entire second row of the array, provide the row index in square brackets just as with one-dimensional arrays.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "myArray = array([[1,2,3],[4,5,6], [7,8,9]])\n", "\n", "print(myArray[1])" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To access the second element *in the second row*, we can add another set of square brackets with the appropriate index inside, just as we did with nested lists. However, for convenience the second set of square brackets can be omitted and the row and column indices can be placed next to each other and separated by a comma.\n", "\n", "```\n", "array_name[row,column]\n", "```" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "myArray = array([[1,2,3],[4,5,6], [7,8,9]])\n", "\n", "print(myArray[1][1]) # This works, but is a bit hard on the eyes\n", "print(myArray[1,1]) # This works also and is easier to look at." ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Accessing Multiple Elements\n", "\n", "Multiple elements of an array can be accessed using a list for the index instead of a single number.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "\n", "myArray = array([1,2,3,4,5,6,7,8,9,10])\n", "\n", "print(myArray[2]) # Extract element 2\n", "print(myArray[ [3,6,9] ]) # Extract elements 3, 6, and 9." ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This can even be done with multi-dimensional arrays. If the index is a single list, the corresponding rows will be extract. If the corresponding list of columns is added to the index list, individual elements will be extracted.\n", "\n", "```\n", "array_name[[rows]] # Access set of rows\n", "```\n", "\n", "```\n", "array_name[[rows], [columns]] # Access set of elements\n", "```" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "myArray = array([[1,2,3],[4,5,6], [7,8,9]])\n", "\n", "print(myArray[[0,1]]) # Extract rows 0 and 1.\n", "\n", "print(myArray[[1,2,0],[0,2,2]]) # Extract elements (1,0), (2,2), and (0,2) " ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Slicing Arrays\n", "\n", "### Multi-dimensional Arrays\n", "\n", "We’ve already shown you how to slice a list using the : operator. The same can be done with arrays. However, for 2D (and higher) arrays the slicing is more powerful (intuitive). It can be helpful to visualize an array as a matrix, even if it is not being treated that way Mathematically. For example, let’s say that you define the following array:" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "myArray = array([[1,2,3],[4,5,6], [7,8,9]])" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which can be visualized as the following matrix:\n", "\n", "$$\n", "\\begin{pmatrix}\n", "1&2&3\\\\\n", "4&5&6\\\\\n", "7&8&9\\\\\n", "\\end{pmatrix}\n", "$$\n", "\n", "To slice out the following $2$ x $2$ sub matrix:\n", "\n", "\n", "$$\n", "\\begin{pmatrix}\n", "5&6\\\\\n", "8&9\\\\\n", "\\end{pmatrix}\n", "$$\n", "\n", "we could do" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "myArray = array([[1,2,3],[4,5,6], [7,8,9]])\n", "\n", "print(myArray[1:3,1:3])" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To include all of the elements in a given dimension, use the `:` alone with no numbers surrounding it.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "myArray = array([[1,2,3],[4,5,6], [7,8,9]])\n", "\n", "print(myArray[:,1:3]) # Extract all rows with columns 1 and 2" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Boolean Slicing\n", "\n", "Boolean operations can be evaluated on arrays to produce corresponding arrays of booleans. The boolean array can then be used to index the original array and extract elements that meet some criteria.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "\n", "a = array([1,2,3,4,5,6])\n", "\n", "boolArray = a > 2\n", "\n", "print(boolArray)\n", "\n", "print(a[boolArray])" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This also works on multi-dimensional arrays although the result is always one-dimensional regardless of the shape of the original array." ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "myArray = array([[1,2,3],[4,5,6], [7,8,9]])\n", "\n", "print(myArray[myArray>2]) # Extract elements that are greater than 2." ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Vectorization and Broadcasting\n", "A major advantage of numpy arrays over lists is that operations *vectorize* across the arrays. This means that mathematical operations propagate through the array instead of requiring a `for` loop. This speeds up the calculation and makes code easier to write and read. Simple mathematical operations like adding, subtracting, etc can be performed on arrays as you would expect and the operation propagates through to all elements." ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "\n", "a = array([1,2,3])\n", "b = array([4,5,6])\n", "\n", "c = a + b\n", "d = a**2\n", "e = 2 * b\n", "f = 2/b\n", "g = a * b\n", "\n", "print(c,d,e,f,g)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All of the common mathematical operations that you learned for numbers now apply to arrays. Cool!\n", "\n", "### Numpy Functions\n", "\n", "The numpy library has a massive collection of vectorized mathematical functions and these functions should be used instead of similar functions from other libraries that are not vectorized (like `math`)." ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "from numpy import sqrt as nsqrt\n", "from math import sqrt as mathsqrt\n", "\n", "squares = array([1,4,9,16,25])\n", "\n", "print(nsqrt(squares))\n", "#print(mathsqrt(squares)) #This will fail because it wasn't a numpy function." ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Arrays of same Dimensions\n", "\n", "If a mathematical operation is performed between two arrays of the same dimensions, the mathematical operation is performed between corresponding elements in the two arrays. For example, if two $2$ x $2$ arrays are added together, element (0,0) of the first array gets added to the corresponding element in the second and so forth for all elements:\n", "\n", "$$\n", "\\begin{pmatrix}\n", "1&2\\\\\n", "3&4\\\\\n", "\\end{pmatrix}+\n", "\\begin{pmatrix}\n", "5&6\\\\\n", "7&8\\\\\n", "\\end{pmatrix}=\n", "\\begin{pmatrix}\n", "6&8\\\\\n", "10&12\\\\\n", "\\end{pmatrix}\n", "$$" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "\n", "a = array([[1,2],[3,4]])\n", "b = array([[5,6],[7,8]])\n", "\n", "c = a + b # Add the elements of the arrays together.\n", "d = a * b # Multiply the elements of the arrays together.\n", "\n", "print(c)\n", "print(d)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Arrays of Different Dimensions\n", "When a mathematical operation between two arrays of different dimensions is attempted, Python has to figure out how to make them have the same shape before performing the operation. *Broadcasting* refers to the set of rules used for operations like this. To handle arrays with different dimensions, NumPy pads or clones the array with fewer dimensions to make it have the same dimensions as the larger array. For example, what would happen if you attempted this operation:\n", "\n", "$$\n", "\\begin{pmatrix}\n", "1&2\\\\\n", "3&4\\\\\n", "\\end{pmatrix}+\n", "\\begin{pmatrix}\n", "2&2\\\\\n", "\\end{pmatrix}\n", "$$\n", "\n", "One array is $2$ x $2$ and the other is $1$ x $2$. Before the addition can take place, NumPy clones the smaller array and repeats it until it has the same size as the bigger array.\n", "\n", "$$\n", "\\begin{pmatrix}\n", "1&2\\\\\n", "3&4\\\\\n", "\\end{pmatrix}+\n", "\\begin{pmatrix}\n", "2&2\\\\\n", "2&2\\\\\n", "\\end{pmatrix}\n", "$$" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "\n", "a = array([[1,2],[3,4]])\n", "b = array([2,2])\n", "c = a + b\n", "\n", "print(c)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are some cases where NumPy simply cannot figure out how to broadcast one of the arrays appropriately and an error results. When broadcasting, NumPy must verify that all dimensions are compatible with each other. Two dimensions are compatible when i) they are equal or ii) one of the dimensions is 1. For example, if we tried to perform the following mathematical operation, broadcasting would fail because the first dimension of the first array is 2 and the first dimension of the second array is 3.\n", "\n", "\n", "\n", "$$\n", "\\begin{pmatrix}\n", "1&2&3\\\\\n", "3&4&5\\\\\n", "\\end{pmatrix}+\n", "\\begin{pmatrix}\n", "1&1&1\\\\\n", "2&2&2\\\\\n", "3&3&3\\\\\n", "\\end{pmatrix}\n", "$$" ] }, { "cell_type": "code", "metadata": {}, "source": [ "#| eval: false\n", "from numpy import array\n", "\n", "a = array([[1,2,3],[4,5,6]])\n", "b = array([[1,1,1],[2,2,2],[3,3,3]])\n", "c = a + b" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "but if we attempted\n", "\n", "$$\n", "\\begin{pmatrix}\n", "1&2&3\\\\\n", "\\end{pmatrix}+\n", "\\begin{pmatrix}\n", "1&1&1\\\\\n", "2&2&2\\\\\n", "3&3&3\\\\\n", "\\end{pmatrix}\n", "$$\n", "\n", "the operation would succeed because the first dimension of the first array is 1 and the second dimension of both arrays are 3.\n", "\n", "\n", "### Vectorizing user-defined functions\n", "\n", "Standard Python functions are often designed to perform a single calculation rather than iterate over a list to perform many calculations. For example, here is a function to calculate the average acceleration of an object given its final velocity and time of travel." ] }, { "cell_type": "code", "metadata": {}, "source": [ "def accel(velocity, time):\n", " return velocity / time\n", "\n", "\n", "print(accel(52.6,5.6))" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now what if I have a list of many times that I'd like to feed into this function and get an acceleration value for each one.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "#| eval: false\n", "def accel(velocity, time):\n", " return velocity / time\n", "\n", "\n", "times = [4.6,7.9,3.2,8.5,9.2,4.7]\n", "print(accel(52.6,times)) # Produces an error because you can't divide by a list" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An error results here because Python does not know how to divide by a list. We can NumPy-ify this function using a function called `vectorize`. The resulting function will behave just like the other functions from the NumPy library, vectorizing across the list of times. \n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import vectorize\n", "def accel(velocity, time):\n", " return velocity / time\n", "\n", "\n", "times = [4.6,7.9,3.2,8.5,9.2,4.7]\n", "vaccel = vectorize(accel) # Vectorize the function!\n", "print(vaccel(52.6,times)) # Succeeds because NumPy knows how to vectorize." ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Of course, we also could have just converted our times list into an array and used the original function.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "def accel(velocity, time):\n", " return velocity / time\n", "\n", "\n", "times = array([4.6,7.9,3.2,8.5,9.2,4.7])\n", "print(accel(52.6,times)) # Succeeds because times is an array not a list." ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Manipulating and Modifying Arrays\n", "\n", "A wealth of functions exist to perform routine manipulation tasks on arrays once they are created. Often these tasks will involve changing the number of rows or columns or merging two arrays into one. The *size* and *shape* of an array are the number of elements and dimensions, respectively. These can be determined using the `shape` and `size` methods.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "\n", "a = array([[1,2,3],[4,5,6]])\n", "print(a.size)\n", "print(a.shape)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Reshaping Arrays\n", "The dimensions of an array can be modified using the `reshape` function. This methods maintains the number of elements and the order of elements but repacks them into a different number of rows and columns. Because the number of elements is maintained, the size of the new array has to be the same as the original. Let's see an example.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array, reshape\n", "\n", "a = array([[1,2,3],[4,5,6]])\n", "\n", "b = reshape(a,[3,2])\n", "\n", "print(a)\n", "print(b)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The original array (`a`) was a $2$ x $3$ and had $6$ elements and the reshaped array also has $6$ elements but is a $3$ x $2$. You can start with a one-dimensional array and reshape it to a higher dimensional array.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import linspace, reshape\n", "\n", "a = linspace(0,10,12)\n", "\n", "b = reshape(a,[3,4])\n", "\n", "print(a)\n", "print(b)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Flattening Arrays\n", "\n", "Flattening an array takes a higher-dimensional array and squishes it into a one-dimensional array. You can \"flatten\" an array with the `flatten` method, but note that `flatten` doesn't actually modify the original array.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array\n", "\n", "a = array([[1,2,3],[4,5,6]])\n", "a.flatten()\n", "\n", "print(a) # 'a' remains unchanged\n", "\n", "a = a.flatten() # If you want to change the definition of a, redifine it.\n", "print(a)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Transposing Arrays\n", "Transposing an array rotates it across the diagonal and can be accomplished with the `transpose` function. There is also a shortcut method for this of `array.T` to accomplish the same thing but just as with `flatten` it does not modify the original array. (neither does `transpose`)\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import array,transpose\n", "\n", "a = array([[1,2,3],[4,5,6]])\n", "transpose(a)\n", "\n", "print(a) # 'a' remains unchanged\n", "\n", "a = a.T # If you want to change the definition of a, redefine it.\n", "print(a)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Merging Arrays\n", "Several function exist for combining multiple arrays into a single array. We'll give examples for a couple and mention the others in reference tables. The most commonly used functions for this task are `vstack` (vertical stacking) and `hstack` (horizontal stacking). `vstack` will stack the original arrays vertically to create the new array and `hstack` will stack them horizontally. Here are some examples." ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy import linspace, hstack, vstack\n", "\n", "a = linspace(0,10,10)\n", "b = linspace(0,5,10)\n", "\n", "c = vstack((a,b))\n", "print(c) # 'a' remains unchanged\n", "\n", "d = hstack((a,b))\n", "print(a.T)\n", "print(d)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Commonly-used Array Methods and Functions\n", "\n", "NumPy contains an extensive listing of array methods and functions and it would be impractical to list them all here. However, below you will find some tables of some of the commonly used ones that can serve as a reference.\n", "\n", "\n", "\n", "| Method | Description|\n", "|--------|------------|\n", "| `shape(array)` or `array.shape`| Returns the dimensions of the array.|\n", "| `ndim(array)` or `array.ndim` | Returns the number of dimensions (i.e. a 2D array is $2$).|\n", "| `size(array)` or `array.size` | Returns the number of elements in an array.|\n", "\n", ": Array Attribute Methods and Functions\n", "\n", "\n", "| Method | Description|\n", "|--------|------------|\n", "| `array.flatten()`| Returns a flattened view of `array`.|\n", "| `reshape(array,dims)` or `array.reshape(dims)` | Returns a view of `array` reshaped into an array with dimensions given by `dims`.|\n", "| `array.resize(dims)` | **Modifies** `array` to be a resized array with dimensions `dims`.|\n", "| `transpose(array)` or `array.T` | Returns a view of the transpose of `array`.|\n", "| `sort(array)` | Returns a view of a sorted version of `array`.|\n", "| `array.sort()` | **Modifies** `array` to be sorted.|\n", "| `argsort(array)` | Returns index values that will sort `array`.|\n", "| `array.fill(x)` | **Modifies** `array` so that all elements are equal to `x`.|\n", "| `vstack(a,b)` | Vertically stack arrays `a` and `b` to form the new array.|\n", "| `hstack(a,b)` | Horizontally stack arrays `a` and `b` to form the new array.|\n", "| `vsplit(array,n)` | Splits `array` vertically into `n` equal parts.|\n", "| `hsplit(array,n)` | Splits `array` horizontally into `n` equal parts.|\n", "| `append(array,x)` | Returns a view of `array` with `x` added to the end of the array.|\n", "| `insert(array,n,x)` | Returns a view of `array` with `x` inserted at location `n`.|\n", "| `delete(array,n)` | Returns a view of `array` with element at location `n` removed.|\n", "| `unique(array)` | Returns a view of the unique elements of `array`.|\n", "\n", "\n", ": Array Modification Methods and Functions\n", "\n", "\n", "\n", "| Method | Description|\n", "|--------|------------|\n", "| `min(array)` or `array.min()`| Returns the minimum value in an array.|\n", "| `max(array)` or `array.max()`| Returns the maximum value in an array.|\n", "| `argmin(array)` or `array.argmin()` | Returns the location of the minimum value in an array.|\n", "| `argmax(array)` or `array.argmax()` | Returns the location of the maximum value in an array.|\n", "| `fmin(array1,array2)` | Returns the minimum between two arrays of the same size. (Arrays are compared element wise.)|\n", "| `fmax(array1,array2)` | Returns the maximum between two arrays of the same size. (Arrays are compared element wise.)|\n", "| `mean(array)` or `array.mean()` | Returns the mean of `array`.|\n", "| `median(array)` or `array.median()` | Returns the median of `array`.|\n", "| `std(array)` or `array.std()` | Returns the standard deviation of `array`.|\n", "| `cumprod(array)` or `array.cumprod()` | Returns the cumulative product of `array`.|\n", "| `cumsum(array)` or `array.cumsum()` | Returns the cumulative sum of `array`.|\n", "| `sum(array)` or `array.sum()` | Returns the sum of all elements in `array`.|\n", "| `prod(array)` or `array.prod()` | Returns the product of all elements in `array`.|\n", "| `floor(array)` | Returns the floor (i.e., rounds down) of all elements in `array`.|\n", "| `ceil(array)` | Returns the ceiling (i.e., rounds up) of all elements in `array`.|\n", "\n", "\n", ": Array Measurement Methods and Functions\n", "\n", "\n", "## Flashcards Part I\n", "1. In what ways are arrays \"better\" than lists? (better might be too strong.)\n", "2. How do you create an array from a list?\n", "3. Explain the usage of the `linspace` function.\n", "4. Explain the usage of the `arange` function.\n", "5. Explain the usage of the `zeros_like` function. \n", "6. How do you access a single element from a two-dimensional array?\n", "7. How do you access **multiple** array elements at once in a one-dimensional array?\n", "8. How do you access **multiple** array elements at once in a two-dimensional array?\n", "9. How do you slice a two-dimensional array? \n", "9. What is boolean slicing? Given an example.\n", "10. What is broadcasting?\n", "11. What has President Nelson taught us about the meaning of the word Israel?\n", "\n", "## Flashcards Part II\n", "1. How can you determine the shape and size of an array?\n", "2. What does the `reshape` function do? Give an example to show the usage.\n", "3. What does the `flatten` function do? Give an example to show the usage.\n", "4. What does the `transpose` function do? Give an example to show the usage.\n", "5. What does the `vstack` function do? Give an example to show the usage.\n", "6. What does the `hstack` function do? Give an example to show the usage.\n", "7. What do the `argmax` and `argmin` functions do?\n", "8. What does the `cumsum` function do?\n", "9. How many temples does the church have in operation currently?\n", "\n", "\n", "\n", "## Exercises \n", "1. [Here](https://en.wikipedia.org/wiki/Unit_circle#/media/File:Unit_circle_angles_color.svg) you will find a picture of the unit circle. \n", " 1. Use the `arange` function to generate all of the angles (in degrees) on the unit circle **displayed in blue or black text**.\n", " 2. Using a single line of code, convert all of the angle from part 1 into radians.\n", " 3. Use the `linspace` function to generate all of the angles (in degrees) on the unit circle **displayed in red or black text**.\n", " 4. Using a single line of code, convert all of the angle from part 3 into radians.\n", " 5. Using a single line of code, evaluate $\\sin()$ onto both sets of angles and verify that they agree with the values on the unit circle.\n", " 6. Using a single line of code, evaluate $\\cos()$ onto both sets of angles and verify that they agree with the values on the unit circle.\n", " 7. Using a single line of code, evaluate ${\\sin(\\theta)\\over \\cos(\\theta)}$ for all of the angles in your arrays. This should be equal to $\\tan(\\theta)$" ] }, { "cell_type": "code", "metadata": {}, "source": [ "# Python code here" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. In the cell below you will find three arrays containing the masses, lengths, and radii for a collection of cylinders. The moment of inertia for each cylinder can be calculated as $$ I = {1\\over 4} M R^2 + {1\\over 12} M L^2$$. \n", "\n", " 1. Using a single line of code, calculate the moment of inertia for all of the values in the arrays.\n", " 2. Determine the largest, smallest, and average of the calculated values.\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "from numpy.random import uniform\n", "mass = uniform(3,8,1000)\n", "radius = uniform(0.5,1.2,1000)\n", "length = uniform(0.8,3,1000)\n" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "3. Construct a two-dimensional array with the following entries: $$\\begin{bmatrix}\n", "1& 5 & 7 & 2\\\\\n", "3& 9 & 1 & 4\\\\\n", "4& 2 & 2 & 8\\\\\n", "9& 1 & 6 & 3\\\\\n", "\\end{bmatrix}$$\n", "\n", " 1. Now access the number in the third column and second row. (It's a $1$)\n", " 2. Slice the array from columns $2 \\rightarrow 4$ and rows $1 \\rightarrow 3$.\n", "\n", "\n", "4. The following temperatures are prominent on the Fahrenheit scale: `[0,32,100,212,451]`. \n", " 1. Create an array that contains these temperatures.\n", " 2. Using a single line of code, convert these temperatures into degrees Celsius using the following formula $$T_C = {5\\over 9}(T_F - 32)$$\n", " 3. Using a single line of code, convert the temperatures from part 2 into Kelvins using the following formula $$T_K = T_C + 273.15$$ \n", " 4. Using a single line of code, convert the temperatures from **part 1** into Kelvins using the following formula $$T_K = {5\\over 9} ( T_F - 32) + 273.15$$ \n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "# Python code here." ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "5. The equation below defines the relationship between energy ($E$) in Joules of a photon and its wavelength ($\\lambda$) in meters. The $h$ is Plank's constant ($6.626 \\times 10^{-34}$ J $\\cdot$ s) and $c$ is the speed of light in a vacuum ($2.998 \\times 10^8$ m/s). $$E = {h c \\over \\lambda}$$\n", "\n", " 1. Generate an array of wavelength values for visible light ($400$ nm $\\rightarrow 800$ nm) in $50$ nm increments. (\"nm\" stands for nanometers or $10^{-9}$ m)\n", " 2. Generate a second array containing the energy of each wavelength of light from part 1. Does the energy of a photon increase or decrease with wavelength?" ] }, { "cell_type": "code", "metadata": {}, "source": [ "# Python code here" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "6. A boat is out at sea with the following location and velocity vectors: $$x_i = \n", "\\begin{bmatrix}\n", "5 \\\\\n", "2\n", "\\end{bmatrix} \\text{ km}$$ $$v_i = \n", "\\begin{bmatrix}\n", "-13 \\\\\n", "25\n", "\\end{bmatrix} \\text{ m/s}$$ when a gust of wind causes the boat to accelerate for approximately $3$ minutes with the following acceleration vector:$$a = \n", "\\begin{bmatrix}\n", "-8 \\\\\n", "-5\n", "\\end{bmatrix} \\text{ m/s}^2$$\n", "We can find the position and velocity vectors of the boat after the wind has died back down with the following equation: $$ \\vec{x_f} = \\vec{x_i} + \\vec{v_i} \\Delta t + {1\\over 2} \\vec{a} \\Delta t^2$$ $$ \\vec{v_f} = \\vec{v_i} + \\vec{a} \\Delta t$$\n", "\n", " 1. Create arrays containing the initial position, initial velocity, and acceleration of the boat **in SI units**.\n", " 2. Using a single line of code, calculate the final position of the boat. (Watch out: You must convert $3$ minutes to seconds before performing the calculation.)\n", " 3. Using a single line of code, calculate the final velocity of the boat. (Watch out: You must convert $3$ minutes to seconds before performing the calculation.)\n", "\n", "\n", "7. Predict the outcome of the following operations between two arrays. Then test your prediction.\n", "\n", " $$\\begin{bmatrix}\n", " 1&8&9\\\\\n", " 8&1&9\\\\\n", " 1&8&1\\\\\n", " \\end{bmatrix}+ \\begin{bmatrix}\n", " 1&1\\\\\n", " 1&1\\\\\n", " \\end{bmatrix} $$ $$\n", " \\begin{bmatrix}\n", " 1&1\\\\\n", " 2&2\\\\\n", " \\end{bmatrix}\n", " + \\begin{bmatrix}\n", " 1\\\\\n", " \\end{bmatrix}$$ $$\n", " \\begin{bmatrix}\n", " 1&8&9\\\\\n", " 8&1&9\\\\\n", " 1&8&1\\\\\n", " \\end{bmatrix} + \\begin{bmatrix}\n", " 1\\\\\n", " 2\\\\\n", " 3\\\\\n", " \\end{bmatrix}$$ $$\n", " \\begin{bmatrix}\n", " 1&8&9\\\\\n", " 8&1&9\\\\\n", " 1&8&1\\\\\n", " \\end{bmatrix}\n", " + \\begin{bmatrix}\n", " 1&2&3\\\\\n", " \\end{bmatrix}$$\n" ] }, { "cell_type": "code", "metadata": {}, "source": [ "# Python code here" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "8. In quantum mechanics you will learn that the allowed energy levels for the harmonic oscillator are given by:$$E_n = \\hbar \\sqrt{k \\over m} (n + {1\\over 2})$$ where $k = 4 \\pi^2$ and $m = 1$ gram.\n", "\n", " 1. Generate an array of $n$ values from $1 \\rightarrow 10$ (inclusive).\n", " 2. Using a single line of code, calculate the first 10 allowed energies for this harmonic oscillator. (Don't forget to convert the mass to kg so everything is in SI units.)\n", " 3. Combine these two arrays into a single $10$ x $2$ array with the first column containing $n$ values and the second column containing the corresponding energies." ] }, { "cell_type": "code", "metadata": {}, "source": [ "# Python code here." ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "9. Generate an array containing integers $0 \\rightarrow 14$ (inclusive)\n", " 1. Reshape the array to be a $3$ x $5$ array.\n", " 2. Transpose the array from part 1 so that it is $5$ x $3$.\n", " 3. Make the array from part 2 one-dimensional.\n", "\n", "\n", "10. The $\\cos()$ function can be written as an infinite sum: $$ \\cos(x) = 1 - {x^2 \\over 2!} + {x^4 \\over 4!} - {x^6 \\over 6!} + \\dots = \\sum_{n = 0}^\\infty {(-1)^n x^{2n} \\over (2n)!}$$. (As it turns out, any function can be written as a sum like this.)\n", " 1. Make an array of integer $n$ values from $0 \\rightarrow 10$.\n", " 2. Using a single line of code, generate the terms in the sum for $x = \\pi$. Note: `scipy.special` has a `factorial` function that will work on arrays. \n", " 3. Use `cumsum` to evaluate the sum.\n", " 4. Interpret the results and verify that as you include more terms in the sum the approximation to $\\cos(x)$ gets better and better.\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 4 }