9  The numpy module

Jupyter Notebook

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.

9.1 Numpy Arrays

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.

myList = [4,5,7]

newList = 2 * myList
print(newList)
[4, 5, 7, 4, 5, 7]

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.

myList = [4,5,7]

newList = []
for i in myList:
    newList.append(i* 2)

print(newList)
[8, 10, 14]

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.

9.2 Array Creation

9.2.1 Type Conversion from List

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.

from numpy import array

myArray = array([4,5,7])

newArray = 2 * myArray 

print(newArray)
[ 8 10 14]

Nested lists, or lists that contain lists as their elements, can be converted to multi-dimensional arrays using the array function.

from numpy import array
myArray = array([[1,2,3],[4,5,6]])

print(myArray)
[[1 2 3]
 [4 5 6]]

9.2.2 The arange and linspace Functions

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:

  1. arange produces an array whereas range produces a list.
  2. The step size for arange does not need to be an integer.
  3. range produces an iterator and arange generates a sequence of values immediately.

The arguments to arange are similar to range

arange(start,stop,step)

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.

linspace(start,stop,number of points)

Below is an example that shows the usage of linspace and arange.

from numpy import linspace,arange

myArray = linspace(0,10,20)
myArray2 = arange(0,10,0.5)
print(myArray)
print(myArray2)
[ 0.          0.52631579  1.05263158  1.57894737  2.10526316  2.63157895
  3.15789474  3.68421053  4.21052632  4.73684211  5.26315789  5.78947368
  6.31578947  6.84210526  7.36842105  7.89473684  8.42105263  8.94736842
  9.47368421 10.        ]
[0.  0.5 1.  1.5 2.  2.5 3.  3.5 4.  4.5 5.  5.5 6.  6.5 7.  7.5 8.  8.5
 9.  9.5]

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.

from numpy import linspace,arange

myArray,mydx = linspace(0,10,20,retstep= True)
print(mydx)
print(myArray)
0.5263157894736842
[ 0.          0.52631579  1.05263158  1.57894737  2.10526316  2.63157895
  3.15789474  3.68421053  4.21052632  4.73684211  5.26315789  5.78947368
  6.31578947  6.84210526  7.36842105  7.89473684  8.42105263  8.94736842
  9.47368421 10.        ]

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.

zeros((rows,columns))

If the array to be created is only one dimensional, the argument can be a single number instead of a tuple.

zeros(n)
from numpy import zeros,ones

myArray = zeros([3,4])
myArray2 = ones(5)
print(myArray)
print(myArray2)
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
[1. 1. 1. 1. 1.]

Arrays of any constant (not just one or zero) can then be easily generated by performing the needed math on the original array.

from numpy import zeros,ones

myArray = zeros([3,4]) + 5
myArray2 = ones(5) * 12
print(myArray)
print(myArray2)
[[5. 5. 5. 5.]
 [5. 5. 5. 5.]
 [5. 5. 5. 5.]]
[12. 12. 12. 12. 12.]

9.2.3 Arrays from Functions

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.

fromfunction(function, shape)

Let’s make a 3 x 3 array where each element is the product of the row and column indices:

from numpy import fromfunction

def prod(x,y):
    return x * y

myArray = fromfunction(prod,(3,3))
print(myArray)
[[0. 0. 0.]
 [0. 1. 2.]
 [0. 2. 4.]]

The table below gives a summary of useful functions for creating numpy arrays. The required arguments are also described.

Common functions for generating arrays
Method Description
linspace(start,stop,n) Returns an array of n evenly-spaced points begining at start and ending at stop.
arange(start,stop,dx) Returns an array beginning at start,ending at stop with a step size of dx.
empty(dims) Returns an empty array with dimensions dim.
zeros(dims) Returns an array of zeros with dimensions dim.
ones(dims) Returns an array of ones with dimensions dim.
zeros_like(arr) Returns an array of zeros with dimensions that match the dimensions of arr.
fromfunction(function,dims) Returns an array of numbers generated by evaluating function on the indices of an array with dimensions dims.
copy(arr) Creates a copy of array arr.
genfromtext(file) Reads file and loads the text into an array (file must only contain numbers).

