A202 / I211 Assignment 10
Table class
Due Thursday, November 11th, 3:00 PM
Pair programming in this assignment if you like. You may
choose any partner in your lab, or work on your own if you prefer.
In lab
- Download the table.py module containing the Table class
presented in lecture this week.
- Load it into the IDLE editor and run it.
- Create a table with test data of your choosing
.
- Test each of the table methods using your test data. Try to reason what
the result will be before running each test.
- Add assert statements to the main method to automate your tests.
- Modify the constructor (__init__) so that the data argument may be
a list of strings, in which case the strings are treated as if they were lines
in a comma-separated file: they are split at commas to obtain a list of
strings.
- Modify the constructor so that the data argument may be a string, in which
case it is taken as the name of a file. The file's lines are read and a list
of these lines is substituted for the data argument value. Place this code
before that added to detect and deal with data that is a list of strings. In
this way the name of a comma-separated-value (CSV) file may be passed as the
argument when creating a table and the resulting table will represent the data
in the file in a manner similar to importing the file into a spreadsheet. Test
this feature by creating a sample CSV file.
- Add a .copy() method to the class that returns a copy of the
current table. The copy should be like the original, but modification of the
copy should not change the original. For this and subsequently added methods,
add associated assert tests to the main method.
- Hint: make a "deep" copy of the data in the current table. That is, copy
not just the list of rows, but also each of the rows. Then construct and
return the new table using the data copy.
- Add a .invert() method to the class that inverts the table
(exchanging rows for columns). That is, the nth row becomes the nth
column, and visa versa. Unlike the copy method, which leaves the current table
unchanged, this method changes the current table.
- Recall that if a method specification does not say it returns something,
it returns nothing. This is the case for all most all methods that mutate
existing data.
- Hint: once again, construct the new data structure, but this time assign
the new structure to the table field of the class rather than making
a new table instance. It is tempting to use zip to construct the new
table structure, but the table should be a list of lists, not a list of
tuples.
- Add a method .makeDict(columnIndex) that returns a
dictionary for which values in the indicated column are keys that are
associated with their rows.
By way of example, here are some tests that could be added to the existing
main method tests. Make up your own additional tests.
tc = t.copy()
assert tc.getData() == [[2, True, 'last'], [3, False, 'column']]
tc.invert()
assert tc.getData() == [[2, 3], [True, False], ['last', 'column']]
dict = t.makeDict(2)
assert dict['last'] == [2, True, 'last']
t = Table(data=['a,1,2', 'b,3,4'])
assert t.getData() == [['a', '1', '2'], ['b', '3', '4']]
When you have completed the last exercise above, or 15 minutes before the
lab ends, whichever comes first, submit your table.py file as
lab 10
in Vincent. Also, before the end of the lab take time to read the assignment
below and ask if after some reflection it is not clear what is required.
This lab contains more exercises than you will probably be able
to finish during the lab time. You are urged
to continue working on these exercises after your lab and before looking at the
solution that will be posted shortly after lab. It is the best preparation for
doing the assignment independently. Of course you are encouraged to work on
these exercises with others, but
are only allowed to work with your partner on the assignment.
Assignment
In this assignment you will be extending the table class
developed in lab by making initially empty tables useful and adding two
additional methods to the class.
- The first step in this assignment is to allow rows or columns to be added
to an initially empty table. For convenience, in the table constructor (its
__init__ method), make the data parameter a keyword parameter that
defaults to the empty list. So for example, Table() is equivalent to
Table([]).
Motivation: Adding rows or columns to the table class
as given in lecture and extended in lab requires that the length of the row or
column be consistent with the length of existing rows or columns in the table.
This means you cannot create an empty table and do anything meaningful with
it. So, for example, it is not possible to build up a table from scratch by
adding rows or columns with an accumulator loop.
Hints
- Besides extensive modification of the constructor, this also requires
modification of the addColumn method. Confirm by inspection of the
code that no other changes are needed.
- Be careful to avoid the mistake pointed out in class of mutating a
default value.
- Add a .project(columnIndices) method that takes a
list of column indices and returns a new table whose columns are those in the
current table indicated by the given indices.
Motivation:
Tables may have a great many columns and it is valuable to be able to restrict
attention to just the columns of interest for a particular use. Most databases
are based on tables and this is a very common database operation.
Hint: use the ability developed in part 1 to start with a new empty
table and add the indicated columns to it.
- Add a .select(rowPredicate) method that takes as its
argument a function rowPredicate of one argument that returns a boolean
value. Return a new table that is like a copy of the current table, but with
only those rows for which rowPredicate returns true when passed the row
as its argument.
Motivation: It is often valuable to be able
to restrict attention to just those rows of a table that have some desired
property. This is commonly done in databases, where the property is
satisfaction of a query's constraints.
Hint: As before, start with an empty table and accumulate the
rows that are desired. Be sure to copy those rows that are added so mutation
of the new table will not change the current table.
A few test follow, including one in which a predicate selects those rows
(only one in the example) with b in the first column.
t = Table()
assert t.getData() == []
t = Table(data=['a,1,2', 'b,3,4'])
assert t.getData() == [['a', '1', '2'], ['b', '3', '4']]
tp = t.project([0, 2])
assert tp.getData() == [['a', '2'], ['b', '4']]
def rowBpred(row):
return row[0] == 'b'
ts = t.select(rowBpred)
assert ts.getData() == [['b', '3', '4']]
When you are done, submit your final table.py file as
a10 using Vincent.