You upload
Source files + documents
Files stay as delivered. Nothing edits them.
How PK/PD source files become analysis tables, and what the formatter does at the six places data preparation usually goes wrong.
You upload
Files stay as delivered. Nothing edits them.
1 · An agent annotates
pk: source: pc.csv value: PCSTRESN unit: PCSTRESU time: PCDTC lloq: 5 ng/mL
Which file feeds which table, which column plays which role, units, identifier patterns, rules. It can read the files. It cannot change a value or produce a table. When the data and documents cannot answer, it asks you.
2 · A library transforms
Deterministic: the same inputs give the same tables on every run. Every step that changes data is written to a record of transformations with row counts. No guessing: what it cannot parse becomes a stated failure or a row marked not eligible, with a reason.
3 · A library of checks
| value | eligible | reason |
|---|---|---|
| 0.246 | yes | |
| — | no | below LLOQ |
Named, versioned checks run once on the whole formatted dataset and raise issues on a subject, a record or a field. A failing row is kept and marked, so a reviewer can find it. Twenty-one checks of the configuration against the data already run while the agent works.
4 · Export
Every study leaves in the same canonical layout: the same tables, columns and units, one set per time basis. A named output preset states what a house format needs, such as a NONMEM-ready file, and the configuration is checked against it before any data is read.
Its only output is the configuration, which you can read line by line.
Unknown unit, unknown dosing label, unplaceable time: failure or a flagged row.
Each question and answer goes to an append-only log beside the configuration.
Eligibility for modeling is a flag with a reason, before and after quality checks.
What goes wrong
You seeSubjects removed, row count, source files, and the two remedies: supply the demographics, or exclude the subject explicitly.
| file | as written | pattern |
|---|---|---|
| dm.csv | ABC-101-0007 | {study}-{site:03d}-{subject:04d} |
| pc.csv | 1010007 | {site:03d}{subject:04d} |
| ex.csv | 101-007 | {site:03d}-{subject:03d} |
| lb.xlsx | SITE 101 · SUBJ 7 (integers) | two columns joined, then {site:d}-{subject:d} |
study template {site:03d}-{subject:04d}→canonical ID 101-0007→stable subject keyLeading zeros, separators and study prefixes are surface form. The parts are read as numbers, so 007, 0007 and 7 are one subject. All joins use the key, never the raw strings.
| key | value |
|---|---|
| 0007·D1·2h | 0.494 |
| key | value |
|---|---|
| 0007·D14·2h | 0.681 |
| 0007·D1·2h | 0.510 |
stops · key 0007·D1·2h is in both files
The row key here is subject · day · time point. The April file re-sends the Day 1, 2 h sample for subject 0007 with a different value, so the run stops and lists that key instead of keeping both rows or picking one.
What goes wrong
You seeThe canonical unit and how many rows were converted from each source unit. A reason on each row that failed its unit rule. A question when the unit is not in the data at all.
| cohort | dose | concentration | LLOQ |
|---|---|---|---|
| A · single dose, lab 1 | 100 mg | 840 ng/mL | 5 ng/mL |
| B · multiple dose, lab 2 | 100 mg | 0.84 µg/mL | 0.01 µg/mL |
| C · microdose, lab 3 | 250 µg | 2.1 µg/L | 0.05 µg/L |
| cohort | dose (mg) | conc. (mg/L) | LLOQ (mg/L) | eligible |
|---|---|---|---|---|
| A | 100 | 0.84 | 0.005 | yes |
| B | 100 | 0.84 | 0.01 | yes |
| C | 0.25 | 0.0021 | 0.00005 | yes |
Each cohort keeps its own LLOQ, scaled with its values. With dose in mg and concentration in mg/L, clearance and volume come out in L/h and L with no hidden factor of 1000.
| reported | formatted | |
|---|---|---|
| conc. 7 U | 7 (as reported) | kept · not eligible · unit not parsed |
| dose 2 mg/kg | 2 mg/kg | kept · not eligible |
| rate 10 mg/min | 600 mg/h | a rate must state its unit |
| rate 10 (no unit) | never assumed per hour |
Every dose and sample can carry an observed time (when it happened) and anominal time (when the protocol planned it). Studies rarely record both for both. The formatter puts every recorded time on one clock per subject, sets zero at that subject's first dose, and publishes up to four bases. Each row is eligible or not on each basis, with a reason.
| recorded as | absolute | published |
|---|---|---|
| 2026-03-04T08:30 | 03-04 08:30 | 48.5 h |
| study day 3, 14:30 | 03-04 14:30 | 54.5 h |
| 26 h since randomisation | 03-02 10:00 | 2.0 h |
| basis | sample time from | dose time from |
|---|---|---|
| observed | recorded | recorded |
| nominal | planned | planned |
| nominal offsets | planned offset | recorded |
| pre-dose anchored | recorded | pre-dose PK draw |
The dosing file has clock times. The PK file has only labels such as "2 H post-dose".
Planned offsets are added to each subject's observed dose time, so a late dose moves its samples with it.
Samples have clock times. Doses have a date and a regimen, no clock.
The dose clock is taken from that day's pre-dose PK draw. Sample times stay as recorded.
Each row has an actual date-time and a planned label. They disagree by minutes.
Both are kept, in separate columns, so the deviation stays visible. The recommended basis is the one with the most PK-eligible subjects.
A sample is recorded with a date and no time of day.
Hours are published as missing and the day is kept; it is not read as midnight. Row reason: "its time is known only to the day".
Later doses are timed, the first is not. Time zero is unknown.
The subject gets no observed times at all. Zero is not moved to the second dose to make the numbers work.
An extra draw between planned visits, often forced onto the nearest planned time.
Rows declared unscheduled get no planned time. Nothing is invented for them, and they stay usable at their real time.
Exports are always in the canonical format, one set of tables per basis.
You seePer table and basis: whether the time annotation is complete, how many rows and subjects lack a time, and which input is missing. A reason on each affected row. A recommended basis: the one with the most PK-eligible subjects.
What goes wrong
You seeThe question, e.g. "2 subjects have more than one row and the rows disagree on weight". The method applied, with row counts before and after.
| subject | weight | unit |
|---|---|---|
| 101-0007 | 72.0 | kg |
| 101-0007 | 72.0 | kg |
| 101-0012 | 81.5 | kg |
| 101-0012 | 84.0 | kg |
The stop message names the table, the columns that disagree and the number of subjects. It carries no data values. The agent may not choose.
| type | example | violates |
|---|---|---|
| continuous | age 54 | |
| positive continuous | weight 72.0 | −72.0 |
| count | prior lines 2 | 2.5 |
| categorical | race: declared set | value outside the set |
| binary | sex F / M | third value |
What goes wrong
You seeThe expansion decision in the record of transformations, the overlap warning, "zero dose" on hold rows, and one question, asked once, when a multi-dose study states no interval anywhere.
| amount | cadence | start | end |
|---|---|---|---|
| 100 mg | BID | Day 1 09:00 | Day 7 09:00 |
| source says | formatter emits |
|---|---|
| hold, or amount 0 | zero-dose rows at their slots · not eligible · later doses unaffected |
| reduction to 50 mg | expands at the amount actually given |
| two records, same time, different amounts | later-starting record wins + warning (or stop, if the study says so) |
| cadence, no end date | left unexpanded, recorded |
| label "Q36H-ALT" not known | format fails, naming the label |
| dose never recorded | stays a gap · nothing is synthesised |
What goes wrong
You seeThe inputs, the formula and, for the longitudinal form, the pairing rule. Missing results where an input was missing or ambiguous, with a count.
| input | reported | used |
|---|---|---|
| creatinine | 88.4 µmol/L | 1.0 mg/dL (÷ 88.4) |
| age | 93 | 89 (capped) |
| sex | F | female |
| height | weight |
|---|---|
| 170 cm | 70 kg |
| form | gives | stored as |
|---|---|---|
| baseline | one value per subject, from baseline inputs | covariate |
| longitudinal | one value per creatinine occasion, age advanced, no carry-forward | its own lab marker |