7

NumPy

Everything should be as simple as it can be, but not simpler.

Roger Sessions (interpreting Einstein)

In This Chapter

This is the first of this book’s chapters on Data Science Libraries. The Python functionality explored so far in this book makes Python a powerful generic language. The libraries covered in this part of the book make Python dominant in data science. The first library we will look at, NumPy, is the backbone of many of the other data science libraries. In this chapter, you will learn about the NumPy array, which is an efficient multidimensional data structure.

Third-Party Libraries

Python code is organized into libraries. All of the functionality you have seen so far in this book is available in the Python Standard Library, which is part of any Python installation. Third-party libraries give you capabilities far beyond this. They are developed and maintained by groups outside the organization that maintains Python itself. The existence of these groups and libraries creates a vibrant ecosystem that has kept Python a dominant player in the programming world. Many of these libraries are available in the Colab environment, and you can easily import them into a file. If you are working outside Colab, you may need to install them, which generally is done using the Python package manager, pip.

Installing and Importing NumPy

NumPy is preinstalled in the Colab environment, and you just need to import it. If you are working outside Colab, there are a few different ways to install it (enumerated at https://scipy.org/install.html), but the most common is to use pip:

pip install numpy

Once you have NumPy installed, you can import it. When you import any library, you can change what it is called in your environment by using the keyword as. NumPy is typically renamed np during import:

import numpy as np

When you have the library installed and imported, you can then access any of NumPy’s functionality through the np object.

Creating Arrays

A NumPy array is a data structure that is designed to efficiently handle operations on large data sets. These data sets can be of varying dimensions and can contain numerous data types—though not in the same object. NumPy arrays are used as input and output to many other libraries and are used as the underpinning of other data structures that are important to data science, such as those in Pandas and SciPy.

You can create arrays from other data structures or initialized with set values. Listing 7.1 demonstrates different ways to create a one-dimensional array. You can see that the array object is displayed as having an internal list as its data. Data is not actually stored in lists, but this representation makes arrays easy to read.

Listing 7.1 Creating an Array

np.array([1,2,3])      # Array from list
array([1, 2, 3])

np.zeros(3)            # Array of zeros 
array([0., 0., 0.])

np.ones(3)             # Array of ones 
array([1., 1., 1.])

np.empty(3)            # Array of arbitrary data 
array([1., 1., 1.])

np.arange(3)           # Array from range of numbers 
array([0, 1, 2])

np.arange(0, 12, 3)    # Array from range of numbers 
array([0, 3, 6, 9])

np.linspace(0, 21, 7)  # Array over an interval 
array([ 0. ,  3.5,  7. , 10.5, 14. , 17.5, 21. ])

Arrays have dimensions. A one-dimensional array has only one dimension, which is the number of elements. In the case of the np.array method, the dimension matches that of the list(s) used as input. For the np.zeros, np.ones, and np.empty methods, the dimension is given as an explicit argument.

The np.range method produces an array in a way similar to a range sequence. The resulting dimension and values match those that would be produced by using range. You can specify beginning, ending, and step values.

The np.linspace method produces evenly spaced numbers over an interval. The first two arguments define the interval, and the third defines the number of items.

The np.empty method is useful in producing large arrays efficiently. Keep in mind that because the data is arbitrary, you should only use it in cases where you will replace all of the original data.

Listing 7.2 shows some of the attributes of an array.

Listing 7.2 Characteristics of an Array

oned = np.arange(21)
oned
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
        11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ])

oned.dtype     # Data type 
dtype('int64')

oned.size      # Number of elements 
21

oned.nbytes    # Bytes(memory) consumed by elements of the array 
168

oned.shape     # Number of elements in each dimension 
(21,)

oned.ndim      # Number of dimensions 
1

If you check the data type of the array, you see that it is np.ndarray:

type(oned)
numpy.ndarray

Note

ndarray is short for n-dimensional array.

