P1-E002 ======= In this exercise, you learn about some more basic concepts of programming, incl. arrays and ways to let your programme automate calculations and make decisions. Information ----------- +----------------------+--------------------------------------------------------+ | Learning goals | +======================+========================================================+ |**Concepts** | | | | * index value | | | * array/vector | | | * for loops | | | * if statements (if, else if, else) | | | * comparison operators | +----------------------+--------------------------------------------------------+ |**Skills** | | | | * work with arrays | | | * implement a for loop | | | * let your programme make a decision | +----------------------+--------------------------------------------------------+ .. topic:: What to Submit Submit your script for the final task of this exercise. The script should be named *[your surname]_e002.[ext]*, where *[ext]* is the file extension. In case of Python, this would be *.py*, for Matlab it is *.m*, etc. Array like collections ---------------------- Collections can hold multiple values and working with them saves a lot of time, effort and memory. We will explore this a little more in today's lesson. In Python, we will use 3 types of array like collections: **Tuples**, **Lists** and **Dictionaries**. Let's have a look at the properties of each and experiment with them. A **tuple** is an *ordered, unchangeable* collection that can store any type of data values (integers, strings, etc.). You can define a tuple using rounded brackets.:: myTuple = ('EARTH', 2140) A **list** is an *ordered, changeable* collection that can store any type of data values (integers, strings, etc.); You can define a list using square brackets, e.g.:: myList = ['EARTH', 2150] A **dictionary** - an *unordered, indexed, changeable* collection that can store any type of data values (integers, strings, etc.); You can define a dictionary using curly brackets, specifying a *key* and a *value* e.g.:: myDict = {'place':'EARTH','year':2160} Start your IDE (e.g. Spyder) and create a tuple, a list and a dictionary in your console, as described above. For each of the array types, we created a *1 dimensional* array with a *length of 2*. In each array, we store 2 values of different data types: 1 string (*'EARTH'*) and 1 integer (year). Next, we will explore the properties of each of the Python arrays. Before we can do that, we need to first familiarise ourselves with the concept of an **index value**. Index Values ------------ If you type: .. code-block:: python print(myTuple) the print function will return to you the full list:: ('EARTH', 2140) However, often you will only want to use one of the values stored in an array. This is where index values come in handy. From mathematical notation, we are already familiar with index values. Think about the notation for a simple sum of all elements in a vector **x** (= 4,1,3,9,2): .. math:: \begin{equation} sum={\sum_{i=1}^{5} }x_i = x_1 + x_2 + x_3 + x_4 + x_5 = 4 + 1 + 3 + 9 + 2 \end{equation} Here, the index value ranges from 1 to 5, and :math:`x_1 = 4, x_2 = 3`, etc. Index values in programming work in the same way. The index value allows you to access a specific value in your array. If the array is ordered, the index value (an integer), corresponds to the a specific position in the ordered array. Depending on your programming language, the first number may either be 1 or 0. In Python, it is 0. A good analogy for indexing are elevator controls that take you to floors with different contents. Python would be a German elevator control panel, on which 0 marks the ground floor. .. figure:: elevator_cc0.jpg The first value in your defined tuple (*EARTH*) can therefore be accessed by referencing *myTuple[0]*. Try this by typing: .. code-block:: python print(myTuple[0]) We can perform calculations on specific values in an array by referencing it with the index value. Try adding 20 years to the year stored in the second position in the array: .. code-block:: python print(myTuple[1]+20) Now let's try to update the year with the same calculation and overwrite the old year with the new year: .. code-block:: python myTuple[1] = myTuple[1]+20 Did this work? It shouldn't, because tuples are unchangable and thus don't allow item assignment. Now try the same with your list and display the updated value using the *print* function: .. code-block:: python myList[1] = myList[1]+20 print(myList[1]) Unlike tuples, lists allow items in the array to be updated as you just did. Finally, let's explore the properties of dictionaries a little. Try accessing and displaying the year value of the dictionary by typing: .. code-block:: python print(myDict[1]) You should get an error message at this point, because dictionaries are not ordered. However, dictionaries are indexed via their *keys*, and we can access a specific value in dictionaries by using the pre-defined *key* values. In case of our dictionary, those keys are *'place'* and *'year'*. Try accessing and displaying the year value by typing: .. code-block:: python print(myDict['year']) This should display to you the year *2160*. Since dictionaries are not ordered, the order in which you define its entries doesn't matter. Try this out by defining a new dictionary just as we did before, but swap the place and year, then repeat the *print* command above. It will yield the same result. Useful List Functions --------------------- Assume we are managing and maintaining weather station data using Python scripts. Somewhere in the scripts, we would probably want to store the names of different weather stations in a list. Let's create this list quickly: .. code-block:: python stationNames = ['Pan de Azucar', 'Santa Gracia', 'La Campana', 'Reserva Nacional Paposo'] It is possible, that we set up a new weather station in Nahuelbuta and now want to include its name in our list. We can do this ussing the *append()* function. .. code-block:: python stationNames.append('Nahuelbuta') Check with the *print()* function if it has been included in the list. You will noticed that it has been appended at the end of the list. Unfortunately, our weather station at 'Reserva Nacional Paposo' will no longer be maintained and has to be removed from our list. We can remove it by using *del* and accessing the entry using an index value to point at the correct position in the list. The station to be removed is in 4th position, which corresponds to the index value 3 in the Python numbering system. Try deleting it by typing: .. code-block:: python del stationNames[3] Check the list again with the *print()* function. It should be back to a length of 4, include *Nahuelbuta* at the end, and no longer include *Reserva Nacional Paposo*. Finally, here are a few more functions before we move on. Check what they do by *printing* the results. .. code-block:: python stationNames.reverse() # This will reverse the order in your list stationNames.sort() # This will sort your values in your list stationNames.count('Pan de Azucar') # This will count the number of times 'Pan de Azucar' occurs in your list stationNames.index('Pan de Azucar') # This will return the index value for 'Pan de Azucar' (0 before and 3 after list reversal) Comparison Operators -------------------- Most decisions are based on examining a value, comparing it to a reference value, and using their relation as a basis for decision. These comparisons are made possible in programming by using comparison operators. Here's a list of those operators: +------------------------+--------------------------------------------------------+ | Operator | Meaning | +========================+========================================================+ | `==` | equal to | +------------------------+--------------------------------------------------------+ | `!=` | not equal to | +------------------------+--------------------------------------------------------+ | `<` | less than | +------------------------+--------------------------------------------------------+ | `<=` | less than or equal to | +------------------------+--------------------------------------------------------+ | `>` | greater than | +------------------------+--------------------------------------------------------+ | `>=` | greater than or equal to | +------------------------+--------------------------------------------------------+ The general *syntax* for comparing two values with these operators is:: [value] [comparison operator] [reference value] We will examine how this is used in practical decision making below. If Statements ------------- We let our programmes make decisions using *If Statements*. Let's look at an example of that. Type the following into your console and make sure the print statement is indented as below. .. code-block:: python exercise='boring' if exercise == 'boring': print('deal with it') print('done') Now that you have dealt with this, let's examine what happened. First, you assigned the value 'boring' to the variable *exercise*. In the next line, you used the comparison operator *==* to check if the value for *exercise* is indeed boring. It is so, because we defined it in the line above. Since the comparison statement is true, the print function was used to display 'deal with it'. .. note:: Python Syntax Note the line that started with **if** ended with a colon. This is important if you use Python. Also important is the the indentation for the next line. It tells Python that the *print* command is still part of your *if statement*. In other programming languages, you explicitly state the end of an if statement with something like *end if*. An unindented code block (in this case the second print statement) following the indented code block tells Python that the end of the if statement is reached. Try the same again, but assign the value 'exciting' to the variable *exercise*. This should result only in the displaying of the word *done*, because the programme ignored the indented *print* statement following a negative result of the value comparisons in the first line. Decisions can be more complex than that. If one condition is not met, we may want to check if an alternative condition is true. We achieve that using the **elif** statement in Python, which is short for *'If the first condition is not met, check this second one'*. The **else** statement translates to *'for all other conditions'*. In your code editor, create a script like below and execute it. .. literalinclude:: decision1.py Here, we have *nested* if statements. Play around with the values you assign to the variables *target* and *i_am* and execute your modified code to get a better feeling for how these if statements work. It's also possible to combine comparisons in an if statement by using **and** as such: .. literalinclude:: decision2.py Note that this time, there is no case for *target == 'human'* and *i_am* being neither *dalek* nor *cyberman*, because we lost one level in the if statement hierarchy. We could add this case with another **elif** statment like: .. code-block:: python elif target == 'human' and i_am != 'dalek' and i_am != 'cyberman': action = 'greet human' However, this would not be particularly efficient. If an action is dependent on only one of several conditions to be true, you can use **or** to state that. Have a look at the third version of this decision making progress: .. literalinclude:: decision3.py If you execute this script, it will display *a bad thing*. Now change the value of the *i_am* variable to *'cyberman'* and see what happens. It should give you the exact same result, because assigning *'a bad thing'* to *action* is conditional on *i_am* being either *dalek* or *cyberman*. For Loops --------- *for loops*, in other languages also referred to as *do loops*, are essential in programming as they allow you to automate things. Let's make the following array: .. code-block:: python year = [1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990] Assume we made a systematic mistake in our list of 10 years and we would like to correct that by adding 1 year to each of our entries. An inefficient way to do this would be: .. code-block:: python year[0] = year[0]+1 year[1] = year[1]+1 year[2] = year[2]+1 year[3] = year[3]+1 year[4] = year[4]+1 year[5] = year[5]+1 year[6] = year[6]+1 year[7] = year[7]+1 year[8] = year[8]+1 year[9] = year[9]+1 This is where *for loops* come in handy. We can tell our programme to do the same calculation for every entry in the array and *print* the updated value as such: .. code-block:: python for i in range(10): year[i] = year[i]+1 print(year[i]) We tell the programme to execute the indented line of code for each instance of the index value *i*, which is updated 10 times by increments of 1, starting with an initial value of 0. If we only want to update everything after 1984, we can specify the index value range as such: .. code-block:: python for i in range(4,10): year[i] += 1 # the += operator allows you to shorten the correction of a value print(year[i]) This loop now explicitly starts with an index value of 4, which corresponds to the 5th value (*1985*) and continue updating the index value until the 10th entry. If you want to create a loop for all values, but do not know the length of your list, you can use the *len()* function to get it. This can be incorporated into your loop as such: .. code-block:: python for i in range(len(year)): year[i] += 1 Your Task --------- You just finished grading 9 exams on atmospheric physics (ugh!). You have 10 students (and number them in a rather dystopian manner), but only 9 of them showed up. You saved the grades in an array and entered a ridiculous number (-9999.0) for the missing student, so you know they didn't take the exam. .. code-block:: python g = [1.7, 2.0, -9999.0, 2.3, 1.3, 1.0, 1.3, 3.3, 1.7, 1.0] You now want to calculate the arithmetic mean of the students' greades according to: .. math:: \begin{equation} mean = \frac { {\sum_{i=1}^{n} }g_i } n \end{equation} Since one of the student was missing, n=9. Write code for at least **two different ways** to calculate the mean grade based on the *g* array. Somewhere in your code, you should make use of the following: * list functions * comparison operators * if statements * for loops Try to keep your code as flexible as possible. Ideally, it should still work next year, when a different student may be missing (corresponding to a different position in your list of grades) or when more students fail to take the exam. Also make sure you comment each line of code to let other programmers know what you are doing. When you are happy with your code, put your two (or more) different methods in one file, save it as *[your surname]_e002.[ext]* and submit it via ILIAS. .. warning:: Late submissions won't be accepted!