9.3 Accessing and Slicing Arrays

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.

9.3.1 One-dimensional Arrays

Elements from a one-dimensional array can be extracted using square brackets ([]) just like we have done with lists.

from numpy import array

myArray = array([3,4,7,8])
print(myArray[2])
7

9.3.2 Multi-dimensional Arrays

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.

from numpy import array
myArray = array([[1,2,3],[4,5,6], [7,8,9]])

print(myArray[1])
[4 5 6]

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.

array_name[row,column]
from numpy import array
myArray = array([[1,2,3],[4,5,6], [7,8,9]])

print(myArray[1][1])  # This works, but is a bit hard on the eyes
print(myArray[1,1])  # This works also and is easier to look at.
5
5

9.3.3 Accessing Multiple Elements

Multiple elements of an array can be accessed using a list for the index instead of a single number.

from numpy import array

myArray = array([1,2,3,4,5,6,7,8,9,10])

print(myArray[2])  # Extract element 2
print(myArray[ [3,6,9] ])  # Extract elements 3, 6, and 9.
3
[ 4  7 10]

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.

array_name[[rows]]  # Access set of rows
array_name[[rows], [columns]]  # Access set of elements
from numpy import array
myArray = array([[1,2,3],[4,5,6], [7,8,9]])

print(myArray[[0,1]]) # Extract rows 0 and 1.

print(myArray[[1,2,0],[0,2,2]])   # Extract elements (1,0), (2,2), and (0,2)  
[[1 2 3]
 [4 5 6]]
[4 9 3]

9.4 Slicing Arrays

9.4.1 Multi-dimensional Arrays

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:

from numpy import array
myArray = array([[1,2,3],[4,5,6], [7,8,9]])

which can be visualized as the following matrix:

\[ \begin{pmatrix} 1&2&3\\ 4&5&6\\ 7&8&9\\ \end{pmatrix} \]

To slice out the following \(2\) x \(2\) sub matrix:

\[ \begin{pmatrix} 5&6\\ 8&9\\ \end{pmatrix} \]

we could do

from numpy import array
myArray = array([[1,2,3],[4,5,6], [7,8,9]])

print(myArray[1:3,1:3])
[[5 6]
 [8 9]]

To include all of the elements in a given dimension, use the : alone with no numbers surrounding it.

from numpy import array
myArray = array([[1,2,3],[4,5,6], [7,8,9]])

print(myArray[:,1:3])  # Extract all rows with columns 1 and 2
[[2 3]
 [5 6]
 [8 9]]

9.4.2 Boolean Slicing

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.

from numpy import array

a = array([1,2,3,4,5,6])

boolArray = a > 2

print(boolArray)

print(a[boolArray])
[False False  True  True  True  True]
[3 4 5 6]

This also works on multi-dimensional arrays although the result is always one-dimensional regardless of the shape of the original array.

from numpy import array
myArray = array([[1,2,3],[4,5,6], [7,8,9]])

print(myArray[myArray>2]) # Extract elements that are greater than 2.
[3 4 5 6 7 8 9]

9.5 Vectorization and Broadcasting

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.

from numpy import array

a = array([1,2,3])
b = array([4,5,6])

c = a + b
d = a**2
e = 2 * b
f = 2/b
g = a * b

print(c,d,e,f,g)
[5 7 9] [1 4 9] [ 8 10 12] [0.5        0.4        0.33333333] [ 4 10 18]

All of the common mathematical operations that you learned for numbers now apply to arrays. Cool!

9.5.1 Numpy Functions

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).

from numpy import array
from numpy import sqrt as nsqrt
from math import sqrt as mathsqrt

squares = array([1,4,9,16,25])

print(nsqrt(squares))
#print(mathsqrt(squares))  #This will fail because it wasn't a numpy function.
[1. 2. 3. 4. 5.]

9.5.2 Arrays of same Dimensions

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:

\[ \begin{pmatrix} 1&2\\ 3&4\\ \end{pmatrix}+ \begin{pmatrix} 5&6\\ 7&8\\ \end{pmatrix}= \begin{pmatrix} 6&8\\ 10&12\\ \end{pmatrix} \]

from numpy import array

