For loops and vectors

Prerequisites

  • Watch the video about vectors by 3Blue1Brown (optionally read video text).

  • View the visualizations about radians, sin/cos/tan, dot product, and cross product

  • Review Collections: Lists section. This is important because we can use lists to represent vectors in Python, for example the two-dimensional vector can be defined as v = [3,2]

  • Complete the section on for loops in 07-Loops notebook. The for loop is important because it allows you to do operations for each element in the list.

  • Review the Fundamentals of math notebook from Sympy (Appendix C in print book)

  • Complete the SymPy > Vectors notebook to learn how to define and use vectors in SymPy. Note we use Matrix object giving it a list as input, e.g. v = Matrix([3,2]) This is a little weird, but OK because a vector is a special type of matrix.

  • Solve all exercises in Chapter 7, as well as E6.4, E6.5

  • Solve all problems from P9.57 until P9.65 (inclusive)

Lists and for loops

Let’s review what we’ve learned about Python lists and for loops because they will be useful for vector calculations in the next step.

scores = [61.0, 85.0, 92.0, 72.0]  # define a list of floats
scores
[61.0, 85.0, 92.0, 72.0]
# lists have a "length"
len(scores)
4
# elements of a list are acccessed using [ ] and 0-based index
scores[0]  # first score
61.0
# lists can be sorted
sorted(scores)  # returns a new list of sorted scores
[61.0, 72.0, 85.0, 92.0]
scores
[61.0, 85.0, 92.0, 72.0]
scores.sort()  # in-place sort the list
scores
[61.0, 72.0, 85.0, 92.0]
scores.reverse()
scores
[92.0, 85.0, 72.0, 61.0]
scores.append(22)
scores
[92.0, 85.0, 72.0, 61.0, 22]
scores.insert(2, 25)
scores
[92.0, 85.0, 25, 72.0, 61.0, 22]
scores.pop()
22

Just like .sort() method, lists have all kinds of useful methods .insert, .remove, .pop, .reverse, …

You can see all those methods by starting to type scores. then pause for a second to see the auto-complete suggestions:

# scores.
scores = [61.0, 85.0, 92.0, 72.0]
scores.sort()

For loops

The “for loop” is a Python code construct of the form:

for el in container:
    <operations on el>

that allows to repeat a block of operations for each element of the list.

# Example 1: print all the scores
for score in scores:
    print(score)
61.0
72.0
85.0
92.0
# Example 2: compute the average score  ==  sum(scores)/len(scores)
total = 0
for score in scores:
    total = total + score

total/len(scores)
77.5

The name of the variable used for the for loop is totally up to you, but in general you should choose logical names for elements of the list.

Here is a for loop that uses the single-letter variable:

for s in scores:
    print(s)



