Python Lesson
1 Lesson outline
- Python scalars
- Native Python lists
- Python control structures: conditionals
- Python control structures: loops
- Application to NumPy data
2 Standard Python scalar types
Python has several built-in types for handling numerical data, strings, Boolean (logical True
or False
) values, and dates and times; they are called scalars.
2.1 Numeric scalars
There are two numeric scalars, float and int. The float type corresponds to a double-precision (64-bit) floating-point number. The int type is an arbitrary precision signed integer.
integer_example = 496 divisor_sum = 1+2+4+8+16+31+62+124+248 print(496//31) print(divisor_sum)
Floats are double precision by default (no single precision type) and they can be expressed in scientific notation
float_example = 0.5*(1.0 + np.sqrt(5.0)) print(float_example)
2.2 Strings
Python is considered a very powerful programming language for string manipulation. Strings are defined surrounding the text by single or double quotes
string_1 = 'This is a string' string_2 = "This is another string" string_3 = "Let's roll!" string_4 = "She said \"Let's roll!\""
You can use one or the other, but always start and end with the same. Depending the one you are using, you can include the other in the string, as in string_3
. If you need to include the same quote than the marker in the string you have to escape it with a backslash, as in string_4
.
Usin triple quotes you can define a multiline string
string_3 = '''This is a multiline string... The string follows here... '''
In strings you can use the count
method
print(string_1.count("i")) print(string_3.count("\n")) # Newline characters
As in the previous example, the backslash is an escape character used to specify special characters.
You can access string characters by its index
print(string_1[2]) print(string_1[2:])
Strings are immutable objects, you cannot alter them by index, try
string_1[2] = "f"
However, you can append or prepend characters to a string with the + operator, that concatenates them
string_4 = "Betis" print("Viva el " + string_4 + "!")
You can also use the replace method
string_5 = string_1.replace("a", "another") print(string_5)
You can join two strings using the sum symbol. This is very used when making graphs. To add a numerical or other scalar to the string you can use the str
function.
Str_legend_0 = "This may be the figure for par = " par_int = 12 Str_legend_int = Str_legend_0 + str(par_int) + " units" par_float = 2.2221 Str_legend_float = Str_legend_0 + str(par_float) + " units" print(Str_legend_int) print(Str_legend_float)
You can further control strings using the format()
method as in the following examples
example_0 = "The approximate value of the golden section is " + str(0.5*(1.0 + np.sqrt(5.0))) print(example_0) example_1 = "The approximate value of {0} is {1:.5f}".format("the golden section", 0.5*(1.0 + np.sqrt(5.0))) print(example_1) example_2 = "The approximate value of {0} is {1:.8g}".format("the golden section", 0.5*(1.0 + np.sqrt(5.0))) print(example_2) summing = "{1:d} plus {2:d} is equal to {0:d}" print(summing.format(3,4,-1))
For more info check https://www.python.org/dev/peps/pep-3101/#format-specifiers.
2.3 Booleans
The Booleans values in Python are True
and False
and can be combined with the keywords or
, and
, and not
.
print(True and False) print(True and not False) print(True or False)
2.4 The None
type
The Python null type is None
, which is returned by any function that does not explicitly return a value.
var_1 = None print(var_1 is None) print(string_4 is not None)
When functions are defined, None
is a common default value for function arguments (check Lesson 4).
2.5 Type casting
You can transform among Python objects with the str
, float
, int
, and bool
commands
string_6 = str(float_example) print(string_6 + string_1) # print(2*float(string_6)) # print(2*int(float_example)) # print(bool(string_6)) # print(bool(0)) print(bool(0.0)) print(bool('')) print(bool(' '))
3 Native Python Lists
The native Python data structures are lists, dicts (AKA hashes), tuples, and sets. In this lesson we cover only lists and the rest will be covered in the following lesson.
3.1 Defining Python Lists
A list is a sequence of Python objects (maybe scalars or not, and may have also different types) that has variable lenght and is mutable. They can be defined using square brackets or the list
command.
list_example = ["This ", "is ", "a ", "list ", "of ", "string"] print(list_example) list_example[5] = "strings" print(list_example)
You can slice Python lists
print(list_example[0:3]) print(list_example[-3:])
As it happens with Numpy, when dealing with native Python structures, one needs to be always aware that Python uses the so called pass by reference and not the pass by value strategy of other programming languages. Thus, an assignment implies a reference to data in the righthand side. If we execute
list_example_2 = [1,2,3,4.0,"Hi"] list_example_3 = list_example_2
the data are not copied and we do not have two data instances. Instead, both lists, list_example_2
and list_example_3
point to the same data. Having this in mind, we can understand what follows
print(list_example_2) print(list_example_3) list_example_2[4] = "Hello" print(list_example_2) print(list_example_3)
You can use the list
function if you want to make a copy of the list
list_example_2 = [1,2,3,4.0,"Hi"] list_example_3 = list(list_example_2) list_example_2[4] = "Hello" print(list_example_2) print(list_example_3)
We will revisit this important aspect once we arrive to the subject of function definition in Python, where we should be careful to avoid unwanted side effects.
You can also create nested lists
nested_list = [["wasabi", "sushi", "sashimi", "miso"],["taco", "gringa", "enchilada", "carnitas"]] print(nested_list[0][2])
3.2 Modifying Python lists
And you can add elements to the end of a list using the append
method.
list_example.append(" Hello!") print(list_example) # aa = [] # This is an empty list (Boolean evaluates to False) print(aa) aa.append(float_example) print(aa)
The insert
method (computationally more expensive than the append
method) allows for the insertion of elements at any place in the list
list_example.insert(2, "Cheerio!") print(list_example)
The opposite method to insert
is pop
, which removes an element at a given place of the list
list_example.pop(2)
print(list_example)
You can also remove elements by value using the remove
method, which
removes the first appearance of a given list element.
list_example.remove("list ") print(list_example)
The in
keyword allow to check if there exists an element of the list
print("Bye!" in list_example) print(" Bye!" in list_example)
And you can obtain the index of a given element with the index()
method that finds the given element and returns its position (remember, indeces start in zero). If the same element is present multiple times, the index of the first occurrence of the element is returned.
print(list_example.index("is "))
Adding two lists concatenates them
list_example_4 = list_example_2 + list_example_3 print(list_example_4)
You can use the method extend
also to join two lists, which is more efficient than directly adding them
list_example_2.extend(list_example_3)
print(list_example_2)
When doing calculations, one should always have in mind that a native Python list is a sequence of objects that can be of different types. This flexibility comes upon a cost on efficiency when compared to Numpy ndarrays, that store data of a single type in contiguous memory blocks.
4 Making choices in Python: conditionals
Conditional statements alter the flow of a program depending on statements True
or False
value.
Possible conditionals
a == b
:True
ifa
equalsb
a != b
:True
ifa
is not equal tob
a < b
,a <= b
:True
ifa
is less than (less than or equal) tob
a > b
,a >= b
:True
ifa
is greater than (greater than or equal) tob
a is b
:True
ifa
andb
reference the same Python objecta is not b
:True
ifa
andb
reference different Python objects
4.1 The if ... elif ... else
control structure
The simplest case is the single if
conditional with syntax
if (conditional): # if block of code
The indented code block only is run if the conditional
statement evaluates to True
.
if (float_example > 0): print("Positive number")
You can test several alternatives with elif
blocks and add a final default clause if none of the previous are true using else
if (float_example > 0): print("Positive number") elif (float_example < 0): print("Positive number") else: print("This is zero...")
We can use this conditional structure to calculate the body mass index from the height and weight of a person (in m and kg units)
weight = 80 height = 1.80 bmi_value = weight/height**2 # if bmi_value < 15: bmi_range = "Very severely underweight" elif bmi_value < 16: bmi_range = "Severely underweight" elif bmi_value < 18.5: bmi_range = "Underweight" elif bmi_value < 25: bmi_range = "Normal(healthy weight)" elif bmi_value < 30: bmi_range = "Overweight" elif bmi_value < 35: bmi_range = "Obese Class I (Moderately obese)" elif bmi_value < 40: bmi_range = "Obese Class II (Severely obese)" else: bmi_range = "Obese Class III (Very severely obese)" ## print(bmi_value, " ", bmi_range)
Exercise 3.1 |
4.2 The ternary expression
This expression combines an if...else..
statement with single blocks formed by a single command into a single line. It’s useful and terse but it does not help increasing the readability of the code.
The syntax is
statement_1 if (conditional) else statement_2
and it is equivalent to
if (conditional): statement_1 else: statement_2
An example is
a = 1001 print("Variable larger than 1000") if (a > 1000) else print("Variable less than or equal to 1000")
5 Control Structures in Python: loops
Control structures alter the program flow. We explain the for
and while
control structures.
5.1 The for
loop
The for
control structure defines a loop over an iterable with a syntax
for value in collection: # do something
A string is a possible iterable
for string_char in string_1: print(string_char)
You can control the loop flow using the keywords continue
and break
. The continue
statement skip the rest of the block and continues with the next iteration
for string_char in string_1: if (string_char == "i" or string_char == "a"): continue else: print(string_char)
The break
statement finish the loop and continues with the ensuing program statements.
for string_char in string_1: if (string_char == "o" or string_char == "u"): break else: print(string_char)
The Python statement range
is specially useful for working with loops. The command range(start, stop, step)
provides and iterable starting in start
, ending in stop-1
and with differences of step
(default value one).
In this way we can sum all multiples of 7 or 13 from zero to ten thousand
total = 0 for number in range(10000): if (number % 7 == 0 or number % 13 == 0): total = total + number print(number, total) print("Total is = ", total)
If we want to sum all even integers from 2 to 100 we proceed as follows
total = 0 for number in range(2,101,2): total += number print(number, total) print("Total is = ", total)
Note that statements as total = total + number
are so common that they can be expressed more succintly as total += number
. There are equivalent statements -=
,
*=
and /=
.
The list
statement allows to materialize an iterable
iterable = range(10) print(iterable) print(list(iterable))
A very common situation is that in a loop we want to access the indexes of the elements that are being iterated. This can be done as follows
index_val = 0 for element in iterable: # work with element and with index = index_val index_val += 1
As this is a common need, Python has a built-in sequence method called enumerate
that returns a sequence of index, values
(a tuple, explained in next lesson).
for index, element in enumerate(iterable): # work with element and with index = index_val
Exercise 3.2 |
5.2 The while
loop
The while
control structure defines a loop depending on a condition. The loop block is run until the conditional becomes False
with a syntax
while (conditional): # do something
For example
total = 1 value = 1 while (total < 5000): value += 1 total *= value print(value, total) print("Value and total after loop are", value, total)
You can also use the keywords continue
and break
to control a while
loop flow.
Sometimes you can fall into an infinite loop as this one
while (1): print("What a mess...")
In this case it is important to know how to interrupt the loop: click on menu entry Kernel -> Interrupt
. You can find an application of an infinite loop in the next lesson.
Sometimes loops become infinite unexpectedly. The following example illustrates the point mentioned above about the possible problems arising when using conditionals with floats. The while loop, seemingly inoffensive, depending on the value of the i2
variable can end up as an infinite loop (try e.g. i2 = 0.3,0.7,0.8,0.9
).
i1, i2 = 0, 0.9 while i1 != i2: print(i1) i1 += 0.1
Exercise 3.3 |
5.3 Testing floats equality
Be always aware that due to the finite precision in their internal representation and rounding/numerical errors conditionals can be tricky when applied to floats. As a rule, testing the equality of two floats x
and y
as x==y
is highly discouraged and may have unpredictable side effects.
The way to avoid such problems is to replace float equality comparisons for inequalities, in this case while i1 <= i2:
or, even better, compare integer quantities. When you need to compare to floats, define a tolerance -depending on the nature of the problem- and compare the difference of the two values versus this tolerance. You can also check the useful NumPy function np.isclose
.
Exercise 3.4 |
6 Application to NumPy data
We can apply the control structures to NumPy data. If we load again a set of data
metdata_orig = np.loadtxt(fname='files/TData/T_Alicante_EM.csv', delimiter=',', skiprows=1) temp_data = metdata_orig[:,1:]
We can check whether these data have a maximum or minimum above some threshold
max_value = 30 min_value = 10 if (temp_data.max() >= max_value): print("These data have a maximum temp larger or equal to ", max_value) else: print("The max temperature limit is not broken.") # if (temp_data.min() <= min_value): print("These data have a minimum T less than or equal to ", min_value) else: print("The min temperature limit is not broken.") # if (temp_data.max() >= max_value and temp_data.min() <= min_value): print("These data have a maximum temp larger or equal to ", max_value, " AND a minimum T less than or equal to ", min_value) else: print("Both max and min temperature limits are not broken simultaneously")
If, for example, we want to check how many years have attained a monthly average temperature larger than a given threshold we can do this as follows
max_value = 30 nyears = 0 for index_year in range(0,136): if (temp_data[index_year,:].max() >= max_value): print(metdata_orig[index_year,0]) nyears += 1 print("A total of {0} years have one or more average monthly temperatures larger than {1} degrees Celsius.".format( nyears, max_value))
We can also, using a second loop, print which months have temperatures larger than the stablished threshold.
max_value = 27 nyears = 0 for index_year in range(0,136): if (temp_data[index_year,:].max() >= max_value): for index_month in range(0,12): if (temp_data[index_year,index_month] >= max_value): print(metdata_orig[index_year,0], index_month) print("") nyears += 1 print("A total of ", nyears, " have at least an average monthly temperature larger than ", max_value, "degrees Celsius.")
Exercise 3.5 |
Of course, you can skip loops using NumPy Boolean matrices indexing. Last commands can be expressed in a very concise form as
max_value = 29 min_value = 10 print(np.any(temp_data > max_value)) print(np.any(temp_data < min_value)) print(np.any(temp_data > max_value) and np.any(temp_data < min_value)) print(np.sum(np.any(temp_data >= max_value, axis = 1))) print(np.sum(np.any(temp_data >= max_value, axis = 0))) # What means this number?
The np.any
NumPy command tests whether any array element along a given axis evaluates to True
. Note that Boolean values are treated as 1 and 0 in arithmetic operations.
To find elements in an array verifying a given condition it is of interest the function np.where
. In can be used in two ways. The first one, just specifying the condition, provides the positions where the condition is fulfilled:
x=np.arange(9).reshape(3,3) print(np.where(x>1))
This is equivalent to the use of:
print((x>1).nonzero())
The other option is to specify the condition and to provide two matrices that should be broadcastable. The syntax for this function is np.where(condition, Array_A, Array_B)
. When condition is True
the corresponding element of Array_A
is selected, the one of Array_B
otherwise. This use resembles the ternary expression
np.where(x>1,x,-1)
As you can see we can replace arr_A
or arr_B
by scalars. If the condition is true you obtain the corresponding element of x
and -1
otherwise.
In this example we use two matrices
np.where(x>5,[["H","H","H"],["H","H","H"],["H","H","H"]],[["s","s","s"],["s","s","s"],["s","s","S"]])
Exercise 3.6 |
Created: 2024-02-21 Wed 16:40