a = array([[1,2],[3,4]])
b = array([[5,6],[7,8]])

c = a + b  # Add the elements of the arrays together.
d = a * b  # Multiply the elements of the arrays together.

print(c)
print(d)
[[ 6  8]
 [10 12]]
[[ 5 12]
 [21 32]]

9.5.3 Arrays of Different Dimensions

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:

\[ \begin{pmatrix} 1&2\\ 3&4\\ \end{pmatrix}+ \begin{pmatrix} 2&2\\ \end{pmatrix} \]

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.

\[ \begin{pmatrix} 1&2\\ 3&4\\ \end{pmatrix}+ \begin{pmatrix} 2&2\\ 2&2\\ \end{pmatrix} \]

from numpy import array

a = array([[1,2],[3,4]])
b = array([2,2])
c = a + b

print(c)
[[3 4]
 [5 6]]

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.

\[ \begin{pmatrix} 1&2&3\\ 3&4&5\\ \end{pmatrix}+ \begin{pmatrix} 1&1&1\\ 2&2&2\\ 3&3&3\\ \end{pmatrix} \]

from numpy import array

a = array([[1,2,3],[4,5,6]])
b = array([[1,1,1],[2,2,2],[3,3,3]])
c = a + b

but if we attempted

\[ \begin{pmatrix} 1&2&3\\ \end{pmatrix}+ \begin{pmatrix} 1&1&1\\ 2&2&2\\ 3&3&3\\ \end{pmatrix} \]

the operation would succeed because the first dimension of the first array is 1 and the second dimension of both arrays are 3.

9.5.4 Vectorizing user-defined functions

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.

def accel(velocity, time):
    return velocity / time


print(accel(52.6,5.6))
9.392857142857144

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.

def accel(velocity, time):
    return velocity / time


times = [4.6,7.9,3.2,8.5,9.2,4.7]
print(accel(52.6,times))  # Produces an error because you can't divide by a list

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.

from numpy import vectorize
def accel(velocity, time):
    return velocity / time


times = [4.6,7.9,3.2,8.5,9.2,4.7]
vaccel = vectorize(accel)  # Vectorize the function!
print(vaccel(52.6,times))  # Succeeds because NumPy knows how to vectorize.
[11.43478261  6.65822785 16.4375      6.18823529  5.7173913  11.19148936]

Of course, we also could have just converted our times list into an array and used the original function.

from numpy import array
def accel(velocity, time):
    return velocity / time


times = array([4.6,7.9,3.2,8.5,9.2,4.7])
print(accel(52.6,times))  # Succeeds because times is an array not a list.
[11.43478261  6.65822785 16.4375      6.18823529  5.7173913  11.19148936]

9.6 Manipulating and Modifying Arrays

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.

from numpy import array

a = array([[1,2,3],[4,5,6]])
print(a.size)
print(a.shape)
6
(2, 3)

9.6.1 Reshaping Arrays

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.

from numpy import array, reshape

a = array([[1,2,3],[4,5,6]])

b = reshape(a,[3,2])

print(a)
print(b)
[[1 2 3]
 [4 5 6]]
[[1 2]
 [3 4]
 [5 6]]

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.

from numpy import linspace, reshape

a = linspace(0,10,12)

b = reshape(a,[3,4])

print(a)
print(b)
[ 0.          0.90909091  1.81818182  2.72727273  3.63636364  4.54545455
  5.45454545  6.36363636  7.27272727  8.18181818  9.09090909 10.        ]
[[ 0.          0.90909091  1.81818182  2.72727273]
 [ 3.63636364  4.54545455  5.45454545  6.36363636]
 [ 7.27272727  8.18181818  9.09090909 10.        ]]

9.6.2 Flattening Arrays

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.

from numpy import array

a = array([[1,2,3],[4,5,6]])
a.flatten()

print(a)  # 'a' remains unchanged

a = a.flatten() # If you want to change the definition of a, redifine it.
print(a)
[[1 2 3]
 [4 5 6]]
[1 2 3 4 5 6]

9.6.3 Transposing Arrays

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)

from numpy import array,transpose

a = array([[1,2,3],[4,5,6]])
transpose(a)

print(a)  # 'a' remains unchanged

