Solutions Python review questions for final quiz

Here are a couple of questions that test some python learning objectives we’ve covered during the course. Try these on your own and I’ll post the solutions Tuesday evening.

[1]:
import numpy as np

Q1: code smell

This is not on the final quiz, but is useful information if plan to use python in the future.

The cell below, which is taken from build_2D_matrix.py isn’t wrong but could be improved. Explain how you could alter it to make it higher quality and less likely to produce an erroneous result.

[2]:
def index_to_row_col(ind, nrows, ncols):
    """
    in a 2D array, returns the row and column value
    associated with a 1D index
    Bottom left is index is zero (0-th row, 0-th column)
    while index one is the 0-th row and 1st column
    """
    if ind > nrows * ncols - 1:
        return 0

    row = int(np.floor(ind / ncols))
    col = int(ind - row * ncols)
    return row, col
[3]:
import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Q1 Answer

When you execute import this in a jupyter notebook cell (see above) you get this pair of lines:

Errors should never pass silently.
Unless explicitly silenced.

For the index_to_row_col function, this means that you should be catching the out of bounds error and writing a useful error message. As it is written above, users would always have to check every call of the index_to_row_col to make sure that it wasn’t returning 0 instead of the expected (row, col) tuple.

Here’s how that might look (Again, exceptions are not on the test, but you definitely need to understand them for future python programming tasks).

[4]:
import numpy as np
def index_to_row_col(ind, nrows, ncols):
    """
    in a 2D array, returns the row and column value
    associated with a 1D index
    Bottom left is index is zero (0-th row, 0-th column)
    while index one is the 0-th row and 1st column
    """
    if ind > nrows * ncols - 1:
        err_msg = (f"ind={ind} exceeds total number of elements"
                   f"tot_num={nrows*ncols}")
        raise ValueError(err_msg)
    row = int(np.floor(ind / ncols))
    col = int(ind - row * ncols)
    return row, col

# the following command will throw an ValueError exception
#
#index_to_row_col(2000,5,12)

Q2 convert row,col to index

Write a function row_col_to_index that takes a 2-dimensional array shape, a row number and a column number and returns the index of that value of the array. i.e. write the function such that calling

ind=row_col_to_index(nrows,ncols,rownum,colnum)

followed by

rownum,colnum = index_to_row_col(ind, nrows,ncols)

gives the same rownum and colnum back you started with.

Q2 answer

[5]:
def row_col_to_index(nrows, ncols, rownum, colnum):
    """
    in a 2D array, returns the 1D index associated
    with a particular row and column
    Index 0 is row 0, col 0 in the bottom left corner
    of the array
    """
    totrows,totcols = the_array.shape
    the_index=rownum*totcols + colnum
    return the_index

nrows=12
ncols=5
rownum=3
colnum=4
the_array = np.zeros([nrows,ncols])
ind=row_col_to_index(nrows,ncols,rownum,colnum)
row, col = index_to_row_col(ind,nrows,ncols)
assert((row, col) == (rownum,colnum))

Q3 build_2D_array

In the cell below, import the function build_2D_matrix and use it along with imports of the Problem_Def and Boundary_Def classes to construct the 2D matrix defined in Question 3 of the 8_finite_volume_by_hand_ans assignment

Q3 build_2D_array answer

[6]:
from build_2D_matrix import Boundary_Def, Problem_Def, build_2D_matrix
import build_2D_matrix as bd
import numpy as np

west = Boundary_Def("const", val=64.)
east = Boundary_Def("const", val=125.)
north = Boundary_Def("flux", val=0.)
south = Boundary_Def("flux", val=0.)
bc_dict = {"west": west, "north": north, "east": east, "south": south}
n_x = 4
n_y = 3
width_x = 800.
width_y = 600.
poro=0.25
Diff=1.e-10
D_matrix = Diff * np.ones((n_y, n_x))
Qsource = np.zeros((n_y, n_x))
prob = Problem_Def(n_x, n_y, poro, width_x, width_y)
A, b = build_2D_matrix(bc_dict, prob, D_matrix, Qsource)
v = np.linalg.solve(A, b)
print(v)
[ 64.          84.33333333 104.66666667 125.          64.
  84.33333333 104.66666667 125.          64.          84.33333333
 104.66666667 125.        ]

Q4 Python Classes

Given the class definition and the instance below, what will this line print?

print(out.data_array[3,:])
[7]:
import numpy as np
class hold_data:
    def __init__(self,nrows,ncols):
        self.data_array=np.arange(0,nrows*ncols)
        self.data_array.shape=[nrows,ncols]

out=hold_data(6,8)
print(out.data_array[3,:])
[24 25 26 27 28 29 30 31]

Q5 Pandas indexing

Given the code in the cell below, what will this line print?

print(test.loc[16,'e'] - test.loc[8,'d'])
[8]:
import pandas as pd
test=pd.DataFrame(out.data_array,
                  columns=['a','b','c','d','e','f','g','h'])
test.set_index('a',inplace=True)
test.head()
[8]:
b c d e f g h
a
0 1 2 3 4 5 6 7
8 9 10 11 12 13 14 15
16 17 18 19 20 21 22 23
24 25 26 27 28 29 30 31
32 33 34 35 36 37 38 39
[9]:
print(test.loc[16,'e'] - test.loc[8,'d'])
9