CoCalc Public FilesLabs / Lab2 / Lab2.ipynb
Views : 86
Compute Environment: Ubuntu 20.04 (Default)

# Lab 2: Lists, Loops and Animation

One of the great things about computers is their ability to repeat tasks quickly, accurately and without getting bored. We will take advantage of this capability many times during this course. This lab will introduce loops, which perform such repetition, and use them to create animations. First, however, we’ll take a look at some of the different kinds of objects SageMath works with.

### Types of Things

In the previous lab, you worked with numbers and functions. You also made plots, and in the process of making them, you encountered words enclosed in quotation marks. For example, when you want to make a plot red, you have to put the word "red" in quotation marks. Such words are called strings, which is short for "character strings". Strings allow computers to handle words, phrases, and typographic symbols. Strings can include numbers, not just letters. But when numbers are treated as strings, they act quite differently from regular numbers.

Exercise 1. Enter the following code into SageMath and compare the outputs.
>>a=5
>>show(a)
>>b="5"
>>show(b)


Exercise 2. What happens when you add 1 to a? To b?

In [1]:
a=5
show(a)

$5$
In [6]:
b="5"
show(b)

$5$
In [3]:
a+1

6
In [77]:
b+1

--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-77-d18ca6847e56> in <module>() ----> 1 b+Integer(1) /ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/rings/integer.pyx in sage.rings.integer.Integer.__add__ (build/cythonized/sage/rings/integer.c:12304)() 1801 return y 1802 -> 1803 return coercion_model.bin_op(left, right, operator.add) 1804 1805 cpdef _add_(self, right): /ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/structure/coerce.pyx in sage.structure.coerce.CoercionModel.bin_op (build/cythonized/sage/structure/coerce.c:11178)() 1253 # We should really include the underlying error. 1254 # This causes so much headache. -> 1255 raise bin_op_exception(op, x, y) 1256 1257 cpdef canonical_coercion(self, x, y): TypeError: unsupported operand parent(s) for +: '<class 'str'>' and 'Integer Ring' 
In [9]:
#When 1 is added the value of a it changes to 6 due to adding the integer. When added to 1 is added to be there is an error due to "b" being a string.


Remark. There is a little bit of vocabulary you should be aware of, as you may see it online and in the SageMath documentation. While the fundamental idea to understand is that different types of objects behave differently even though they may look alike, programmers often use the word "type" for simple objects like strings and numbers and "class" for more complex ones like graphs. Just think "type" when you see “class” and you’ll be fine. You can find out what the type of an object is using the type command.

>>type(a)
<type ’sage.rings.integer.Integer’>


This output means that a is a SageMath integer.

>>type(b)
<type 'str'>


This output means that b is a character string.

Exercise 3. Find the types of the number 0.2 and the graph (plot) of $f(x) = x^2$. (Hint: Look back at the end of Lab 1 fo how to make a plot).
In [10]:
type(plot(x^2, x,-10,10))

<class 'sage.plot.graphics.Graphics'>
In [12]:
type(0.2)

<class 'sage.rings.real_mpfr.RealLiteral'>

In the above exercises, the variables a and b look the same when displayed using show but act very differently when used in an arithmetical calculation. This happens because a is the integer 5 while b is the character string "5". We say these variables have different types, which just means they’re different kinds of things. The type of a is "integer", while the type of b is "string". This explains what you saw in Exercise 2. Adding 1 to an integer is not a problem, but adding 1 to a character string makes no sense and results in an error.
Actually, addition is defined for strings in SageMath. Here’s how it works.

>>"5"+"1"
'51'


You can see that the + symbol (or "operator") still embodies the idea of "putting things together". However, "putting things together" means something different for integers than for character strings or for the plots in Lab 1 #33, so the exact meaning of + changes depending on the types of objects it’s acting on. (Programmers say this makes + an overloaded operator.)

Exercise 4. Give another example of a type of SageMath object for which addition is defined and explain what addition means for that type of data.
In [1]:
"1"+"7"

'17'

### Lists

Scientific data and the outputs of model simulations often come in the form of lists of numbers. SageMath gives us many tools for working with such lists and tables.

You make a list by enclosing its elements, separated by commas, in square brackets:

["Bacteria", "Protists", "Plants", "Fungi", "Animals"]
[2,3,5,7,11,13]


Each element of a list can be accessed by its position in the list, typically called its index. In Python, indexing starts with 0, so the first element of a list with $k$ elements has index 0 and the last element has index $k − 1$.

