This exercise page on NumPy arrays was prepared for use in the course ITSE 1302 at Austin Community College.
NumPy is the fundamental package for scientific computing with Python. It contains among other things:
This exercise page deals mainly with the first two items in the above list. The random number capability shown in item 4 is also important for this competency. However once you understand the first two items, you should have no difficulty learning about the random number capabilities on your own.
The numpy library provides many capabilities that are not discussed in this tutorial. For further information on NumPy, see the Reference Manual. Also see the NumPy User Guide.
Some of those capabilities, such as sine, cosine, and exponential are also important for this competency. However, as above, once you understand the first two items in the above list, you should have no difficulty learning about those capabilities on your own.
You will often see a numpy array referred to as an ndarray. This name derives from the fact that an ndarray is an N-dimensional array.
This exercise page provides a variety of usage examples for the numpy array.
In order to make it possible to copy the code for the examples into a Python script and run them under the stand-alone Python interpreter, most of the code in this exercise page was written in such a way that it can stand alone and run independently of any code that was written earlier in the notebook.
To extract a section of code from the Jupyter notebook format and run it under the Python interpreter,
Copy the import statements into your Python source code editor.
Copy the Python code of interest into the same Python source code file.
Execute the Python code under your Python version 3+ interpreter.
Note, you must have installed a Python version 3+ distribution such as Anaconda or WinPython that inludes matplotlib, numpy, scipy, etc.
Let's begin by importing the libraries required by the code on this exercise page. Before we can create and use the numpy library, we must import it. The convention is to import the numpy library as np as shown below. This reduces the typing effort required to use it later.
One of the examples in this tutorial compares the execution speed of a numpy array with a list. That example requires access to the datetime library, which is also imported below.
import numpy as np
from datetime import datetime
First we need to learn how to create and access numpy arrays. The following code creates a one-dimensional array and prints its contents. The code also prints the type and shape of the array. Note that while type is a function that can deal with many types of objects, shape is a property of the array object. This is evident from the different ways that type and shape are accessed.
The output tells us that the type of the array is a numpy N-Dimensional array (ndarray). The output from shape tells us that this is a one-dimensional array with three elements.
myArray = np.array([1,2,3])
print(myArray)
print(type(myArray))
print(myArray.shape)
The easiest way to access the elements in an array is to use brackets just like accessing the elements of a list. We will learn later that we can also slice arrays much like we slice lists.
The following code creates a three-element, one-dimensional array and accesses and prints each of the elements in reverse order.
myArray = np.array([1,2,3])
print(myArray[2],myArray[1],myArray[0])
The following code modifies the contents of an array element using bracket notation just like with a list.
myArray = np.array([1,2,3])
print(myArray)
myArray[1] = 100
print(myArray)
This leads to some strange and possibly unexpected behavior. The following example creates an array containing two strings. Later it tries to replace one of the strings with an integer. However, instead of throwing an error, the integer is converted to a string and stored as a string even though it was not surrounded by single or double quotes.
myArray = np.array(['Hello','World'])
print(myArray)
myArray[1] = 200
print(myArray)
It doesn't work the other way around, however. The following code creates an array and populates it with two integers. Later it attempts to replace one of the integers with a string and an error is thrown. Perhaps this behavior results from the fact that you can always convert an integer into a string, but you can't always convert a string into an integer. (Note that it was necessary for me to disable the offending statement with a comment indicator to prevent the error from interfering with the remaining code execution on this Jupyter page.)
myArray = np.array([1,2,3])
print(myArray)
#myArray[1] = 'world'
print(myArray)
Let's take one more look at this topic. The following code attempts to create an array containing one string and one integer. The integer is automatically converted to a string and stored in the array as a string.
myArray = np.array(['hello',100])
print(myArray)
myArray[1] = 'world'
print(myArray)
Now let's create and access a two-dimensional array. I used strings to identify the row and column index in which each element resides. (Row and column indices begin with zero.) The code prints the shape of the array and then accesses and prints the contents of the element at row index = 1 and column index = 2. The output tells us that this array has two rows and three columns and that the string stored in the specified element is 'r1-c2'.
myArray = np.array([['r0-c0','r0-c1','r0-c2'],
['r1-c0','r1-c1','r1-c2']])
print(myArray)
print(myArray.shape)
print(myArray[1][2])
L = [1,2]
print("list =",L)
A = np.array([1,2])
print("array =",A)
Append a value to the list by calling the append method and print the result.
L = [1,2]
L.append(3)
print("list =",L)
You cannot append a value to an array by calling the append method on the array. An attempt to do so will throw an error. Once created, the size of an array is fixed. (Note that it was necessary for me to disable the offending statement with a comment indicator to prevent the error from interfering with the remaining code execution on this Jupyter page.)
A = np.array([1,2])
print("array =",A)
#A.append(3)
Add a Python list to a second Python list using the plus operator. Then print the result. The result is a longer list containing the concatenated elements from each of the original lists.
L1 = [1,2,3]
L2 = [4,5]
L3 = L1 + L2
print("L3 =",L3)
Now add two arrays using the plus operator and print the result. You can only add arrays that are the same size and the same shape. (See Broadcasting below for exceptions to this rule.) The result of adding two arrays of the same size and shape in this case is a new array where the elements in the new array are the sums of the corresponding elements in each of the two original arrays.
The plus sign with lists does concatenation but the plus sign with arrays does element-by-element addition (vector addition). Many of the functions available for processing arrays operate on an element-by-element basis.
A1 = np.array([1,2])
A2 = np.array([3,4])
A3 = A1 + A2
print("A3 =",A3)
The following code adds an array containing float data to an array containing integer data. This works okay because the integer data is upcast to float which is a legal operation.
A1 = np.array([1,2])
A2 = np.array([3.0,4.0])
A3 = A1 + A2
print("A3 =",A3)
The following code attempts to add an array containing string data to an array containing float data. As you may have guessed, this code throws an error. (Note that it was necessary for me to disable the offending statement with a comment indicator to prevent the error from interfering with the remaining code execution on this Jupyter page.)
A1 = np.array(['joe','sue'])
A2 = np.array([3.0,4.0])
#A3 = A1 + A2
print("A3 =",A3)
The previous explanation also holds true for multi-dimensional arrays. The following code creates and adds two 2D arrays. Then it prints the original arrays followed by the sum of the two arrays. In this case, the values for the two original arrays were chosen such that the values of each of the elements in the sum of the two arrays would be zero based on element-by-element addition.
A1 = np.array([[1,2],[3,4]])
A2 = np.array([[-1,-2],[-3,-4]])
A3 = A1 + A2
print("A1 =\n",A1)
print("A2 =\n",A2)
print("A3 =\n",A3)
The following code shows what happens when you multiply a list by a scalar. The code creates a list, multiplies it by a scalar, and prints the result. The result of multiplying a list by a scalar is a new list containing multiple copies of the elements in the original list where the number of copies is equal to the value of the scalar. This is equivalent to concatenating a list with itself one or more times. Multiplying a list by zero results in an empty list.
L = [1,2,3]*2
print("L =",L)
The following code creates an array, multiplies it by a scalar, and prints the result. The result of multiplying an array by a scalar is that each element in the array is individually multiplied by the scalar.
A1 = np.array([1,2,3])
print("A1 =",A1)
A2 = A1 * 2
print("A2 =",A2)
The following code computes and then prints the square of an integer.
Then the code attempts to compute and print the square of a list. However, you cannot directly square or take the square root of a list. Any attempt to do so will throw an error. (Note that it was necessary for me to disable the offending statements with comment indicators to prevent the error from interfering with the remaining code execution on this Jupyter page.)
K = 10
kSquare = K**2
print("kSquare =",kSquare)
m = [1,2]
#mSquare = m**2
#print("mSquare =",mSquare)
However you can square and take the square root of an array containing numeric data. When you do, the result is a set of new values in the array with the operation having been performed on an element-by-element basis.
The following code creates and populates an array, squares it, and prints the before and after results.
A1 = np.array([1.1,2.2,3.3])
print("A1 =",A1)
A2 = A1**2
print("A2 =",A2)
Simmilarly, the following code takes the square root of the squared array and prints the results. The resulting values of the elements in the output array match the values in the original array before it was squared in the code above.
A1 = np.array([1.1,2.2,3.3])
print("A1 =",A1)
A2 = A1**2
A3 = np.sqrt(A2)
print("A3 =",A3)
The numpy library provides a variety of functions such as sqrt, log, and exp that perform operations on the elements of an array on an element-by-element basis.
The following code creates two lists of the same size and attempts to multiply one by the other. However, you cannot multiply one list by another list. Any attempt to do so will throw an error. (Note that it was necessary for me to disable the offending statement with a comment indicator to prevent the error from interfering with the remaining code execution on this Jupyter page.)
L1 = [1,2]
L2 = [3,4]
#print(L1 * L2)
You can multiply one array by another array provided they are the same size and shape. (See exception under Multiplication of arrays of different shapes below.) This is an important operation, particularly in processes that involve linear algebra.
The following code creates, populates, and prints two one-dimensional arrays each having two elements. Then it multiplies the two arrays, creating a third one-dimensional array having two elements. That array is printed as well. As you can see, the result is a new array of the same size and shape as the two original arrays whose elements are the products of the corresponding elements in the two original arrays.
A1 = np.array([1,2])
print("A1 =",A1)
A2 = np.array([3,4])
print("A2 =",A2)
A3 = A1 * A2
print("A3 =",A3)
Multi-dimensional arrays can also be multiplied on an element-by-element basis provided that they are of the same size and shape. (See exception under Multiplication of arrays of different shapes below.) The following code creates and populates a pair of 2D arrays. Then it multiplies the two 2D arrays, prints each of those arrays, and prints the array resulting from the product of those two arrays. The result shows the element-by-element products of the corresponding elements in the two original arrays having been deposited in the elements of the new array.
A1 = np.array([[1, 2, 3], [4, 5, 6]])
A2 = np.array([[2, 3, 4], [5, 6, 7]])
A3 = A1 * A2
print("A1 =\n",A1)
print("A2 =\n",A2)
print("A3 =\n",A3)
There are other ways to create arrays in addition to those shown above. Some examples follow.
The following code creates an array with five rows and three columns populated with random values. Note the syntax of the first statement that lacks the typical np.array term.
myRandomArray = np.random.random((5,3))
print(myRandomArray)
The zeros method takes a tuple containing the dimensions of an array, creates an array having those dimensions, and fills the array with float values of zero. The following code creates a 3D array having two planes of three rows and four columns and fills the array with zeros. The dimensions are shown by printing the shape property.
myArrayOfZeros = np.zeros((2,3,4))
print(myArrayOfZeros)
print(myArrayOfZeros.shape)
The ones method accepts a tuple specifying the dimensions of an array, creates the array, and puts a float 1.0 in each array element. This is similar to the zeros method except that it fills the array with ones instead of zeros. The follow code creates an array having three planes of four rows and five columns and fills all the elements with ones.
myArrayOfOnes = np.ones((3,4,5))
print(myArrayOfOnes)
print(myArrayOfOnes.shape)
The method named full can be used to create an array that is full of a given value. The full method takes two arguments. The first argument is a tuple that specifies the dimensions of the desired array. The second argument specifies the value that is to be put into each element in the array. The following code creates an array with two rows and three columns where every element contains the string 'hello'.
myNonZeroArray = np.full((2,3),'hello')
print(myNonZeroArray)
print(myNonZeroArray.shape)
The eye method creates a matrix of the specified size and shape with a 1 in each diagonal position and a 0 in every other position. This is a special matrix that is often needed in matrix algebra. Note in particular the name of the method that is used to accomplish this. Compare this name with the name of the method used to create an array full of ones as discussed above.
mySpecialMatrix = np.eye(4,4)
print(mySpecialMatrix)
print(mySpecialMatrix.shape)
As I mentioned earlier, you can access an array using slicing in much the same way that you can use slicing to access the elements in a list.
The following code creates a 3 by four array of strings. It uses ordinary indexing to access and print four contiguous elements from the array. Then it uses slicing to extract a subarray containing the same four elements and prints the subarray.
myArray = np.array([['r0-c0','r0-c1','r0-c2','r0-c3'],
['r1-c0','r1-c1','r1-c2','r1-c3'],
['r2-c0','r2-c1','r2-c2','r2-c3']])
print('Print the array and its shape')
print(myArray)
print(myArray.shape)
print('Print four elements in the array')
print(myArray[0][1],myArray[0][2])
print(myArray[1][1],myArray[1][2])
print('Use slicing to extract a subarray containing the same four elements and print it')
mySubArray = myArray[0:2, 1:3] #Slice by row then by column.
print(mySubArray)
The following code extracts the same subarray as above and then used ordinary indexing to access its elements.
myArray = np.array([['r0-c0','r0-c1','r0-c2','r0-c3'],
['r1-c0','r1-c1','r1-c2','r1-c3'],
['r2-c0','r2-c1','r2-c2','r2-c3']])
print('Print the array and its shape')
print(myArray)
print(myArray.shape)
print('Print four elements in the array')
print(myArray[0][1],myArray[0][2])
print(myArray[1][1],myArray[1][2])
print('Use slicing to extract a subarray containing the same four elements and print it')
mySubArray = myArray[0:2, 1:3] #Slice by row then by column.
print(mySubArray)
print('Access all four elements in the subarray and print them')
print(mySubArray[0][0],mySubArray[0][1])
print(mySubArray[1][0],mySubArray[1][1])
When you extract a subarray by slicing an array using the code shown below, you don't create a new array. Instead you simply create a new reference to a rectangular portion of the original array. If you modify an element in the subarray, you are in fact modifying an element in the original array. This is a very important distinction that can lead to subtle programming errors if not fully understood and taken into account when programming.
That is illustrated by the following code.
myArray = np.array([['r0-c0','r0-c1','r0-c2','r0-c3'],
['r1-c0','r1-c1','r1-c2','r1-c3'],
['r2-c0','r2-c1','r2-c2','r2-c3']])
print('Print the array and its shape')
print(myArray)
print(myArray.shape)
print('Print four elements in the array')
print(myArray[0][1],myArray[0][2])
print(myArray[1][1],myArray[1][2])
print('Use slicing to extract a subarray containing the same four elements and print it')
mySubArray = myArray[0:2, 1:3] #Slice by row then by column.
print(mySubArray)
print('Access all four elements in the subarray and print them')
print(mySubArray[0][0],mySubArray[0][1])
print(mySubArray[1][0],mySubArray[1][1])
print('Modify an element in the subarray and print it')
mySubArray[0][0] = 'Oops!'
print(mySubArray)
print('Now print the original array and note that it too is modified')
print(myArray)
You can also use slicing to extract a rectangular portion of an array into a subarray that is independent of the original array using the code shown below. The following code extracts and modifies an element in a subarray without modifying an element in the original array. This is a very important distinction that can lead to subtle programming errors if not fully understood and taken into account when programming.
myArray = np.array([['r0-c0','r0-c1','r0-c2','r0-c3'],
['r1-c0','r1-c1','r1-c2','r1-c3'],
['r2-c0','r2-c1','r2-c2','r2-c3']])
print('Print the array and its shape')
print(myArray)
print(myArray.shape)
print('Print four elements in the array')
print(myArray[0][1],myArray[0][2])
print(myArray[1][1],myArray[1][2])
print('Use slicing to extract a subarray containing the same four elements into ')
print('an independent array and print it')
mySubArray = np.array(myArray[0:2, 1:3])
print(mySubArray)
print('Access all four elements in the subarray and print them')
print(mySubArray[0][0],mySubArray[0][1])
print(mySubArray[1][0],mySubArray[1][1])
print('Modify an element in the subarray and print it')
mySubArray[0][0] = 'Oops!'
print(mySubArray)
print('Now print the original array and note that it has not been modified')
print(myArray)
The following code shows how you can combine indexing with slicing to extract a rectangular subarray from an array.
myArray = np.array([['r0-c0','r0-c1','r0-c2','r0-c3'],
['r1-c0','r1-c1','r1-c2','r1-c3'],
['r2-c0','r2-c1','r2-c2','r2-c3']])
print('Print the array and its shape')
print(myArray)
print(myArray.shape)
print('Print two elements in the array')
print(myArray[2][1],myArray[2][2])
print('Combine indexing and slicing to extract a subarray containing the same ')
print('two columns from from row 2 and print it')
mySubArray = myArray[2, 1:3] #Combine indexing and slicing
print(mySubArray)
Suppose you needed to know how many times the string 'tom' occurs in an array. You can create a boolean filter, apply it to the array, and call the size method on the resulting array to get your answer. This is illustrated in the following code.
myArray = np.array([['atom','bill','tom','r0-c3'],
['r1-c0','r1-c1','tommy gun','r1-c3'],
['Tom','tom','joe','r2-c3']])
print('Print the array')
print(myArray)
print('Create and print a filter, which will consist of an array of the same size and ')
print('shape but containing only True and False elements.')
filter = [(myArray == 'tom')]
print(filter)
print('Apply the filter and print the result, which is a one-dimensional array containing')
print('all of the matching elements extracted from the original array')
filteredArray = myArray[filter]
print(filteredArray)
print('Print the size property of the resulting array, which is the number of matching')
print('elements.')
print("size =",filteredArray.size)
print('You can do the same thing with a shorter version.')
print('Use a shorter version and print the results')
print(myArray[myArray == 'tom'])
Suppose you needed to know how many times the string 'tom' or the string 'Tom' occurs in an array. Again, you can create a boolean filter, apply it to the array, and call the size method on the resulting array to get your answer. This is illustrated in the following code.
myArray = np.array([['atom','bill','tom','r0-c3'],
['r1-c0','r1-c1','tommy gun','r1-c3'],
['Tom','tom','joe','r2-c3']])
print('Print the array')
print(myArray)
print('Create and print a filter, which will consist of an array of the same size and ')
print('shape but containing only True and False elements.')
#For the record, note that this is a bitwise or operator, not a logical or operator.
filter = [(myArray == 'tom') | (myArray == 'Tom')]
print(filter)
print('Apply the filter and print the result, which is a one-dimensional array containing')
print('all of the matching elements extracted from the original array')
filteredArray = myArray[filter]
print(filteredArray)
print('Print the size property of the resulting array, which is the number of matching')
print('elements.')
print("size =",filteredArray.size)
Suppose you need to replace the strings 'tom' and 'Tom' in an array with the string 'Thomas. This can be accomplished with the following code.
myArray = np.array([['atom','bill','tom','r0-c3'],
['r1-c0','r1-c1','tommy gun','r1-c3'],
['Tom','tom','joe','r2-c3']])
print('Print the array')
print(myArray)
print("Create a filter that will change 'tom' or 'Tom' to 'Thomas' in the original array.")
filter = [(myArray == 'tom') | (myArray == 'Tom')]
myArray[(myArray == 'tom') | (myArray == 'Tom')] = 'Thomas'
print('Apply the filter to modify the array.')
myArray[filter]
print('Print the modified array')
print(myArray)
We learned earlier that all elements in a numpy array must be the same type. We saw that if you try to create an array containing strings and integers, the integers will be convrted to strings by default.
The following code illustrates how to force an array being populated with strings and integers to be type np.float64. (Note that if the string cannot be converted to a float, this code will throw an error.)
This might be useful, for example, if you read a file of numeric strings and you need to convert them to floats (or integers, dtype=np.int64) in the process of saving them in an array.
floatArray = np.array(['123',100], dtype = np.float64)
print("floatArray =",floatArray)
print("floatArray.dtype =",floatArray.dtype)
Let's begin by computing some mean values, sums, and medians of the elements in an array.
Note: Given a vector V of length N, the median of V is the middle value of a sorted copy of V when N is odd, and the average of the two middle values of a sorted copy of V when N is even. The median of all the values in a multidimensional array is the median of a flattened version of the array.
myArray = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
print(myArray)
print(myArray.shape)
print("mean of all elements =",myArray.mean())
print("means by row =",myArray.mean(axis = 1))
print("means by column =",myArray.mean(axis = 0))
print("sum of all elements =",myArray.sum())
print("sums by row =",myArray.sum(axis = 1))
print("sums by column =",myArray.sum(axis = 0))
print("median of all elements =",np.median(myArray))
print("medians by row =",np.median(myArray, axis = 1))
print("medians by column =",np.median(myArray, axis = 0))
The array sort method is an in-place sort. This means that the method modifies the array on which it is called. The following code makes a copy of a one-dimensional array, sorts it, and prints the sorted array.
originalArray = np.array([12,11,10,9,8,7,6,5,4,3,2,1])
print("originalArray =",originalArray)
tempArray = np.array(originalArray)
print("unsorted tempArray",tempArray)
tempArray.sort()
print("sorted tempArray =",tempArray)
The array sort method can also be used to sort a two-dimensional array by row or by column. The following code makes a copy of a 2D array, sorts it, and prints the resulting sorted array. It makes another copy of the original array, sorts it by column, and prints the resulting sorted array.
originalArray = np.array([[9,8,7],[6,5,4],[3,2,1]])
print("originalArray =\n",originalArray)
print()
tempArray = np.array(originalArray)
print("unsorted tempArray =\n",tempArray)
print()
print("Sort tempArray by rows, axis = 1")
tempArray.sort(axis=1)
print("sorted tempArray\n",tempArray)
print()
tempArray = np.array(originalArray)
print("unsorted tempArray =\n",tempArray)
print()
print("Sort tempArray by columns, axis = 0")
tempArray.sort(axis=0)
print("sorted tempArray\n",tempArray)
print()
The unique method can be used to extract the unique elements from a single or multi-dimensional array into a one-dimensional array. This is illustrated by the following code, which reduces the nine elements in a 2D array to the five unique elements in a one-dimensional array by eliminating duplicates.
myArray = np.array([['bill','tom','sue'],['sam','bill','john'],['sue','john','tom']])
print("myArray =\n",myArray)
uniqueElementArray = np.unique(myArray)
print("uniqueElementArray =",uniqueElementArray)
You can perform set operations on arrays.
We will begin with intersection. The intersect1d method finds the intersection of two arrays and returns the sorted, unique values that are in both of the input arrays in a one-dimensional array. The use of this method is illustrated in the following code.
Note that this method and several other method in the following sections return their results in a one-dimensional array, regardless of the dimensions of the arrays being processed.
array01 = np.array([['tom','dick','harry'],['bill','joe','sue']])
array02 = np.array([['bill','harry','tom'],['amos','charley','thomas']])
print(array01)
print()
print(array02)
print()
array03 = np.intersect1d(array01,array02)
print(array03)
array01 = np.array([['tom','dick','harry'],['bill','joe','sue']])
array02 = np.array([['bill','harry','tom'],['amos','charley','thomas']])
print(array01)
print()
print(array02)
print()
array03 = np.union1d(array01,array02)
print(array03)
The setdiff1d(ar1,ar2) method finds the set difference of two arrays and returns a one-dimensional array containing the sorted, unique values in ar1 that are not in ar2. The use of this method is illustrated in the following code. Note that the order of the input arrays is important in this case.
array01 = np.array([['tom','dick','harry'],['bill','joe','sue']])
array02 = np.array([['bill','harry','tom'],['amos','charley','thomas']])
print(array01)
print()
print(array02)
print()
array03 = np.setdiff1d(array01,array02)
print(array03)
print()
array04 = np.setdiff1d(array02,array01)
print(array04)
array01 = np.array([['tom','dick','harry'],['bill','joe','sue']])
array02 = np.array([['bill','harry','tom'],['amos','charley','thomas']])
print(array01)
print()
print(array02)
print()
array03 = np.setxor1d(array01,array02)
print(array03)
Broadcasting allows you to perform arithmetic operations on arrays of different shapes. "Subject to certain constraints, the smaller array is “broadcast” across the larger array so that they have compatible shapes." The general broadcasting rules are available here. This section provides examples that comply with those rules.
The following code adds 1x3 row array to each row of a 2x3 array producing a 2x3 array as a result.
A1 = np.array([[1, 2, 3], [4, 5, 6]])
print(A1)
print("A1.shape =",A1.shape)
print()
A2 = np.array([[2, 3, 4]])
print(A2)
print("A2.shape =",A2.shape)
print()
A3 = A1 + A2
print("A3 = A1 + A2 =\n",A3)
print("A3.shape =",A3.shape)
The following code adds 1x3 column array to each column of a 3x3 array producing a 3x3 array as a result.
A1 = np.array([[1, 2, 3], [4, 5, 6],[7,8,9]])
print(A1)
print("A1.shape =",A1.shape)
print()
A2 = np.array([[2, 3, 4]])
#Transpose A2 to convert it to a column array
A2 = A2.T
print(A2)
print("A2.shape =",A2.shape)
print()
A3 = A1 + A2
print("A3 = A1 + A2 =\n",A3)
print("A3.shape =",A3.shape)
The following code adds 1x1 array to each element of a 3x3 array producing a 3x3 array as a result.
A1 = np.array([[1, 2, 3], [4, 5, 6],[7,8,9]])
print(A1)
print("A1.shape =",A1.shape)
print()
A2 = np.array([5])
print(A2)
print("A2.shape =",A2.shape)
print()
A3 = A1 + A2
print("A3 = A1 + A2 =\n",A3)
print("A3.shape =",A3.shape)
It isn't always necessary for two arrays to be the same shape to multiply them. The following code multiplies a 2x3 array by a 1x3 array producing a 2x3 array as a result. This is a common operation in matrix algebra.
A1 = np.array([[1, 2, 3], [4, 5, 6]])
print("A1.shape =",A1.shape)
A2 = np.array([[2, 3, 4]])
print("A2.shape =",A2.shape)
A3 = A1 * A2
print("A3 = A1 * A2 =\n",A3)
print("A3.shape =",A3.shape)
This section provides examples of several miscellaneous operations on arrays.
A one-dimensional array can be treated as a vector in linear algebra. The sum of the products of two vectors is something called the dot product. This is illustrated below.
The following code creates two vectors as one-dimensional numpy arrays, prints them, multiplies them, prints the array containing the products, sums the products and prints the result as the dot product.
V1 = np.array([1,2])
print("V1 =",V1)
V2 = np.array([3,4])
print("V2 =",V2)
P1 = V1*V2
print("P1 =",P1)
DP1 = np.sum(P1)
print("DP1 =",DP1)
This is such a common operation in linear algebra that there is a numpy function for computing the dot product between two vectors. That function, can be used to replace some of the code shown above. The following code uses the function to compute the dot product of two vectors that are the same as defined above and prints the result. As expected, the result matches that shown above.
V1 = np.array([1,2])
print("V1 =",V1)
V2 = np.array([3,4])
print("V2 =",V2)
DP2 = V1.dot(V2)
print("DP2 =",DP2)
One of the uses of the dot product is to compute the cosine of the angle between two vectors in a multidimensional space. Once the cosine of the angle is obtained, the angle between the two vectors can be determined.
The following code creates two vectors. V1 has a length of 1.414 and an angle of 45 degrees relative to the horizontal axis. V2 has the same length and lies at an angle of -45 degrees relative to the horizontal axis. Thus the angle between the two is 90 degrees. The cosine of 90 degrees is 0, which is the value produced by the dot product of the two vectors.
V1 = np.array([1,1])#plus 45 degrees
V2 = np.array([1,-1])#minus 45 degrees
DP = V1.dot(V2)
print("V1 =",V1)
print("V2 =",V2)
print("DP =",DP)
The following code creates two vectors. V1 has a length of 1.0 and an angle of 45 degrees relative to the horizontal axis. V2 has the same length and lies at an angle of 0 degrees relative to the horizontal axis. Thus the angle between the two is 45 degrees. The cosine of 45 degrees is 0.707, which is the value produced by the dot product of the two vectors.
V1 = np.array([0.707,0.707])
V2 = np.array([1,0])
DP = V1.dot(V2)
print("V1 =",V1)
print("V2 =",V2)
print("DP =",DP)
The following example is more expansive. This code creates two vectors. V1 has coordinates of (1, 1.73205). You may recognize the second coordinate value as the square root of 3. This results in a vector with a length of 2.0 as computed using the follow equation:
$$L1 = \sqrt{x1^2 + y1^2}$$V2 has coordinates of (1,0) resulting in a length of 1.0. The normalized dot product of the two vectors is used to compute and print the angle between the two vectors in radians and the angle in degrees. The comments in the code provide additional explanation. As you can see, the angle between the vectors is 60 degrees.
#Declare coordinates of first vector, create the vector as an array, compute and print the
# length
x1 = 1.0
y1 = np.sqrt(3)
V1 = np.array([x1,y1])
print("V1 =",V1)
L1 = np.sqrt(x1*x1 + y1*y1)
print("L1 =",L1)
#Declare coordinates of second vector, create the vector as an array, compute and print the
# length
x2 = 1.0
y2 = 0
V2 = np.array([x2,y2])
print("V2 =",V2)
L2 = np.sqrt(x2*x2 + y2*y2)
print("L2 = ",L2)
#Compute and print the dot product of the two vectors
DP = V1.dot(V2)
print("DP =",DP)
#The cosine of the angle between the two vectors is the dot product normalized by the
#product of the lengths of the two vectors. Compute and print the cosine of the angle
#between the two vectors.
CosineOfAngle = DP/(L1 * L2)
print("CosineOfAngle = ",CosineOfAngle)
#Compute and print the angle in radians.
AngleInRadians = np.arccos(CosineOfAngle)
print("AngleInRadians = ",AngleInRadians)
#Compute and print the angle in degrees.
AngleInDegrees = AngleInRadians*180/np.pi
print("AngleInDegrees =",AngleInDegrees)
The following code compares the execution speed of array processing with the execution speed of list processing for a specific type of operation.
A list and an array are each populated with the same set of 1000000 random values from a standard normal distribution. The sum of the values contained in the list is computed and the time required to do that is recorded.
Then the sum of the values contained in the array is computed and the time required to do that is recorded.
The ratio of the two times is computed, showing that the time required to process the list for this operation is much greater than the time required to process the array.
Finally, the sums computed from the list and the array are printed to confirm that the results of the two processes were the same.
The main takeaway is, if speed is an issue, always use numpy arrays and the functions available for processing numpy arrays instead of using Python lists if possible.
(For more accurate estimates of execution time, you should probably use the Python's Timer function.)
#Populate a list with 1000000 normally distributed random values
np.random.seed([1])
aList = np.random.randn(1000000)
#Populate an array with the same 1000000 normally distributed random values
np.random.seed([1])
anArray = np.array([np.random.randn(1000000)])
#Get current time
time0 = datetime.now()
#Compute the sum of the values in the list
listSum = sum(aList)
#Get the current time and compute the time required to process the list.
time1 = datetime.now()
listDeltaTime = time1-time0
print("listDeltaTime =",listDeltaTime)
#Get the current time
time0 = datetime.now()
#Compute the sum of the values in the array
arraySum = np.sum(anArray)
#Get the current time and compute the time required to process the array.
time2 = datetime.now()
arrayDeltaTime = time2-time0
print("arrayDeltaTime =",arrayDeltaTime)
#Print the ratio of the time required to process the list to the time required
#to process the array.
print("listDeltaTime/arrayDeltaTime =",listDeltaTime/arrayDeltaTime)
#Print the two sums to confirm that they match.
print("listSum =",listSum)
print("arraySum =",arraySum)
Author: Prof. Richard G. Baldwin, Austin Community College, Austin, TX
File: NumpyArrays01.ipynb
Revised: 04/22/18
Copyright 2018 Richard G. Baldwin