BONUS

In these questions, we'll look at some more complex looping examples.

Part A

Sometimes, one loop over a list of numbers isn't enough. Sometimes you have to a level deeper (inception BWAAAAA).

In the function below, write code to create a list of tuples (yes; each element of the list is itself a tuple) that represents all possible pairs of elements of the original input list.

(for those of you interested in set theory: this is known as a power set)

For example, let's say the input list is [1, 2, 3]. A pair of numbers from this list would be (1, 2). Another would be (1, 3). However, (3, 1) would NOT be a valid pairing, because it is not unique: it's just a transpose of the (1, 3) we've already seen.

The power set, then, would be all possible unique pairs of numbers from this one list.

That's what the code you write below needs to do: compute the power set of the input list. To complete the example, the power set of [1, 2, 3] would be [(1, 2), (1, 3), (2, 3)]. Note that the output list itself has three elements, and each individual element is a tuple of two elements.

Back to loops: the easiest way to generate a power set is to use nested for loops. This means you'll have an outer loop, and inside that you'll have another loop. The outer loop is essentially responsible for one of the elements in each tuple, while the inner loop fixes the other element of each tuple.

A couple of restrictions:

  • Your power set cannot contain self-pairings. Note in the example that 1 is not paired with 1, 2 is not paired with 2, etc. However, if 1 appeared multiple times, then those 1s should be paired. This is simply to say the element at a certain position should not be paired with itself.
  • You cannot use external Python libraries (because itertools.combinations() does this exact thing--definitely take a look at it once you've finished! but you can't use it).

Put your answer in the list_of_pairs variable.


In [ ]:
def my_pairs(x):
    list_of_pairs = []
    
    ### BEGIN SOLUTION
    
    ### END SOLUTION
    
    return list_of_pairs

In [ ]:
try:
    itertools.combinations
except:
    assert True
else:
    assert False

In [ ]:
from itertools import combinations as c

i1 = [1, 2, 3]
a1 = set(list(c(i1, 2)))
assert a1 == set(my_pairs(i1))

In [ ]:
i2 = [8934, 123, 23, 1, 6, 8, 553, 8, 98.345, 354, 876796.5, 34]
a2 = set(list(c(i2, 2)))
assert a2 == set(my_pairs(i2))

Part B

For better or worse, linear algebra is a huge component of data science. We'll cover it in a bit more detail after the midterm, but some of the basic data structures involved in linear algebra make for really good questions on writing loops. So we'll take a first look at a matrix data structure!

You can think of a matrix like a list, where each element of the list is itself another list. Basically it's a rectangular, two-dimensional list--an Excel spreadsheet is a matrix in practice. There are rows and columns, and that's pretty much all you need for it to be a matrix.

Representing a matrix in code is another matter. There are plenty of ways of doing it, and here we'll look at one of the more rudimentary methods (I would never, ever, recommend doing it this way in practice, but it makes for a great exercise! bua ha ha...or something).

Here, you'll work with a matrix that is literally a list of lists. As in, you have a list, and each element is itself a list. Your goal is to convert each element of the outer list into a tuple:

  • The 2nd element of the tuple is the list that's already at that position
  • The 1st element of the tuple, however, is the index or position in the current outer list

Basically, you're converting each row of the matrix into a tuple, where the first element is the actual row number, and the second element is the row itself.

For example, here is a 2-rows-by-3-columns matrix:

x = [ [1, 2, 3], [4, 5, 6] ]

The first row is [1, 2, 3], the second row is [4, 5, 6].

Your job would be to modify this matrix so each element of the outer list becomes a tuple:

y = [ (0, [1, 2, 3]), (1, [4, 5, 6]) ]

Now, the first element of y (y[0]) is a tuple: (0, [1, 2, 3]). This tuple has two elements: y[0][0] is the row number (in this case, 0), and y[0][1] is the actual row, which is [1, 2, 3]. The second element of y (y[1]) is also a tuple of two elements: (1, [4, 5, 6]). The first element of this tuple, y[1][0], is the row number (in this case, 1), and the second element (y[1][1]) is the row itself: [4, 5, 6].

Put your answer in the tuple_matrix variable.


In [ ]:
def add_row_ids(matrix):
    tuple_matrix = []
    
    ### BEGIN SOLUTION
    
    ### END SOLUTION
    
    return tuple_matrix

In [ ]:
i1 = [ [1, 2, 3], [4, 5, 6] ]
a1 = set((1, (4, 5, 6)))
i, t = add_row_ids(i1)[1]
assert a1 == set((i, tuple(t)))

In [ ]:
i2 = [ [1, 2], [2, 3], [4, 5], [6, 7], [8, 9] ]
a2 = set((4, (8, 9)))
i, t = add_row_ids(i2)[4]
assert a2 == set((i, tuple(t)))