Schema Care

Extracting Panel Results Like a CBC or CMP

By Schema CarePublished Updated

Extracting panel results like a CBC or CMP means capturing every analyte on the report as its own value while keeping each one tied to the panel it came from. Give each analyte a panel ID, keep CBC percentages and absolute counts apart, copy calculated values as printed, and count the analytes against the expected list.

What a CBC or CMP panel looks like as data

A CBC or CMP panel is a group of related tests ordered together under one name, and as data it is one parent with many children. The panel is the parent record: one name, one collection time, one order. Each analyte is a child: one test name, one value, one unit, one range and one flag.

An analyte is the single substance or cell type a test measures, such as sodium or platelets. A complete blood count (CBC) and a comprehensive metabolic panel (CMP) are two panels that appear often on outside lab reports. The MedlinePlus page on the comprehensive metabolic panel lists 14 tests in a CMP: glucose, calcium, sodium, potassium, bicarbonate, chloride, albumin, total protein, ALP, ALT, AST, bilirubin, BUN and creatinine.

The MedlinePlus page on the complete blood count describes a CBC as measuring red blood cells, white blood cells, platelets, hemoglobin, hematocrit and mean corpuscular volume, among other values. Labs print more rows than that list, and the exact set varies by lab, so the printed report is the authority for what one panel contains.

FHIR, the HL7 standard for exchanging health data, models the same parent-child shape. The FHIR R4 Observation page describes its hasMember element for "a group observation (e.g. a battery, a panel of tests, a set of vital sign measurements)". You do not need FHIR to use the idea. Any spreadsheet or database can hold a panel row and a set of analyte rows that point to it.

Should each panel analyte be its own record?

Each panel analyte should be its own record, and each record should carry the ID of the panel it belongs to. Flat rows with no panel link lose the grouping. A single panel record with the analytes packed into one text field loses the values. The parent-with-children layout keeps both.

Flat rows are the simplest layout: one row per analyte with the patient, date, test name, value and unit repeated on each. They are easy to load into a spreadsheet. The trouble starts when two panels share a date. A patient with a CMP and a separate basic metabolic panel on the same morning will have two sodium rows, and without a panel ID nobody can tell which belongs to which.

A packed panel record goes the other way: one row for the panel with all the results in one long text field. It reads well, but the values cannot be sorted, trended or checked one at a time.

The parent-with-children layout uses two kinds of records:

  • Panel record. Panel ID, panel name as printed, patient, collection date and time, ordering provider, performing lab and the source page reference.
  • Analyte record. Panel ID, analyte name as printed, value, unit, reference range, flag, and its position on the page.

A fictional CMP as data

The table shows a fictional CMP for a made-up patient, Riley Example. The panel ID, values and units are all example values invented for this post and say nothing about any real person.

Panel IDAnalyteValueUnit
EX-CMP-0412Glucose92mg/dL
EX-CMP-0412Calcium9.4mg/dL
EX-CMP-0412Sodium139mmol/L
EX-CMP-0412Potassium4.2mmol/L
EX-CMP-0412Bicarbonate25mmol/L
EX-CMP-0412Chloride102mmol/L
EX-CMP-0412Albumin4.3g/dL
EX-CMP-0412Total protein7.0g/dL
EX-CMP-0412ALP71U/L
EX-CMP-0412ALT22U/L
EX-CMP-0412AST24U/L
EX-CMP-0412Bilirubin, total0.6mg/dL
EX-CMP-0412BUN14mg/dL
EX-CMP-0412Creatinine0.9mg/dL

Analyte count check (example): expected 14, extracted 14, missing none.

Every row carries the same panel ID, so the 14 values stay together even after they are sorted, filtered or loaded into another system. The count line is the completeness check described in the last section of this post.

Keep the printed analyte name

Store the analyte name exactly as the lab printed it, even when you also map it to a standard name or code. One lab prints "BUN", another prints "Urea nitrogen", and a third prints "Blood urea nitrogen". Keeping the printed name lets a reviewer find the row on the page in seconds. A separate column can hold the standard name your system uses. If the two disagree later, the printed name shows what the lab actually reported.

The same rule applies to the panel name. A report may print "Comprehensive metabolic panel", "CMP" or "Chem 14" for the same order. Record the printed panel name on the panel record, and map it to your own panel name in a separate field.