Example 1. Enter the list of biological kingdoms into SageMath and call it kingdoms.

>>kingdoms = ["Bacteria", "Protists", "Plants", "Fungi", "Animals"]


To access the first element of this list, enter:

>>kingdoms[0]
'Bacteria'

Exercise 5. A bacteria population is doubling every hour. Its sizes at different times are 100, 200, 400 and 800. Make a list of these values.

[100, 200, 400, 800]

Exercise 6. Assign the list of bacteria population sizes to the variable bacteria. (You can just copy and paste the list.)

Exercise 7. Find the type of the variable bacteria from the previous exercise.

Exercise 8. What is the value of bacteria[1]? What about bacteria[0]? First, answer without entering the command into SageMath. Then, use SageMath to check your answers.

In [13]:
kingdoms = ["Bacteria", "Protists", "Plants", "Fungi", "Animals"]

In [14]:
kingdoms[0]

'Bacteria'
In [15]:
bacteria=[100,200,400,800]

In [16]:
type(bacteria)

<class 'list'>
In [2]:
#bateria[1] should equal 200 and bacteria[0] should equal 100

In [17]:
bacteria[1]

200
In [18]:
bacteria[0]

100


In [ ]:



You can add an element to the end of a list using listname.append(element). (The generic names listname or list are just placeholders for the real name of your list.) For instance if you wanted to add the string “Archaea” to the list named kingdoms, the code would look something like this:

>> kingdoms.append("Archaea")


Note that the above code does not output anything to the screen. This is because the append() function only tells the computer to save its input to specified list. To see the result, we would have to type kingdoms and evaluate the cell.

Exercise 9. Append the number 1600 to bacteria and call it to display its value. Don’t paste or retype any output.
[100, 200, 400, 800, 1600]

Exercise 10. What is the next value of the population? Append it to the list.

Exercise 11. What would happen in the example above if we did kingdoms.append("Archaea") twice before viewing kingdoms? Try this out and explain why you got the result that you did.

In [20]:
bacteria.append(1600)

In [21]:
bacteria

[100, 200, 400, 800, 1600]
In [22]:
#the next value would be 3200

In [23]:
bacteria.append(3200)

In [24]:
bacteria

[100, 200, 400, 800, 1600, 3200]
In [25]:
kingdoms.append("Archaea")

In [26]:
kingdoms

['Bacteria', 'Protists', 'Plants', 'Fungi', 'Animals', 'Archaea']
In [27]:
#archaea was appended/added on


### Plotting lists

To plot the entries in a list, use the list_plot function. If you give this function a single list of numbers as an input, it will plot each number against its position in the list. For example, the command list_plot(bacteria) plots the list of population sizes you just created in Example 6, producing the graph below.

Notice that the $x$-coordinate of the first point is 0, not 1. This happens because SageMath starts counting at zero, so the index of the first element of a list is 0.

Exercise 12. Plot the list [3,5,7,9,11].
In [28]:
x=[3,5,7,9,11]

In [30]:
list_plot(x, color="blue", size=30)

In [ ]:


In [ ]:



Often, we will need to plot lists of points. For example, suppose we have the points (1,2), (2,1), (3,4), and (4,3), with the first number in the ordered pair an $x$-coordinate and the second a $y$-coordinate. How do we plot these points in SageMath?

First, we enter the list of points:

>>g = [(1,2), (2,1), (3,4), (4,3)]


Then, we use the list_plot function to produce the figure below

>>list_plot(g)


This plot is technically correct, but the points are a little hard to see. To make them more noticeable, we might color them red and change their size. The command list_plot(g, color="red", size=30) produces the second figure below.

In [ ]:


Exercise 13. Define your own list of pairs of values and plot it. Make sure your plot is legible.
In [31]:
x=[(2,3), (4,1), (3,5), (5,6)]

In [32]:
list_plot(x, color="red", size=30)


Whether you’re plotting a list of numbers using list_plot() or a mathematical function using plot(), you can label the axes of the plot using the axes_labels plotting option.

Example 2. Adding axes labels to our plot of bacteria population sizes.
list_plot(bacteria, axes_labels=["time", "population"])


Notice that axes_labels is a variable that we set equal to a list of labels. In SageMath, square brackets always mean that a list is involved. Another feature of list_plot is the ability to connect the points of the list together. This is accomplished by using the plotjoined option:

