Appendix J — Assignments

K Assignment 5

K.1 Question 1: Using slice notation

Given a collection, return a subset starting and stopping at the specified location, using the specified step size. If not start, stop or step are given, then use the default values.

Examples:

A5Q1([1, 2, 3, 4, 5], 1, -2)  # Returns [2, 3]
A5Q1(c='abcdefghi', start=-4, stop=2, step=-1)  # Returns 'fed'

K.2 Question 2: Using sets

Given 2 lists find the elements in common and the elements unique to each, and return both in that order.

Examples:

A5Q2([1, 2, 3], [2, 3, 4])  # Returns ({2, 3}, {1, 4})

K.3 Question 3: Fixing formatting

Fix all the whitespace mistakes in the following function according to pep8:

def A5Q3(a, b=1, c = 5):
    ans1=( -b + (b**2-4*a*c) ** 0.5 ) /(2*a) # One root
    ans2 = (-b-(b**2-4*a*c)**0.5)/(2*a)  # The other root
    return(ans1, ans2)

K.4 Question 4: Catching Errors

Write a function which accepts two arbitrary values and attempts to multiply them together. If multiplication is not possible, return None.

Examples:

A5Q4(2, 5)  # Returns 10
A5Q4('abcd', [1, 2, 3])  # Returns None

K.5 Question 5: Default Arguments

Write a function which accepts between 1 and 4 integers and multiplies them all together.

Examples:

A5Q5(1, 2, 3)  # Returns 6
A5Q5(3, 3)  # Returns 9

K.6 Question 6: Docstrings

Update the following function to include a docstring with both Parameters and Returns sections.

def A5Q6(a, b, c=0):
    hypt = (a**2 + b**2 + c**2)**0.5
    return hypt

L Assignment 6

L.1 Question 1: Convert between types

Given an ndarray containing floats, convert the array type to int and return it.

arr = np.array([2.2, 4.8, 0.9, 3.0])
A6Q1(arr)  # Returns array([2, 4, 0, 3])

L.2 Question 2: Perform mathematical operations

Given the radius values for N spheres, compute the volumes and surface areas, and return the results in an N-by-2 ndarray.

A6Q2([4.4, 3.2, 8.3])  # Returns array([[ 356.8179048 ,  243.28493509],
                       #                [ 137.25827743,  128.67963509],
                       #                [2395.09578482,  865.69727162]])

L.3 Question 3: Create your own split function

Create a new function which splits a 2D array both horizontally and vertically at the given locations. The returned result should be a list which can be used to access the respective quadrants of the input array. Use slice notation.

arr = [[0, 1, 2, 6, 7],
       [3, 4, 5, 8, 9]]
A6Q3(arr, [1, 3])  # Returns [[array([0, 1, 2]), array([6, 7])], [array([3, 4, 5]), array([8, 9])]]

L.4 Question 4: Counting values

Write a function which accepts an ndarray of values and a threshold value, and returns a tuple containing the number of items which are less than, equal to, and greater than the threshold, in that order. Do not use a for-loop.

arr = [[4, 6, 2], [5, 4, 7]]
A6Q4(arr, 5)  # Returns (3, 1, 2)

L.5 Question 5: Normalize an array

Write a function which takes an ndarray and returns a “normalized” ndarray such that all values are between 0 and 1. Assume the input array can have any real numbers (not +/- infinity). (As usual, differences in the decimal places do not matter).

arr = [[1, 4, -2], [-2, 0, 1]]
A6Q5(arr)  # Returns array([[0.500, 1.000, 0.000],
           #                [0.000, 0.333, 0.500]])

L.6 Question 6: Find maximum and minimum

Given a 2D ndarray, find the maximum and minimum of each column, and return the results as an ndarray with a shape of 2-by-Ncols.

arr = [[3, 5, 2, 4],
       [2, 6, 1, 5],
       [2, 4, 4, 8],
       [1, 1, 5, 3]]
