Convert, Compute, and Report
Keep the source line visible while converting valid readings, recording one expected failure, and computing a report from accepted values.
Reading measurements.txt gives the program one string for each source line.
Numerical work needs a different representation: labels paired with
floating-point readings. The conversion should happen near the input boundary,
before those values reach the summary function.
Here is the same file, with its source line numbers shown for reference:
1 north,18.5
2 east,not recorded
3 west,24.0
4 café,21.5
5 north,19.0
6 west,23.5
Line 2 has invalid numeric text. Chapter 7 established the policy for this expected case: record which row was skipped and continue with the valid rows.
Parse the Stated Row Format
The file format says that each line has two comma-separated fields. We can therefore clean the line ending and split once at that stated separator:
For source line 4, the values are visible:
| Stage | Value |
|---|---|
| source line | "café,21.5\n" |
| cleaned line | "café,21.5" |
| label | "café" |
| reading text | "21.5" |
strip and split were introduced with strings in Chapter 5. Their use here
comes from the file format: newline and surrounding whitespace are not fields,
while the comma separates the two fields. If the format did not promise
comma-separated fields, calling split(",") would be a guess rather than a
parser.
Keep the source line number beside the row. A report such as “line 2 was skipped” is useful even when two rows share the same label or the invalid text is empty.
Q1. Preserve the source identity
After cleaning source line 4, which values should the parser keep before numeric conversion?
Select one choice, then check.
HintRetain evidence before conversion
One value locates the row; the other two values come from splitting its stated fields.
SolutionKeep line number, label, and reading text
Retain 4, "café", and "21.5". The parser can then convert the reading
while preserving where it came from.
Convert at the Input Boundary
The field named reading_text still contains a string. Convert it before
adding the row to numerical data:
The try block contains only the expected conversion. A ValueError records
the source line, label, and rejected text, then continue prevents that row
from reaching accepted. Other defects remain visible rather than being
mistaken for invalid input.
Tracing all six rows gives:
| Line | Label | Reading text | Decision |
|---|---|---|---|
| 1 | north | "18.5" | accept 18.5 |
| 2 | east | "not recorded" | reject and record line 2 |
| 3 | west | "24.0" | accept 24.0 |
| 4 | café | "21.5" | accept 21.5 |
| 5 | north | "19.0" | accept 19.0 |
| 6 | west | "23.5" | accept 23.5 |
The accepted values are now suitable for computation. The rejected row remains evidence; it has not disappeared silently.
Q2. Choose the narrow invalid-row policy
What should happen when float("not recorded") raises ValueError for line 2?
Select one choice, then check.
HintDo not invent or erase data
The source contains no numerical value for east, but its failed row still belongs in the report.
SolutionRecord and skip the invalid row
Add (2, "east", "not recorded") to the rejected rows and continue. The
remaining numerical rows can still be summarized.
Keep Reading, Parsing, and Computation Separate
One function can own each role. The reader obtains source lines, and the parser
converts those lines into clean records. This call supplies only accepted
numbers to the already tested mean function; the function still preserves
its earlier contract by ignoring an explicit None marker:
The boundaries are now visible:
| Function | Receives | Returns |
|---|---|---|
read_lines | a file path | numbered text lines |
parse_measurements | numbered text lines | accepted numeric records and rejected-row evidence |
mean | numbers, with optional None markers | the mean of available numbers, or None when none are available |
This separation keeps file details out of mean. It also lets a parsing test
supply a short list of strings without opening a file, and lets a mean test
supply numbers without constructing text rows.
Build Report Text from the Result
The accepted readings are 18.5, 24.0, 21.5, 19.0, and 23.5. Their
computation is
small enough to check by hand:
total = 18.5 + 24.0 + 21.5 + 19.0 + 23.5 = 106.5
count = 5
mean = 106.5 / 5 = 21.3
The program can construct a short report from that result. Here, accepted,
rejected, and values are the lists produced by the earlier functions:
join appears here because the program is constructing output text from
separate report lines. The report has not yet been saved. The next lesson will
write derived output and read it back before treating the write as verified.
Q3. Parse and summarize the measurement file
Replace the marked parser statements. Convert each reading near the input
boundary, retain the invalid row, and leave the tested mean function
unchanged. The program should report five accepted rows, one rejected row, and
the checked mean.
Editable Python
Ready to run.
HintKeep the successful append outside the handler
Replace 0.0 with float(reading_text). On ValueError, append a tuple
containing line_number, label, and reading_text to rejected, then
continue to the next source line.
SolutionConvert, record, and continue
Only a successful conversion reaches accepted.append. The values passed to
mean are therefore all floating-point numbers.
Reading preserves numbered source text; parsing applies the stated field format and converts near the boundary; computation receives only clean numerical values. The invalid east row remains explicit rather than becoming zero or disappearing. The resulting report text is ready to be written and verified.