0% found this document useful (0 votes)
76 views13 pages

Array: 3.1 Generating Sequential Arrays

This document discusses various ways to generate sequential arrays in NumPy including linspace, arange, zeros, ones, empty, rand, and randn. It also covers useful NumPy array attributes and methods such as shape, size, mean, min, and max. Indexing arrays using slicing notation like foo[2:5] is also explained to access specific elements or subarrays.

Uploaded by

ELI
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
76 views13 pages

Array: 3.1 Generating Sequential Arrays

This document discusses various ways to generate sequential arrays in NumPy including linspace, arange, zeros, ones, empty, rand, and randn. It also covers useful NumPy array attributes and methods such as shape, size, mean, min, and max. Indexing arrays using slicing notation like foo[2:5] is also explained to access specific elements or subarrays.

Uploaded by

ELI
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

Chapter 3

Array

3.1 Generating sequential arrays


Often we need vectors whose elements follow a simple order, for example a vector containing ele-
ments [10, 11, 12, 13] or [5, 10, 15, 20] or [1.0, 1.2, 1.4, 1.6, 1.8, 2.0]. We see that in these vectors,
items follow some simple order, so it would be nicer if there are easy way to define these kinds of
vectors. Some of the way to create these vectors are following:

3.1.1 linspace

If we are interested in generating the vector, whose elements are uniformly spaced and we know the
upper, lower limit and the number of elements, then in that case linspace is the preferred choice.

>>> import numpy as np


>>> np.linspace( 0, 2, 9 )
array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ])

Because linspace lies in numpy library, so first we have imported the library and have given it
an abbreviated name. Then we call the linspace with lower limit, upper limit and the number of
element to be generated. In this example, 0 is the lower limit, 2 is the upper limit, and number of
elements are 9. Let us generate one more vector to understand more about this function, this time
we take lower limit as 0, upper limit as 2π, and number of elements to be 100.

>>> x = np.linspace( 0, 2*pi, 100 )

By default the number of elements are 50, so if we do not specify the number of elements, we get
50 elements with equal spacing. We can use len function to get the length of any array.

>>> foo = np.linspace(0,1)


>>> len(foo)
50
22 Chapter 3. Array

3.1.2 arange
Suppose again we want to generate a vector whose elements are uniformly spaced, but this time we
do not know the number of elements, we just know the increment between elements. In such situ-
ation arange is used. arange also requires lower and upper bounds. In the following example we
are generating the vector having lower element as 10, upper element as 30 and having an increment
of 30. So from the knowledge of linspace we will do something like this.

>>> np.arange( 10, 30, 5 )


array([10, 15, 20, 25])

Oh! What happened? Why did Python not print 30. Because arange function does not include
second argument in the elements. So we want to print upto 30, we would do.

>>> np.arange( 10, 31, 5 )


array([10, 15, 20, 25, 30])

This time we get the required output. The arange can also take a float increment. Let us generate
some vector with lower bound of 0, upper bound of 2 and with an increment of 0.3.

>>> np.arange( 0, 2, 0.3 ) # it accepts float arguments


array([ 0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])

In the case of float increment also, the maximum value of generated elements is lesser than the
second argument given to the arange.

3.1.3 zeros
zeros is used when we want to generate all the items in vector as 0.

>>> foo = np.zeros(5)


>>> foo
array([ 0., 0., 0., 0., 0.])

3.1.4 ones
ones is used when all the required elements in vector are 1. Let us say, we want to generate a
variable foo which has all the elements equal to one, and has the dimension of 3 × 2.

>>> foo = np.ones((3,2))


>>> foo
array([[ 1., 1.],
[ 1., 1.],
[ 1., 1.]])

Remember that if the number of dimensions are more than one, the dimension are given as tuple,
e.g. (2,5).
3.2. Useful attributes and methods 23

3.1.5 empty
empty is useful in initializing the variables. This assigns the garbage values to the elements, which
are to be modified later.

>>> foo = np.empty((2,5))


>>> foo
array([[ 6.94573181e-310, 2.76947193e-316, 2.74957018e-316,
0.00000000e+000, 0.00000000e+000],
[ 0.00000000e+000, 0.00000000e+000, 6.94573152e-310,
6.34874355e-321, 0.00000000e+000]])

Additionally in zeros, ones, empty, the data type (e.g. int, float etc.) also can be defined.

>>> foo = np.empty((2,5),int)


>>> foo
array([[ 140583176970856, 56931856, 59487840,
-3617040655747907584, 0],
[ 0, 0, 140583171090560,
1285, 0]])

You can see that all the elements of foo are now integer, even though the values are useless.