As mentioned earlier, you can make arrays of many dimensions. Two-dimensional arrays are used as matrixes. Listing 7.3 creates a two-dimensional array from a list of three three-element lists. You can see that the resulting array has 3×3 shape and two dimensions.

Listing 7.3 Matrix from Lists

list_o_lists = [[1,2,3],
                [4,5,6],
                [7,8,9]]

twod = np.array(list_o_lists)
twod
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

twod.shape
(3, 3)

twod.ndim
2

You can produce an array with the same elements but different dimensions by using the reshape method. This method takes the new shape as arguments. Listing 7.4 demonstrates using a one-dimensional array to produce a two-dimensional one and then producing one-dimensional and three-dimensional arrays from the two-dimensional one.

Listing 7.4 Using reshape

oned = np.arange(12)
oned
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

twod = oned.reshape(3,4)
twod
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

twod.reshape(12)
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

twod.reshape(2,2,3)
array([[[ 0,  1,  2],
        [ 3,  4,  5]],
       [[ 6,  7,  8],
       [ 9, 10, 11]]])

The shape you provide for an array must be consistent with the number of elements in it. For example, if you take the 12-element array twod and try to set its dimensions with a shape that does not include 12 elements, you get an error:

twod.reshape(2,3)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-295-0b0517f762ed> in <module>
----> 1 twod.reshape(2,3)

ValueError: cannot reshape array of size 12 into shape (2,3)

Reshaping is commonly used with the np.zeros, np.ones, and np.empty methods to produce multidimensional arrays with default values. For example, you could create a three-dimensional array of ones like this:

np.ones(12).reshape(2,3,2)
array([[[1., 1.],
        [1., 1.],
        [1., 1.]],
       [[1., 1.],
        [1., 1.],
        [1., 1.]]])

Indexing and Slicing

You can access the data in arrays by indexing and slicing. In Listing 7.5, you can see that indexing and slicing with a one-dimensional array is the same as with a list. You can index individual elements from the start or end of an array by supplying an index number or multiple elements using a slice.

Listing 7.5 Indexing and Slicing a one-Dimensional Array

oned = np.arange(21)
oned
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 
           11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ])

oned[3]
3

oned[-1]
20

oned[3:9]
array([3, 4, 5, 6, 7, 8])

For multidimensional arrays, you can supply one argument for each dimension. If you omit the argument for a dimension, it defaults to all elements of that dimension. So, if you supply a single number as an argument to a two-dimensional array, that number will indicate which row to return. If you supply single-number arguments for all dimensions, a single element is returned. You can also supply a slice for any dimension. In return you get a subarray of elements, whose dimensions are determined by the length of your slices. Listing 7.6 demonstrates various options for indexing and slicing a two-dimensional array.

Listing 7.6 Indexing and Slicing a Two-Dimensional Array

twod = np.arange(21).reshape(3,7)
twod
array([[ 0,  1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12, 13],
       [14, 15, 16, 17, 18, 19, 20]])

twod[2]           # Accessing row 2
array([14, 15, 16, 17, 18, 19, 20])

twod[2, 3]        # Accessing item at row 2, column 3
17

twod[0:2]         # Accessing rows 0 and 1
array([[ 0,  1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12, 13]])

twod[:, 3]        # Accessing column 3 of all rows 
array([ 3, 10, 17])

twod[0:2, -3:]    # Accessing the last three columns of rows 0 and 1
array([[ 4,  5,  6],
       [11, 12, 13]])

You can assign new values to an existing array, much as you would with a list, by using indexing and slicing. If you assign a values to a slice, the whole slice is updated with the new value. Listing 7.7 demonstrates how to update a single element and a slice of a two-dimensional array.

Listing 7.7 Changing Values in an Array

twod = np.arange(21).reshape(3,7)
twod
array([[ 0,  1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12, 13],
       [14, 15, 16, 17, 18, 19, 20]])

twod[0,0] = 33
twod
array([[33,  1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12, 13],
       [14, 15, 16, 17, 18, 19, 20]])

