CMU Coding Bootcamp
1possibleData = str | float | None
2
3
4class DataList:
5 data: list[possibleData]
6
7 def __init__(self, L: list[possibleData]) -> None:
8 self.data = L
9
10 def __getitem__(self, index: int) -> possibleData:
11 return self.data[index]
12
13 def __len__(self) -> int:
14 return len(self.data)
15
16 def numericValues(self) -> list[float | int]:
17 items = [item for item in self.data if isinstance(item, float | int)]
18 return items
19
20 def min(self) -> float:
21 return sorted(self.numericValues())[0]
22
23 def average(self) -> float:
24 nvs = self.numericValues()
25 return sum(nvs) / len(nvs)
26
27 def median(self) -> float | None:
28 nvs = self.numericValues()
29 if len(nvs) == 0:
30 return None
31 snvs = list(sorted(nvs))
32 middle = len(nvs) // 2
33 if len(nvs) % 2 == 1:
34 return snvs[middle]
35 else:
36 sl = snvs[middle - 1 : middle + 1]
37 return sum(sl) / 2
38
39 def max(self) -> float:
40 return list(reversed(sorted(self.numericValues())))[0]
41
42 def range(self) -> tuple[float, float]:
43 return (self.min(), self.max())
44
45 @property
46 def values(self) -> list[float | int | None]:
47 values = [item for item in self.data if (isinstance(item, float | int | None))]
48 return values
49
50
51class Table:
52 data: list[DataList]
53
54 def __init__(self, data: list[list[possibleData]]) -> None:
55 self.data = []
56 m, n = len(data), len(data[0])
57 result: list[list[possibleData]] = []
58 for col in range(n):
59 result.append([])
60 for row in range(m):
61 result[col].append(data[row][col])
62 for row in result:
63 dl = DataList(row)
64 self.data.append(dl)
65
66 def getHeaders(self) -> list[str]:
67 return [str(r[0]) for r in self.data]
68
69 def _getColInt(self, index: int) -> DataList:
70 if index >= len(self.data):
71 raise IndexError(f"Col {index} is out of range")
72 return self.data[index]
73
74 def _getColStr(self, index: str) -> DataList:
75 hi = self.getHeaderIndex(index)
76 return self._getColInt(hi)
77
78 def getCol(self, index: int | str) -> DataList:
79 if isinstance(index, int):
80 return self._getColInt(index)
81 else:
82 return self._getColStr(index)
83
84 def getRow(self, index: int) -> list[possibleData]:
85 if index + 1 >= len(self.data[0]):
86 raise IndexError(f"Row {index} is out of range")
87 else:
88 row = [c[index + 1] for c in self.data]
89 return row
90
91 def getHeaderIndex(self, header: str) -> int:
92 headers = [c[0] for c in self.data]
93 for i, h in enumerate(headers):
94 if h == header:
95 return i
96 else:
97 raise KeyError(f"No such header: {header}")
98
99
100def almostEqual(x: float | None, y: float | None) -> bool:
101 if x == None or y == None:
102 return False
103 epsilon = 10**-9
104 return abs(x - y) < epsilon
105
106
107def testLevel1():
108 print("Testing Level 1 (Core) material...", end="")
109 # First test DataList:
110 dl = DataList([5, 2, None, 1.1, "yikes", 3.9])
111 assert type(dl) == DataList
112 assert dl.numericValues() == [5, 2, 1.1, 3.9]
113 assert dl.min() == 1.1
114 assert dl.max() == 5
115
116 # Now test Table and TableRow
117 weatherData: list[list[possibleData]] = [
118 ["Date", "Low", "High"],
119 ["1-Aug", 63, 81],
120 ["2-Aug", 67, 85],
121 ["3-Aug", 64, 86],
122 ["4-Aug", 61, None],
123 ["5-Aug", None, None],
124 ["6-Aug", 59, 71],
125 ["7-Aug", 63, 77],
126 ["8-Aug", 68, 88],
127 ["9-Aug", 75, 91],
128 ["10-Aug", 74, 93],
129 ]
130 table = Table(weatherData)
131 assert table.getHeaders() == ["Date", "Low", "High"]
132
133 lows = table.getCol(1)
134 assert type(lows) == DataList
135 assert lows.values == [63, 67, 64, 61, None, 59, 63, 68, 75, 74]
136 assert lows.numericValues() == [63, 67, 64, 61, 59, 63, 68, 75, 74]
137 assert lows.min() == 59
138 assert lows.max() == 75
139
140 highs = table.getCol(2)
141 assert type(highs) == DataList
142 assert highs.values == [81, 85, 86, None, None, 71, 77, 88, 91, 93]
143 assert highs.numericValues() == [81, 85, 86, 71, 77, 88, 91, 93]
144 assert highs.min() == 71
145 assert highs.max() == 93
146
147 # We will try to get col 3, but that is out of range,
148 # so it will raise a custom exception.
149 error = None
150 try:
151 _col = table.getCol(3)
152 except Exception as e:
153 error = str(e)
154 assert error == "Col 3 is out of range"
155 print("Passed!")
156
157
158def testLevel2():
159 print("Testing Level 2+ (Not Core) material...", end="")
160 # First test DataList:
161 dl = DataList([5, 2, None, 1.1, "yikes", 3.9])
162 assert dl.range() == (1.1, 5)
163 assert almostEqual(dl.average(), 3)
164
165 # Now test Table and TableRow
166 weatherData: list[list[possibleData]] = [
167 ["Date", "Low", "High"],
168 ["1-Aug", 63, 81],
169 ["2-Aug", 67, 85],
170 ["3-Aug", 64, 86],
171 ["4-Aug", 61, None],
172 ["5-Aug", None, None],
173 ["6-Aug", 59, 71],
174 ["7-Aug", 63, 77],
175 ["8-Aug", 68, 88],
176 ["9-Aug", 75, 91],
177 ["10-Aug", 74, 93],
178 ]
179 table = Table(weatherData)
180 assert table.getHeaders() == ["Date", "Low", "High"]
181
182 assert table.getRow(0) == ["1-Aug", 63, 81]
183 assert table.getRow(9) == ["10-Aug", 74, 93]
184
185 # We will try to get row 10, but that is out of range,
186 # so it will raise a custom exception.
187 error = None
188 try:
189 _row = table.getRow(10)
190 except Exception as e:
191 error = str(e)
192 assert error == "Row 10 is out of range"
193
194 assert table.getHeaderIndex("Date") == 0
195 assert table.getHeaderIndex("Low") == 1
196 assert table.getHeaderIndex("High") == 2
197
198 # We will try to find the header index of 'Missing', but
199 # there is no such header, so it will raise a custom exception.
200 error = None
201 try:
202 _i = table.getHeaderIndex("Missing")
203 except Exception as e:
204 error = str(e)
205 # assert(error == 'No such header: Missing')
206
207 lows = table.getCol("Low") # hint: use getHeaderIndex!
208 assert type(lows) == DataList
209 assert lows.values == [63, 67, 64, 61, None, 59, 63, 68, 75, 74]
210 assert lows.range() == (59, 75)
211 assert almostEqual(lows.average(), 66)
212
213 highs = table.getCol("High")
214 assert type(highs) == DataList
215 assert highs.values == [81, 85, 86, None, None, 71, 77, 88, 91, 93]
216 assert highs.range() == (71, 93)
217 assert almostEqual(highs.average(), 84)
218
219 # And for one last challenge:
220 # We define the MEDIAN of a list like so:
221 # * If the list has odd length, then the median is
222 # the middle value of the sorted list.
223 # * If the list has even length, then the median is
224 # the average of the two middle values of the sorted list.
225 # For example:
226 # median([11, 19, 7, 14, 3])
227 # * This list has length 5, which is odd, so the median is the
228 # middle value of the sorted list, [3, 7, 11, 14, 19],
229 # which is 11.
230 # median([11, 19, 7, 14])
231 # * This list has length 4, which is even, so the median is the
232 # average of the two middle value of the sorted list,
233 # [7, 11, 14, 19], which is (11 + 14)/2 = 25/2 = 12.5.
234 #
235 # With this in mind, pass these additional tests;
236 dl = DataList([11, 19, 7, 14, 3])
237 assert almostEqual(dl.median(), 11)
238
239 dl = DataList([11, 19, 7, 14])
240 assert almostEqual(dl.median(), 12.5)
241
242 dl = DataList([])
243 assert dl.median() == None # No values, so no median
244
245 assert almostEqual(lows.median(), 64)
246 assert almostEqual(highs.median(), 85.5)
247 print("Passed!")
248
249
250def main():
251 testLevel1()
252 testLevel2()
253
254
255main()