3.1.6 rand
rand is used to generate uniformly distributed random variables over the range of 0 to 1.

>>> foo = np.random.rand(3,2)


>>> foo
array([[ 0.75644359, 0.07754619],
[ 0.50267515, 0.91460249],
[ 0.85992345, 0.58662329]])

3.1.7 randn
randn is used to generate random variable having normal distribution with mean equal to zero and
variance equal to one.

>>> foo = np.random.randn(2,4)


>>> foo
array([[-0.66317015, -1.80129451, 0.56398575, -1.11912727],
[ 0.19218091, 0.21957804, -1.10891128, -0.87418933]])

3.2 Useful attributes and methods


The ndarray (array generated using numpy) provides attributes to perform commonly used task
quickly. These attributes are used to quickly get properties of ndarray. So let us first generate some
vector whose elements are normally distributed random numbers, and try these attributes. Here I
24 Chapter 3. Array

am using normally distributed random variable to demonstrate, but these attributed can be used with
any numpy array. We are generating a 2 dimensional vector of size 5 × 100.

>>> foo = np.random.randn(5,100)

Let us check the number of dimension (not the size, or shape of the array). Number of dimension
means how many dimensions are associated with array. For example, in mathematics terminology
vector has one dimension, matrix has two dimension.

>>> foo.ndim
2

The dimension of the array is accessed by using shape attribute.

>>> foo.shape
(5, 100)

The size attribute provides the total number of elements in the array. This is simply the multiplica-
tion of all the elements given by shape attributes.

>>> foo.size
500

The data type (i.e. float, integer etc.) is extracted using the attribute dtype.

>>> foo.dtype
dtype('float64')

This tells us that, the variable foo is float, and has 64 bits. The average or mean of the variable is
computed by using mean method.

>>> foo.mean()
-0.11128938014455608

This provides the mean of entire array (i.e. 500 elements in this case). Suppose we want to estimate
the mean across some dimension say second (1) dimension, then in this case we need to provide
additional parameter to mean, i.e. axis.

>>> foo.mean(axis=1)
array([-0.07311407, 0.0705939 , -0.09218394, 0.0775191 , 0.01026461])

The minimum, maximum, standard deviation and variance of the array are estimated using min, max,
std, and var methods.

>>> # to get the minimum vale


>>> foo.min()
-3.5160295160193256
>>> # to get the maximum value
>>> foo.max()
3.0989215376354817
3.3. Indexing 25

>>> # to get the standard deviation


>>> foo.std()
0.9528004743319175
>>> # to get the variance
>>> foo.var()
0.90782874388712709

Remember that the line starting with # represents the comments. Comments make it easier to read
and understand the code. So put comments whenever you do something, which is not easy to
interpret from the code.

The trace of the matrix represent the sum of diagonal elements, and has meaning in case of square
matrix. Python even allows to estimate the trace even when matrix is not square, and trace is com-
puted by using the trace attributes.

>>> foo.trace()
1.081773080044246

There are number of attributes associated with each class, dir function is a useful tool in exploring
the attributes and method associated with any variable, class, library etc. Let us see what all methods
and attributes our variable foo have.

>>> # to get the list of all the attributes associated with foo variable
>>> dir(foo)
['T', '__abs__', ............. 'flat', 'view']

The output of dir(foo) is very long, and is omitted for brevity. The attributes/method starting with
_ are supposed to be the private attributes and are often not needed.

3.3 Indexing
In this section, we will discuss how to refer to some elements in the numpy array. Remember that in
Python first indices is 0. We shall generate some array, say some array whose elements are powered
to 3 of the sequence [0,1, ..., 9].

>>> foo = np.arange(10)**3


>>> foo
array([ 0, 1, 8, 27, 64, 125, 216, 343, 512, 729])

Print the third item in the array. Third item means we need to put indices as 2.

>>> foo[2]
8

Suppose, we would like to print some sequence of array, say at indices of 2,3, and 4.

>>> foo[2:5]
array([ 8, 27, 64])
26 Chapter 3. Array

We used 2:5 to get the values at indices of 2,3 and 4. This is same as saying that
foo[np.arange(2,5,1)]. When we do not specify the third value in the indices for array, it is
by default taken as 1. If we want to print value at 2 to 8, with an interval of 3. Now because the
interval is not 1, so we need to define it.

>>> foo[2:10:3]
array([ 8, 125, 512])

If we leave the first entry in the index as blank i.e. to get array elements form the beginning of array
with an interval of 2 and upto 6, we issue the following command:

>>> foo[:6:2] # gives the element at 0,2,4


array([ 0, 8, 64])