twod[1:,:3] = 0
array([[33,  1,  2,  3,  4,  5,  6],
       [ 0,  0,  0, 10, 11, 12, 13],
       [ 0,  0,  0, 17, 18, 19, 20]])

Element-by-Element Operations

An array is not a sequence. Arrays do share some characteristics with lists, and on some level it is easy to think of the data in an array as a list of lists. There are many differences between arrays and sequences, however. One area of difference is when performing operations between the items in two arrays or two sequences.

Remember that when you do an operation such as multiplication with a sequence, the operation is done to the sequence, not to its contents. So, if you multiply a list by zero, the result is a list with a length of zero:

 [1, 2, 3]*0
 []

You cannot multiply two lists, even if they are the same length:

 [1, 2, 3]*[4, 5, 6]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-325-f525a1e96937> in <module>
----> 1 [1, 2, 3]*[4, 5, 6]

TypeError: can't multiply sequence by non-int of type 'list'

You can write code to perform operations between the elements of lists. For example, Listing 7.8 demonstrates looping through two lists in order to create a third list that contains the results of multiple pairs of elements. The zip() function is used to combine the two lists into a list of tuples, with each tuple containing elements from each of the original lists.

Listing 7.8 Element-by-Element Operations with Lists

L1 = list(range(10))
L2 = list(range(10, 0, -1))
L1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

L2
 [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

L3 = []
for i, j in zip(L1, L2):
L3.append(i*j)
L3
[0, 9, 16, 21, 24, 25, 24, 21, 16, 9]

While it is possible to use loops to perform element-by-element operations on lists, it is much simpler to use NumPy arrays for such operations. Arrays do element-by-element operations by default. Listing 7.9 demonstrates multiplication, addition, and division operations between two arrays. Notice that the operations in each case are done between the elements of the arrays.

Listing 7.9 Element-by-Element Operations with Arrays

array1 = np.array(L1)
array2 = np.array(L2)
array1*array2
array([ 0,  9, 16, 21, 24, 25, 24, 21, 16,  9])

array1 + array2
array([10, 10, 10, 10, 10, 10, 10, 10, 10, 10])

array1 / array2
array([0.        , 0.11111111, 0.25      , 0.42857143, 0.66666667,
       1.        , 1.5       , 2.33333333, 4.        , 9.        ])

Filtering Values

One of the most used aspects of NumPy arrays and the data structures built on top of them is the ability to filter values based on conditions of your choosing. In this way, you can use an array to answer questions about your data.

Listing 7.10 shows a two-dimensional array of integers, called twod. A second array, mask, has the same dimensions as twod, but it contains Boolean values. mask specifies which elements from twod to return. The resulting array contains the elements from twod whose corresponding positions in mask have the value True.

Listing 7.10 Filtering Using Booleans

twod = np.arange(21).reshape(3,7)
twod
array([[ 0,  1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12, 13],
       [14, 15, 16, 17, 18, 19, 20]])

mask = np.array([[ True,  False,  True,  True,  False, True, False],
                 [ True,  False,  True,  True,  False, True, False],
                 [ True,  False,  True,  True,  False, True, False]])
twod[mask]
array([ 0,  2,  3,  5,  7,  9, 10, 12, 14, 16, 17, 19])

Comparison operators that you have seen returning single Booleans before return arrays when used with arrays. So, if you use the less-than operator (<) against the array twod as follows, the result will be an array with True for every item that is below five and False for the rest:

twod < 5

You can use this result as a mask to get only the values that are True with the comparison. For example, Listing 7.11 creates a mask and then returns only the values of twod that are less than 5.

Listing 7.11 Filtering Using Comparison

mask = twod < 5
mask
array([[ True,  True,  True,  True],
       [ True, False, False, False],
       [False, False, False, False]])

twod[mask]
array([0, 1, 2, 3, 4])

As you can see, you can use comparison and order operators to easily extract knowledge from data. You can also combine these comparisons to create more complex masks. Listing 7.12 uses & to join two conditions to create a mask that evaluates to True only for items meeting both conditions.

Listing 7.12 Filtering Using Multiple Comparisons

mask = (twod < 5) & (twod%2 == 0)
mask
array([[ True, False,  True, False],
       [ True, False, False, False],
       [False, False, False, False]])

twod[mask]
array([0, 2, 4])

Note

Filtering using masks is a process that you will use time and time again, especially with Pandas DataFrames, which are built on top of NumPy arrays. You will learn about DataFrames in Chapter 9, “Pandas.”

Views Versus Copies

NumPy arrays are designed to work efficiently with large data sets. One of the ways this is accomplished is by using views. When you slice or filter an array, the returned array is, when possible, a view and not a copy. A view allows you to look at the same data differently. It is important to understand that memory and processing power are not used in making copies of data every time you slice or filter. If you change a value in a view of an array, you change that value in the original array as well as any other views that represent that item. For example, Listing 7.13 takes a slice from the array data1 and names it data2. It then replace the value 11 in data2 with -1. When you go back to data1, you can see that the item that used to have a value of 11 is now set to -1.

Listing 7.13 Changing Values in a View

data1 = np.arange(24).reshape(4,6)
data1
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])