Example 3. Making the bacteria graph plot joined and adding axes labels:
list_plot(bacteria, axes_labels=["time","population"],plotjoined = True)


Note that the value of plotjoined is either True or False. This type of data is called a boolean. You will learn more about this data type in the future. Now that we know how to label axes and join points, we should do so whenever it is reasonable to do so. We should join points whenever we want to see a curve. Labeling axes is a good way to keep track of which values correspond to which axes, especially when we plot lists against each other.

However, list_plot requires a list of points, not two lists of numbers, as input. To avoid typing long lists of points and all the required parentheses by hand, we turn to the function zip. This function takes two lists and turns them into a list of ordered pairs. (It can also take more than two lists and turn them into a list of $n$-tuples.) Actually, for reasons that are beyond the scope of this class, we have to next apply the list function to the output of zip. For example:

>>list(zip([1,2,3], [4,5,6]))
[(1, 4), (2, 5), (3, 6)]
>>list(zip([1,2,3], [4,5,6], [7,8,9]))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]


This is the kind of input that list_plot needs. It’s common to nest the list(zip()) command inside the list_plot command, as below.

>>list_plot(list(zip([1,2,3], [4,5,6])))


This means the same thing as:

>>pairs = list(zip([1,2,3], [4,5,6]))
>>list_plot(pairs)

Exercise 14. You are studying populations of hippos and crocodiles in a river in Africa. Over five years, the hippo population at your study site has been 62, 81, 75, 90 and 67. In the same years, the crocodile population has been 20, 34, 18, 25 and 31. Plot the system’s states in hippo-crocodile space, labeling your axes appropriately and making the points red.
In [78]:
hippos=[62,81,75,90,67]

In [79]:
crocodiles=[20,34,18,25,31]

In [80]:
hc=zip(hippos,crocodiles)

In [83]:
list_plot(hc, size=30, axes_labels=["hippos", "crocodiles"])

--------------------------------------------------------------------------- TypeError Traceback (most recent call last) /ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/plot/plot.py in list_plot(data, plotjoined, **kwargs) 3007 from sage.rings.all import RDF -> 3008 RDF(data[0]) 3009 data = list(enumerate(data)) TypeError: 'zip' object is not subscriptable  During handling of the above exception, another exception occurred: TypeError Traceback (most recent call last) <ipython-input-83-5b2645f83134> in <module>() ----> 1 list_plot(hc, size=Integer(30), axes_labels=["hippos", "crocodiles"]) /ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/misc/decorators.py in wrapper(*args, **kwds) 491 options['__original_opts'] = kwds 492 options.update(kwds) --> 493 return func(*args, **options) 494 495 #Add the options specified by @options to the signature of the wrapped /ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/plot/plot.py in list_plot(data, plotjoined, **kwargs) 3016 # So, the only other check we need to do is whether data[0] is an 3017 # element of the Symbolic Ring. -> 3018 if data[0] in sage.symbolic.ring.SR: 3019 data = list(enumerate(data)) 3020 TypeError: 'zip' object is not subscriptable 

The zip function is very helpful in plotting time series graphs. All you need to do is make a list of time values and zip it with the values of your state variable. It is usually used together with the list function which converts the output of zip into a list.

Exercise 15. The list in Exercise 10 gives the size of a population of bacteria at one-hour intervals. Since one hour is 1/24 of a day, create a list of time points for these observations with time in days. Then, plot a time series graph of the population.
In [38]:
bacteria=[100,200,400,800,1600,3200]

In [39]:
time=[1/24, 2/24, 3/24, 4/24, 5/24, 6/24]

In [40]:
list_plot(bacteria, axes_labels=["time", "population"])


Having developed some basic tools, we will now use them to work with real data. Your worksheet contains lists called wt5_time, wt5_heartrate and wt5_temp. These lists contain heart rate and body temperature data for a wild type (control) rat, measured over 72 hours as part of a real study of circadian rhythms.

Exercise 16. Plot time series of the rat’s heart rate and body temperature, using different colors. Make sure the plot uses the given time values, not just 0, 1, 2.... and that your axes are labeled.

Exercise 17. Compare the plots and describe any relationships you observe.

Exercise 18. Plot the data as a trajectory in temperature-heart rate space. Make sure to label your axes.

In [43]:
list_plot(wt5_heartrate, axes_labels=["time", "rate"])