A6Q6(arr)  # Returns array([[3, 6, 5, 8],
           #                [1, 1, 1, 3]])

L.7 Question 7: Join multiple arrays

Given 2 ndarrays, stack them either horizontally or vertically, whichever is feasible based on the shapes of the two arrays. If the shape of the inputs is such that it is unclear which direction to stack or not possible, then return None.

arr1 = [1, 2, 3]
arr2 = [[4, 5, 6],
        [7, 8, 9]]
A6Q7(arr1, arr2)  # Returns array([[1, 2, 3],
                  #                [4, 5, 6],
                  #                [7, 8, 9]])

arr1 = [1, 2, 3]
arr2 = [4, 5]
A6Q7(arr1, arr2)  # Returns array([1, 2, 3, 4, 5])

arr1 = [1, 2]
arr2 = [4, 5]
A6Q7(arr1, arr2)  # Returns None

L.8 Question 8

Given an ndarray of ints, find the sum of all the even numbers. Do not use a for-loop!

arr = [[4, 3, 7],
       [2, 6, 9],
       [0, -2, 1]]
A6Q8(arr)  # Returns 10

M Assignment 7

M.1 Question 1: Broadcasting

Given two arrays, check to see if they can be successfully broadcast together and return True or False accordingly

arr1 = np.ones((3, 3))
arr2 = np.ones((1, 3))
A7Q1(arr1, arr2)  # Returns True

arr1 = np.ones((3, 3))
arr2 = np.ones((1, 4))
A7Q1(arr1, arr2)  # Returns False

arr1 = 1.0
arr2 = np.ones((1, 4))
A7Q1(arr1, arr2)  # Returns True

arr1 = np.ones((3, 3))
arr2 = np.ones((4, ))
A7Q1(arr1, arr2)  # Returns False

M.2 Question 2: Write a wrapper function

Write a function that accepts two arrays and an operator symbol in the form of a string (i.e. '+', '-'), and perform the operation using the equivalent Numpy ufunc (i.e. add(), subtract). The function must support +, -, *, /, //, % and **.

arr1 = np.arange(4)
arr2 = np.arange(4, 8)
A7Q2(arr1, arr2, "+")  # Returns np.array([4, 6, 8, 10])
A7Q2(arr1, arr2, "*")  # Returns np.array([0, 5, 12, 21])

M.3 Question 3: Find bounding box

Write a function which computes the corners of a box that bounds a set of 2D points. The corners should be returned as a tuple like ((x_lower, y_lower), (x_upper, y_upper)). The box is oriented along the x-y axes.

pts = np.array([[0.3, 0.2],
                [0.8, 0.3],
                [0.4, 0.7],
                [0.2, 0.5]])
A7Q3(pts)  # Returns ((0.2, 0.2), (0.8, 0.7))

M.4 Question 4: Normalize a vector

Write a function which accepts an N-dimensional vector and returns the normalized vector (i.e. unit length). As usual, do not fret over values in trailing decimal places.

vec = [4.1, 3.3, 5.0, -2.0]
A7Q4(vec)  # Returns array([ 0.54449303,  0.43825049,  0.66401589, -0.26560636])

M.5 Question 5: Matrix multiplication

Write a function which accepts a 2D coefficient matrix \(A\), and a 1D vector of \(x\) values, and returns the product of \(Ax\).

A = np.arange(16).reshape((4, 4))
x = np.arange(100, 104)
A7Q5(A, x)  # Returns array([ 614, 2238, 3862, 5486])

A = np.arange(16).reshape((4, 4))
x = np.arange(100, 104).reshape((4, 1))
A7Q5(A, x)  # Returns array([ 614, 2238, 3862, 5486])

M.6 Question 6: Boolean indexing meets linear algebra

Write a function which computes the trace of a square matrix without using any of Numpy’s built-in trace functions, or for-loops (Hint: use boolean indexing)

A = np.arange(16).reshape((4, 4))
A7Q6(A)  # Returns 30