a = a.T # If you want to change the definition of a, redefine it.
print(a)
[[1 2 3]
 [4 5 6]]
[[1 4]
 [2 5]
 [3 6]]

9.6.4 Merging Arrays

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.

from numpy import linspace, hstack, vstack

a = linspace(0,10,10)
b = linspace(0,5,10)

c = vstack((a,b))
print(c)  # 'a' remains unchanged

d = hstack((a,b))
print(a.T)
print(d)
[[ 0.          1.11111111  2.22222222  3.33333333  4.44444444  5.55555556
   6.66666667  7.77777778  8.88888889 10.        ]
 [ 0.          0.55555556  1.11111111  1.66666667  2.22222222  2.77777778
   3.33333333  3.88888889  4.44444444  5.        ]]
[ 0.          1.11111111  2.22222222  3.33333333  4.44444444  5.55555556
  6.66666667  7.77777778  8.88888889 10.        ]
[ 0.          1.11111111  2.22222222  3.33333333  4.44444444  5.55555556
  6.66666667  7.77777778  8.88888889 10.          0.          0.55555556
  1.11111111  1.66666667  2.22222222  2.77777778  3.33333333  3.88888889
  4.44444444  5.        ]

9.7 Commonly-used Array Methods and Functions

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.

Array Attribute Methods and Functions
Method Description
shape(array) or array.shape Returns the dimensions of the array.
ndim(array) or array.ndim Returns the number of dimensions (i.e. a 2D array is \(2\)).
size(array) or array.size Returns the number of elements in an array.
Array Modification Methods and Functions
Method Description
array.flatten() Returns a flattened view of array.
reshape(array,dims) or array.reshape(dims) Returns a view of array reshaped into an array with dimensions given by dims.
array.resize(dims) Modifies array to be a resized array with dimensions dims.
transpose(array) or array.T Returns a view of the transpose of array.
sort(array) Returns a view of a sorted version of array.
array.sort() Modifies array to be sorted.
argsort(array) Returns index values that will sort array.
array.fill(x) Modifies array so that all elements are equal to x.
vstack(a,b) Vertically stack arrays a and b to form the new array.
hstack(a,b) Horizontally stack arrays a and b to form the new array.
vsplit(array,n) Splits array vertically into n equal parts.
hsplit(array,n) Splits array horizontally into n equal parts.
append(array,x) Returns a view of array with x added to the end of the array.
insert(array,n,x) Returns a view of array with x inserted at location n.
delete(array,n) Returns a view of array with element at location n removed.
unique(array) Returns a view of the unique elements of array.
Array Measurement Methods and Functions
Method Description
min(array) or array.min() Returns the minimum value in an array.
max(array) or array.max() Returns the maximum value in an array.
argmin(array) or array.argmin() Returns the location of the minimum value in an array.
argmax(array) or array.argmax() Returns the location of the maximum value in an array.
fmin(array1,array2) Returns the minimum between two arrays of the same size. (Arrays are compared element wise.)
fmax(array1,array2) Returns the maximum between two arrays of the same size. (Arrays are compared element wise.)
mean(array) or array.mean() Returns the mean of array.
median(array) or array.median() Returns the median of array.
std(array) or array.std() Returns the standard deviation of array.
cumprod(array) or array.cumprod() Returns the cumulative product of array.
cumsum(array) or array.cumsum() Returns the cumulative sum of array.
sum(array) or array.sum() Returns the sum of all elements in array.
prod(array) or array.prod() Returns the product of all elements in array.
floor(array) Returns the floor (i.e., rounds down) of all elements in array.
ceil(array) Returns the ceiling (i.e., rounds up) of all elements in array.

9.8 Flashcards Part I

  1. In what ways are arrays “better” than lists? (better might be too strong.)
  2. How do you create an array from a list?
  3. Explain the usage of the linspace function.
  4. Explain the usage of the arange function.
  5. Explain the usage of the zeros_like function.
  6. How do you access a single element from a two-dimensional array?
  7. How do you access multiple array elements at once in a one-dimensional array?
  8. How do you access multiple array elements at once in a two-dimensional array?
  9. How do you slice a two-dimensional array?
  10. What is boolean slicing? Given an example.
  11. What is broadcasting?
  12. What has President Nelson taught us about the meaning of the word Israel?

