This workflow starts with physical-slide measurements. It does not read native whole slides, train a DNN or validate a candidate as one erythrocyte. The reusable package preserves distinctions needed for those later stages: measured red area, spot candidates, unresolved bulk events, explicit support and provenance. Software validation checks contracts and arithmetic, not biological accuracy.
The examples use deterministic synthetic data. One record is deliberately missing signal measurements and another has zero measured tissue support.
results <- bs_example()
bs_validate(results)
results$slides[, c("physical_slide_id", "organ", "tissue_area_mm2",
"red_area_mm2", "candidate_spots")]
#> physical_slide_id organ tissue_area_mm2 red_area_mm2 candidate_spots
#> 1 DEMO-001 Liver 10 0.00 0
#> 2 DEMO-002 Liver 20 0.20 110
#> 3 DEMO-003 Liver 15 0.45 240
#> 4 DEMO-004 Liver 0 0.00 0
#> 5 DEMO-005 Spleen 12 1.20 1200
#> 6 DEMO-006 Spleen 18 2.70 2700
#> 7 DEMO-007 Spleen 25 1.00 800
#> 8 DEMO-008 Spleen 16 NA NACanonical tables identify physical slides with nonempty character strings, areas in square millimetres and nonnegative whole-valued counts. The operational count is candidate spots plus unresolved bulk events. Counts use numeric storage and must remain exactly representable within the documented double-precision bound. Existing QC/organ labels are metadata, never inferred from signal strength.
A group’s red-positive fraction is
100 * sum(red_area_mm2) / sum(tissue_area_mm2). Its
candidate density uses the same tissue denominator. Pooled rates are not
means of slide rates: a small and a large slide contribute different
support.
by_organ <- bs_summarize(results, strata = "organ")
by_organ[, c("organ", "n_slides", "tissue_area_mm2", "red_area_mm2",
"red_percent", "spots_per_mm2")]
#> organ n_slides tissue_area_mm2 red_area_mm2 red_percent spots_per_mm2
#> 1 Liver 4 45 0.65 1.444444 7.777778
#> 2 Spleen 4 71 NA NA NA
is.na(by_organ$red_percent[by_organ$organ == "Spleen"])
#> [1] TRUEThe spleen summary is missing because one contributing measurement is missing. It would be misleading to silently sum only its known values while retaining the full tissue denominator. A true zero numerator on positive support is zero; zero or unknown support gives a missing rate. Excluding incomplete or compromised slides requires a declared analysis decision and a retained membership record.
Group jointly by tissue and the supplied quality strata:
results$groups <- bs_summarize(results, strata = c("organ", "stratum"))
results$groups[, c("organ", "stratum", "n_slides", "red_percent")]
#> organ stratum n_slides red_percent
#> 1 Liver No substantial finding 1 0
#> 2 Liver Structure only 1 1
#> 3 Liver Raster only 1 3
#> 4 Liver Uncertain 1 NA
#> 5 Spleen No substantial finding 1 10
#> 6 Spleen Structure only 1 15
#> 7 Spleen Raster only 1 4
#> 8 Spleen Uncertain 1 NAOnly observed combinations appear. slide_ids retains
membership. A qc_revision can replace metadata when it
uniquely covers every measured slide; it cannot replace numerical
measurements. Historical study-specific thresholds and exclusion
decisions are not imposed by this package.
The legacy importer accepts a canonical CSV or a saved
analysis.json with a rows table. The
p079 profile also maps tissue_mm2 and
red_mm2; it does not assign controls or invent missing
calibration. Source file identity is hashed.
source_file <- tempfile(fileext = ".csv")
utils::write.csv(results$slides, source_file, row.names = FALSE, na = "")
imported <- bs_import_legacy(source_file, profile = "canonical")
identical(imported$slides$physical_slide_id, results$slides$physical_slide_id)
#> [1] TRUE
bs_status(imported)
#> $kind
#> [1] "results"
#>
#> $schema_version
#> [1] "1.0"
#>
#> $slides
#> [1] 8
#>
#> $scientific_status
#> [1] "exploratory"
#>
#> $source
#> $source$path
#> [1] "/tmp/Rtmpokgo19/fileed0666c26e4.csv"
#>
#> $source$sha256
#> [1] "55e5b25a3e459947f41aaad2c3c6cc109bb5b6fa1fc49f5a7a1c707aa3eb8123"
unlink(source_file)The source hash is an integrity receipt, not proof of authentic
acquisition or independent biological validation. Canonical result JSON
exported by the package contains a schema-versioned slides
table for portable exchange. The importer accepts that format as well as
the historical rows format.
Equal slide IDs alone do not prove equal measurement support. Here
both runs explicitly reference the same synthetic support identity, so
paired changes can be computed. Without matching support,
support = "common" fails; the union view preserves
unmatched records with unverifiable differences missing.
first <- results
second <- results
first$slides$support_id <- paste0("synthetic-support-", first$slides$physical_slide_id)
second$slides$support_id <- first$slides$support_id
second$slides$red_area_mm2[2] <- 0.25
comparison <- bs_compare(list(baseline = first, comparison = second))
comparison[, c("physical_slide_id", "support_verified", "red_area_mm2_delta")]
#> physical_slide_id support_verified red_area_mm2_delta
#> 1 DEMO-001 TRUE 0.00
#> 2 DEMO-002 TRUE 0.05
#> 3 DEMO-003 TRUE 0.00
#> 4 DEMO-004 TRUE 0.00
#> 5 DEMO-005 TRUE 0.00
#> 6 DEMO-006 TRUE 0.00
#> 7 DEMO-007 TRUE 0.00
#> 8 DEMO-008 TRUE NA
results$comparison <- comparisonEach delivery uses a new destination directory. Existing deliveries are never overwritten; the parent directory must exist. HTML text is escaped, exports keep missing values, and a SHA-256 manifest records generated files.
delivery <- tempfile("bloodspottr-example-report-")
report <- bs_report(results, delivery, author = "RH",
background = "Synthetic demonstration; no biological inference.")
list.files(delivery)
#> [1] "Comparison.csv" "Groups.csv" "Methods.csv" "report.html"
#> [5] "results.json" "SHA256SUMS" "Slides.csv"
file.exists(c(report, file.path(delivery, "SHA256SUMS")))
#> [1] TRUE TRUEbs_export_results(results, out) writes tables without
HTML. Set xlsx = TRUE for optional
openxlsx2 workbook output. The initial renderer does
not embed raw image masks, native detail crops, training weights or
individual events; the study-specific TeX/PDF supplement is a separate
artifact.
bs_app(results) # Local interactive session
app <- bs_app(launch = FALSE) # Empty application object; explicit demo buttonThe application filters by tissue, recorded QC stratum and literal slide ID. Pooled cards and plots follow the selected rows; the source object is unchanged. It exposes exact measurements, supplied comparisons, provenance and filtered CSV exports. Its explicit demonstration label prevents synthetic values from being mistaken for a loaded study. Browser uploads accept CSV/JSON, not executable serialized objects. It does not launch a GPU backend or claim native annotation editing or model training controls.