01 · Mandatory task · Lab results preprocessing
Tame the spreadsheet
Lab results arrive as spreadsheets typed by many people. The batch a sample came from is buried in two free-text columns, Expt. No. and Lot No., in whatever format the analyst preferred that day, and sometimes in the wrong column.
The capstone's first job was to turn those sheets into tidy, joinable records. This page generates a synthetic sheet with the same kind of chaos and parses it in your browser.
Generate a sheet
Rows
Messiness50%
Share of rows typed in a non-standard way.
Unparseable rows
Rows in two workbooks
1,200
660 + 540 rows
Set aside
4
Dept, Expt. or Lot No. blank or typed as n/a
Parsed
99.8%
1,194 of 1,196 rows that entered the pipeline
Routed to other
2
1 unmatched · 1 unknown dept
How the Parsed figure arises: every generated row has a small, independent chance of being genuinely unparseable, set by the “Unparseable rows” control. “Some” is tuned so the share lands near the team's reported 99.7%, and it moves from seed to seed. Messiness only changes which case catches a row, because every messy variant has a case.
The pipeline
- 1
Merge the workbooks
Stack both files, keep each row's workbook and row number, and set aside 4 rows missing a key field. This uses merge_result's default (remove_nan=True); the 2021 notebook switched it off and those rows fell out later, in the stream filters and merges.
- 2
Split the department
A Dept No. such as 310 Downstream becomes DepartmentID 310 and Stream Downstream. Anything not starting with digits is kept as DepartmentAlternative.
- 3
Run each stream's cases
Ordered pattern cases per stream; the first match writes BatchID1, BatchID2 and Process Step. Sample Name (Expt. No. + space + Lot No.) and Compound are added to every row.
- 4
Route the rest
Whatever no case recognises goes to other (2 rows here) so a person can add a case or fix the entry.
Cases per stream
310 Upstream · Cell-culture runs. The run ID (CC + four digits) should lead Expt. No.; everything else, minus compound tags, becomes BatchID2.
U1Run ID leads Expt. No.
265 rows
- Tests
- Expt. No. against
/^CC ?\d{4}/i - Then
- Normalise the run ID (upper case, no space) into BatchID1. Join Expt. No. and Lot No., drop the run ID and any Compound-n tag, and keep the rest as BatchID2.
- Example
- cc 1043 Compound-2 | D07
U2Columns swapped
38 rows
- Tests
- Lot No. against
/^CC ?\d{4}/i - Then
- Same as U1, but the run ID was typed into Lot No.
- Example
- Harvest | CC1187
- No case matched → the row goes to other for a person to look at. It is never silently dropped.
Before and after
1,196 rows
| File·Row | Dept No. | Expt. No. | Lot No. | BatchID1 | BatchID2 | Process Step | Compound | Case |
|---|---|---|---|---|---|---|---|---|
| 1·0 | 310 Upstream | CC1829 | D07 | CC1829 | D07 | · | · | U1 |
| 1·1 | 310 Upstream | CC1899 | D14 | CC1899 | D14 | · | · | U1 |
| 1·2 | 310 Upstream | CC 1170 | D07 | CC1170 | D07 | · | · | U1 |
| 1·3 | 310 Upstream | CC1547 | Harvest | CC1547 | Harvest | · | · | U1 |
| 1·4 | 420 Concentrate | cpn 06 | 2232FC19 pre_filtration | CPN06 | 2232FC19 | pre_filtration | · | C1 |
| 1·5 | 420 Concentrate | CPN10 | 2276HF17 post_filtration | CPN10 | 2276HF17 | post_filtration | · | C1 |
| 1·6 | 420 Concentrate | CPN05 | 2288DF03 post_filtration | CPN05 | 2288DF03 | post_filtration | · | C1 |
| 1·7 | 420 Concentrate | CPN10 | 2206da13 final_conc | CPN10 | 2206DA13 | final_conc | · | C1 |
| 1·8 | 420 Concentrate | 2265FF11 bulk_hold | CPN04 | CPN04 | 2265FF11 | bulk_hold | · | C2 |
| 1·9 | 420 Concentrate | CPN07 | 2267AF07 pre_filtration | CPN07 | 2267AF07 | pre_filtration | · | C1 |
| 1·10 | 420 Concentrate | CPN10 | 2213CE08 post_filtration | CPN10 | 2213CE08 | post_filtration | · | C1 |
| 1·11 | 420 Concentrate | CPN07 | 2238EG06 post_filtration | CPN07 | 2238EG06 | post_filtration | · | C1 |
| 1·12 | 420 Concentrate | CPN07 | 2242CA11 final_conc | CPN07 | 2242CA11 | final_conc | · | C1 |
| 1·13 | 420 Concentrate | CPN09 | 2275DG04 final_conc | CPN09 | 2275DG04 | final_conc | · | C1 |
| 1·14 | 420 Concentrate | CPN02 | 2283DB02 post_filtration | CPN02 | 2283DB02 | post_filtration | · | C1 |
| 1·15 | 420 Concentrate | CPN05 | 2227CC15 bulk_hold | CPN05 | 2227CC15 | bulk_hold | · | C1 |
| 1·16 | 420 Concentrate | CPN02 | 2228CG19 final_conc | CPN02 | 2228CG19 | final_conc | · | C1 |
| 1·17 | 420 Concentrate | 2234BD09 post_filtration | CPN08 | CPN08 | 2234BD09 | post_filtration | · | C2 |
| 1·18 | 420 Concentrate | 2226GF20 pre_filtration | CPN04 | CPN04 | 2226GF20 | pre_filtration | · | C2 |
| 1·19 | 420 Concentrate | CPN08 | 2209EC03 bulk_hold | CPN08 | 2209EC03 | bulk_hold | · | C1 |
Try your own row
- F1Underscore suffix
- F2Spaced hyphen
- F3Space suffix
- F4Hyphen suffix
- F5Suffix glued on
- F6Batch only
- F7Columns swapped
- DepartmentID
- 530
- Stream
- Formulation
- BatchID1
- F239117
- BatchID2
- A12
- Process Step
- Stability
- Compound
- ·
- Sample Name
- “Stability F239117 - A12”