data2 = data1[:2,3:]
data2
array([[ 3,  4,  5],
       [ 9, 10, 11]])

data2[1,2] = -1
data2
array([[ 3,  4,  5],
       [ 9, 10, -1]])

data1
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, -1],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])

This behavior can lead to bugs and miscalculations, but if you understand it, you can gain some important benefits when working with large data sets. If you want to change data from a slice or filtering operation without changing it in the original array, you can make a copy. For example, in Listing 7.14, notice that when an item is changed in the copy, the original array remains unchanged.

Listing 7.14 Changing Values in a Copy

data1 = np.arange(24).reshape(4,6)
data1
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])

data2 = data1[:2,3:].copy()
data2
array([[ 3,  4,  5],
       [ 9, 10, 11]])

data2[1,2] = -1
data2
array([[ 3,  4,  5],
       [ 9, 10, -1]])

data1
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])

Some Array Methods

NumPy arrays have built-in methods both to get statistical summary data and to perform matrix operations. Listing 7.15 shows methods producing summary statistics. There are methods to get the maximum, minimum, sum, mean, and standard deviation. All these methods produce results across the whole array unless an axis is specified. If an axis value of 1 is specified, an array with results for each row is produced. With an axis value of 0, an array of results is produced for each column.

Listing 7.15 Introspection

data = np.arange(12).reshape(3,4)
data
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

data.max()         # Maximum value
11

data.min()         # Minimum value 
0

data.sum()         # Sum of all values 
66

data.mean()        # Mean of values 
5.5

data.std()         # Standard deviation 
3.452052529534663

data.sum(axis=1)   # Sum of each row 
array([ 6, 22, 38])

data.sum(axis=0)   # Sum of each column 
array([12, 15, 18, 21])

data.std(axis=0)   # Standard deviation of each row 
array([3.26598632, 3.26598632, 3.26598632, 3.26598632])

data.std(axis=1))  # Standard deviation of each column 
array([1.11803399, 1.11803399, 1.11803399])

Listing 7.16 demonstrates some of the matrix operations that are available with arrays. These include returning the transpose, returning matrix products, and returning the diagonal. Remember that you can use the multiplication operator (*) between arrays to perform element-by-element multiplication. If you want to calculate the dot product of two matrices, you need to use the @ operator or the .dot() method.

Listing 7.16 Matrix Operations

A1 = np.arange(9).reshape(3,3)
A1
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

A1.T             # Transpose 
array([[0, 3, 6],
       [1, 4, 7],
       [2, 5, 8]])

A2 = np.ones(9).reshape(3,3)
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])