--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-43-51775618f6d7> in <module>() ----> 1 list_plot(wt5_heartrate, axes_labels=["time", "rate"]) NameError: name 'wt5_heartrate' is not defined 
In [44]:
list_plot(wt5_temp, axes_labels=["time", "rate"])

--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-44-734ab826c155> in <module>() ----> 1 list_plot(wt5_temp, axes_labels=["time", "rate"]) NameError: name 'wt5_temp' is not defined 
In [ ]:


In [ ]:



### For loops

Suppose you wanted to print out a series of sentences listing your favorite foods. You might use the print command and write:

print("Pizza is one of my favorite foods.")
print("Chocolate is one of my favorite foods.")
print("Green curry is one of my favorite foods.")


This works, but it’s rather tedious, especially if you like many kinds of food. A shortcut would be useful.

Looking at the example, you can see that the print command and the string " is one of my favorite foods." are the same in every line. The only thing that changes is the name of the food. It would be convenient if we could just make a list of the foods and insert them into the code one at a time.

We can do this using something called a for loop. One way to handle the foods example with a for loop is the following:

favorites = ["Pizza", "Chocolate", "Green curry"]
for food in favorites:
print(food + " is one of my favorite foods.")


In this example, food is a variable that takes on the value "Pizza" the first time the computer executes the statement print food + " is one of my favorite foods.", "Chocolate" the second time and "Green curry" the third time.

Exercise 19. Print out sentences listing five of your favorite books, songs or movies using a for loop.
In [48]:
favorites=["Night", "Say Sorry", "Say Hellow", "Feathersome", "Hello"]

In [49]:
for songs in favorites:
print(songs + "is one of my favorite songs")

Nightis one of my favorite songs Say Sorryis one of my favorite songs Say Hellowis one of my favorite songs Feathersomeis one of my favorite songs Hellois one of my favorite songs

More generally, a for loop in SageMath (or Python) has the form:

for var in list:
loop body


The loop body must be indented. After the loop body, go back to your previous level of indentation.

Example 4. A string is similar in most ways to a list. This loop will print each character in the word “dynamics” on a separate line.

>>for char in "dynamics":
>> print(char)
d
y
n
a
m
i
c
s


As you can see, the variable given immediately after the word for takes on the value of each list element in turn. First char was "d", then it was "y", then "n", and so on. This is the key to how SageMath for loops work. The body of the loop is executed for each element in the list. The body stays the same, while the list element changes. When writing loops, think about what should stay the same and what should change.

Example 5. The following loop computes the base-10 logarithm of 10, 100, and 1000 in SageMath:

>>for val in [10, 100, 1000]:
>> print(log(val, 10))
1
2
3

Exercise 20.Use a for loop to print your name vertically.

Exercise 21. Use a for loop to square the numbers 15, 27, 39 and 84.

225
729
1521
7056

In [ ]:


In [53]:
for char in "Andrew":
print(char)

A n d r e w
In [55]:
for char in [15,27,39,84]:
print(char^2)

225 729 1521 7056

## Things to Do with Lists and Loops

### Using loops to make lists

In the previous exercises, you used loops to output values. Often, it is useful to store these values in a list.

To start, you’ll need to create a list with no elements. (Think of this as tearing out a piece of paper and titling it “Groceries” when making a shopping list.) To make such an empty list, enter listname = []. Then, use listname.append() to add computed values to your list.

Example 6. This code makes a list of the first ten multiples of 2.

>>mult2 = []         #Set up empty list
>>for n in [1,2,3,4,5,6,7,8,9,10]:
>> mult2.append(2*n) #Compute the n’th multiple of 2 and append to list
>>mult2              #Display the list
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

Exercise 22. Make a list containing the first five multiples of 3.
[3, 6, 9, 12, 15]

Exercise 23. Write a loop that makes a list of the squares of the numbers 0.1, 0.2, $\dots$, 0.7. Then, plot your list.

[0.0100000000000000, 0.0400000000000000, 0.0900000000000000, 0.160000000000000, 0.250000000000000, 0.360000000000000, 0.490000000000000]

Exercise 24. Create a function and apply it to the numbers 0 through 5, inclusive. Plot the list of resulting values.

In [56]:
mult3=[]
for n in [1,2,3,4,5]:
mult3.append(3*n)

In [57]:
mult3

[3, 6, 9, 12, 15]
In [58]:
sqr=[]
for n in [0.1,0.2,0.3,0.4,0.5,0.6,0.7]:
sqr.append(n^2)

