Hydro Python Manual
Hydro Python Manual
Earth Scientists
2 Basics of Python 7
2.1 Starting up Python . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1.1 Python through a text terminal . . . . . . . . . . . . . . . 7
2.1.2 IDLE integrated development environment . . . . . . . . 9
2.1.3 SPYDER integrated development environment . . . . . . 10
2.2 Working with Python . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2.1 Commenting your code . . . . . . . . . . . . . . . . . . . 10
2.2.2 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.3 Using Python as a calculator . . . . . . . . . . . . . . . . 11
2.2.4 Use of modules to load functions . . . . . . . . . . . . . . 12
2.2.5 Variables and data types . . . . . . . . . . . . . . . . . . 14
2.2.6 Formatting of numbers using string format conversion specifiers 17
2.2.7 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2.8 Combining or splitting arrays of data . . . . . . . . . . . 21
3 Python scripts 24
3.1 What is a script? . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.2 Example script . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.3 Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.4 Useful snippets of code . . . . . . . . . . . . . . . . . . . . . . . 27
5 Functions in Python 33
5.1 Basic functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
5.1.1 Help() function . . . . . . . . . . . . . . . . . . . . . . . 33
5.1.2 Find() function . . . . . . . . . . . . . . . . . . . . . . . 35
5.1.3 Mod() and floor() functions . . . . . . . . . . . . . . . . 36
5.1.4 Range functions . . . . . . . . . . . . . . . . . . . . . . 36
3
5.2 Statistical functions . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.3 Meteorological and hydrological functions . . . . . . . . . . . . . 38
5.3.1 General meteorological functions: meteolib.py . . . . . . 38
5.3.2 Evaporation functions: evaplib.py . . . . . . . . . . . . . 39
5.4 Defining your own functions . . . . . . . . . . . . . . . . . . . . 39
5.4.1 Defining a named function . . . . . . . . . . . . . . . . . 39
5.4.2 Lambda functions . . . . . . . . . . . . . . . . . . . . . . 42
5
Another branch of programming languages are the interpreted languages. In-
terpreted programming languages are designed to be processed by an application
that translates your plain text instructions into actions inside the target application.
Here, your commands can be typed in and are directly evaluated by the interpreter
when you press the ENTER key, thus without the need for prior compilation. A
number of consecutive commands can be saved in an ASCII text file, which is
then called a script. Examples of interpreted languages are Matlab, Python and R.
This also means that as long as you have any of these programs installed on your
computer, you can run the script independent of the operating system.
A Matlab license is quite costly and you may not have Matlab available at
home. A good alternative is the open source Python language. Python is a very
versatile open-source programming language that can be used to process data in a
rather easy way. This document aims to teach you basic Python programming and
give you hints and examples on how to use Python in your scientific career.
Chapter 2
Basics of Python
7
Figure 2.1: Installing Python packages on a Debian OS distribution using the
Synaptic package manager.
Figure 2.2: Running Python from within a Debian Linux terminal window. Python
has started up and several commands have been entered at the Python prompt
(>>>).
Figure 2.3: IDLE shell window for working with Python.
The interfacing with Python is done in plain ASCII (American Standard Code
for Information Interchange) text that is typed in. If you use a Python terminal, a
separate ASCII text editor, such as the fancy Xemacs editor or the simple Notepad
or Wordpad text editors can be used to store your commands for later re-use.
Multi-line comments are made by placing text between three single quotes (”’) in
a row:
>>> ’ ’ ’ T h i s i s a m u l t i l i n e comment
t h a t spans
3 lines ’’’
’ T h i s i s a m u l t i l i n e comment \n t h a t s p a n s \n 3 l i n e s ’
>>>
Assignment
Write two comment lines in the shell window. The first is a multi-line comment
and contains a text explaining the purpose of your script (make something up) and
the second line is a single line comment with the date and your name. Now open a
new text file in IDLE (open > new window) and copy these lines into the text file
and then save the file as yourname.py. This is now your script file. You can execute
or run this file by selecting the Run > run module commands that are displayed in
the top menu of your script file in Idle (see Figure 2.3), or pressing the ”run” button
in Spyder. Note that running the file with comments does not produce any output
in the shell window.
2.2.2 Operators
Python has operators that allow you to do basic calculations or work otherwise
with variables numbers or variables. The most well-known ones are the standard
mathematical operators (e.g. +, -, *, /), the ** power function operator, the %
modulus (returns remainder of division) and the \\ floor operators (returns division
result without decimal part).
Other operators are for comparison of values, such as == (equal to), != (not
equal to), logical operators such as and) and or, bitwise operators (e.g. the binary
& and | (or) operators, or membership operators (in, not in).
Assignment
Using Python as a calculator, try to calculate the following in Python. Take a good
look at the output and keep the answers in mind while you read the next sections.
sin ( 3 .1 4 ) # take the sine of 3.14
2∗∗4 # F o u r t h power o f 2
2 . ∗ ∗ 4 # F o u r t h power o f 2 . 0
3+4
3.0 −4
3.0/4
3/4.0
3./4
3/4
1. import X: imports module X with all its functions and creates a reference
to it in the current namespace. You can now use function func(), defined
in module X, by calling X.func(). Functions with the same name can be
defined in different libraries. For instance, the function sin() is defined in the
module math, but also in the SCIentific PYthon scipy module. Using import
math and import scipy would give you two options for the same function.This
is the preferred way of importing functions as you always know for sure
which implementation of the function you use. As an example for the sin(x)
function, with x in radians, as defined in the math and scipy modules we see:
>>> import math
>>> import s c i p y
>>> math . s i n ( 2 )
0.90929742682568171
>>> s c i p y . s i n ( 2 )
0.90929742682568171
Note that if func() was already defined in another module imported earlier
(for instance the scipy module), it will be overwritten and the latest imported
version will be used. This may create some confusion as two different func-
tions with the same name could potentially be available in your program.
4. Y = import (’X’): this is like using import X but you can pass the
module name X to an arbitrary string (called Y here) and assign the module
to variable Y.
>>> N = i m p o r t ( ’ numpy ’ )
>>> N . c o s ( 0 . 7 5 )
0.7316888688738209
As said before, the preferred way is to use the first option (import X) as the other
options may create confusion, for instance when functions with the same name
(cos, sin) are defined in multiple modules (defined in the modules math, numpy –
numerical python module, scipy, etc.).
Two function libraries (modules) have been developed by M.J. Waterloo for use
in Hydrology and micro-meteorology. These are the meteolib.py module with stan-
dard meteorological functions and the evaplib.py module that has several functions
for calculating evaporation. See Section 5.3 for more on these modules.
Assignments
1. Import all functions from the the scipy module using method 2, then calculate
the square root (sqrt() function) of 2
2. Import the math module using method 1 and calculate the square root of 2.
3. Import only the function sqrt() from the numpy module and calculate the
square root of 2
4. Use method 4 to import the math module as M and calculate the square root
of 2
In the assignment in Section 2.2.3 you have calculates 3/4 and may have no-
ticed that you got a value of 0 as the answer, rather than 0.75. Note again above
that the division of a by b gives a value of 1, instead of 1.66666666. This has to
do with how Python treats values. In the first case, the values 3 and 4 are both
integers, and the resulting division is therefore also an integer (0). If either one of
these values is a real value (float, 3., 3.0, 4., 4.0), then the division of 3.0 by 4.0
will also report a float (0.75). The type of value (integer, float, etc.) therefore is
important for the outcome of your calculations!
The same holds for the variable types. We again defined a and b as integer data
types by assigning them integer values (3, 5), instead of real or float data types
(3.0, 5.0). Multiplying or dividing two integer values with each other will return
an integer, but when one of these values is a float, the result of the calculation will
also be a float. We can define a or b as floats by adding a decimal point to the value
using statements:
>>> a = 5 . 0
>>> a
5.0
>>>d = a / b
>>>d
1.6666666666666667
>>>
Alternatively, you can convert an integer to a float or vice versa using the float()
and int() functions:
>>> f l o a t ( b )
3.0
>>>i n t ( a )
5
>>>
Integer and float types are two of the simple data types defined in Python. Other
simple data types dare the boolean bool, i.e. true or false) and complex (1+0j) data
types. The above variables only held one value and were therefore assigned simple
data types. You can see what type a variable is using the type() function:
>>> t y p e ( b )
<t y p e ’ f l o a t ’>
>>>
To handle complex data, such as variables holding multiple values and/or text,
Python also know a number of complex data types called tuple, string, unicode,
list, set, frozenset and dictionary. The tuple can hold a mix of values and text that
are separated by commas, and these values cannot be changed:
>>> t e s t 1 = ( 1 , 2 . 0 , 5 , ( 3 + 2 j ) , ’ t h e dog i s on t h e r o o f ’ )
>>> t e s t 1
( 1 , 2 . 0 , 5 , ( 3 + 2 j ) , ’ t h e dog i s on t h e r o o f ’ )
>>> t y p e ( t e s t 1 )
<t y p e ’ t u p l e ’>
>>>
Another useful data type in Python is the dictionary type. This data type con-
sists of unordered key:value pairs that can for instance be used to use a kwargs
dictionary variable to pass arguments to a function. Dictionary values can be of
any type and can be duplicated in a dictionary, but the keys have to be unique and
can consist of strings, numbers or tuples. A dictionary can be made using the dict()
statement, for instance the following could be used to pass different arguments to
the genfromtxt() function to import data from a file (Section 7.2.1):
k w a r g s = d i c t ( d e l i m i t e r =” , ” , \
m i s s i n g v a l u e s ={ ” \”NAN\” ” } , \
s k i p h e a d e r =5 ,\
s k i p f o o t e r =0 ,\
f i l l i n g v a l u e s ={ −9999} ,\
c o n v e r t e r s = { 0 : s t r p d a t e 2 n u m ( ’\”%Y−%m−%d %H:%M:%S\” ’
) }\
)
You will note that when printing, we get a lot of decimals for the calculated Q.
There are several ways to control the output format. To format a number for print-
ing or saving to file, we convert it to a string and we can use string format con-
version specifiers to choose between decimal notation, scientific notation, etc., and
also control how many decimals we want to print. In other ways, there are controls
to format the printing of a number. We do this by converting the number into a
string using the str() command. There are several format specifiers that control
the output as shown in Table 2.1. Several examples for formatting of the value of
Q are given below. The general syntax is the format specifier (for instance ’%.4g’
for general with four decimals) followed by a % operator and the variable Q:
>>> p r i n t ( s t r ( ’ %.4 g ’ % Q) )
0.08864
>>> p r i n t ( s t r ( ’ %.2 f ’ % Q) )
0.09
>>> p r i n t ( s t r ( ’ %.4 f ’ % Q) )
0.0886
>>> p r i n t ( s t r ( ’ %.2 e ’ % Q) )
8 . 8 6 e −02
>>> p r i n t ( s t r ( ’ %.2E ’ % Q) )
8 . 8 6 E−02
>>> p r i n t ( s t r ( ’ %.4 f ’ % Q) )
0.0886
>>> p r i n t ( s t r ( ’ %.4 i ’ % Q) )
1
repr(object) returns a string containing a printable representation of an object, i.e. the same value
yielded by conversions (surrounded by reverse quotes)
0000
>>> s t r ( ’ %0.3 f ’ % Q)
’ 0.089 ’
>>> s t r ( ’ %10.3 f ’ % Q)
’ 0.089 ’
The string formatting operator, % (the percent sign) uses two inputs, i.e. the string
with formatting characters ’%.2E’ and the data Q to be formatted, separated by the
percent sign, thus ’%.4i’ % Q. The number before the dot (.) in the string with
formatting characters specifies the total number of characters to be used, whereas
the number following the dot in the string with formatting characters specifies the
number of decimals (e.g. %5.1f would give a value of five characters (can be filled
by spaces for values lower than 1000) with 1 decimal ’1234.5’ or ’ 34.5’). Note
that the conversion to integer ’(%.4i’ %) of this float gives four zeros due to the .4
in the format specifier (%.4i). Note that the str() statement changes the type of the
variable to string (str) type, in this case from float to str as shown below.
>>> t y p e (Q)
<t y p e ’ f l o a t ’>
>>> t y p e ( s t r ( ’ %.2 f ’ % Q) )
<t y p e ’ s t r ’>
>>>
We can also use the str() statement end-format specifiers to format two or more
numbers at a time:
>>> p r i n t ( s t r ( ’ %0.4 f \%.2 e ’ % (Q, H) ) )
0 . 0 8 8 6 3 . 5 0 e −01
2.2.7 Arrays
Most of our hydrological and meteorological data consist of measurements made
over time, such as water level data, sapflow data, conductivity measurements, etc.
With meteorological data collected by our Campbell 21X datalogger, we have one
row of data, such as radiation, temperature, relative humidity, air pressure, etc at
every time interval. In most cases individual data values will be of type float, for
instance a temperature of 27.82 ◦ C, or net radiation of 256.9 W m−2 . The various
rows and columns of data can be considered an array or matrix, where every row
represents a line of data collected at a certain date and time. This means that this
array would be of the tuple type.
Each value in an array can be accessed by its index numbers. Say that we
define the array t data as an array of 5 rows and 6 columns containing year, day,
time, temperature and temperature variance and standard deviation data:
>>> # I m p o r t t h e s c i p y module t o work w i t h a r r a y s
>>> from s c i p y import ∗
>>> # Now d e f i n e t h e a r r a y t d a t a
>>> t data = array ([[2007 ,121 ,1435 ,22.95 ,.287 ,.536] ,
[2007 ,121 ,1440 ,22.45 ,.188 ,.434] ,
[2007 ,121 ,1445 ,21.84 ,.207 ,.455] ,
[2007 ,121 ,1450 ,21.90 ,.265 ,.514] ,
[2007 ,121 ,1455 ,22.34 ,.476 ,.69]])
>>> t d a t a
array ([[ 2 . 0 0 7 0 0 0 0 0 e +03 , 1 . 2 1 0 0 0 0 0 0 e +02 , 1 . 4 3 5 0 0 0 0 0 e +03 ,
2 . 2 9 5 0 0 0 0 0 e +01 , 2 . 8 7 0 0 0 0 0 0 e −01 , 5 . 3 6 0 0 0 0 0 0 e −01] ,
[ 2 . 0 0 7 0 0 0 0 0 e +03 , 1 . 2 1 0 0 0 0 0 0 e +02 , 1 . 4 4 0 0 0 0 0 0 e +03 ,
2 . 2 4 5 0 0 0 0 0 e +01 , 1 . 8 8 0 0 0 0 0 0 e −01 , 4 . 3 4 0 0 0 0 0 0 e −01] ,
[ 2 . 0 0 7 0 0 0 0 0 e +03 , 1 . 2 1 0 0 0 0 0 0 e +02 , 1 . 4 4 5 0 0 0 0 0 e +03 ,
2 . 1 8 4 0 0 0 0 0 e +01 , 2 . 0 7 0 0 0 0 0 0 e −01 , 4 . 5 5 0 0 0 0 0 0 e −01] ,
[ 2 . 0 0 7 0 0 0 0 0 e +03 , 1 . 2 1 0 0 0 0 0 0 e +02 , 1 . 4 5 0 0 0 0 0 0 e +03 ,
2 . 1 9 0 0 0 0 0 0 e +01 , 2 . 6 5 0 0 0 0 0 0 e −01 , 5 . 1 4 0 0 0 0 0 0 e −01] ,
[ 2 . 0 0 7 0 0 0 0 0 e +03 , 1 . 2 1 0 0 0 0 0 0 e +02 , 1 . 4 5 5 0 0 0 0 0 e +03 ,
2 . 2 3 4 0 0 0 0 0 e +01 , 4 . 7 6 0 0 0 0 0 0 e −01 , 6.90000000 e −01]])
>>>
We can select an entire column or row of data using the colon (:). Say that we
want to assign the fourth column with temperature data to the variable T , then we
would use the statement t = t data[:,3].
>>> t data [: ,3] # f o u r t h column i n a r r a y
array
( [ 22.95 , 22.45 , 21.84 , 21.9 , 22.34])
>>> t data [1 ,:] # S e c o n d row i n a r r a y
array
( [ 2 . 0 0 7 0 0 0 0 0 e +03 , 1 . 2 1 0 0 0 0 0 0 e +02 , 1 . 4 4 0 0 0 0 0 0 e +03 ,
2 . 2 4 5 0 0 0 0 0 e +01 , 1 . 8 8 0 0 0 0 0 0 e −01 , 4 . 3 4 0 0 0 0 0 0 e −01])
>>> T = t d a t a [ : , 3 ] # P u t t e m p e r a t u r e v a l u e s i n v a r i a b l e T
If we use a minus (-) sign in the index, we start counting from the final rows or
columns of the array. As such t data[-1,-1] gives us the last, lower right value, in
the array (0.69) and t data[:,-2] gives us the column before the last one in the array.
>>> t d a t a [ : , − 1 ]
array ( [ 0.536 , 0.434 , 0.455 , 0.514 , 0.69 ] )
>>> t d a t a [ : , − 2 ]
array ( [ 0.287 , 0.188 , 0.207 , 0.265 , 0.476])
We can also place a colon (:) before an array index to make more selective
choices. If we want only the first three columns of an array, we can use t data[:,:3]
to select these:
>>> t d a t a [ : , : 3 ]
array ( [ [ 2007. , 121. , 1435.] ,
[ 2007. , 121. , 1440.] ,
[ 2007. , 121. , 1445.] ,
[ 2007. , 121. , 1450.] ,
[ 2007. , 121. , 1455.]])
>>>
Using a negative sign will provide everything except the columns/rows counted
from the end of the array. With t data having six columns, we could select only the
first column by:
>>> t d a t a [ : , : − 5 ]
array ( [ [ 2007.] ,
[ 2007.] ,
[ 2007.] ,
[ 2007.] ,
[ 2007.]])
>>>
Finally, we could also select everything except the first and last columns by:
>>> t d a t a [ : , 1 : − 1 ]
array ( [ [ 2007.] ,
[ 2007.] ,
[ 2007.] ,
[ 2007.] ,
[ 2007.]])
>>>
These tricks are of good use when providing exact index values is difficult because
the size of the array is not known, or changes during program execution.
Splitting a data array is also possible, using vsplit() or hsplit(). These com-
mands have two arguments, being the array to be slit and the indices or sections
where the split is desired.
You can use vstack() and hstack() statements to rebuild arrays divided by vs-
plit() and hsplit(), respectively.
Assignment
Make three variables a, b and c with respective values of 10.3, 5 and 137.289. Now
calculate:
• a+b+c
• a/b-c
• (b+c)/a
• 2*b+5.1/c+a*15.9
√
• a
• log b
NOTE: The log and square root functions are defined in the math module or the
scipy (scientific python). To access the sqrt() and log() functions you need to load
one of these modules first by executing the following command in IDLE:
>>> from s c i p y import ∗
>>>
Store these commands in the script file that you made earlier. If you run the
script file, no output will be produced. You can produce output using the print
command by specifying
p r i n t a +b+ c
print s qr t ( a )
• What answers do you get when you type dummy[0,2], dummy[:,0] and dummy[1,:]
in the shell window?
• Make a new variable dummy2 that holds the second row of dummy divided
by 2.0
Chapter 3
Python scripts
24
Figure 3.1: JEdit programmer’s text editor showing the formatting for a python
script file.
Figure 3.2: Idle editor window showing example script.
3.3 Debugging
The process of removing errors (bugs) in your script is called debugging. There
are two forms of errors, those that impact the syntax of the code (forgotten :, },
etc.) and those that are syntactically correct but produce wrong output. The latter
ones are usually much more difficult to find. These would include, for instance,
erroneous use of a variable name in an equation or use of wrong unit conversions
(e.g. multiply by 100 instead of 1000). Most program codes contain such errors,
which sometimes only appear under certain conditions, or are never detected at all
because the output seems reasonable.
The Python compiler will stop when an error in the program code syntax is
detected. When you run a script and an error occurs, a message will be displayed
in the Python shell window. The error may have been caused in your script, but
may also be transferred to Pylab, Math, Scipy or other function libraries that you
have imported in your script. When variables or functions have been defined in the
code that has been executed before the error occurred, these will remain available in
memory and can be therefore accessed/used by typing in commands at the prompt
in the shell window. This is very useful for debugging your script, as you can often
detect why a bug occurred by looking at the variables that have loaded.
You can use the Run > Run module menu entries in the Idle editor to run
a script. Alternatively, within the Idle shell window, or any other Python shell
window, a script can be executed using the following command:
>>> e x e c f i l e ( ” s c r i p t . py ” )
A script file can also be executed independently from Idle or Python using the
following command in a terminal window:
watm@pc06 −091:˜\ $ p y t h o n s c r i p t . py
Chapter 4
29
4.2 The IF statement
If statements are useful to perform an action based on whether a condition is met
or not. For instance, the code below sets a solar radiation Rs variable at -10.0 W
m−2 and then tests if Rs < 0 W m−2 . If the condition is met (true, Rs < 0) it will
set Rs = 0.0 and print a short text and the value of Rs. If not true, it prints ”Solar
radiation larger than or equal to zero.”:
>>> Rs = −10.0
>>> i f Rs < 0 :
Rs = 0 . 0
p r i n t ’ N i g h t t i m e s o l a r r a d i a t i o n = ’ , Rs
else :
print ’ S olar r a d i a t i o n l a r g e r than or equal to zero . ’
Nightime s o l a r r a d i a t i o n = 0. 0
>>>
Note that the conditional statement Rs<0 is ended with a colon (:). You could
also include elif : statements between the if and else statements to make further
distinctions, for instance for very bright sunlight conditions:
>>> Rs = −10.0
>>> i f Rs < 0 :
Rs = 0 . 0
p r i n t ’ N i g h t t i m e s o l a r r a d i a t i o n = ’ , Rs
e l i f Rs > 1 0 0 0 :
p r i n t ’ Very b r i g h t s u n l i g h t ! ’
else :
print ’ S olar r a d i a t i o n l a r g e r than or equal to zero . ’
Nightime s o l a r r a d i a t i o n = 0. 0
>>>
range ( . . . )
range ( [ s t a r t , ] s t o p [ , s t e p ] ) −> l i s t o f i n t e g e r s
R e t u r n a l i s t c o n t a i n i n g an a r i t h m e t i c p r o g r e s s i o n
o f i n t e g e r s . range ( i , j ) r e t u r n s [ i , i +1 , i +2 , . . . ,
j −1]; s t a r t ( ! ) d e f a u l t s t o 0 . When s t e p i s g i v e n ,
i t s p e c i f i e s t h e i n c r e m e n t ( or d e c r e m e n t ) . F o r
example , range ( 4 ) r e t u r n s [ 0 , 1 , 2 , 3 ] . The end
p o i n t i s o m i t t e d ! These a r e e x a c t l y t h e v a l i d
i n d i c e s for a l i s t of 4 elements .
>>> range ( 5 )
[0 , 1 , 2 , 3 , 4]
>>>
Now let’s use this in a for loop, the else statement is optional and is executed when
the i becomes outside the defined range:
>>> f o r i i n range ( 1 , 1 0 , 2 ) :
print ’ i = ’ , i
else :
p r i n t ’We r e a c h e d t h e end o f t h e l o o p ’
i= 1
i= 3
i= 5
i= 7
i= 9
We r e a c h e d t h e end o f t h e l o o p
>>>
Suppose that we do not want to print these values but store them in a list, together
with a second value that is twice as large as i. We can define a list data type variable
with the statement testlist = []. We would then have to append each line of new
values created in the loop and temporarily stored in the ”dummy” variable to the
list of previous values stored in the variable testlist using a testlist.append(dummy)
instruction. The resulting list can then be converted to an array data type using the
array() function. This can be done as follows:
>>> from s c i p y import ∗
>>> t e s t l i s t = [ ] # D e f i n e a v a r i a b l e t o s t o r e y o u r v a l u e s
>>> f o r i i n range ( 1 , 1 0 , 2 ) :
j = 2.0 ∗ i
dummy = i , j
# append t h e dummy v a r i a b l e t o p r e v i o u s v a l u e s
t e s t l i s t . a p p e n d ( dummy )
>>> t e s t l i s t
[(1 , 2.0) , (3 , 6.0) , (5 , 10.0) , (7 , 14.0) , (9 , 18.0) ]
>>> t e s t l i s t = array ( t e s t l i s t ) # convert l i s t of values to array
>>> t e s t l i s t
array ([[ 1. , 2.] ,
[ 3. , 6.] ,
[ 5. , 10.] ,
[ 7. , 14.] ,
[ 9. , 18.]])
>>>
This procedure is very useful for calculating daily averages, sums, standard devia-
tions, etc.
The while is another looping statement that repeatedly executes statements as
long as a condition is true. It can also have an optional ELSE clause. In the
following example we assign a value of 5 to a. In the while statement we increase
a with one and print out the text a is lower than 10 every time an iteration is made.
As you can see below, the loop stops when a reaches 10.
>>> a = 5 # S e t a t o 5
>>> w h i l e a < 1 0 :
p r i n t a , ’ i s l o w e r t h a n 10 ’
a = a + 1 # I n c r e a s e a by 1
5 is lower than 10
6 is lower than 10
7 is lower than 10
8 is lower than 10
9 is lower than 10
>>>
The break and continue statements also allow looping but will not be discussed
here.
Chapter 5
Functions in Python
We have already been introduced to the float(), int(), type(), sqrt() and log() func-
tions in the above, but there are many more (predefined) functions in Python. Be-
low are some examples.
pow ( . . . )
pow ( x , y [ , z ] ) −> number
>>> pow ( 3 , 2 )
9
>>> 3 ∗ ∗ 2 . 0
9.0
>>>
This tells you that pow(x,y) will give you x to the power of y (as does x ∗ ∗y)
and that the function is built into Python and in this case returns an integer (9). As
mentioned earlier a lot of functions are defined in function libraries, such as in the
math module (sin(), cos(), tan(), pow() and sqrt() functions, pi, e constants, etc.).
A function library is called a module. The pow() function is also defined in the
33
Figure 5.1: Documentation screen in Spyder that provides explanation of functions
in Python.
module math, that has to be loaded before you can use the power function using
from math import pow, or load all functions in math by using from math import *.
>>> from math import pow
>>> h e l p ( pow )
Help on b u i l t −i n f u n c t i o n pow i n module math :
pow ( . . . )
pow ( x , y )
R e t u r n x ∗∗ y ( x t o t h e power o f y ) .
>>> pow ( 3 , 2 )
9.0
>>>
You can see which functions the math module holds by typing in help(math).
In Spyder, there is a separate screen with a tab labelled doc (Figure 5.1) for
documentation, that provides documentation for functions. In addition, next to the
tools menu entry a question mark (?) provides access to addition documentation of
several important Python modules.
Assignment
Consult help for the functions sqrt, log, rand, len and math and write down what
each of these functions does.
5.1.2 Find() function
The find() function is defined in the pylab module, which should be imported be-
fore you can use this function. The find() function returns the indices from an
array where the condition that you use in the function is TRUE. It will do so in
an 1-dimensional array. Remember that we assigned temperature to the variable T
before. Suppose that we want to know when the temperature is higher than 22.0
◦ C, we could then issue the following commands:
>>> T
array ( [ 22.95 , 22.45 , 21.84 , 21.9 , 22.34])
>>> T h i g h = f i n d ( T > 2 2 . 0 )
>>> T h i g h
array ([0 , 1 , 4])
>>>
This shows you that T[0], T[1] and T[4] satisfy the condition of being larger than
22.0 ◦ C. Suppose that we only want these values to be converted to K. We would
then do:
>>> T
array ( [ 22.95 , 22.45 , 21.84 , 21.9 , 22.34])
>>> T k e l v i n = T [ f i n d ( T > 2 2 . 0 ) ] + 2 7 3 . 1 6
>>> T k e l v i n
a r r a y ( [ 296.1 , 295.6 , 295. 49])
>>>
The above shows that we can use the find() function within other functions, which
is very useful.
An alternative to find() is to use a condition as an array index. If we want to
find the temperatures after, for instance, 14:45 h, we could do:
>>> # F i n d t e m p e r a t u r e s f o r t i m e a f t e r 1 4 : 4 5 h
>>> t d a t a [ t d a t a [ : , 2 ] > 1 4 4 5 , 3 ]
array ( [ 21.9 , 22.34])
>>>
You can also use a third, additional argument in the range() and arange() functions
to influence the steps:
>>> x= a r a n g e ( 0 , 1 0 , 2 )
>>> x
array ([0 , 2 , 4 , 6 , 8])
>>>
It is also possible to do linear regression using the polyfit() function from the
scipy module. The polyfit() function is based on least-square analysis and takes
three arguments, x-data, y-data and a number n describing the polynomial order
(1 = linear regression, 2 = quadratic, 3 = cubic, etc.). In the linear case (y =
ax + b) it returns two coefficients, the slope a and the intercept b. You call the
function for linear analyses as: a,b = polyfit(xdata,ydata,1). This function will not
provide you with any further statistics. If you need such detailed statistics (r 2 , F-
test, etc.) then you could download and use the module ols.py, which you can get
at http://www.scipy.org/Cookbook/OLS.
5.3 Meteorological and hydrological functions
We have defined several meteorological functions and evaporation formulae in
the meteolib.py and evaplib.py modules. You can download these modules from
http://python.hydrology-amsterdam.nl/.
• date2doy(): converts a date (dd, mm, yyyy) to a ”day of year” (1-366) value.
• sun NR(): calculates daylength and extraterrestrial radiation input, used for
calculating evaporation
Use the help() function to get information about the other functions in the meteolib
module.
5.3.2 Evaporation functions: evaplib.py
A second library, evaplib.py was developped by M.J. Waterloo and contains func-
tions to calculate daily evaporation rates according to the Penman open water
evaporation [E0(); Penman, 1948, 1956, 1963, Valiantzas, 2006], the FAO Penman
Monteith reference evaporation [ET0pm(); Allen et al., 1998], the Dutch Makkink
reference crop evaporation [Em(); de Bruin, 1987] and the Priestley Taylor evapo-
ration [Ept(); Priestley and Taylor, 1972]. The following statement calculates the
Priestley Taylor evaporation for a temperature of 21.65 ◦ C, a relative humidity of
67%, air pressure of 101300 Pa, net radiation input of 18200000 J m−2 day−1 and
a soil heat flux of 600000 J m−2 day−1 .
>>> from e v a p l i b import ∗
>>> E p t ( 2 1 . 6 5 , 6 7 . 0 , 1 0 1 3 0 0 , 1 8 2 0 0 0 0 0 , 6 0 0 0 0 0 )
6.3494561161280778
>>>
Now suppose that you want to calculate Qt , for which you would need Q0 , k and
t as the functions arguments. You could then define the function linreservoir()
as follows, where we import the exponential function from the scipy within the
function and we state in the function argument list that Q0 and k are of type float:
d e f l i n r e s e r v o i r ( q0 = f l o a t ( ) , k = f l o a t ( ) , t ) :
from s c i p y import exp # i m p o r t e x p o n e n t i a l f u n c t i o n
q t = q0 ∗ exp (−1 / k ∗ t )
return qt
Note that the function statements are all indented and that the final statement (return
qt) provides output of the function. If we save this function in a script file called
linres.py, we can import the function linreservoir() as follows:
>>> from l i n r e s import l i n r e s e r v o i r
>>> l i n r e s e r v o i r ( 1 0 0 . 0 , 3 0 . 0 , 6 7 )
10.717059875230674
>>>
The above works only with single values as input. If you want to work with arrays
of time values as input, you can specify this in the function argument list:
# i m por t modules
from s c i p y import a r r a y
d e f l i n r e s e r v o i r ( q0= f l o a t ( ) , k= f l o a t ( ) , t = a r r a y ( [ ] ) ) :
from s c i p y import exp
q t = q0 ∗ exp (−1 / k ∗ t )
return qt
If we now use the function with t as an array, we als get an array of qt as output:
>>> t = a r r a y ( [ 1 , 2 , 3 , 4 ] )
>>> t
array ([1 , 2 , 3 , 4])
>>> q = l i n r e s e r v o i r ( 1 0 0 . 0 , 5 0 . 0 , t )
>>> q
a r r a y ( [ 98.01986733 , 96.07894392 , 94.17645336 , 92.31163464])
>>>
It is always very important to document your programs well so that other people
may use or modify them. The first thing that springs to mind is adding a help
entry text in your function. This means that if you would type help(linreservoir)
it would show you how to use the function and what its inputs and outputs are.
Adding a help entry is easy. Just insert the help text as a multi-line comment after
the function definition, as shown by the example below.
# i m por t modules
from s c i p y import a r r a y
d e f l i n r e s e r v o i r ( f l o a t ( q0 ) , f l o a t ( k ) , t = a r r a y ( [ ] ) ) :
’ ’ ’ The f u n c t i o n l i n r e s e r v o i r ( ) c a l c u l a t e s t h e
d i s c h a r g e a t t i m e t from an i n i t i a l
d i s c h a r g e Q( t = 0 ) and a r e c e s s i o n
constant
Input :
−−−−−−−−−
t : ( a r r a y of ) time
q0 : i n i t i a l d i s c h a r g e v a l u e
k : recession constan
Output :
−−−−−−−−−
qt : ( a r r a y of ) d i s c h a r g e values a t time t
Example :
−−−−−−−−−
>>> l i n r e s e r v o i r ( 1 5 . 0 , 3 0 . 0 , 20 )
2.6359713811572676
’’’
# Do t h e c a l c u l a t i o n s
q t = q0 ∗ exp (−1 / k ∗ t )
return qt
If we now invoke the help function (e.g. help(linreservoir)) we get the following
output with our help text:
>>> h e l p ( l i n r e s e r v o i r )
Help on f u n c t i o n l i n r e s e r v o i r i n module main :
l i n r e s e r v o i r ( q0 = 0 . 0 , k = 0 . 0 , t = a r r a y ( [ ] , d t y p e = f l o a t 6 4 ) )
The f u n c t i o n l i n r e s e r v o i r ( ) c a l c u l a t e s t h e
d i s c h a r g e a t t i m e t from an i n i t i a l
d i s c h a r g e Q( t = 0 ) and a r e c e s s i o n c o n s t a n t
Input :
−−−−−−−−−
t : time
q0 : i n i t i a l d i s c h a r g e v a l u e
k: recession constant
Output :
−−−−−−−−−
qt : d i s c h a r g e a t time t
Examples :
−−−−−−−−−
>>> l i n r e s e r v o i r ( 1 5 . 0 , 3 0 . 0 , 20 )
7.70125678548888
>>>
Many meteorological functions are defined like this in the meteolib and evaplib
modules (see Sections 5.3.1 and 5.3.2).
In the previous chapters you have learned the basics of Python. This included data
types, use of variables and function definitions that combined in a script form a
program that can be used to perform certain actions. In many computer models,
parts of code are implemented with minor changes at different places in the pro-
gram. This implies that if such a part of code is changed or used differently, the
change in function code should be implemented in different places in the program,
making it subject to errors. It is much more efficient to develop code as an object
in one place, that serves as a recipe use elsewhere in the program. A computer
language that can do this, such as Python, is called an object-oriented language.
For instance, if you want to develop a program that defines functions for soils,
you have to deal with differences between soil types, such as for example course
grained, clay and peat soils. If you have very simple functions you could use a
dictionary data type (Section 2.2.5) to achieve this:
>>> s o i l t y p e = { ’ c o u r s e ’ : ’ e x e c u t e p r o g r a m c o d e f o r c o u r s e s o i l s ’ , \
’ c l a y ’ : ’ e x e c u t e program code f o r c l a y s o i l s ’ ,\
’ p e a t ’ : ’ e x e c u t e program code f o r p e a t s o i l s ’ ,\
}
>>> s o i l t y p e . g e t ( ’ p e a t ’ , ’ s o i l t y p e unknown ’ )
’ e x e c u t e program code f o r p e a t s o i l s ’
>>> s o i l t y p e . g e t ( ’ s i l t ’ , ’ s o i l t y p e unknown ’ )
’ s o i l t y p e unknown ’
>>>
You could also achieve the same using if – elif –else statements (Section 4.2)
that would select the appropriate function code:
>>> s o i l = ’ s a n d ’
>>> i f s o i l == ’ c o u r s e ’ :
p ri n t ’ e x e c u t e f u n c t i o n code f o r c o u r s e s o i l s ’
e l i f s o i l == ’ c l a y ’ :
43
print ’ execute f u n c t i o n code f o r c l a y s o i l s ’
e l i f s o i l == ’ p e a t ’:
print ’ execute f u n c t i o n code f o r p e a t s o i l s ’
else :
p r i n t ’ unknown s o i l type ’
unknown s o i l t y p e
>>>
It is very likely that the functions used for these different soils show similarity and
you therefore have to edit three functions if you make modifications.
Object oriented programming makes it possible to develop a kind of general
recipe or prototype for a set of functions that can have several instances with dif-
ferent functioning, i.e. for each of the soil types, in your program. This recipe can
contain constants, variables and functions. The latter are then called the methods
of the object.
A prototype of an object can be created in Python by defining a Class type.
Such a class type contains variables, properties and methods that characterize the
objects of the class. The attributes of a class are called data members and meth-
ods that can be accessed through a . (dot) notation. For instance if a class is
constructed with the name of address, address.street might show the street name
variable, whereas address.city would yield the name of the city.
Below is a simple program using a soil() class with two functions for calculat-
ing arbitrary soil values K and H:
’ ’ ’ This i s a c l a s s f o r handling s o i l data
An i n s t a n c e o f t h i s c l a s s c a n be c r e a t e d by c a l l i n g s o i l (C ) ,
where C i s a c o n s t a n t u s e d i n t h e c a l c u l a t i o n s . C v a r i e s
between s o i l t y p e s .
W r i t t e n by M. J . W a t e r l o o a s an e x a m p l e .
’’’
class s oi l ( object ) :
# D e f i n e an i n i t i a l v a r i a b l e t h a t i s r e l a t e d t o e a c h c l a s s
# instance
def init ( s e l f , C) :
# Get a c o n s t a n t C f o r e a c h c l a s s i n s t a n c e c r e a t e d
s e l f .C = C
def calcK ( s e l f , t h e t a ) :
# Calculate a K variable according to moisture s t a t u s
# t h e t a (0 −1) and t h e c o n s t a n t C
K = 1/ the ta ∗ s e l f .C
return K
def calcH ( s e l f , t h e t a ) :
# C a l c u l a t e w a t e r p r e s s u r e b a s e d on s o i l m o i s t u r e t h e t a
H = s e l f . C / t h e t a ∗ 100
return H
# Calculate s o i l p r o p e r t i e s K and H
kclay = clay . calcK ( 0 . 4 )
ksand = sand . calcK ( 0 . 4 )
p r i n t ’K f o r clay at t h e t a = 0.4 i s : ’ , kclay
p r i n t ’K f o r sand a t t h e t a = 0. 4 i s : ’ , ksand
In the above first a soil() class and functions in the class are defined, where def
init (self,C): is done when the instance of the soil class is initiated. In this case
C varies between soil types and is used to calculate K and H based on a θ input
variable. Then an instance of soil is made for clay with C = 0.05 by calling clay
= soil(0.05) (and also for sand with a different C value) and K is calculated for
θ = 0.4. The same is done for sand. When we run the script we get:
>>>
K for clay at t h e t a = 0.4 i s : 0.125
K f or sand a t t h e t a = 0. 4 i s : 1.75
>>>
Note that the outcome differs between soil types for the same value of θ. We could
now easily add an instance for peat by including:
peat = s o i l ( 0.1)
We then have three instances, one for each soil type, that we can call to calculate
corresponding K values.
Chapter 7
Now you can use the os.getcwd() function to see where you are (/home/watm is my
home directory under unix):
>>> o s . g e t c w d ( )
’ / home / watm ’
>>>
You can change to another directory using the os.chdir() function. To change to an-
other directory use the os.chdir() command, for instance type os.chdir(’C:\svae python’)
in MS Windows, or if you work under unix os.chdir(’/home/watm/svae python’).
It is often easy to define a variable, for instance called ”dataroot”, holding the name
of your working directory. For unix:
>>> d a t a r o o t = ’ / home / watm / s v a e p y t h o n ’
>>> o s . c h d i r ( d a t a r o o t )
>>> o s . g e t c w d ( )
’ / home / watm / s v a e p y t h o n ’
>>>
46
or in MS windows:
>>> d a t a r o o t = ’ c : \ s v a e p y t h o n ’
>>> o s . c h d i r ( d a t a r o o t )
>>> o s . g e t c w d ( )
’c :\ svae python ’
>>>
If you have a long directory path and file name, such as /home/watm/svae python/data.txt
the os.path module provides possibilities to split or join these paths and file names
or file name extensions. For instance, if you have the variable /home/watm/s-
vae python/data.txt and you want to separate the filename from the directory path,
you can do:
>>> import o s
>>> pathname , f i l e n a m e = o s . p a t h . s p l i t ( ’ / home / watm / s v a e p y t h o n / d a t a .
txt ’)
>>> p a t h n a m e
’ / home / watm / s v a e p y t h o n ’
>>> f i l e n a m e
’ data . txt ’
>>>
If you want to split the path (/home/watm/svae python), file name (data) and its
extension you could use os.path.splitext()
>>>p a t h f i l e n a m e , e x t n a m e= o s . p a t h . s p l i t e x t ( ’ / home / watm / s v a e p y t h o n /
data . tx t ’ )
>>> p a t h f i l e n a m e
’ / home / watm / s v a e p y t h o n / d a t a ’
>>> e x t n a m e
’ . txt ’
>>>
These commands are very useful for in case you want to place files in a different
directory than where these were originally, or to change file names.
Assignment
Based on the above, add a few lines of program code to your personal script file
that sets your working directory and gives a list of the files in this directory.
7.2 Working with ASCII data files
7.2.1 Reading data from file with loadtxt()
Often we get our data from automatic data registration units, or data loggers. These
data loggers store the values from the instruments attached to them in an ACSCII
or text file. The values in this file are separated by a delimiter, which usually is
a space ( ), a tab ( ) or a comma (,). The latter file is often called a comma
separated values file and has .csv as its file name extension. Comments in such
files are often preceded by the pound (#) symbol. Output for a VU water level data
logger is, for example:
# DOY day month y e a r h r min s e c H
124 04 05 09 10 15 30 +00002
124 04 05 09 10 30 30 −00005
124 04 05 09 10 45 30 −00010
124 04 05 09 11 00 30 −00020
The data on the first line represents the day of year (124, doy), day (04), month
(05), year (09), hour (10), minutes (15), seconds (30) and the water level in 1/100
of a cm (+00002). Note that all values are separated by spaces.
If these data were stored in a file called wl.txt, we can read these into an array
variable (here called wldata) using the loadtxt() function that is defined in the scipy
(Scientific Python) or pylab modules:
>>> from s c i p y import l o a d t x t
>>> w l d a t a = l o a d t x t ( ’ wl . t x t ’ , comments= ’ # ’ , d e l i m i t e r = ’ ’ )
>>>
Note that you cannot have missing values in the file, so every column must have
the same length and you should not have empty lines at the end of the file. If you
have missing data in one or more columns, you could fill these locations first with
-9999, a value commonly used to indicate missing data. Now we have an array in
which the different columns are stored. The first header lines (#) was skipped due
to the comments=’#’ argument in the loadtxt() function. The first column of the
array (wldata[:,0]) holds the DOY, and the last column (wldata[:,7]) the water level
data. You can now perform calculations with the data:
>>> wl = [ ]
>>> wl = w l d a t a [ : , 7 ] / 1 0 0 0 0 . 0
The water level is now stored in the variable wl and its unit is now m, rather than
in tenths of a mm.
Assignment
1. Use the loadtxt() function (defined in the pylab or scipy modules) to read
the file wl.txt (available at http://ecohydro.falw.vu.nl/python) and place the
data in a variable named wldata. Note that the first line in wl.txt contains a
comment with the header for each column.
5. The actual water level H was 0.31 m at the beginning of the measurements
when the logger H was set to zero and the logger H values are in 0.1 mm units.
Correct H so that it represents the water level in m.
The Campbell Scientific CR1000 data logger that we use for our meteorologi-
cal measurements stores data differently than the VU water level data logger. Here
is an example of the data output of the logger:
”TOA5” , ” M y D a t a l o g g e r ” , ” CR1000 ” , ” E3686 ” , ” CR1000 . S t d . 2 7 ” , ”CPU :
c r 1 0 0 0 m e t e r o l o g y v 1 . 1 2 P o r t u g a l 2 0 1 4 . CR1” , ” 28966 ” , ” f a s t t a b l e
”
”TIMESTAMP” , ”RECORD” , ” AvgTCa” , ” s t d T C a ”
”TS” , ”RN” , ” Deg C” , ” Deg C”
” ” , ” ” , ” Avg ” , ” S t d ”
” 2014−06−05 1 5 : 0 5 : 0 0 ” , 0 1 , ”NAN” , ”NAN”
” 2014−06−05 1 5 : 1 0 : 0 0 ” , 0 2 , ”NAN” , ”NAN”
” 2014−06−05 1 5 : 1 5 : 0 0 ” , 0 3 , 2 7 . 0 1 , 1 . 7 9 8
” 2014−06−05 1 5 : 2 0 : 0 0 ” , 0 4 , 2 4 . 2 5 , 0 . 9 8
We see that it is a comma separated values (csv) file, the first few lines iden-
tify the logger and the meaning of the data in the four columns (”TIMESTAMP”,
”RECORD”, ”AvgTCa”, ”stdTCa”), their units, etc., followed by data lines, which
all start with a date string (”2014-06-05 15:05:00”), float values (27.01) and in
some cases ”NAN”, which is the abbreviation for Not A Number for when the
sensor had not yet been connected.
If you use the loadtxt() function as above, it will give you an error telling you
that it can only read float values. As such we have to extend loadtxt() arguments to
tell it:
1. that we should skip the first four lines of the file because these contain a
header with text
meteodata = l o a d t x t ( ’ f i l e na m e . da t ’ ,\
comments= ’ # ’ , \
d e l i m i t e r = ’ , ’ ,\
s k i p r o w s =4 ,
c o n v e r t e r s = { 0 : s t r p d a t e 2 n u m ( ’\”%Y−%m−%d %H:%M
:%S \” ’ ) }\
)
Finally we have to make sure that the ”NAN” values are converted to indicate
missing values (-9999).
k w a r g s= d i c t ( d e l i m i t e r = ’ \ t ’ , \
d e l e t e c h a r s = ’ ’ ,\
c o n v e r t e r s = { 0 : m a t p l o t l i b . d a t e s . s t r p d a t e 2 n u m ( ’%Y%m%d ’ ) } , \
s k i p h e a d e r =1 ,\
names= True , \
s k i p f o o t e r =0 ,\
f i l l i n g v a l u e s = −9999 ,\
m i s s i n g v a l u e s ={ ’ \”NaN\” ’ }\
)
p t q d a t a = s c i p y . g e n f r o m t x t ( d a t a f i l e , ∗∗ k w a r g s )
We have first stored the arguments for scipy.genfromtxt in the kwargs dict dic-
tionary data container type variable (Section 2.2.5) that can store keys and their
corresponding values (e.g. delimiter=’,’). The converters key is used to convert
the date to a matplotlib.dates date object (pylab numerical date value) and missing
values are identified as NaN (Not a Number).
In the statement scipy.genfromtxt(datafile, **kwargs) statement point to the
previously declared kwargs dictionary using **. We have seen in Section 2.2.3
that the ** operator is used as a power operator (x**4 raises x to the power of
four), but now ** is used for keyword argument unpacking. In this statement **
therefore unpacks the kwargs dictionary into separate keyword options as defined
in the kwargs dict() statement. Note that a singe ** before a variable (e.g. *args)
would mean argument unpacking. So both * and ** are used to unpack data struc-
tures (e.g. lists of arguments or keywords) of the arguments that these precede.
Executing the above statements gives:
>>> p t q d a t a
array ([(721354.0 , 9.4 , 3.7 , 0.26) , (721355.0 , 16.7 , 4.3 , 0.43) ,
(721356.0 , 13.3 , 5.0 , 1.418) , . . . , (733983.0 , 8.7 , 17.2 ,
nan ) ,
( 7 3 3 9 8 4 . 0 , 0 . 0 , 1 9 . 5 , nan ) , ( 7 3 3 9 8 5 . 0 , 0 . 1 , 1 9 . 3 , nan ) ] ,
d t y p e = [ ( ’ D a t e ’ , ’<f 8 ’ ) , ( ’ P ’ , ’<f 8 ’ ) , ( ’T ’ , ’<f 8 ’ ) , ( ’Q ’ , ’<
f8 ’ ) ] )
>>>
The data is now in a ndarray with formats according to dtype and we can address,
for instance, the entire date column, the first date value only, and the fifth T value
by:
>>> p t q d a t a [ ’ D a t e ’ ]
array ( [ 721354. , 721355. , 721356. , . . . , 733983. , 733984. ,
733985.])
>>> p t q d a t a [ ’ D a t e ’ ] [ 0 ]
721354.0
>>> p t q d a t a [ ’T ’ ] [ 4 ]
5.0999999999999996
>>>
In hydrology we often work with time series. The datetime module provides the
tools to conveniently work with dates and times. Detailed information about the
functions in the datetime module is given in http://docs.python.org/library/datetime.html.
We have to import the module first. If we need the current date or date and time
we can do this using the datetime.date() and datetime.now() functions:
>>> from d a t e t i m e import ∗
>>> d a t e . t o d a y ( )
datetime . date (2009 , 6 , 1)
>>> d a t e t i m e . now ( )
d a t e t i m e . d a t e t i m e (2009 , 6 , 1 , 16 , 54 , 44 , 395137)
In the above example, the year (2009) is followed by the month (6), day (1), hour
(16), minutes (54), seconds (44) and microseconds (395137). We can also have an
ordinal value of the date, which in Python is the number of days since 01-01-0001.
For the first of June 2009 this would be:
>>> d a t e . t o o r d i n a l ( d a t e . t o d a y ( ) )
733559
>>>d a t e . f r o m o r d i n a l ( 7 3 3 5 5 9 )
datetime . date (2009 , 6 , 1)
We often have data where the year, month, day, and perhaps time (in hour, min-
utes, seconds) are provided in separate columns (e.g. 2009 10 29 15 31 03). There
are several ways to get the day of year (doy, 1–366) from these date values. The
first one is to use the date2doy() function that is defined in the meteolib function
library. For instance if we have a number of rows with year (YYYY), month (MM)
and day of month (DD) values, we could call the following to assign day of year
values to the variable doy:
doy= d a t e 2 d o y (YYYY,MM,DD)
54
We can also use the datetime module to combine these date values into a date-
time object using the following code, where the year is again given in the variable
YYYY, the month as MM and the day as DD.
>>> import d a t e t i m e
>>>YYYY=2000
>>>MM=9
>>>DD=18
>>>datum = d a t e t i m e . d a t e (YYYY,MM,DD)
>>>datum
datetime . date (2000 , 9 , 18)
With the function strftime() we can now convert these dates to other values, such
as the day of year as a decimal number (doy, 0-366, %j format), the week number
(%U), weekday as a decimal number (%w, 0 (Sunday) – 6), etc. This is shown
below:
>>>datum . s t r f t i m e ( ’%j ’ )
’ 262 ’
>>> datum . s t r f t i m e ( ’%U ’ )
’ 38 ’
>>> datum . s t r f t i m e ( ’%w ’ )
’1 ’
So let’s do something useful. This script reads in a file with three columns con-
taining the year, month and day of the month. It then creates a datetime object that
is stored in an array called alldata and converts the date to the day of year using
the strftime() function. We use the int to convert the string that results from the
strftime() function to an integer doy number. We use append to add data to the end
of the arrays.
>>>from s c i p y import l o a d t x t
>>>import d a t e t i m e
>>> d a t a f i l e = ’ t e s t . t x t ’
>>># D e f i n e d e l i m i t e r
>>>d e l i m i t e r = ’ , ’
>>># Load d a t a f r o m f i l e
>>>d a t a = l o a d t x t ( d a t a f i l e , comments= ’ # ’ , d e l i m i t e r = d e l i m i t e r )
>>># P l a c e e a c h column i n i t s own v a r i a b l e
>>>YYYY= d a t a [ : , 0 ]
>>>MM= d a t a [ : , 1 ]
>>>DD= d a t a [ : , 2 ]
>>># D e f i n e a r r a y s h o l d i n g d a t a
>>>a l l d a t a = [ ]
>>>doy = [ ]
>>># Loop t h r o u g h d a t a t o c o n v e r t t o d a t e t i m e o b j e c t
>>># and doy v a l u e s
>>>f o r i i n range ( 0 , l e n ( d a t a ) ) :
>>> datum = d a t e t i m e . d a t e (YYYY[ i ] ,MM[ i ] ,DD[ i ] )
>>> a l l d a t a . a p p e n d ( datum )
>>> doy . a p p e n d ( i n t ( datum . s t r f t i m e ( ’%j ’ ) ) )
>>> p r i n t doy
[ 335 , 336 , 337]
Chapter 9
This allows you to create very nice graphics. For meteorological data, it is often
useful to plot a variable, such as wind speed, temperature or radiation against time.
In order to do so we use the plot() function in the pylab module. Let’s use the
temperature data that were introduced in Section 2.2.7 as an example for plotting.
We assigned the temperature data to a variable T , whereas the time data was given
in the third column of the array (i.e. t data[:,2]). We can plot the data by simply
defining a figure (pylab.figure(1), giving the pylab.plot() command followed by the
pylab.show() command:
>>> p y l a b . f i g u r e ( 1 )
<m a t p l o t l i b . f i g u r e . F i g u r e i n s t a n c e a t 0 xb6d571ec >
>>> p y l a b . p l o t ( t d a t a [ : , 2 ] , T )
[< m a t p l o t l i b . l i n e s . Line2D i n s t a n c e a t 0 x b 5 e e 4 2 e c >]
>>> p y l a b . show ( )
This will create a figure window as in Figure 9.1. If you want to issue additional
commands in Idle, close the figure window first. We can fancy things up a bit by
giving the graph a label for the x-axis:
>>> x l a b e l ( ’ Time ’ )
<m a t p l o t l i b . t e x t . T e x t i n s t a n c e a t 0 xb5de64cc >
57
Figure 9.1: Plot of temperature data versus time using the plot command of the
pylab module.
Figure 9.2: Plot of temperature data versus time, now with x-axis label and title.
This will result in the graph shown in Figure 9.2. For the y-axis label we need a
superscript in the ◦ C unit for temperature. For this we need to invoke LATEX style
code, where the \circ command will create the small circle and adding the ∧ will
put format it as superscript. We need to set the following first to activate LATEX text
interpretation:
>>> r c ( ’ t e x t ’ , u s e t e x = T r u e )
>>>
Then we need to use the ylabel() command with LATEX text. In LATEX, a formula
starts and ends with the $ symbol. Text between the $ signs will be formatted as an
equation, which means that spaces are ignored. If you need a space you should use
the tilde (∼) symbol between two words. We often want normal text in the label,
and then use a superscript, for instance, in the unit. We can explicitly state that the
text should be in normal roman-font text font by placing it in a \rm environment,
i.e. between accolades. We also have to place an r before the label text to tell
Python that this is a raw string so it does not interpret Python escape commands,
such as \n that would make a new line, or \t that would insert a tab in Python. So
the ylabel() statement could look like this:
>>> y l a b e l ( r ’ $ {\rm T e m p e r a t u r e ˜ [ } ˆ \ c i r c {\ rm C] } $ ’ , f o n t s i z e = 1 6 )
<m a t p l o t l i b . t e x t . T e x t i n s t a n c e a t 0 xb4e93bec >
>>>
to get Wind speed [m s−1 ]. Note again the use of ∼ where we need spaces in
the text. In these examples we also increased the font to 16 points by the fontsize
statement in the ylabel() command, as this makes the figure better readable in your
report. The result is shown in Figure 9.3. We also need to increase the font sizes
of the x- and y-ticks, using xticks(fontsize=15) and yticks(fontsize=15) commands,
and increase the linewidth, include a legend label and specify a legend. We should
then save the figure in the image directory of our report. All commands to create
Figure 9.4 are listed below (without Python responses).
>>> figure (1)
>>> p l o t ( t d a t a [ : , 2 ] , T , l a b e l = ’ Air Temperature ’ , l i n e w i d t h =1. 5)
>>> x l a b e l ( ’ Time ’ , f o n t s i z e = 1 6 )
>>> y l a b e l ( r ’ $ {\rm T e m p e r a t u r e ˜ [ } ˆ \ c i r c {\ rm C] } $ ’ , f o n t s i z e = 1 6 )
>>> x t i c k s ( f o n t s i z e =15)
>>> y t i c k s ( f o n t s i z e =15)
>>> legend ( )
>>> s a v e f i g ( ’ t e m p e r a t u r e . eps ’ , dpi =300)
Figure 9.3: Plot of temperature data versus time, now with x- and y-axis labels, the
latter with fontsize=16.
>>> show ( )
Inserting the saved figure (as encapsulated postscript, eps, file) in your report re-
sults in Figure 9.5.
You can also use markers, where the ’o’ represents a closed circle, a ’+’ gives
you a plus marker, etc. By default, markers are filled but you can create open
dots and determine the fill and edge colours using the facecolors and edgecolors
options. For example we can create a scatter plot graph displaying x and y val-
ues that we obtain from the random module in scipy. The scipy.random.randn()
function gives you random samples from the standard normal distribution (a nor-
mal distribution whit average value of 0 and variance of 1). The s= option in the
pylab.scatter function represents the size of the markers.
>>> import p y l a b
>>> import s c i p y
>>> x= s c i p y . random . r a n d n ( 1 0 0 )
>>> y= s c i p y . random . r a n d n ( 1 0 0 )
>>> a = s c i p y . random . r a n d n ( 1 0 0 )
>>> b= s c i p y . random . r a n d n ( 1 0 0 )
>>> figure (1)
>>> p y l a b . s c a t t e r ( x , y , s =50 , f a c e c o l o r s = ’ none ’ , e d g e c o l o r s = ’ b ’ )
>>> p y l a b . s c a t t e r ( a , b , s =70 , f a c e c o l o r s = ’ y ’ , e d g e c o l o r s = ’ g ’ )
Figure 9.4: Plot of temperature data versus time, with x- and y-axis labels (fontsize
16), x- and y-ticks fontsize 15, a legend and a linewidth of 1.5 instead of the default
1.0.
Figure 9.5: Saved EPS figure of temperature data versus time, with x- and y-axis
labels (fontsize 16), x- and y-ticks fontsize 15, a legend and a linwidth of 1.5
instead of the default 1.0.
4
−1
−2
−3
−4
−4 −3 −2 −1 0 1 2 3 4
Figure 9.6: Example of the use of the pylab.scatter function with open and closed
markers of different colours.
>>> p y l a b . show ( )
The above commands would result in a plot similar to that shown in Figure 9.6.
p l o t ( time , T , ’ ro ’ , l a b e l = ’ Temperature ’ )
# Place a legend
l egend ( l o c =1)
# Use t w i n x ( ) t o c r e a t e a s e c o n d s e t o f a x e s on t o p o f t h e f i r s t
set
twinx ( )
# C r e a t e l a b e l f o r t h e r i g h t y−a x i s
y l a b e l ( r ’ $ {\rm S t . ˜ dev . ˜ T e m p e r a t u r e ˜ [ } ˆ \ c i r c {\rm C] } $ ’ )
# Now p l o t s t a n d a r d d e v i a t i o n o f T v e r s u s t i m e
p l o t ( t i m e , s t d T , l a b e l = ’ S t . dev ’ )
# Place a legend f o r the second s e t of axes
l egend ( l o c =2)
# S a v e t h e f i g u r e a s EPS f i l e
s a v e f i g ( ’ twoaxes . eps ’ , dpi =300)
Figure 9.8: Saved EPS figure of time series of temperature and standard deviation
of temperature using the twinx() function to create two y-axes.
Bibliography
H. L. Penman. Natural evaporation from open water, bare soil and grass. Proceed-
ings of the Royal Society, 193:120–146, 1948. Series A.
J.D. Valiantzas. Simplified versions for the Penman evaporation equation using
routine weather data. Journal of Hydrology, 331(3–4):690–702, 2006. doi:
10.1016/j.jhydrol.2006.06.012.
66
Index
67
else, 43 help() function, 33
ELSE statement, 31, 32 hsplit(), 22
Em() function, 39 hstack(), 21
Enthought Python, 7
Ept() function, 39 IDE, 9
Error, 27 IDLE, 9
Error message, 27 Idle editor, 24
es calc() function, 38 IDLE editor window, 24
ET0pm() function, 39 IDLE shell window, 9, 24
Evaplib function library, 39 IF statement, 30
Evaplib module, 14, 38, 39, 42 If statement, 43
Evaporation functions, 38 import, 13
Executing a script, IDLE, 27 Importing modules, 34
Executing script files, 27 Indentation, 29
Exponential function, 39 Instance, 44
Extraterrestrial radiation function, 38 int() function, 16, 54
Integer, 15
facecolors, 59 Integer data type, 15
False, 29 Integrated Development Environment, 9
figure() function, 56 interactive plotting, 61
File load, 27 ion() function, 61
File saving, 51 Ipython, 7
find() function, 35
float, 52 JEdit programmer’s text editor, 24
Float data type, 15
Keyword argument unpacking, 50
float() function, 16
Floating point decimal format, 18 L calc() function, 38
Floating point exponential format, 18 Lambda function, 42
fontsize, 58 lambda function, 39
FOR loop, 30 legend() function, 58
FOR statement, 30 legend.fontsize variable, 62
Format of number, 18 len() function, 51
from–import, 13 Linear reservoir function, 39
Frozenset data type, 16 linewidth, 58
Function argument list, 39, 40 linspace() function, 36
Function definition, 39 List data type, 16, 31, 51
Function library, 12, 33 list data type, 31
gamma calc() function, 38 Load a function, 34
genfromtxt() function, 17, 49 loadtxt(), skiprows, 49
GRIB file format, 52 log() function, 22
tan() function, 33
Terminal, 7
Text console, 7
Text terminal, 7
text.fontsize variable, 62
time, 53
True, 29
Tuple data type, 16
twinx() function, 62
Type, 15
type() function, 16
Variable names, 14
Vertical stack, 22
vpd calc() function, 38
vsplit(), 22
Xemacs, 9
xlabel() function, 56