CBC differentials: percentages and absolute counts side by side

A CBC differential reports each type of white blood cell, and many reports print it twice: once as a percentage of all white cells and once as an absolute count. Each version is a separate result with its own unit and range. Extract them as two analytes with distinct names, never as one value with two units.

The MedlinePlus page on the blood differential test says the test "measures the amount and/or percentage of each type of white blood cell." That "and/or" is the extraction problem. One lab prints both versions, another prints only percentages, and a third prints only counts.

On the page, the two versions often sit side by side or in two blocks. A fictional layout might print "Neutrophils 58 %" in one row and "Neutrophils 3.4 x10^3/uL" a few rows lower, both with the same test name. If the extractor keys on the name alone, the second value overwrites the first, or the two get merged under one name with mismatched units.

Name each version so the difference survives without the unit. For example, record "Neutrophils %" and "Neutrophils, absolute" as two analytes under the same CBC panel ID. Keep the unit with each one as printed. When the report prints only one version, extract only that one. Do not compute the missing version from the total white count, because that creates a value the lab never reported.

Differentials also expose table layout problems. Cell names wrap onto two lines, and labs merge cells to group the percentage and count columns. The guide to wrapped test names and merged cells in lab report tables explains how to read those rows.

Should calculated panel values like anion gap and eGFR be extracted?

Calculated panel values like anion gap and eGFR should be extracted exactly as printed, as their own analytes, and marked as calculated if the report says so. Do not recompute them from the other values. The lab chose the formula and the inputs, and the printed number is the value the lab reported.

A calculated value is a result the lab works out from other results rather than measuring directly. The MedlinePlus page on the anion gap blood test says the anion gap "is a calculation that's done with the results of an electrolyte panel." The MedlinePlus page on the glomerular filtration rate test says creatinine levels alone cannot determine eGFR, and that the information is put into a formula to find it. That page names age, weight, height and sex among the inputs.

Those inputs are why recomputing is a mistake. Your extracted data may not hold every input, and formulas differ. A recomputed number that disagrees with the printed one creates two versions of the same result, and only one of them came from the lab.

The FHIR Observation page gives a way to record the link without recomputing. Its derivedFrom element points to "a measurement from which this observation value is derived," and gives "a calculated anion gap" as its example. In a simpler layout, add a "calculated" flag to the analyte record and note which panel analytes it depends on.

Calculated values also change the completeness count. If the lab prints an anion gap or eGFR under the CMP heading, the panel on the page has more rows than the 14 in the MedlinePlus list. Decide whether you count printed rows or a fixed list, and apply that rule every time. Counting printed rows is safer, because it follows what the lab actually sent.

A completeness check for extracted CBC and CMP panels

A completeness check for extracted CBC and CMP panels compares the number of analytes extracted with the number printed under the panel heading on the page. If the counts differ, a row was dropped or added. Run the check on every panel before the values are filed, and send any panel that fails to a person.

The check has three parts:

  1. Count the printed rows. Count every result row under the panel heading on the page, including calculated values and both versions of each differential cell.
  2. Count the extracted analytes. Count the analyte records that carry this panel's ID.
  3. Compare and list. If the counts match, record the match. If not, list which analytes are missing or extra by name.

A fixed expected list helps as a second check. For a CMP, the 14 tests in the MedlinePlus list should all be present unless the report says one was not performed. A CMP that comes out with 13 analytes and no note on the page usually means a row was dropped during extraction, often a short row such as BUN that sat next to a footnote.

What a failed check looks like

Here is a fictional failure. A CMP for Riley Example prints 15 rows: the 14 standard tests plus an eGFR line. The extracted data holds 14 analytes. The comparison shows eGFR present and chloride missing. The chloride row was printed on a line that wrapped, and the extractor joined it to the row above. A reviewer opens the page, sees the chloride row, enters it, and the panel passes at 15 of 15.

Panels across several dates

Some reports show the same panel on several collection dates in one grid. Each date is its own panel with its own ID, and the count check runs once per date. The guide to cumulative lab report extraction covers how to turn that grid into one panel per date.

The panels that fail the check are the ones to put in front of a person. How review queues and spot checks work in general is covered in the human review category. Schema Care gives each extracted lab field a confidence score and a link to its place on the page, and fields below your threshold wait for a reviewer, as the review workflow explains.

All posts in Lab Reports