for (i=0; i<length(scores); i=i+1){
    s = scores[i];
    print(s);
}
  File "/tmp/ipykernel_1701/2181820327.py", line 6
    for (i=0; i<length(scores); i=i+1){
          ^
SyntaxError: invalid syntax
scores
enumerate(scores)
# Bonus concept: use `enumerate` to get pairs (index, value) from a list
# enumerate(scores) == [(0, 61.0), (1, 72.0), (2, 85.0), (3, 92.0)]

# example
for index, score in enumerate(scores):
    # this for loop has two variables index and score
    print("Processing score", score, "which is at index", index, "in the list")
# New concept: use `zip(list1,list2)` to get pairs (value1, value2) from two lists 
# list(zip([1,2,3], ['a','b','c'])) == [(1, 'a'), (2, 'b'), (3, 'c')]

# example
list1 = [1,2,3]
list2 = [3,3,5]
list3 = [11, 22, 33]

for value1, value2, value3 in zip(list1, list2, list3):
    print("Processing  value1 =", value1, " and  value2 =", value2, 'val3=', value3)
list1 = [1,2,3]
list2 = [3,3,5]


list(zip(list1, list2))

List functions

Your turn to play with lists now! Complete the code required to implement the functions compute_mean and compute_std below.

Question 1: Mean

The formula for the mean of a list of numbers \([x_1, x_2, \ldots, x_n]\) is: $\( \text{mean} = \overline{x} = \frac{1}{n}\sum_{i=1}^n x_i = \tfrac{1}{n} \left[ x_1 + x_2 + \cdots + x_n \right]. \)$

Write the function compute_mean(numbers): a function that computes the mean of a list of numbers

def compute_mean(numbers):
    """
    Computes the arithmetic mean of the `numbers` list using a for loop.
    """
    total = 0
    for number in numbers:
        total = total + number
    mean = total/len(numbers)  
    return mean


compute_mean([100,101])
# TEST CODE (run this code to test you solution)

def random_list(n=10, min=0.0, max=100.0):
    """Returns a list of length `n` of random floats between `min` and `max`."""
    import random
    values = []
    for i in range(n):
        r = random.random()
        value = min + r*(max-min)
        values.append(value)
    return values


def test_compute_mean(function):
    """
    Run a few lists to check if value returned by `function` matches expected.
    """
    import math, statistics
    assert function([1,1,1]) == 1
    assert function([61,72,85,92]) == 77.5
    list10 = random_list(n=10)
    assert math.isclose(function(list10), statistics.mean(list10))
    list100 = random_list(n=100)
    assert math.isclose(function(list100), statistics.mean(list100))
    print("All tests passed. Good job y'all!")



# RUN TESTS
test_compute_mean(compute_mean)
(1 + 1e-15)  ==  1 
import math
math.isclose(1 + 1e-10, 1)

Question 2: Sample standard deviation

The formula for the sample standard seviation of a list of numbers is: $\( \text{std} = s = \sqrt{ \tfrac{1}{n-1}\sum_{i=1}^n (x_i-\overline{x})^2 } = \sqrt{ \tfrac{1}{n-1}\left[ (x_1-\overline{x})^2 + (x_2-\overline{x})^2 + \cdots + (x_n-\overline{x})^2\right]}. \)$

Note the division is by \((n-1)\) and not \(n\). Strange, no? You’ll have to wait until stats to see why this is the case.

Write compute_std(numbers): computes the sample standard deviation

import math
import statistics

def compute_std(numbers):
    """
    Computes the sample standard deviation (square root of the sample variance)
    using a for loop.
    """
    mean = compute_mean(numbers) 
    total = 0
    for number in numbers:
        total = total + (number-mean)**2
    var = total/(len(numbers)-1)    
    return math.sqrt(var)

numbers = list(range(0,100))
compute_std(numbers)
# compare to known good function...
statistics.stdev(numbers)
# TEST CODE (run this code to test you solution)

def test_compute_std(function):
    """
    Run a few lists to check if value returned by `function` matches expected.
    """
    import math, statistics
    assert function([1,1,1]) == 0
    assert math.isclose(function([61,72,85,92]), 13.771952173409064)
    list10 = random_list(n=10)
    assert math.isclose(function(list10), statistics.stdev(list10))
    list100 = random_list(n=100)
    assert math.isclose(function(list100), statistics.stdev(list100))
    print("All tests passed. Good job y'all!")


# RUN TESTS
test_compute_std(compute_std)

Vectors

A vector \(\vec{v} \in \mathbb{R}^n\) is an \(n\)-tuple of real numbers:

\[ \vec{v} = (v_1,v_2,v_3, \ldots, v_n) \ \in \ \mathbb{R}^n. \]

To specify the vector \(\vec{v}\), we specify the values for its components \(v_1\), \(v_2\), \(v_3\), …, \(v_n\).

In Python, we can represent vectors as lists:

# the two-dimensional vector represented as a Python list
v = [3,2]
v
# try playing around with a vector using a for loop...

Other ways of representing vectos (optional reading)

# Option 2: vector as a n-by-1 SymPy Matrix (useful for doing math calculations)
from sympy import Matrix
v_sym = Matrix([3,2])
v_sym
# Option 3: numpy array (useful for doing fast numerical calculations)
import numpy as np
v_np = np.array([3,2])
v_np
# Option 4: pandas series (used in statistics and data science)
# Option 5: pytorch array (used in machine learning)

Vector functions

To get to know vectors better, you’ll now implement a few common vector operations. Make sure all the functions you create work for vector of any length.

Question 3:

Writh norm(v) a function that takes n-dimensional vector and computes its Euclidean norm

def norm(v):
    """
    Compute the L2-norm (Euclidean norm) of the vector `v` (a list).
    """
    total=0
    if len(v) in range(2,10):
       for number in v:
          total=total+(number)**2
       from math import sqrt    
       norm_of_v= math.sqrt(total)
       return float(norm_of_v)
          
# TEST CODE (run this code to test you solution)
import numpy

def test_norm(function):
    """
    Run a few lists to check if value returned by `function` matches expected.
    """
    from math import isclose
    assert isclose(function([1,1,1]), 1.7320508075688772)
    assert isclose(function([61,72,85,92]), 156.8247429457482)
    list3 = random_list(n=3)
    assert isclose(function(list3), numpy.linalg.norm(list3))
    print("All tests passed. Good job y'all!")
    
# END OF TEST CODE


# Let's run the tests...
test_norm(norm)

Question 4

Write dot(u,v) a function that takes two n-dimensional vectors and computes their dot product:

def dot(u,v):
    """
    Takes two n-dimensional vectors and computes their dot product.
    """ 
    total = 0
    for ui, vi in zip(u,v):
        total = total + (ui*vi)
    return float(total)
# TEST CODE (run this code to test you solution)

def test_dot(function):
    """
    Run a few lists to check if value returned by `function` matches expected.
    """
    from math import isclose
    import numpy as np
    assert function([1,2], [3,4]) == 1*3 + 2*4
    u3 = random_list(n=3)
    v3 = random_list(n=3)
    assert isclose(function(u3,v3), np.array(u3).dot(np.array(v3)))
    for n in range(4, 10):
        un = random_list(n=n)
        vn = random_list(n=n)
        assert isclose(function(un,vn), np.array(un).dot(np.array(vn)))
    print("All tests passed. Good job y'all!")


# Let's run the tests...
test_dot(dot)

Question 5:

Write cross(u,v): a function that takes two 3-dimensional vectors and computes their cross product

def cross(u, v):
    """
    A function that computes the cross product u x v of the inputs u and v, which
    are assumed to be 3-dimensional vectors.
    Returns a list.
    """
    if len(u) != 3 or len(v) != 3:
        # this will make the function reject vectors that are not 3-dimensional
        raise ValueError("Inputs vects must have dim. 3, else cross product not defined.")
    cross = [(u[1]*v[2]-u[2]*v[1]),
             (u[2]*v[0]-u[0]*v[2]),
             (u[0]*v[1]-u[1]*v[0])]
    return [float(cross[0]),
            float(cross[1]),
            float(cross[2])]
# TEST CODE (run this code to test you solution)

def test_cross(function):
    """
    Run a few lists to check if value returned by `function` matches expected.
    """
    import numpy as np
    u3 = random_list(n=3)
    v3 = random_list(n=3)
    w3_computed = np.array( function(u3,v3), dtype=float)
    np.isclose(w3_computed, np.cross(np.array(u3), np.array(v3)))
    print("All tests passed. Good job!")


# Let's run the tests...
test_cross(cross)

Question 6

Write the function angle_between(u,v): a function that takes two n-dimensional vectors and computes their angle between them (answer returned should be in radians)

def angle_between(u, v):
    """
    Compute the angle between vectors `u` and `v` (answer returned in radians).
    """                                       
    cos_theta = dot(u,v)/(norm(v)* norm(v))
    import math  
    return math.acos(cos_theta)                                 # or ME: import math
# TEST CODE (run this code to test you solution)

def test_angle_between(function):
    """
    Run a few lists to check if value returned by `function` matches expected.
    """
    from math import isclose
    isclose(function([1,0], [0,1]), 1.5707963267948966)
    isclose(function([1,1], [1,0]), 0.7853981633974484)
    isclose(function([1,0],[-1,0]), 3.141592653589793)
    print("All tests passed. Good job!")


# Let's run the tests...
test_angle_between(angle_between)

Things to discuss

## 1. imports
from math import acos
acos(1)
# vs
import math
math.acos(1)



## 2. list membership
v = [3,2]
len(v) == range(2,10)



## 3. joint-iteration over two lists using zip


## 4. float(cross) error


## 5. syntax for defining vs. calling functions