9.9 Flashcards Part II

  1. How can you determine the shape and size of an array?
  2. What does the reshape function do? Give an example to show the usage.
  3. What does the flatten function do? Give an example to show the usage.
  4. What does the transpose function do? Give an example to show the usage.
  5. What does the vstack function do? Give an example to show the usage.
  6. What does the hstack function do? Give an example to show the usage.
  7. What do the argmax and argmin functions do?
  8. What does the cumsum function do?
  9. How many temples does the church have in operation currently?

9.10 Exercises

  1. Here you will find a picture of the unit circle.
    1. Use the arange function to generate all of the angles (in degrees) on the unit circle displayed in blue or black text.
    2. Using a single line of code, convert all of the angle from part 1 into radians.
    3. Use the linspace function to generate all of the angles (in degrees) on the unit circle displayed in red or black text.
    4. Using a single line of code, convert all of the angle from part 3 into radians.
    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.
    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.
    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)\)
# Python code here
  1. 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\].

    1. Using a single line of code, calculate the moment of inertia for all of the values in the arrays.
    2. Determine the largest, smallest, and average of the calculated values.
from numpy.random import uniform
mass = uniform(3,8,1000)
radius = uniform(0.5,1.2,1000)
length = uniform(0.8,3,1000)
  1. Construct a two-dimensional array with the following entries: \[\begin{bmatrix} 1& 5 & 7 & 2\\ 3& 9 & 1 & 4\\ 4& 2 & 2 & 8\\ 9& 1 & 6 & 3\\ \end{bmatrix}\]

    1. Now access the number in the third column and second row. (It’s a \(1\))
    2. Slice the array from columns \(2 \rightarrow 4\) and rows \(1 \rightarrow 3\).
  2. The following temperatures are prominent on the Fahrenheit scale: [0,32,100,212,451].

    1. Create an array that contains these temperatures.
    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)\]
    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\]
    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\]
# Python code here.
  1. 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}\]

    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)
    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?
# Python code here
  1. A boat is out at sea with the following location and velocity vectors: \[x_i = \begin{bmatrix} 5 \\ 2 \end{bmatrix} \text{ km}\] \[v_i = \begin{bmatrix} -13 \\ 25 \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 = \begin{bmatrix} -8 \\ -5 \end{bmatrix} \text{ m/s}^2\] 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\]

    1. Create arrays containing the initial position, initial velocity, and acceleration of the boat in SI units.
    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.)
    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.)
  2. Predict the outcome of the following operations between two arrays. Then test your prediction.

    \[\begin{bmatrix} 1&8&9\\ 8&1&9\\ 1&8&1\\ \end{bmatrix}+ \begin{bmatrix} 1&1\\ 1&1\\ \end{bmatrix} \] \[ \begin{bmatrix} 1&1\\ 2&2\\ \end{bmatrix} + \begin{bmatrix} 1\\ \end{bmatrix}\] \[ \begin{bmatrix} 1&8&9\\ 8&1&9\\ 1&8&1\\ \end{bmatrix} + \begin{bmatrix} 1\\ 2\\ 3\\ \end{bmatrix}\] \[ \begin{bmatrix} 1&8&9\\ 8&1&9\\ 1&8&1\\ \end{bmatrix} + \begin{bmatrix} 1&2&3\\ \end{bmatrix}\]

# Python code here
  1. 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.

    1. Generate an array of \(n\) values from \(1 \rightarrow 10\) (inclusive).
    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.)
    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.
# Python code here.
  1. Generate an array containing integers \(0 \rightarrow 14\) (inclusive)
    1. Reshape the array to be a \(3\) x \(5\) array.
    2. Transpose the array from part 1 so that it is \(5\) x \(3\).
    3. Make the array from part 2 one-dimensional.
  2. 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.)
    1. Make an array of integer \(n\) values from \(0 \rightarrow 10\).
    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.
    3. Use cumsum to evaluate the sum.
    4. Interpret the results and verify that as you include more terms in the sum the approximation to \(\cos(x)\) gets better and better.