A1 @ A2          # Matrix product 
array([[ 3.,  3.,  3.],
       [12., 12., 12.],
       [21., 21., 21.]])

A1.dot(A2)       # Dot product 
array([[ 3.,  3.,  3.],
       [12., 12., 12.],
       [21., 21., 21.]])

A1.diagonal()    # Diagonal 
array([0, 4, 8])

An array, unlike many sequence types, can contain only one data type. You cannot have an array that contains both strings and integers. If you do not specify the data type, NumPy guesses the type, based on the data. Listing 7.17 shows that when you start with integers, NumPy sets the data type to int64. You can also see, by checking the nbytes attribute, that the data for this array takes 800 bytes of memory.

Listing 7.17 Setting Type Automatically

darray = np.arange(100)
darray
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
        34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
        51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
        68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
        85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99])

darray.dtype
dtype('int64')

darray.nbytes
800

For lager data sets, you can control the amount of memory used by setting the data type explicitly. The int8 data type can represent numbers from –128 to 127, so it would be adequate for a data set of 1–99. You can set an array’s data type at creation by using the parameter dtype. Listing 7.18 does this to bring the size of the data down to 100 bytes.

Listing 7.18 Setting Type Explicitly

darray = np.arange(100, dtype=np.int8)
darray
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
        34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
        51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
        68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
        85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99],                
        dtype=int8)

darray.nbytes
100

Note

You can see the many available NumPy data types at https://numpy.org/devdocs/user/basics.types.html.

Because an array can store only one data type, you cannot insert data that cannot be cast to that data type. For example, if you try to add a string to the int8 array, you get an error:

darray[14] = 'a'
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-335-17df5782f85b> in <module>
----> 1 darray[14] = 'a'

ValueError: invalid literal for int() with base 10: 'a'

A subtle error with array type occurs if you add to an array data of a finer granularity than the array’s data type; this can lead to data loss. For example, say that you add the floating-point number 0.5 to the int8 array:

darray[14] = 0.5

The floating-point number 0.5 is cast to an int, which leaves a value of 0:

darray[14]
0

As you can see, it is important to understand your data when deciding on the best data type.

Broadcasting

You can perform operations between arrays of different dimensions. Operations can be done when the dimension is the same or when the dimension is one for at least one of the arrays. Listing 7.19 adds 1 to each element of the array A1 three different ways: first with an array of ones with the same dimensions (3, 3), then with an array with one dimension of one (1, 3), and finally by using the integer 1.

Listing 7.19 Broadcasting

A1 = np.array([[1,2,3],
               [4,5,6],
               [7,8,9]])

A2 = np.array([[1,1,1],
               [1,1,1],
               [1,1,1]])

A1 + A2
array([[ 2,  3,  4],
       [ 5,  6,  7],
       [ 8,  9, 10]])

A2 = np.array([1,1,1])
A1 + A2
array([[ 2,  3,  4],
       [ 5,  6,  7],
       [ 8,  9, 10]])

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

In all three cases, the result is the same: an array of dimension (3, 3). This is called broadcasting because a dimension of one is expanded to fit the higher dimension. So if you do an operation with arrays of dimensions (1, 3, 4, 4) and (5, 3, 4, 1), the resulting array will have the dimensions (5, 3, 4, 4). Broadcasting does not work with dimensions that are different but not one.

Listing 7.20 does an operation on arrays with the dimensions (2, 1, 5) and (2, 7, 1). The resulting array has the dimensions (2, 7, 5).

Listing 7.20 Expanding Dimensions

A4 = np.arange(10).reshape(2,1,5)
A4
array([[[0, 1, 2, 3, 4]],
       [[5, 6, 7, 8, 9]]])

A5 = np.arange(14).reshape(2,7,1)
A5
array([[[ 0],
       [ 1],
       [ 2],
       [ 3],
       [ 4],
       [ 5],
       [ 6]],
      [[ 7],
       [ 8],
       [ 9],
       [10],
       [11],
       [12],
       [13]]])