M.7 Question 7: Boolean indexing meets linear algebra, part 2

Write a function that determines if a given matrix is upper or lower triangular. Because this requires comparing floats you may need to round values.

A1 = np.array([[1, 2, 3, 4],
               [0, 2, 3, 4],
               [0, 0, 3, 4],
               [0, 0, 0, 4]])
A7Q7(A1)  # Returns True

A2 = np.array([[1, 2, 3, 4],
               [3, 2, 3, 4],
               [3, 0, 3, 4],
               [1, 0, 1, 4]])
A7Q7(A2)  # Returns False

N Assignment 8

N.1 Question 1: Generate a scatter plot

Write a function which accepts x and y data in the form of Numpy arrays and plots them using a scatter plot. The function should return a tuple containing the figure and axis handles, in that order.

import numpy as np
x, y = np.random.rand(2, 200)
A8Q1(x, y)  # Should return (fig, ax)

N.2 Question 2: Generate a plot with several subplots

Write a function which accepts N Numpy arrays (2 <= N <= 5) and plots a histogram for each array. The histograms should all be contained in a single figure with N separate axis in a row. The function should return a tuple containing the figure handle and one axis handle which can be used access each subplot.

x, y, z = np.random.rand(3, 200)
A8Q2(x, y, z)  # Should return (fig, ax)

Note that len(ax) == N should be True.

N.3 Question 3: Plot an image

Write a function which accepts a 2D Numpy array and plots is as an image. The function should return a tuple containing a figure and axis handle, in that order.

im = np.random.rand(100, 100)
A8Q3(im)  # Should return (fig, ax)

N.4 Question 4: Set axis properties

Write a function which accepts a figure handle and Matplotlib named color as a string, changes the background of the figure to the given color, and returns the figure handle.

A8Q4(fig=fig, color='grey')  # Should return fig

N.5 Question 5: Validate data

Given two Numpy arrays x and y. Generate an x-y scatter plot of the data, but only include data points that lie between 0 and 1. Any data that is less than 0 or greater than 1 should be ignored. The function should return a tuple containing a figure and axis handle, in that order.

A8Q5(x, y)  # Should return (fig, ax)

O Assignment 9

O.1 Question 1: Overload the __setitem__ method

Create a subclass of a dict that overloads the __setitem__ method to ensure that all the dictionary keys are strings. If a user attempts to use a key that is not a string, nothing should be written to the object. Optionally, you can raise an Exception.

c = A9Q1()
c['val1'] = 1.0  # This should work
c[0] = 2.0  # This should raise an Exception

O.2 Question 2: Overwrite the __init__ method

Create a subclass of a dict that accepts another dict as a positional argument and incorporates it into it self, and accepts keyword arguments but ignores them. To do this question you’ll need to read Section 11.5.

c = A9Q2({'key1': 0.3, 'key2': 22}, key3=10.0)

O.3 Question 3: Add a size method

Create a subclass of a list which has a size method that returns the number of elements in the list. Treat any nested lists as single elements.

c = A9Q3([1, 2, 5, 2])
c.size()  # Should return 4
c = A9Q3([1, 2, [3, 2, 1]])
c.size()  # Should return 3

O.4 Question 4: Create your own DataFrame class

Create a subclass of a dict which behaves like a simple version of the pandas.DataFrame. The initialization of the array should only accept 1 argument: the allowed length of the arrays. This class should do the following:

  • Convert all values to Numpy ndarrays
  • Ensure all arrays are 1D, and prevent writing if not
  • If a scalar is received, it should be “broadcast” to a 1D array of the correct length, with all elements set to the scalar value.
  • Ensure all arrays are the same length, and prevent writing if not
c = A9Q4(4)
c['arr1'] = np.arange(4)  # Should work
c['arr2'] = np.ones(5)  # Should fail
c['arr3'] = [1, 3, 6, 10.0]  # Should work
c['arr4'] = np.random.rand(4, 2)  # Should fail
c['arr5'] = 1.0  # Should work