End-to-end data pipeline documentation covering the complete flow from raw data sources through transformations to API responses.
- Overview
- Reference Data Pipeline
- Runtime Data Flow
- SIR Interpretation Flow
- Antibiogram Generation Flow
- Run Persistence Flow
- Data Refresh Flow
- Error and Dead-Letter Flow
- Data Transformations
Python AMR processes two distinct types of data:
- Reference Data - Static datasets (microorganisms, antimicrobials, breakpoints)
- Runtime Data - User inputs and analytical results
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Reference Data Pipeline β
β External β Import β NDJSON β Transforms β Parquet β
β Sources β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Application Layer β
β API/CLI β Core Logic β Reference Store β Results β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Persistence Pipeline β
β Results β Queue β Repository β Database β Storage β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
External Sources (TSV/CSV/XLSX/RDA)
β
βββΊ data-raw/external/
β β’ Loinc.csv (200 MB)
β β’ SNOMED_*.txt (5 MB)
β β’ GBIF Taxon.tsv (1 GB)
β β’ EUCAST breakpoints
β
βΌ
Import Scripts (scripts/import_raw_sources.py)
β
βββΊ Format detection and parsing
βββΊ Schema validation
βββΊ Deduplication
β
βΌ
Canonical NDJSON (data-raw/sources/)
β
βββΊ microorganisms.ndjson (75k records)
βββΊ antimicrobials.ndjson (465 records)
βββΊ antivirals.ndjson (85 records)
βββΊ clinical_breakpoints.ndjson (50k records)
βββΊ interpretive_rules.ndjson (1.3k records)
βββΊ intrinsic_resistant.ndjson (10k records)
β
βΌ
Transform Pipeline (scripts/run_data_pipeline.py)
β
βββΊ Load NDJSON with Polars
βββΊ Apply dataset-specific transforms
βββΊ Add computed fields
βββΊ Fix data quality issues
β
βΌ
Parquet Snapshots (data/snapshots/)
β
βββΊ microorganisms.parquet
βββΊ antimicrobials.parquet
βββΊ antivirals.parquet
βββΊ clinical_breakpoints.parquet
βββΊ interpretive_rules.parquet
βββΊ intrinsic_resistant.parquet
β
βΌ
Snapshot Manifest (data/manifests/snapshot_manifest.json)
β
βββΊ Metadata, checksums, schemas
# scripts/import_raw_sources.py
def import_external_sources():
"""
Import external formats to canonical NDJSON.
Supported formats:
- CSV/TSV (Polars)
- XLSX (pandas + openpyxl)
- Feather/IPC (Polars)
- Parquet (Polars)
- RDA (via feather intermediate)
"""
mapping = load_import_mapping() # data-raw/import_mapping.json
for dataset_name, source_file in mapping.items():
# Detect format
format = detect_format(source_file)
# Parse with appropriate loader
df = parse_file(source_file, format)
# Validate schema
validate_schema(df, dataset_name)
# Write NDJSON
output_path = f"data-raw/sources/{dataset_name}.ndjson"
df.write_ndjson(output_path)# scripts/run_data_pipeline.py
from amr.data.pipeline import DataPipeline
from amr.data.transforms import TRANSFORM_REGISTRY
def run_pipeline():
"""
Execute transformation pipeline.
Order defined in data-raw/pipeline.json
"""
pipeline = DataPipeline(
raw_dir="data-raw/sources",
snapshot_dir="data/snapshots"
)
# Load execution order
execution_order = load_pipeline_config()
for dataset_name in execution_order:
# Load NDJSON
df = pipeline.load_raw(dataset_name)
# Apply transforms
transform = TRANSFORM_REGISTRY.get(dataset_name)
if transform:
df = transform.apply(df)
# Write snapshot
pipeline.write_snapshot(dataset_name, df)
# Build manifest
pipeline.build_manifest()Microorganisms Transform:
# src/amr/data/transforms/microorganisms.py
class MicroorganismsTransform:
"""Transform microorganisms dataset."""
def apply(self, df: pl.DataFrame) -> pl.DataFrame:
"""
Apply transformations:
1. Add fullname column (genus + species)
2. Normalize prevalence scores
3. Add rank_index for sorting
4. Validate taxonomy hierarchy
"""
return (
df
.with_columns([
# Combine genus and species
(pl.col("genus") + " " + pl.col("species")).alias("fullname"),
# Normalize prevalence
(pl.col("prevalence") / pl.col("prevalence").max()).alias("prevalence_norm"),
# Add rank index
pl.col("rank").map_dict({
"kingdom": 1,
"phylum": 2,
"class": 3,
"order": 4,
"family": 5,
"genus": 6,
"species": 7,
"subspecies": 8
}).alias("rank_index")
])
.filter(pl.col("genus").is_not_null()) # Remove invalid entries
)Clinical Breakpoints Transform:
# src/amr/data/transforms/clinical_breakpoints.py
class ClinicalBreakpointsTransform:
"""Transform clinical breakpoints dataset."""
def apply(self, df: pl.DataFrame) -> pl.DataFrame:
"""
Apply transformations:
1. Parse MIC/disk breakpoint ranges
2. Add host_type (human/animal)
3. Validate guideline consistency
4. Add effective_date
"""
return (
df
.with_columns([
# Parse MIC breakpoints
pl.col("breakpoint_S").cast(pl.Float64, strict=False),
pl.col("breakpoint_R").cast(pl.Float64, strict=False),
# Infer host type
pl.when(pl.col("host").is_not_null())
.then(pl.lit("animal"))
.otherwise(pl.lit("human"))
.alias("host_type"),
# Extract year from guideline
pl.col("guideline")
.str.extract(r"(\d{4})", 1)
.cast(pl.Int32, strict=False)
.alias("guideline_year")
])
.filter(
# Remove invalid breakpoints
(pl.col("breakpoint_S").is_not_null()) |
(pl.col("breakpoint_R").is_not_null())
)
)βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. HTTP Request β
β POST /v1/sir/interpret β
β {values: [0.5, 2, 16], mo: "B_ESCHR_COLI", ...} β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. FastAPI Handler β
β β’ Pydantic validation β
β β’ Request parsing β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. Core Logic (amr.core.interpretation) β
β β’ Load breakpoints from reference store β
β β’ Apply interpretation rules β
β β’ Return SIR results β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4. Persistence Queue (optional) β
β β’ Enqueue run data β
β β’ Return optimistic run_id β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 5. HTTP Response β
β {results: ["S", "S", "R"], run_id: 123} β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
User Input: values=[0.5, 2, 16], mo="B_ESCHR_COLI", ab="CIP"
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β 1. Normalize Inputs β
β β’ mo β "B_ESCHR_COLI" (if needed) β
β β’ ab β "CIP" (if needed) β
β β’ Detect method (MIC/disk) β
ββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β 2. Load Reference Data β
β ReferenceStore.get_breakpoints() β
β βββΊ Returns Polars DataFrame β
ββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β 3. Filter Breakpoints β
β df.filter( β
β (pl.col("mo") == mo) & β
β (pl.col("ab") == ab) & β
β (pl.col("guideline") == guide) β
β ) β
ββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β 4. Apply Breakpoint Logic β
β For each value: β
β if value <= S_breakpoint: "S" β
β elif value >= R_breakpoint: "R" β
β else: "I" β
ββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β 5. Apply Interpretive Rules (optional) β
β β’ EUCAST expert rules β
β β’ Intrinsic resistance β
β β’ Disk/MIC discrepancy resolution β
ββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
Output: ["S", "S", "R"]
# src/amr/core/interpretation.py
def as_sir(
values: list,
mo: str | None,
ab: str | None,
guideline: str = "EUCAST 2025",
method: str | None = None,
add_intrinsic_resistance: bool = True,
interpretive_rules: str | None = None
) -> list[str]:
"""
Interpret MIC/disk values to SIR categories.
Data flow:
1. Input validation
2. Reference data lookup
3. Breakpoint application
4. Rule application
5. Result return
"""
# 1. Validate inputs
if not values:
raise ValueError("values cannot be empty")
# 2. Load breakpoints
from amr.data.reference_store import get_breakpoints
breakpoints_df = get_breakpoints()
# 3. Filter to relevant breakpoints
filtered = breakpoints_df.filter(
(pl.col("mo") == mo) &
(pl.col("ab") == ab) &
(pl.col("guideline") == guideline) &
(pl.col("method") == (method or "MIC"))
)
if filtered.height == 0:
raise ValueError(f"No breakpoints found for {mo}/{ab}/{guideline}")
# 4. Extract breakpoint values
s_breakpoint = filtered["breakpoint_S"][0]
r_breakpoint = filtered["breakpoint_R"][0]
# 5. Apply breakpoints
results = []
for value in values:
if value <= s_breakpoint:
sir = "S"
elif value >= r_breakpoint:
sir = "R"
else:
sir = "I"
results.append(sir)
# 6. Apply interpretive rules
if interpretive_rules == "EUCAST":
results = apply_eucast_rules(results, mo, ab)
if add_intrinsic_resistance:
results = apply_intrinsic_resistance(results, mo, ab)
return resultsInput DataFrame:
patient_id | organism | AMX | CIP | GEN
1 | E. coli | R | S | S
2 | E. coli | S | S | S
3 | K. pneumo | R | R | S
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β 1. Normalize Organisms β
β organism β mo_code via as_mo() β
ββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β 2. Group by Pathogen β
β df.group_by("mo_code") β
ββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β 3. Calculate Resistance % β
β For each (pathogen, antimicrobial): β
β n_resistant = count(SIR == "R") β
β n_total = count(*) β
β pct = 100 * n_resistant / n_total β
ββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β 4. Filter by Minimum β
β Keep only pathogens with n >= min β
ββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β 5. Compute Confidence Intervals β
β Wilson score interval for each % β
ββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βΌ
Output DataFrame:
microorganism | AMX | CIP | GEN
Escherichia coli | 50.0 | 0.0 | 0.0
K. pneumoniae | 100 | 100 | 0.0
API Handler
β
β enqueue_persist(run_data)
βΌ
βββββββββββββββββββββββββββββββββββ
β AsyncQueue (4096 max) β
β βββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ β
β β 1 β 2 β 3 β 4 β 5 β...β β
β βββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ β
βββββββββββ¬ββββββββββββββββββββββββ
β
β queue.get()
βΌ
βββββββββββββββββββββββββββββββββββ
β Worker (async coroutine) β
β β’ Retry loop (max 3) β
β β’ Exponential backoff β
β β’ Error handling β
βββββββββββ¬ββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββ
β RunRepository β
β β’ save_run_async() β
βββββββββββ¬ββββββββββββββββββββββββ
β
ββββΊ SQLite/Postgres (metadata)
β INSERT INTO runs (...)
β
ββββΊ DuckDB (output rows)
INSERT INTO run_outputs (...)
# Input (API request)
{
"values": [0.5, 2, 16],
"microorganism": "B_ESCHR_COLI",
"antimicrobial": "CIP",
"guideline": "EUCAST 2025"
}
# Transformed for persistence
{
"run_type": "sir",
"input_data": {
"values": [0.5, 2, 16],
"microorganism": "B_ESCHR_COLI",
"antimicrobial": "CIP",
"guideline": "EUCAST 2025"
},
"output_data": {
"results": ["S", "S", "R"]
},
"row_count": 3,
"created_at": "2026-02-15T12:00:00Z",
"metadata": {
"source": "lab_system",
"batch_id": "2026-02-15-001"
}
}
# Stored in database
runs table:
id | run_type | row_count | created_at | metadata_json
run_outputs table (DuckDB):
run_id | row_index | input_value | sir_result
123 | 0 | 0.5 | S
123 | 1 | 2.0 | S
123 | 2 | 16.0 | RExternal Source
β
βββΊ Download Loinc.csv (200 MB)
β from loinc.org
βΌ
βββββββββββββββββββββββββββββββββββ
β scripts/refresh_loinc.py β
β β’ Parse Loinc.csv β
β β’ Filter DRUG/TOX classes β
β β’ Normalize names β
βββββββββββ¬ββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββ
β Load antimicrobials.ndjson β
β β’ Read existing data β
βββββββββββ¬ββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββ
β Fuzzy Match β
β β’ Normalize both sides β
β β’ Match by name similarity β
β β’ Add LOINC codes β
βββββββββββ¬ββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββ
β Write antimicrobials.ndjson β
β β’ Preserve existing data β
β β’ Add 'loinc' field β
βββββββββββ¬ββββββββββββββββββββββββ
β
βΌ
Run Data Pipeline
βββΊ Rebuild snapshots
Queue Worker
β
β save_run_async(run_data)
βΌ
βββββββββββββββββββββββββββββββββββ
β Try: Save to database β
βββββββββββ¬ββββββββββββββββββββββββ
β
βββΊ Success
β βββΊ Increment counter
β
βββΊ Retriable Error (transient)
β βββΊ Retry with backoff
β βββΊ Retry 1 (50ms delay)
β βββΊ Retry 2 (100ms delay)
β βββΊ Retry 3 (200ms delay)
β
βββΊ Permanent Failure
βββΊ Save to Dead Letter Queue
β
βΌ
βββββββββββββββββββββββββββββββββββ
β dead_letters table β
β β’ Redact sensitive data β
β β’ Truncate large payloads β
β β’ Store error message β
β β’ Add timestamp β
βββββββββββββββββββββββββββββββββββ
df.with_columns([
pl.col("numeric_field").cast(pl.Float64, strict=False),
pl.col("date_field").str.strptime(pl.Date, "%Y-%m-%d"),
pl.col("boolean_field").cast(pl.Boolean)
])df.with_columns([
pl.col("field").fill_null("DEFAULT_VALUE"),
pl.when(pl.col("field").is_null())
.then(pl.lit("N/A"))
.otherwise(pl.col("field"))
.alias("field_safe")
])df.with_columns([
pl.col("name")
.str.to_uppercase()
.str.strip_chars()
.str.replace_all(r"\s+", " ")
.alias("name_normalized")
])df.with_columns([
(pl.col("genus") + " " + pl.col("species")).alias("fullname"),
(pl.col("resistance_count") / pl.col("total_count") * 100).alias("resistance_pct")
])- Architecture Overview - System design
- Data Contracts - Schema specifications
- Data Pipeline Deep Dive - Transform details
- Data Refresh Guide - Refresh procedures
Last Updated: 2026-02-15