3. Numpy for mathematics#

We had a preview of the possibilities offered by Numpy in the previous notebook when we multiplied two arrays element-wise without having to write a for loop. Numpy offers vast possibilities to extend this approach in much more complex cases. In this notebook we are going to see in a very succint way a few of the main features offered by Numpy. Numpy is a large library offering many tools for various problems. For your own work, you will often have to find the approriate function to achieve your goal, and usually a Google search is the fastes way to find information.

import numpy as np

Creating arrays#

We have seen that we can turn regular lists into arrays with the array() function. However this becomes quickly impractical for larger arrays. Numpy offers several functions to create particular arrays.

Common simple arrays#

For example an array full of zeros or ones:

one_array = np.ones(shape=(2,3))
one_array
array([[1., 1., 1.],
       [1., 1., 1.]])
zero_array = np.zeros(shape=(2,3))
zero_array
array([[0., 0., 0.],
       [0., 0., 0.]])

We see here that the two functions take a shape argument that describes the shape of the output array. Indeed arrays are not just lists but can also be lists of lists!

If we obtain an array we can use the same shape property to find out the shape of an array:

zero_array.shape
(2, 3)

Let’s check the dtype:

one_array.dtype
dtype('float64')

By default, Numpy creates float arrays. As previously, if needed, we can fix this with the astype method.

Complex arrays#

We are not limited to create arrays containing ones or zeros. Very common operations involve e.g. the creation of arrays containing regularly arrange numbers. For example a “from-to-by-step” list:

arranged_array = np.arange(0, 10, 2)
arranged_array
array([0, 2, 4, 6, 8])

Here also, we can find out what the shape of the array is as we didn’t specify it explicitly. Since it’s a 1D array we only get one value out:

arranged_array.shape
(5,)

Or equidistant numbers between boundaries:

np.linspace(0,1, 10)
array([0.        , 0.11111111, 0.22222222, 0.33333333, 0.44444444,
       0.55555556, 0.66666667, 0.77777778, 0.88888889, 1.        ])

Statistical arrays#

Numpy offeres also many options to create arrays filled with numbers drawn from a given distribution. Most of these functions are located in a sub-module called np.random. For example we can draw numbers from a Poisson distribution \(P(x = k, \lambda) = \frac{\lambda^ke^{\lambda}}{k!}\)

poisson = np.random.poisson(lam=5, size=20)
poisson
array([ 6, 11,  7,  4,  0,  2,  5,  5,  6,  3,  7,  3,  2,  2,  3,  8,  4,
        6,  4,  8])

Applying functions to arrays#

We have already seen that we can combine arrays of the same size (there are exceptions to this rule, if you want to know more you can read about broadcasting). We can however also do mathematics with a single array.

Numpy implements many standard mathematical functions that you can use as if you were dealing with a single number.

Arithmetics#

In the simplest case you just to arithmetics:

poisson + 3
array([ 9, 14, 10,  7,  3,  5,  8,  8,  9,  6, 10,  6,  5,  5,  6, 11,  7,
        9,  7, 11])
poisson / 3.5
array([1.71428571, 3.14285714, 2.        , 1.14285714, 0.        ,
       0.57142857, 1.42857143, 1.42857143, 1.71428571, 0.85714286,
       2.        , 0.85714286, 0.57142857, 0.57142857, 0.85714286,
       2.28571429, 1.14285714, 1.71428571, 1.14285714, 2.28571429])

In both examples the operation is applied element-wise.You see that here again, Python dynamically decided of the type. In the first example the output stays an integer because we added an integer, while in the second one we devided by a float and therefore the output is a float.

Functions#

In addition to simple arithmetic, Numpy offers a vast choice of functions that can be directly applied to arrays. These always have the same syntax as in regular mathematics \(y = f(x)\), albeit here \(y\) and \(x\) are arrrays. Here a few examples, e.g. in trigonometry:

np.cos(poisson)
array([ 0.96017029,  0.0044257 ,  0.75390225, -0.65364362,  1.        ,
       -0.41614684,  0.28366219,  0.28366219,  0.96017029, -0.9899925 ,
        0.75390225, -0.9899925 , -0.41614684, -0.41614684, -0.9899925 ,
       -0.14550003, -0.65364362,  0.96017029, -0.65364362, -0.14550003])

or for exponentials and logarithms:

np.exp(poisson)
array([1.09663316e+03, 7.38905610e+00, 5.45981500e+01, 5.45981500e+01,
       1.00000000e+00, 2.00855369e+01, 2.00855369e+01, 1.48413159e+02,
       7.38905610e+00, 4.03428793e+02, 2.20264658e+04, 2.71828183e+00,
       2.20264658e+04, 5.45981500e+01, 2.00855369e+01, 2.98095799e+03,
       1.48413159e+02, 4.03428793e+02, 5.45981500e+01, 1.48413159e+02])
np.log10(poisson+1)
array([0.84509804, 1.07918125, 0.90308999, 0.69897   , 0.        ,
       0.47712125, 0.77815125, 0.77815125, 0.84509804, 0.60205999,
       0.90308999, 0.60205999, 0.47712125, 0.47712125, 0.60205999,
       0.95424251, 0.69897   , 0.84509804, 0.69897   , 0.95424251])

Statistics#

When we applied the above functions to arrays, the output always had the same size as the input. However this is not the case if we want to summarize data with statistics. For example we can take the mean of an array:

np.mean(poisson)
4.8

Or find the maximal value in an array:

np.max(poisson)
11

The dot notation#

As we have already seen with astype() for example, Numpy arrays come with a lot of functions attached to them (methods). In particular many of the statistics functions can be called in that way, e.g. the mean:

poisson.mean()
4.8

Exercise#

  1. Find out how to generate a list of 10 numbers drawn from a normal distribution with a mean of 10 and standard deviation of 2.

  2. Create a list of 10 numbers evenly spaced between 0 and 90.

  3. Assuming that the values you obtained in (2) are angles in degrees, find a function that converts degrees to radians and apply it to the array.

  4. Calculate the standard deviation of the array obtained in (3). Use both a numpy function (np.myfun) and a method attached to the array.