In [59]:
sqr

[0.0100000000000000, 0.0400000000000000, 0.0900000000000000, 0.160000000000000, 0.250000000000000, 0.360000000000000, 0.490000000000000]
In [60]:
list_plot(sqr)


We can use loops to process data.

Exercise 25. The time in wt5_time is measured in hours. Create another list in which it is given in minutes.

Exercise 26. Convert the temperatures in wt5_temp from Celsius to Fahrenheit. (The formula is $F = (9/5) C + 32$.)

Exercise 27. Plot a time series of your transformed data.

Exercise 28. Plot a trajectory of the transformed temperature data and the original heart rate data.

In [63]:
min=[]
for n in wt5_time:
min.append(n*50

--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-63-062800fa2616> in <module>() 1 min=[] ----> 2 for n in wt5_time: 3 min.append(n*Integer(50)) NameError: name 'wt5_time' is not defined 
In [ ]:


In [ ]:


In [ ]:



## Animations

When investigating functions and models, it can be useful to animate their response to changes in parameters. Sage’s animate function allows us to easily produce such animations.

Animations are created by showing a series of still images one after the other, fast enough to create the illusion of motion. The animate function takes a list of plots as input and animates it.

Example 7. The following code shows how a change in the slope of a line affects the line’s appearance.

plots = []       #Set up empty list to hold plots
slopes = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] #Make a list of slopes
for m in slopes: #For each m in slope, create plot, add to list
p=plot(m*x, (x,-10,10))
plots.append(p)
a=animate(plots) #Create the animation
show(a)          #Necessary to display the animation


Try this code now. The show command is necessary to view the animation; it can also be used with other graphics.

Oops! The code produces an animation all right, but the animation is useless because it’s the axes, not the line, that move. To stop this from happening, we can specify maximum and minimum values for $y$, fixing the $y$-axis in place.

Example 8. Fixing the y-axis in an animation

plots = []        #Set up empty list to hold plots
slopes = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] #Make a list of slopes
for m in slopes:  #For each m in slope, create plot, add to list
p=plot(m*x, (x,-10,10), ymin=-50, ymax=50)
plots.append(p)
a=animate(plots)  #Create the animation
show(a)           #Necessary to display the animation


This code produces a useful animated plot.

Exercise 29. Change the animation in Example 8 to make the line green rather than blue.

Exercise 30. Change the previous animation to make the slope range from -3 to 3 in steps of 0.5.

Exercise 31. Rewrite the animation in Exercise 29 so that the slope of the line plotted is always 1 but the $y$-intercept ranges between -5 and 5.

In [87]:
plots = []        #Set up empty list to hold plots
slopes = [-3, -2.5, -2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2, 2.5, 3] #Make a list of slopes
for m in slopes:  #For each m in slope, create plot, add to list
p=plot(m+x, (x,-5,5), ymin=-50, ymax=50)
plots.append(p)
a=animate(plots, color="green")  #Create the animation
show(a)           #Necessary to display the animation

--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-87-77c47acae03e> in <module>() 2 slopes = [-Integer(3), -RealNumber('2.5'), -Integer(2), -RealNumber('1.5'), -Integer(1), -RealNumber('0.5'), Integer(0), RealNumber('0.5'), Integer(1), RealNumber('1.5'), Integer(2), RealNumber('2.5'), Integer(3)] #Make a list of slopes 3 for m in slopes: #For each m in slope, create plot, add to list ----> 4 p=plot(m+x, (x,-Integer(5),Integer(5)), ymin=-Integer(50), ymax=Integer(50)) 5 plots.append(p) 6 a=animate(plots, color="green") #Create the animation /ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/rings/integer.pyx in sage.rings.integer.Integer.__add__ (build/cythonized/sage/rings/integer.c:12304)() 1801 return y 1802 -> 1803 return coercion_model.bin_op(left, right, operator.add) 1804 1805 cpdef _add_(self, right): /ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/structure/coerce.pyx in sage.structure.coerce.CoercionModel.bin_op (build/cythonized/sage/structure/coerce.c:11178)() 1253 # We should really include the underlying error. 1254 # This causes so much headache. -> 1255 raise bin_op_exception(op, x, y) 1256 1257 cpdef canonical_coercion(self, x, y): TypeError: unsupported operand parent(s) for +: 'Integer Ring' and '<class 'list'>' 
In [ ]:


In [ ]: