Science One CS – More Loops and Plotting¶
The following material is covered in NumPy's Absolute Basics (https://numpy.org/doc/stable/user/absolute_beginners.html), and MatPlotLib's Quick guide (https://matplotlib.org/stable/users/explain/quick_start.html) in case you want to read more about it. We will also work on exercises based on what you did in your physics lectures last week!
Last lesson we introduced the idea of loops. This lets us perform the same process repeatedly. This week we will see how to use loops to create arrays and plot the result.
Learning Objectives:¶
Know:
- NumPy's
array,zeros,append,linespace, indexing arrays witha[i] - MatPlotLib's
plot,show,title,xlabel,ylabel, andgrid
Be able to:
- Add items to NumPy arrays in a loop
- Plot arrays with a title and axis labels
NumPy Arrays¶
We often work with multiple measurements at once in science, so we use Numerical Python (NumPy) to help us collect and handle these data. Today we will explore a data structure called Numpy arrays that allows us to do this. In your physics labs, you also refer to these data structures as vectors.
Type (or copy) the following code to make an array with the first few odd integers:
import numpy as np # To use NumPy, we must import it into Python. We give it the "nickname" np for convenience.
a = np.array([1, 3, 5, 7])
print(a)
import numpy as np # To use NumPy, we must import it into Python
a = np.array([1, 3, 5, 7])
print(a)
[1 3 5 7]
Exercise 1:¶
Try answering exploring the following NumPy ideas. Dissuss your thoughts with your neighbour!
a) What is the data type of a? Remember, you can get this from the function type().
print(type(a))
<class 'numpy.ndarray'>
b) We can show individual elements in an array by indexing a[0] or slicing a[1:3]. The shortcut a[-1] gets the last element of a. Try printing these values and see if they match your expectation. What does a[4] do?
print(a[0])
print(a[1:3])
print(a[-1])
print(a[4]) # this tries to access the (4 + 1)th element which does not exist in `a`
1 [3 5] 7
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) Cell In[3], line 4 2 print(a[1:3]) 3 print(a[-1]) ----> 4 print(a[4]) IndexError: index 4 is out of bounds for axis 0 with size 4
c) What type is an element of a NumPy array? Is it what you expect?
print(type(a[3])) #Numpy has its own type for floating point values. However, it most cases it is the same as the builtin python version
<class 'numpy.int64'>
d) You can use len() to quickly find the length of an array. What is the length of this array?
print(len(a))
4
e) Can you reassign entries of an array? Try asigning the second element to $8$.
a[1] = 8
print(a)
[1 8 5 7]
f) What happens if we try to assign the second element to 2.6? Is this what you expect?
a[1] = 2.6
print(a) # No error! But 2.6 got converted to an integer, and it also rounded down towards zero!
[1 2 5 7]
g) If we want to quickly generate an array to work with, we can use b = np.zeros(n) to make an array filled with n zeros. Try making a list with $10$ zeros, and set second element b[1] to $1$.
b = np.zeros(10)
b[1] = 1
print(b) # Notice this generated and array of floats, not ints
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
h) We can also create an empty array with c = np.array([]), and add values to the end of the array one at a time with np.append(c, value).
Try making [1 3 5 7] this way. Does this make the same array as np.array([1 3 5 7])?
a = np.array([])
a = np.append(a, 1)
a = np.append(a, 3)
a = np.append(a, 5)
a = np.append(a, 7)
print(a) # Again, this created an array of floats!
[1. 3. 5. 7.]
Loops and arrays¶
Exercise 2¶
Use a loop to make an array y where each entry is $x^2$ for $x = 0, 1, 2, ..., 9$. The array should look something like np.array([0 1 4 ... 81])
Your code should look something like this:
y = ??? # initialize an array y
x = ??? # initialize x
while # some condition here :
# some more code in here
# print the array
# Solution 1
y = np.zeros(10) # initialize an array y
x = 0 # initialize x
while x < 10 :
y[x] = x**2
x = x + 1
print(y)
[ 0. 1. 4. 9. 16. 25. 36. 49. 64. 81.]
# Solution 2
y = np.array([]) # initialize an array y
x = 0 # initialize x
while x < 10 :
y = np.append(y, x**2)
x = x + 1
print(y)
[ 0. 1. 4. 9. 16. 25. 36. 49. 64. 81.]
(Bonus) Did you start with an array of zeros, or an empty array? Try it the other way!
For a simple array where we know the formula for each entry, we can use NumPy's np.linspace(start, end, length) to simplify the code.
x = np.linspace(0, 9, 10) # We want 10 evenly spaced points between 0 and 9
y = x**2
print(y)
[ 0. 1. 4. 9. 16. 25. 36. 49. 64. 81.]
Plotting¶
It would be handy to make a plot of $y$ vs $x$ to visualize the function $y = x^2$.
We can use a package in Python called matplotlib.pyplot to make graphs, but it requires an array of data for each the $y$-axis and the $x$-axis to do so. Lucky for us, we just created those arrays.
Exercise 3¶
Try it:
Run the cell below to import matplotlib.pyplot under the new convenient "nickname" plt
import matplotlib.pyplot as plt
The library matplotlib.pyplot contains very simple functions to plot and includes handy features to label your axes. The two basic functions are plt.plot and plt.show.
plt.plot(x, y)
plt.show()
Try it:
- Run the cell below and see what it does. Change the title, and axis labels to double-check your understanding of those functions. Put
Falsein the argument forplt.grid()to see what that does. Recall from last Python lesson:Falseis not the same asfalse; capitalization matters in Python. - Play with the plot parameters in the
plt.plot()function. For instance, instead of using the "r-" argument, you could use "b.". Try it and see what that does. You can use any combinations from this list ['-', '--', ':', '-.', '-x', '-*'] for line/marker styles and any colors from this list ['r', 'g', 'b', 'c', 'm', 'y', 'k']. You can customize further with different marker/line styles and even more colors, but I'll let you use your favourite search engine to discover how to do it.
plt.title('Science One Python Appreciation')
plt.plot(x, y)
plt.ylabel('How much you like python')
plt.xlabel('Number of lessons')
plt.grid(True)
plt.show()
Matplotlib has SO MANY features. We'll only see a few today. You can adjust axes limits and the ticks shown.
plt.title('Science One Python Appreciation')
plt.plot(x, y, 'ro') #The r specifies plotting in red and the o tells Python to plot dots
plt.xlim([-0.5, 9.5])
plt.ylim([-2, 85])
plt.yticks(ticks=[0, 4, 9, 16, 25, 36, 49, 64, 81])
plt.xticks(ticks=x)
plt.show()
Bonus Challanges¶
Exercise 4¶
Make an array of the first 10 powers of 2 using a while loop. The array should look something like np.array([2 4 8 ... 1024]).
Your code should look something like this:
# initialize an array
while # some condition here :
# some more code in here
# print the array
(This is almost idential to the Exercise that was in "L02 Booleans and Loops" Exercise 5)
# Solution 1
a = np.array([])
i = 1
while len(a) < 10:
i = i*2 # notice we update the index first since we want to start at 2, not 1
a = np.append(a, i)
print(a)
[ 2. 4. 8. 16. 32. 64. 128. 256. 512. 1024.]
# Solution 2
a = np.zeros(10)
i = 0
while i < 10:
a[i] = 2**(i+1) # need to shift by one since we want the first term to be 2=2^1, not 1=2^0
i = i + 1
print(a)
[ 2. 4. 8. 16. 32. 64. 128. 256. 512. 1024.]
Can you use np.linspace to make the same array?
i = np.linspace(1, 10, 10)
a = 2**i
print(a)
[ 2. 4. 8. 16. 32. 64. 128. 256. 512. 1024.]
Exercise 5¶
Use the array np.array([2 4 8 ...]) you made in the previous exercise to calculate the following sum we saw in last lesson "L02 Booleans and Loops" Exercise 3:
$$\frac{1}{2}+\frac{1}{4}+\frac{1}{8}+...+\frac{1}{1024}.$$
You should be able to do this in one line with np.sum(...). Remember $2^{10} = 1024$.
print(np.sum(1 / a))
0.9990234375
Make an array S with "partial sums", where
$$\begin{aligned}
S[0] &= \frac{1}{2} \\
S[1] &= \frac{1}{2} + \frac{1}{4} \\
S[2] &= \frac{1}{2} + \frac{1}{4} + \frac{1}{8} \\
&\vdots \\
S[9] &= \frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \dots + \frac{1}{1024}.
\end{aligned}
$$
S = np.zeros(10)
n = 0
terms = 1 / a # create an array with the terms [1/2 1/4 1/8 ...]
while n < 10:
S[n] = np.sum(terms[0:n+1]) # sum the first i terms
n = n + 1
print(S)
[0.5 0.75 0.875 0.9375 0.96875 0.984375 0.9921875 0.99609375 0.99804688 0.99902344]
Plot this array vs the number of terms in the partial sum. What do you notice?
n = np.linspace(1, 10, 10)
plt.title("Partial Sum of S")
plt.plot(n, S)
plt.xlabel("number of terms")
plt.ylabel("partial sum")
plt.show()
As we add more terms, the partial sum gets closer and closer to 1!
Exercise 6¶
The Fibonacci sequence starts with the numbers $0$ and $1$, and adds the two previous numbers in the sequence to get the next number. After $0, 1$, the next four numbers in the sequence would be $1, 2, 3, 5$ because $$\begin{aligned} 0 + 1 &= 1 \\ 1 + 1 &= 2 \\ 1 + 2 &= 3 \\ 2 + 3 &= 5. \end{aligned}$$
Print an array of the first 12 numbers in the Fibonacci sequence. You can learn more about the sequence here https://mathworld.wolfram.com/FibonacciNumber.html.
Hint: Exercise 1 g) may help you initialize the array.
(This is the same Exercise that was in "L02 Booleans and Loops" Exercise 6)
# One possible solution
# Initialize an array where the first two entries are 0 and 1
f = np.zeros(12)
f[1] = 1
i = 2 # start by computing the third element of the array (index "2" in Python)
while i < 12:
f[i] = f[i-1] + f[i-2] # Add the previous two numbers in the sequence
i = i + 1
print(f)
[ 0. 1. 1. 2. 3. 5. 8. 13. 21. 34. 55. 89.]
# A solution just starting with the first two numbers in the sequence
f = np.array([0, 1])
while len(f) < 12:
next_number = f[-2] + f[-1] # add the last two numbers
f = np.append(f, next_number)
print(f)
[ 0 1 1 2 3 5 8 13 21 34 55 89]
# A clever solution without indexing
# Start with an empty array
f = np.array([])
# Define the starting numbers 0 and 1
a = 0
b = 1
while len(f) < 12:
f = np.append(f, a) # add a to the array
a_and_b = a + b # calculate the next term
a = b # copy b into a
b = a_and_b # copy a+b into b
print(f)
[ 0. 1. 1. 2. 3. 5. 8. 13. 21. 34. 55. 89.]