We get element upto the indices of 4, because arange does not go upto the second argument. We
can use indices also to modify the existing elements in the array, in the same way as we accessed
them. Let us replace the existing value of elements at 0,2 and 4 indices, by -1000.

>>> foo[:6:2] = -1000 # modify the elements at 0,2,4


>>> foo
array([-1000, 1, -1000, 27, -1000, 125, 216, 343, 512, 729])

We get the last elements of an array by indices -1. We can also use this to reverse the array, by
giving the increment of -1.

>>> foo[::-1] # reversed a


array([ 729, 512, 343, 216, 125, -1000, 27, -1000, 1, -1000])

We can perform the calculation on entire numpy array at once. Suppose we are interested in esti-
mating the square root of the numpy array, we can use sqrt function of numpy library.

>>> np.sqrt(foo) # compute the square root


array([ nan, 1. , nan, 5.19615242,
nan, 11.18033989, 14.69693846, 18.52025918,
22.627417 , 27. ])
Warning: invalid value encountered in sqrt

nan represents that the element is ‘Not A Number’. So when the value of element is negative the
output of sqrt become nan. The Warning issued by Python tells that there were some invalid values
in the input for which sqrt can not produce any sensible output, and it provides warning (not errors).
In reality, the square root of negative number is complex number, but because we did not define the
variable as complex, numpy can not perform operations of complex numbers on this. We need
library which handles complex number for such situation.

3.4 Array Manipulation


Often we need to change the array, transpose it, get some elements, or change some elements. This
is illustrated by this example, in which first we create the array and then play with it. We have
already seen in the previous section, that we can change the value of any element by calling it by the
3.4. Array Manipulation 27

indices, and then assigning new value to it. First, we generate normally distributed random number
of size (2 × 5) to create an array, which we would like to manipulate.

>>> foo = np.random.randn(2,3)


>>> foo
array([[ 1.02063865, 1.52885147, 0.45588211],
[-0.82198131, 0.20995583, 0.31997462]])

The array is transposed using T attributes.

>>> foo.T
array([[ 1.02063865, -0.82198131],
[ 1.52885147, 0.20995583],
[ 0.45588211, 0.31997462]])

We can access some elements of the array, and if we want, new values also can be assigned to them.
In this example, we shall first access element at (0,1) indices, and then we shall replace it by 5.
Finally we will print the variable to check if the variable got modified.

>>> foo[0,1]
-0.82198131397870833
>>> foo[0,1]=5
>>> foo
array([[ 1.02063865, 5. ],
[ 1.52885147, 0.20995583],
[ 0.45588211, 0.31997462]])

The shape of any array is changed by using the reshape method. During reshape operation, the
change in number of elements is not allowed. In the following example, first we shall create an
array having size of (3 × 6), and the we shall change its shape to (2 × 9).

>>> foo = np.random.randn(3,6)


array([[ 2.01139326, 1.33267072, 1.2947112 , 0.07492725, 0.49765694,
0.01757505],
[ 0.42309629, 0.95921276, 0.55840131, -1.22253606, -0.91811118,
0.59646987],
[ 0.19714104, -1.59446001, 1.43990671, -0.98266887, -0.42292461,
-1.2378431 ]])
>>> foo.reshape(2,9)
array([[ 2.01139326, 1.33267072, 1.2947112 , 0.07492725, 0.49765694,
0.01757505, 0.42309629, 0.95921276, 0.55840131],
[-1.22253606, -0.91811118, 0.59646987, 0.19714104, -1.59446001,
1.43990671, -0.98266887, -0.42292461, -1.2378431 ]])

Like we can access the any elements of the array and change it, in similar way we can access the any
attributes, and modify them. However, the modification is only allowed if the attributes is writeable,
and the new value makes some sense to the variable. We can use this behaviour, and change the
shape of variable using the shape attributes.

>>> foo = np.random.randn(4,3)


28 Chapter 3. Array

>>> foo.shape
(4, 3)
>>> foo
array([[-1.47446507, -0.46316836, 0.44047531],
[-0.21275495, -1.16089705, -1.14349478],
[-0.83299338, 0.20336677, 0.13460515],
[-1.73323076, -0.66500491, 1.13514327]])
>>> foo.shape = 2,6
>>> foo.shape
(2, 6)
>>> foo
array([[-1.47446507, -0.46316836, 0.44047531, -0.21275495, -1.16089705,
-1.14349478],
[-0.83299338, 0.20336677, 0.13460515, -1.73323076, -0.66500491,
1.13514327]])

In the above example, first an array is defined with a size of (4 × 3) and then its shape is assigned a
value of (2,6), which makes the array of size (2 × 6). As we can not change the number of elements,
so if we define one dimension of the new variable, second dimension can be computed with ease.
Numpy allow us to define -1 for the default dimension in this case. We can make the desired change
in the shape of variable by using default dimension also.

>>> foo.shape = -1,6


>>> foo.shape
(2, 6)

We can flatten the array (make array one dimensional) by using the ravel method, which is ex-
plained in the following example:

>>> foo = np.random.rand(2,3)


>>> foo
array([[ 0.82866532, 0.99558838, 0.58213507],
[ 0.48877906, 0.67700479, 0.35975352]])
>>> foo.shape
(2, 3)
>>> a = foo.ravel()
>>> a.shape
(6,)
>>> a
array([ 0.82866532, 0.99558838, 0.58213507, 0.48877906, 0.67700479,
0.35975352])
Chapter 4

Basic applications in Hydrology

4.1 Introdution
This chapter will provide applications of python in hydrology. Most of the problems given in
this chapter are taken from the book titled “Applied Hydrology” by Chow et al, and for detailed
description of them, you should refer to the book. These examples include the equations commonly
encountered in the hydrology. I have choose these problems to teach Python by using examples,
and additionally in every example we will be learning new things about Python.

4.2 Water Vapor


Approximately, the saturation vapor pressure (es ) is related to the air temperature (T ) by the follow-
ing equation,  
17.27T
es = 611 exp , (4.1)
237.3 + T
where, es is in pascals and T is in degrees Celcius. Let us calculate the value of es at T = 50.

>>> T = 50
>>> es = 611*np.exp(17.27*T/(237.3+T))
>>> print(es)
12340.799081

Let us plot the variation of es versus T over the range of −100 ≤ T ≤ 100. The plt.plot(x,y)
makes the line plot of y versus x, with default color of blue. The plt.xlabel() and plt.ylabel()” are
used to write labels on x and y axis respectively. The input to xlable and ylabel must be a string,
or a variable which contains a string. The plt.show() displays the graph on computer screen.

>>> import numpy as np


>>> T = np.linspace(-100,100,50)
>>> es = 611*np.exp(17.27*T/(237.3+T))
>>> plt.plot(T,es)
>>> plt.xlabel('T (degree Celcius)')
30 Chapter 4. Basic applications in Hydrology

>>> plt.ylabel('es (Pa)')


>>> plt.show()

The resulted plot is shown in Fig. 4.1. This example demonstrates how to graphically visualize the
variation of one variable with respect to the another variable, while former is explicit function of
later.

120000

100000

80000
es (Pa)

60000

40000

20000

0100 50 0 50 100
T (degree Celcius)

Figure 4.1: The variation of saturation vapor pressure (es ) versus temperature (T ).

4.3 Precipitation

The terminal velocity (Vt ) of a falling raindrop is given by:


  1/2
4gD ρw
Vt = −1 , (4.2)
3Cd ρa

where, g is the acceleration due to gravity, D is the diameter of the falling raindrop, ρw is the density
of water, ρa is the density of air, and Cd is the drag coefficient. The Stoke’s law can be used to
calculate drag coefficient (Cd = 24/Re), which is valid for raindrop having diameter less than 0.1
mm. Re is the Reynold number, which can be calculated as ρaV D/µa . Let us assume, that the Re is
given as 5.0, and the raindrop has diameter of 0.05 mm, and we want to estimate the Vt . (ρw = 998,
ρa = 1.2).

>>> import numpy as np


>>> Re = 5.0; rho_w = 998; rho_a = 1.2; g = 9.8; D = 0.05E-3
>>> Cd = 24/Re
>>> Vt = np.sqrt((4*g*D)/(3*Cd)*(rho_w/rho_a-1))
>>> Vt
0.3362483649967134

In this example we see that ‘;’ allows us to define many expressions in one line.
4.4. Rainfall 31

4.4 Rainfall
Often, we are given a rainfall recorded by a rain gauge which provides the rainfall depths recorded
for successive interval in time, and we want to compute the cumulative rainfall. In this example first
we shall create rainfall using the random numbers, and we shall also create time variable having
values [0,5,10, ...., 100].

>>> import numpy as np


>>> time = np.linspace(0,100,21) # create time variable
>>> time
array([ 0., 5., 10., 15., 20., 25., 30., 35., 40.,
45., 50., 55., 60., 65., 70., 75., 80., 85.,
90., 95., 100.])
>>> rainfall = np.random.rand(21) # generate rainfall
>>> rainfall
array([ 0.08155645, 0.88821997, 0.33355457, 0.49600859, 0.6315054 ,
0.0722053 , 0.06165701, 0.96105307, 0.56483934, 0.5194715 ,
0.35780167, 0.98950575, 0.67866578, 0.31274527, 0.80022389,
0.53321842, 0.82370443, 0.73212013, 0.77039599, 0.06392391,
0.53481488])

Now we make a bar plot using the plt.bar(), for the rainfall which depicts temporal behaviour of
the rainfall.

>>> import matplotlib.pyplot as plt


>>> plt.bar(time,rainfall)
>>> plt.xlabel('Time')
>>> plt.ylabel('Incremental rainfall')
>>> plt.savefig('/home/tomer/articles/python/tex/images/rain.png')

The resulted bar plot of rainfall is shown in Fig 4.2. You might have noticed that in the section 4.2,
we used the plt.show(), while in the above example we used plt.savefig. The plt.show()
shows the graph on computer screen, which can be saved later, while the plt.savefig() saves the
graphs in computer, which can be viewed after opening the file. It is just matter of taste, what you
like, optionally both can be done on same graph. I prefer to save the figures in the computer and
then see them.

The cumulative sum is calculated by using the cumsum function of the numpy library.

>>> cum_rainfall = np.cumsum(rainfall)

Now we plot the cumulative rainfall. The resulted cumulative rainfall is shown in Fig. 4.3. The
plt.clf() clears the current figure, and is quiet useful when making multiples plots, and there is
any existing plot in the python memory. Just don’t use the clf in this, and see the difference.

>>> plt.clf()
>>> plt.plot(time,cum_rainfall)
>>> plt.xlabel('Time')
>>> plt.ylabel('Cummulative rainfall')
>>> plt.savefig('/home/tomer/articles/python/tex/images/cum_rain.png')
32 Chapter 4. Basic applications in Hydrology

Figure 4.2: Temporal variation in the incremental rainfall.

Figure 4.3: Temporal behaviour of the cumulative rainfall .

Usually, we are given the rainfall at some rain gauges, and we want to make the isohyete (contour)
plot of the rainfall. To demonstrate this situation, fist we shall generate locations (x,y) and rainfall
for ten stations using random numbers. The generated locations of the rain gauges is shown in Fig.
4.4.

>>> # import required modules


>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>>
>>> #genrate locations and rainfall
>>> x = np.random.rand(10)
>>> y = np.random.rand(10)
>>> rain = 10*np.random.rand(10)
>>>
>>> #plot the locations
>>> plt.scatter(x,y)
>>> plt.xlabel('X')
>>> plt.ylabel('Y')
>>> plt.savefig('/home/tomer/articles/python/tex/images/loc.png')
4.4. Rainfall 33

I prefer to add blank lines after a section of code, and comment on the top of section what it is doing.
This increases the readability of the code. The plt.scatter() makes the scatter plot, i.e. the dots
are plotted instead of lines. When there is no order in the data with respect to their position in the
array, then scatter plot is used. Like in this case, it is possible that two stations which are close by,
but might be placed at distant in the array.

Figure 4.4: Spatial distribution of the rain gauges.

The flow chart of preparing contour map is given in Fig. 4.5. First, we need to generate the
grid with regular spacing having the same extent as of the locations of rainfall gauges. Then,
from the given location and rainfall data, we need to compute data at regular grid using some
interpolation scheme. After this contour maps can be obtained. The griddata function of the
scipy.interpolate library is useful in obtaining the gridded data (data at regular grid). When
we need only one or few functions from the library, it is better to call them explicitly, e.g.
from scipy.interpolate import griddata, like in the following example. We use meshgrid
function of numpy library, to create the mesh from the given x and y vectors.
>>> from scipy.interpolate import griddata
>>> #generate the desired grid, where rainfall is to be interpolated
>>> X,Y = np.meshgrid(np.linspace(0,1,1000), np.linspace(0,1,1000))
>>>
>>> #perform the gridding
>>> grid_rain = griddata((x,y), rain, (X, Y))
Now, we can make the contour plot of the gridded data, which is made by plt.contourf() function.
The contourf makes filled contours, while contour() provides simple contour. Try using the
contour instead of contourf, and you will see the difference. We begin by clear current figure
by using the plt.clf(), as there might be some existing figure in the memory especially if you
are following all the examples in the same session. We are also overlaying the locations of rainfall
gauges using the plt.scatter(). The s and c are used to define the size and color of the markers
respectively. The plt.xlim() and plt.ylim() limits the extent of the x and y axis respectively.

>>> plt.clf()
>>> plt.contourf(X,Y,grid_rain)
>>> plt.colorbar()
>>> plt.xlabel('X')
>>> plt.ylabel('Y')

You might also like