A6 = A4 - A5
A6
array([[[ 0,  1,  2,  3,  4],
       [-1,  0,  1,  2,  3],
       [-2, -1,  0,  1,  2],
       [-3, -2, -1,  0,  1],
       [-4, -3, -2, -1,  0],
       [-5, -4, -3, -2, -1],
       [-6, -5, -4, -3, -2]],
      [[-2, -1,  0,  1,  2],
       [-3, -2, -1,  0,  1],
       [-4, -3, -2, -1,  0],
       [-5, -4, -3, -2, -1],
       [-6, -5, -4, -3, -2],
       [-7, -6, -5, -4, -3],
       [-8, -7, -6, -5, -4]]])
A6.shape
(2, 7, 5)

NumPy Math

In addition to the NumPy array, the NumPy library offers many mathematical functions, including trigonometric functions, logarithmic functions, and arithmetic functions. These functions are designed to be performed with NumPy arrays and are often used in conjunction with data types in other libraries. This section takes a quick look at NumPy polynomials.

NumPy offers the class poly1d for modeling one-dimensional polynomials. To use this class, you need to import it from NumPy:

[1]    1 from numpy import poly1d

Then create a polynomial object, giving the coefficients as an argument:

poly1d((4,5))
poly1d([4, 5])

If you print a poly1d object, it shows the polynomial representation:

c = poly1d([4,3,2,1])
print(c)
    3     2
4 x + 3 x + 2 x + 1

If for a second argument you supply the value True, the first argument is interpreted as roots rather than coefficients. The following example models the polynomial resulting from the calculation (x – 4)(x – 3)(x – 2)(x – 1):

r = poly1d([4,3,2,1], True)
print(r)
   4      3      2
1 x - 10 x + 35 x - 50 x + 24

You can evaluate a polynomial by supplying the x value as an argument to the object itself. For example, you can evaluate the preceding polynomial for a value of x equal to 5:

r(5)
24.0

The poly1d class allows you to do operations between polynomials, such as addition and multiplication. It also offers polynomial functionality as special class methods. Listing 7.21 demonstrates the use of this class with polynomials.

Listing 7.21 Polynomials

p1 = poly1d((2,3))
print(p1)
2 x + 3
   
p2 = poly1d((1,2,3))
print(p2)
   2
1 x + 2 x + 3
   
print(p2*p1)           # Multiplying polynomials
   3     2
2 x + 7 x + 12 x + 9
   
print(p2.deriv())      # Taking the derivative
2 x + 2
   
print(p2.integ())      # Returning anti-derivative
        3     2
0.3333 x + 1 x + 3 x

The poly1d class is just one of the many specialized mathematical tools offered in the NumPy toolkit. These tools are used in conjunction with many of the other specialized tools that you will learn about in the coming chapters.

Summary

The third-party library NumPy is a workhorse for doing data science in Python. Even if you don’t use NumPy arrays directly, you will encounter them because they are building blocks for many other libraries. Libraries such as SciPy and Pandas build directly on NumPy arrays. NumPy arrays can be made in many dimensions and data types. You can tune them to control memory consumption by controlling their data type. They are designed to be efficient with large data sets.

Questions

1.   Name three differences between NumPy arrays and Python lists.

2.   Given the following code, what would you expect for the final value of d2?

d1 = np.array([[0, 1, 3],
               [4, 2, 9]])
d2 = d1[:, 1:]

3.   Given the following code, what would you expect for the final value of d1[0,2]?

d1 = np.array([[0, 1, 3],
               [4, 2, 9]])
d2 = d1[:, 1:]
d2[0,1] = 0

4.   If you add two arrays of dimensions (1, 2, 3) and (5, 2, 1), what will be the resulting array’s dimensions?

5.   Use the poly1d class to model the following polynomial:

   4     3     2
6 x + 2 x + 5 x + x -10
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.62.239