Using the RDML7 package

RDML7 authors

2026-10-14

Introduction

The RDML7 package provides tools for importing, exporting, validating, inspecting, modifying, and converting quantitative PCR (qPCR) data represented by the Real-time PCR Data Markup Language (RDML).

RDML stores both fluorescence measurements and experimental metadata in a structured format. The format is designed to make qPCR experiments easier to exchange, reproduce, and reanalyse.

RDML7 is a major redesign of the original RDML R package described in:

Rödiger S, Burdukiewicz M, Spiess A-N, Blagodatskikh K. Enabling reproducible real-time quantitative PCR research: the RDML package. Bioinformatics 33(24), 4012–4014. https://doi.org/10.1093/bioinformatics/btx528

The original package used an R6 object model. RDML7 uses S7 classes and a primarily functional API. The main workflow is now:

file
  |
  v
readRDML()
  |
  v
rdmlType
  |
  +---- asTable() -------- metadata / curve index
  |
  +---- getFData() ------- fluorescence data
  |
  +---- setFData() ------- add or replace fluorescence data
  |
  +---- summary() ---- summary
  |
  +---- validateRDML() --- validation
  |
  +---- asDendrogram() --- hierarchy
  |
  +---- mergeRDMLs() ----- merge RDML objects
  |
  +---- writeRDML() ------ RDML / XML / RDES / other registered formats

The old names such as AsTable(), GetFData(), SetFData(), AsDendrogram(), asXML(), and MergeRDMLs() are retained as compatibility wrappers where possible. New code should use the camelCase API.

Main changes from the original RDML package

The most important changes are:

  1. S7 object model instead of R6.
  2. Value semantics: functions that modify an object return a modified object, which should normally be assigned back.
  3. CamelCase functional API, for example asTable() and getFData().
  4. Centralised file I/O with readRDML() and writeRDML().
  5. Extensible format registry with automatic format detection.
  6. Direct support for importer modules through a canonical intermediate representation.
  7. Validation and summary functions for RDML objects.
  8. RDES import/export in addition to RDML and vendor/table formats.
  9. A revised asTable() interface with documented fields and reusable {field} name templates.

A migration summary is shown below.

Original API RDML7 API
RDML$new(filename = file) readRDML(file)
RDML$new() rdmlType()
x$AsTable() / AsTable(x) asTable(x)
x$GetFData() / GetFData(x) getFData(x)
x$SetFData() / SetFData(x) x <- setFData(x, ...)
x$AsDendrogram() / AsDendrogram(x) asDendrogram(x)
x$asXML() asXML(x) or writeRDML(x, ...)
MergeRDMLs(...) mergeRDMLs(...)
file-specific constructor logic readRDML() + format registry

Loading RDML7

library(RDML7)

The package contains example RDML files that can be used throughout this vignette.

filename <- system.file(
  "extdata",
  "lc96_bACTXY.rdml",
  package = "RDML7"
)

filename
#> [1] "C:/tmp/RtmpKYPudu/Rinst45747907173b/RDML7/extdata/lc96_bACTXY.rdml"

Reading files

readRDML()

Use readRDML() as the main entry point for file import.

lc96 <- readRDML(filename)
#> 
#> Loading experiment: ca1eb225-ecea-4793-9804-87bfbb45f81d
#>  run: 65aeb1ec-b377-4ef6-b03f-92898d47488b

The default format selection is automatic. If a format is ambiguous, it can be specified explicitly.

lc96 <- readRDML(
  filename,
  format = "rdml"
)

This replaces the old pattern:

# Original RDML package
# lc96 <- RDML$new(filename = filename)

Discovering available file formats

The set of available formats is no longer hard-coded into the user-facing import function. It is stored in a registry.

formats <- listRDMLFormats()
formats
#>         format    extensions read write
#> 1          abi           eds TRUE FALSE
#> 2    rotorgene           rex TRUE FALSE
#> 3        excel     xlsx, xls TRUE FALSE
#> 4          csv           csv TRUE FALSE
#> 5      dtprime           r96 TRUE FALSE
#> 6          fqd           txt TRUE FALSE
#> 7  innova-qstd          qstd TRUE FALSE
#> 8         rdes tsv, csv, txt TRUE  TRUE
#> 9     rdml-xml           xml TRUE  TRUE
#> 10        rdml     rdml, rdm TRUE  TRUE
#> 11  roche-lc96         lc96p TRUE FALSE

listRDMLFormats() reports, among other things, the registered format name, extensions, aliases, whether reading/writing/sniffing is available, the priority, API version, capabilities, and whether the handler is built in.

To see which reader or writer would be selected for a particular path:

detectRDMLFormat(
  filename,
  operation = "read"
)
#> [1] "rdml"

RDML7 first uses the extension and, when necessary, registered format sniffers. If several handlers remain equally suitable, use format = explicitly.

The repository includes readers for RDML/XML and several vendor or table formats. The exact supported set should be queried with listRDMLFormats() rather than assumed from a fixed list.

RDML7 object structure

RDML7 mirrors the hierarchy of the RDML schema. The principal top-level fields include:

The important hierarchy containing fluorescence data is approximately:

rdmlType
  `-- experiment
       `-- run
            `-- react
                 `-- data
                      |-- adp   amplification data points
                      `-- mdp   melting data points

S7 property access

RDML7 schema objects are S7 objects. Use $ to access their properties.

lc96$version
#> [1] "1.3"

lc96$experiment[[1]]$id$id
#> [1] "ca1eb225-ecea-4793-9804-87bfbb45f81d"

Collections such as experiments, runs, samples, and targets are keyed by their RDML identifiers. This makes it possible to inspect identifiers first and then select an element.

expname <- names(lc96$experiment)[1]

experiment <- lc96$experiment[[expname]]

names(experiment$run)
#> [1] "65aeb1ec-b377-4ef6-b03f-92898d47488b"

For routine data analysis, direct traversal of the full hierarchy is usually unnecessary. asTable() and getFData() provide a flatter interface.

Summarising and validating RDML data

summary()

Use summary() for a compact overview of an RDML object.

summary(lc96)

This is preferable to manually counting nested experiments, runs, reactions, targets, and curve types.

validateRDML() and isValidRDML()

RDML7 provides validation (as for RDML 1.3 version) functions in addition to S7 property validators.

validateRDML(lc96)

isValidRDML(lc96)

Use validation before exporting data or after programmatically modifying a large RDML object.

Visualising the hierarchy

asDendrogram() creates a dendrogram representation of the experiment structure.

asDendrogram(lc96)

This is the RDML7 replacement for the old x$AsDendrogram() method.

Creating a table representation with asTable()

asTable() creates one metadata row per RDML dataType. This table is the main bridge between the hierarchical RDML object and ordinary tabular R workflows.

tbl <- asTable(lc96)

head(tbl)
#> Ключ: «fdataName»
#>                 fdataName                                expId
#>                    <char>                               <char>
#> 1:  D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 2:    D03_Sample 39_std_X ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 3:    D03_Sample 39_std_Y ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 4: D03_Sample 39_std_bACT ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 5:  D04_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 6:    D04_Sample 39_std_X ca1eb225-ecea-4793-9804-87bfbb45f81d
#>                                   runId reactId position    sample target
#>                                  <char>  <char>   <char>    <char> <char>
#> 1: 65aeb1ec-b377-4ef6-b03f-92898d47488b      39      D03 Sample 39    IPC
#> 2: 65aeb1ec-b377-4ef6-b03f-92898d47488b      39      D03 Sample 39      X
#> 3: 65aeb1ec-b377-4ef6-b03f-92898d47488b      39      D03 Sample 39      Y
#> 4: 65aeb1ec-b377-4ef6-b03f-92898d47488b      39      D03 Sample 39   bACT
#> 5: 65aeb1ec-b377-4ef6-b03f-92898d47488b      40      D04 Sample 39    IPC
#> 6: 65aeb1ec-b377-4ef6-b03f-92898d47488b      40      D04 Sample 39      X
#>    targetDyeId sampleType    adp    mdp
#>         <char>     <char> <lgcl> <lgcl>
#> 1:         Cy5        std   TRUE  FALSE
#> 2:         Hex        std   TRUE  FALSE
#> 3:   Texas Red        std   TRUE  FALSE
#> 4:         FAM        std   TRUE  FALSE
#> 5:         Cy5        std   TRUE  FALSE
#> 6:         Hex        std   TRUE  FALSE

The standard scalar fields are:

The camelCase names replace the old fdata.name, exp.id, run.id, react.id, sample.type, and target.dyeId columns.

Selecting standard columns

The revised interface uses columns to select built-in output columns.

tbl_small <- asTable(
  lc96,
  columns = c(
    "expId",
    "runId",
    "position",
    "sample",
    "sampleType",
    "target"
  )
)

head(tbl_small)
#> Ключ: «fdataName»
#>                 fdataName                                expId
#>                    <char>                               <char>
#> 1:  D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 2:    D03_Sample 39_std_X ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 3:    D03_Sample 39_std_Y ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 4: D03_Sample 39_std_bACT ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 5:  D04_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 6:    D04_Sample 39_std_X ca1eb225-ecea-4793-9804-87bfbb45f81d
#>                                   runId position    sample sampleType target
#>                                  <char>   <char>    <char>     <char> <char>
#> 1: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03 Sample 39        std    IPC
#> 2: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03 Sample 39        std      X
#> 3: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03 Sample 39        std      Y
#> 4: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03 Sample 39        std   bACT
#> 5: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D04 Sample 39        std    IPC
#> 6: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D04 Sample 39        std      X

Without columns, all built-in columns are returned.

namePattern templates

fdataName must identify a fluorescence series. RDML7 supports readable {field} templates:

tbl <- asTable(
  lc96,
  namePattern =
    "{expId}_{runId}_{position}_{sample}_{sampleType}_{target}"
)

head(tbl$fdataName)
#> [1] "ca1eb225-ecea-4793-9804-87bfbb45f81d_65aeb1ec-b377-4ef6-b03f-92898d47488b_D03_Sample 39_std_IPC" 
#> [2] "ca1eb225-ecea-4793-9804-87bfbb45f81d_65aeb1ec-b377-4ef6-b03f-92898d47488b_D03_Sample 39_std_X"   
#> [3] "ca1eb225-ecea-4793-9804-87bfbb45f81d_65aeb1ec-b377-4ef6-b03f-92898d47488b_D03_Sample 39_std_Y"   
#> [4] "ca1eb225-ecea-4793-9804-87bfbb45f81d_65aeb1ec-b377-4ef6-b03f-92898d47488b_D03_Sample 39_std_bACT"
#> [5] "ca1eb225-ecea-4793-9804-87bfbb45f81d_65aeb1ec-b377-4ef6-b03f-92898d47488b_D04_Sample 39_std_IPC" 
#> [6] "ca1eb225-ecea-4793-9804-87bfbb45f81d_65aeb1ec-b377-4ef6-b03f-92898d47488b_D04_Sample 39_std_X"

Available built-in template fields are:

{expId}
{runId}
{reactId}
{position}
{sample}
{target}
{targetDyeId}
{sampleType}
{adp}
{mdp}

Custom columns supplied through ... are also available to namePattern.

Including expId and runId is recommended when an object may contain more than one experiment or run. It substantially reduces accidental duplicate fdataName values.To avoid duplication asTable automatically adds number indices to duplicated fdataName values. But it’s generate warning.

Adding custom columns

Custom columns no longer need a separate addColumns argument. Supply a named expression directly through ....

request <- asTable(
  lc96,
  columns = c(
    "expId",
    "runId",
    "position",
    "reactId",
    "sample",
    "sampleType",
    "target"
  ),
  sampleTarget = paste(
    sample,
    target,
    sep = ":"
))

head(request)
#> Ключ: «fdataName»
#>                 fdataName                                expId
#>                    <char>                               <char>
#> 1:  D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 2:    D03_Sample 39_std_X ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 3:    D03_Sample 39_std_Y ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 4: D03_Sample 39_std_bACT ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 5:  D04_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 6:    D04_Sample 39_std_X ca1eb225-ecea-4793-9804-87bfbb45f81d
#>                                   runId position reactId    sample sampleType
#>                                  <char>   <char>  <char>    <char>     <char>
#> 1: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03      39 Sample 39        std
#> 2: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03      39 Sample 39        std
#> 3: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03      39 Sample 39        std
#> 4: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03      39 Sample 39        std
#> 5: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D04      40 Sample 39        std
#> 6: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D04      40 Sample 39        std
#>    target   sampleTarget
#>    <char>         <char>
#> 1:    IPC  Sample 39:IPC
#> 2:      X    Sample 39:X
#> 3:      Y    Sample 39:Y
#> 4:   bACT Sample 39:bACT
#> 5:    IPC  Sample 39:IPC
#> 6:      X    Sample 39:X

The ready-to-use scalar fields available in custom expressions are:

expId, runId, reactId, position, sample, target,
targetDyeId, sampleType, adp, mdp

Advanced custom columns

For metadata not exposed as a built-in scalar field, ... also provides the current structured RDML objects:

experiment
run
react
data
samples
targets
dateMade
dateUpdated
id
experimenter
documentation
dye
thermalCyclingConditions

These objects are intended for advanced extraction. Because they are structured S7/RDML objects, they are not inserted directly into namePattern.

For example, a scalar property can be extracted from the current data object and then used as a template field:

tbl <- asTable(
  lc96,
  cq = data$cq,
  namePattern =
    "{position}:{sample}_{target}-{cq}"
)

Once cq is created as a scalar custom column, {cq} becomes available in namePattern.

This design means users do not need to know internal helper functions such as .rdmlIdChr(), .rdmlReactPosition(), or .rdmlSampleType() merely to construct an fdataName.

Example: deriving concentration from a sample name

User metadata can also be derived from existing scalar fields. For example, if a concentration is encoded as a separate underscore-delimited component of the sample name:


r96 <- readRDML(system.file(
  "extdata",
  "cd53_efficiency.r96",
  package = "RDML7"
))
concValues <- c("1", "0.5", "0.1", "0.01")

tbl <- asTable(
  r96,
  concentration = {
    parts <- strsplit(sample, "_", fixed = TRUE)[[1]]

    value <- parts[parts %in% concValues]

    if (length(value)) {
      as.numeric(value[[1]])
    } else {
      NA_real_
    }
  }
)

head(tbl)

This approach avoids falsely extracting a number that is only part of another identifier.

Legacy default and addColumns

default and addColumns may be retained for compatibility with older code, but new code should normally use:

columns + named expressions in ...

This gives asTable() a single, predictable column-selection model.

Getting fluorescence data

Fluorescence data are stored separately from the flattened metadata table. Use getFData() to extract them. You can provide special request to access some part of data or access all available without request (executes asTable() with default parameters under the hood) - see later.

Amplification data (adp)

Default data is adp.

amp <- getFData(lc96, request)

head(amp)
#> Ключ: «cyc»
#>      cyc D03_Sample 39_std_IPC D03_Sample 39_std_X D03_Sample 39_std_Y
#>    <num>                 <num>               <num>               <num>
#> 1:     1             0.0183606           0.0335976           0.0499775
#> 2:     2             0.0180048           0.0325430           0.0484642
#> 3:     3             0.0177101           0.0320003           0.0475724
#> 4:     4             0.0175328           0.0316902           0.0469673
#> 5:     5             0.0173891           0.0313358           0.0464253
#> 6:     6             0.0172979           0.0311102           0.0460783
#>    D03_Sample 39_std_bACT D04_Sample 39_std_IPC D04_Sample 39_std_X
#>                     <num>                 <num>               <num>
#> 1:              0.0765203             0.0198442           0.0358064
#> 2:              0.0740042             0.0192934           0.0345357
#> 3:              0.0723341             0.0190384           0.0338550
#> 4:              0.0713094             0.0189303           0.0335737
#> 5:              0.0699647             0.0188875           0.0334760
#> 6:              0.0692598             0.0188215           0.0332843
#>    D04_Sample 39_std_Y D04_Sample 39_std_bACT D05_Sample 41_std_IPC
#>                  <num>                  <num>                 <num>
#> 1:           0.0536608              0.0815654             0.0191602
#> 2:           0.0517852              0.0785972             0.0188048
#> 3:           0.0508504              0.0768843             0.0187248
#> 4:           0.0504487              0.0758016             0.0186919
#> 5:           0.0501193              0.0749237             0.0186583
#> 6:           0.0500132              0.0746428             0.0186720
#>    D05_Sample 41_std_X D05_Sample 41_std_Y D05_Sample 41_std_bACT
#>                  <num>               <num>                  <num>
#> 1:           0.0339051           0.0517698              0.0779549
#> 2:           0.0327662           0.0501112              0.0751758
#> 3:           0.0323423           0.0496300              0.0740962
#> 4:           0.0322557           0.0495610              0.0736120
#> 5:           0.0319371           0.0493506              0.0728351
#> 6:           0.0318878           0.0491510              0.0727694
#>    D06_Sample 41_std_IPC D06_Sample 41_std_X D06_Sample 41_std_Y
#>                    <num>               <num>               <num>
#> 1:             0.0207541           0.0370556           0.0566644
#> 2:             0.0202325           0.0356479           0.0547598
#> 3:             0.0199968           0.0350582           0.0540312
#> 4:             0.0198780           0.0346749           0.0534543
#> 5:             0.0198824           0.0345360           0.0532268
#> 6:             0.0199653           0.0343352           0.0531516
#>    D06_Sample 41_std_bACT D07_Sample 43_std_IPC D07_Sample 43_std_X
#>                     <num>                 <num>               <num>
#> 1:              0.0856682             0.0175444           0.0310695
#> 2:              0.0826272             0.0169826           0.0296340
#> 3:              0.0811635             0.0167133           0.0290403
#> 4:              0.0801425             0.0165369           0.0286743
#> 5:              0.0794916             0.0164757           0.0284034
#> 6:              0.0791170             0.0164641           0.0281735
#>    D07_Sample 43_std_Y D07_Sample 43_std_bACT D08_Sample 43_std_IPC
#>                  <num>                  <num>                 <num>
#> 1:           0.0473832              0.0718652             0.0200607
#> 2:           0.0453774              0.0689529             0.0196742
#> 3:           0.0445243              0.0671860             0.0194035
#> 4:           0.0439118              0.0662575             0.0192953
#> 5:           0.0435639              0.0656260             0.0192834
#> 6:           0.0432672              0.0650626             0.0192326
#>    D08_Sample 43_std_X D08_Sample 43_std_Y D08_Sample 43_std_bACT
#>                  <num>               <num>                  <num>
#> 1:           0.0356145           0.0544387              0.0818625
#> 2:           0.0345259           0.0528708              0.0792517
#> 3:           0.0338534           0.0519989              0.0776971
#> 4:           0.0334567           0.0514629              0.0764136
#> 5:           0.0333187           0.0511263              0.0759889
#> 6:           0.0330787           0.0508951              0.0755568
#>    D09_Sample 45_std_IPC D09_Sample 45_std_X D09_Sample 45_std_Y
#>                    <num>               <num>               <num>
#> 1:             0.0185860           0.0336384           0.0508826
#> 2:             0.0181555           0.0322999           0.0489802
#> 3:             0.0178374           0.0316383           0.0480418
#> 4:             0.0176324           0.0310238           0.0472458
#> 5:             0.0176419           0.0308866           0.0470960
#> 6:             0.0177665           0.0308295           0.0470439
#>    D09_Sample 45_std_bACT D10_Sample 45_std_IPC D10_Sample 45_std_X
#>                     <num>                 <num>               <num>
#> 1:              0.0782385             0.0203383           0.0360929
#> 2:              0.0753689             0.0200826           0.0352363
#> 3:              0.0736838             0.0198944           0.0346983
#> 4:              0.0723196             0.0198353           0.0342507
#> 5:              0.0717019             0.0199068           0.0340923
#> 6:              0.0714182             0.0199474           0.0341286
#>    D10_Sample 45_std_Y D10_Sample 45_std_bACT E03_Sample 51_std_IPC
#>                  <num>                  <num>                 <num>
#> 1:           0.0554231              0.0853050             0.0178333
#> 2:           0.0541550              0.0831106             0.0176003
#> 3:           0.0533848              0.0817363             0.0174533
#> 4:           0.0528364              0.0806183             0.0174767
#> 5:           0.0525911              0.0802673             0.0174976
#> 6:           0.0525880              0.0799331             0.0176317
#>    E03_Sample 51_std_X E03_Sample 51_std_Y E03_Sample 51_std_bACT
#>                  <num>               <num>                  <num>
#> 1:           0.0329191           0.0494571              0.0752976
#> 2:           0.0319224           0.0481465              0.0730672
#> 3:           0.0314371           0.0475546              0.0716360
#> 4:           0.0312886           0.0473839              0.0712866
#> 5:           0.0311553           0.0472380              0.0707699
#> 6:           0.0311438           0.0473775              0.0706796
#>    E04_Sample 51_std_IPC E04_Sample 51_std_X E04_Sample 51_std_Y
#>                    <num>               <num>               <num>
#> 1:             0.0191185           0.0341296           0.0517345
#> 2:             0.0187005           0.0330325           0.0502056
#> 3:             0.0184264           0.0323452           0.0491989
#> 4:             0.0182750           0.0320491           0.0486962
#> 5:             0.0182767           0.0319013           0.0484829
#> 6:             0.0183389           0.0319011           0.0484655
#>    E04_Sample 51_std_bACT E05_Sample 53_unkn_IPC E05_Sample 53_unkn_X
#>                     <num>                  <num>                <num>
#> 1:              0.0794618              0.0191654            0.0351678
#> 2:              0.0768112              0.0187222            0.0342129
#> 3:              0.0750117              0.0186089            0.0337373
#> 4:              0.0735410              0.0184260            0.0334261
#> 5:              0.0732064              0.0183844            0.0331846
#> 6:              0.0727683              0.0183905            0.0329880
#>    E05_Sample 53_unkn_Y E05_Sample 53_unkn_bACT E06_Sample 54_unkn_IPC
#>                   <num>                   <num>                  <num>
#> 1:            0.0526014               0.0790541              0.0180577
#> 2:            0.0511012               0.0763099              0.0175595
#> 3:            0.0504312               0.0750849              0.0172984
#> 4:            0.0498503               0.0742467              0.0171720
#> 5:            0.0495193               0.0735078              0.0170461
#> 6:            0.0492999               0.0727997              0.0174254
#>    E06_Sample 54_unkn_X E06_Sample 54_unkn_Y E06_Sample 54_unkn_bACT
#>                   <num>                <num>                   <num>
#> 1:            0.0323227            0.0497062               0.0746011
#> 2:            0.0311048            0.0477509               0.0718295
#> 3:            0.0303910            0.0469619               0.0700444
#> 4:            0.0299897            0.0464032               0.0689996
#> 5:            0.0297086            0.0459665               0.0681219
#> 6:            0.0301393            0.0467093               0.0692167
#>    E07_Sample 55_unkn_IPC E07_Sample 55_unkn_X E07_Sample 55_unkn_Y
#>                     <num>                <num>                <num>
#> 1:              0.0196414            0.0343248            0.0530040
#> 2:              0.0192629            0.0333167            0.0516058
#> 3:              0.0191915            0.0329667            0.0511760
#> 4:              0.0191301            0.0327180            0.0508276
#> 5:              0.0191429            0.0324963            0.0506607
#> 6:              0.0191732            0.0323865            0.0505238
#>    E07_Sample 55_unkn_bACT E08_Sample 56_unkn_IPC E08_Sample 56_unkn_X
#>                      <num>                  <num>                <num>
#> 1:               0.0796174              0.0185813            0.0327999
#> 2:               0.0770337              0.0181217            0.0315971
#> 3:               0.0760521              0.0178966            0.0309754
#> 4:               0.0755209              0.0178717            0.0307675
#> 5:               0.0749050              0.0178558            0.0305352
#> 6:               0.0744834              0.0178572            0.0304046
#>    E08_Sample 56_unkn_Y E08_Sample 56_unkn_bACT E09_Sample 57_unkn_IPC
#>                   <num>                   <num>                  <num>
#> 1:            0.0497605               0.0763902              0.0186737
#> 2:            0.0480795               0.0736332              0.0181635
#> 3:            0.0473462               0.0721514              0.0179202
#> 4:            0.0469277               0.0716271              0.0178013
#> 5:            0.0466651               0.0707240              0.0177887
#> 6:            0.0466256               0.0703912              0.0178492
#>    E09_Sample 57_unkn_X E09_Sample 57_unkn_Y E09_Sample 57_unkn_bACT
#>                   <num>                <num>                   <num>
#> 1:            0.0328913            0.0502748               0.0769695
#> 2:            0.0317731            0.0485992               0.0742408
#> 3:            0.0310958            0.0475705               0.0726216
#> 4:            0.0306493            0.0470146               0.0716452
#> 5:            0.0304404            0.0469395               0.0709691
#> 6:            0.0303937            0.0467636               0.0706903
#>    E10_Sample 58_unkn_IPC E10_Sample 58_unkn_X E10_Sample 58_unkn_Y
#>                     <num>                <num>                <num>
#> 1:              0.0198076            0.0345351            0.0529905
#> 2:              0.0193775            0.0332707            0.0511514
#> 3:              0.0190470            0.0324626            0.0501097
#> 4:              0.0188502            0.0320468            0.0493770
#> 5:              0.0188191            0.0317203            0.0490187
#> 6:              0.0189098            0.0316947            0.0489793
#>    E10_Sample 58_unkn_bACT
#>                      <num>
#> 1:               0.0808244
#> 2:               0.0779781
#> 3:               0.0762271
#> 4:               0.0748687
#> 5:               0.0741372
#> 6:               0.0738226

For amplification data, the wide table uses cyc as its primary coordinate. If cycle temperatures are present, tmp may also be available.

Melting data (mdp)

biorad <- readRDML(system.file(
  "extdata",
  "BioRad_qPCR_melt.rdml",
  package = "RDML7"
))
#> 
#> Loading experiment: All Wells
#>  run: Amp Step 3_FAM
#> 
#>  run: Amp Step 3_Cy5
#> 
#> Combining Bio-Rad runs
melt <- getFData(
  biorad,
  dpType = "mdp"
)

head(melt)
#> Ключ: «tmp»
#>      tmp A01_Alm12_pos_Cy5 A01_Alm12_pos_EvaGreen A02_Alm12_pos_Cy5
#>    <num>             <num>                  <num>             <num>
#> 1:    35          2756.696               2763.424          2773.549
#> 2:    36          2756.427               2762.044          2773.658
#> 3:    37          2755.606               2761.538          2773.060
#> 4:    38          2754.828               2761.131          2772.298
#> 5:    39          2753.944               2761.432          2771.104
#> 6:    40          2753.553               2761.993          2769.961
#>    A02_Alm12_pos_EvaGreen A03_Alm13_pos_Cy5 A03_Alm13_pos_EvaGreen
#>                     <num>             <num>                  <num>
#> 1:               2873.025          2742.910               3052.787
#> 2:               2873.490          2744.053               3045.722
#> 3:               2872.137          2744.902               3036.568
#> 4:               2870.911          2745.605               3027.403
#> 5:               2869.085          2745.972               3017.348
#> 6:               2867.012          2746.133               3006.884
#>    A04_Alm13_pos_Cy5 A04_Alm13_pos_EvaGreen A05_Alm14_pos_Cy5
#>                <num>                  <num>             <num>
#> 1:          2746.844               3080.472          2769.365
#> 2:          2747.364               3074.555          2769.943
#> 3:          2747.194               3066.430          2769.771
#> 4:          2747.010               3058.135          2769.473
#> 5:          2746.638               3048.621          2768.866
#> 6:          2746.478               3038.660          2768.158
#>    A05_Alm14_pos_EvaGreen A06_Alm14_pos_Cy5 A06_Alm14_pos_EvaGreen
#>                     <num>             <num>                  <num>
#> 1:               3274.659          2780.037               3420.850
#> 2:               3260.485          2779.134               3406.961
#> 3:               3242.934          2778.603               3388.401
#> 4:               3225.100          2777.859               3369.726
#> 5:               3205.429          2777.288               3348.903
#> 6:               3185.546          2776.973               3327.265
#>    A07_katG 315_unkn_Cy5 A07_katG 315_unkn_EvaGreen A08_katG 315_unkn_Cy5
#>                    <num>                      <num>                 <num>
#> 1:              2789.874                   3186.918              2765.162
#> 2:              2788.184                   3176.197              2763.108
#> 3:              2786.631                   3160.919              2762.324
#> 4:              2785.046                   3145.450              2761.476
#> 5:              2783.440                   3127.665              2761.093
#> 6:              2782.113                   3109.363              2760.839
#>    A08_katG 315_unkn_EvaGreen A09_H2O_ntc_Cy5 A09_H2O_ntc_EvaGreen
#>                         <num>           <num>                <num>
#> 1:                   2886.544        2799.886             2966.643
#> 2:                   2873.045        2800.627             2951.056
#> 3:                   2856.397        2800.243             2930.423
#> 4:                   2839.687        2799.959             2909.759
#> 5:                   2821.571        2799.111             2886.796
#> 6:                   2803.040        2798.460             2863.552
#>    A10_H2O_ntc_Cy5 A10_H2O_ntc_EvaGreen D01_Alm12_pos_Cy5-2
#>              <num>                <num>               <num>
#> 1:        2770.013             2912.120            3029.842
#> 2:        2770.333             2897.536            2981.122
#> 3:        2770.212             2879.021            2937.551
#> 4:        2770.012             2860.436            2895.814
#> 5:        2769.426             2839.864            2858.862
#> 6:        2769.049             2819.133            2827.073
#>    D01_Alm12_pos_EvaGreen D02_Alm12_pos_Cy5-2 D02_Alm12_pos_EvaGreen
#>                     <num>               <num>                  <num>
#> 1:               3249.911            3111.624               3130.243
#> 2:               3248.812            3054.088               3127.589
#> 3:               3246.821            3001.551               3123.570
#> 4:               3244.042            2951.363               3118.885
#> 5:               3239.934            2906.673               3112.844
#> 6:               3234.515            2868.178               3105.478
#>    D03_Alm13_pos_Cy5-2 D03_Alm13_pos_EvaGreen D04_Alm13_pos_Cy5-2
#>                  <num>                  <num>               <num>
#> 1:            3177.210               2892.511            3230.802
#> 2:            3122.861               2897.547            3169.641
#> 3:            3070.189               2901.540            3110.204
#> 4:            3018.797               2905.218            3052.316
#> 5:            2970.171               2907.869            2997.615
#> 6:            2925.582               2909.278            2947.708
#>    D04_Alm13_pos_EvaGreen D05_Alm14_pos_Cy5-2 D05_Alm14_pos_EvaGreen
#>                     <num>               <num>                  <num>
#> 1:               2897.084            3834.142               3007.557
#> 2:               2904.210            3749.747               3010.505
#> 3:               2909.674            3658.824               3012.562
#> 4:               2914.579            3567.399               3014.506
#> 5:               2917.967            3473.200               3015.672
#> 6:               2919.770            3379.075               3016.321
#>    D06_Alm14_pos_Cy5-2 D06_Alm14_pos_EvaGreen D07_katG 315_unkn_Cy5-2
#>                  <num>                  <num>                   <num>
#> 1:            3704.775               2949.503                4759.427
#> 2:            3630.238               2952.510                4715.604
#> 3:            3548.570               2954.214                4666.245
#> 4:            3466.663               2955.879                4616.444
#> 5:            3381.847               2956.814                4563.872
#> 6:            3297.544               2957.345                4510.842
#>    D07_katG 315_unkn_EvaGreen D08_katG 315_unkn_Cy5-2
#>                         <num>                   <num>
#> 1:                   2867.968                4620.729
#> 2:                   2864.048                4581.642
#> 3:                   2858.544                4536.475
#> 4:                   2853.213                4491.069
#> 5:                   2847.496                4442.651
#> 6:                   2842.057                4393.480
#>    D08_katG 315_unkn_EvaGreen D09_H2O_ntc_Cy5-2 D09_H2O_ntc_EvaGreen
#>                         <num>             <num>                <num>
#> 1:                   2823.160          2691.723             2832.783
#> 2:                   2816.158          2692.969             2815.562
#> 3:                   2809.917          2693.935             2794.965
#> 4:                   2803.811          2694.902             2774.229
#> 5:                   2798.283          2695.685             2751.654
#> 6:                   2793.139          2696.460             2728.161
#>    D10_H2O_ntc_Cy5-2 D10_H2O_ntc_EvaGreen H01_Alm12_pos_Cy5-2_rr
#>                <num>                <num>                  <num>
#> 1:          2726.723             3140.937               2769.448
#> 2:          2725.322             3123.298               2763.957
#> 3:          2724.844             3100.413               2760.002
#> 4:          2724.377             3077.349               2756.102
#> 5:          2724.290             3051.524               2752.876
#> 6:          2724.353             3024.968               2750.178
#>    H01_Alm12_pos_EvaGreen H02_Alm12_pos_Cy5-2_rr H02_Alm12_pos_EvaGreen
#>                     <num>                  <num>                  <num>
#> 1:               3203.395               2772.874               3280.179
#> 2:               3193.828               2766.819               3269.991
#> 3:               3183.147               2762.863               3257.336
#> 4:               3172.441               2758.907               3244.322
#> 5:               3161.100               2755.771               3229.904
#> 6:               3149.400               2752.933               3214.990
#>    H03_Alm13_pos_Cy5-2_rr H03_Alm13_pos_EvaGreen H04_Alm13_pos_Cy5-2_rr
#>                     <num>                  <num>                  <num>
#> 1:               2834.579               3151.868               2825.395
#> 2:               2825.138               3143.335               2817.148
#> 3:               2815.621               3135.343               2808.790
#> 4:               2806.086               3127.204               2800.246
#> 5:               2796.594               3119.124               2791.697
#> 6:               2787.476               3110.741               2783.157
#>    H04_Alm13_pos_EvaGreen H05_Alm14_pos_Cy5-2_rr H05_Alm14_pos_EvaGreen
#>                     <num>                  <num>                  <num>
#> 1:               3122.619               3077.596               3175.637
#> 2:               3114.527               3055.775               3165.526
#> 3:               3106.466               3034.333               3155.793
#> 4:               3098.070               3012.344               3145.971
#> 5:               3089.467               2990.026               3136.116
#> 6:               3080.319               2966.911               3125.618
#>    H06_Alm14_pos_Cy5-2_rr H06_Alm14_pos_EvaGreen H07_katG 315_unkn_Cy5-2_rr
#>                     <num>                  <num>                      <num>
#> 1:               3186.674               3359.428                   4799.703
#> 2:               3161.281               3347.999                   4746.312
#> 3:               3134.485               3335.472                   4692.246
#> 4:               3107.100               3322.806                   4638.466
#> 5:               3078.686               3309.481                   4584.585
#> 6:               3049.764               3295.479                   4530.886
#>    H07_katG 315_unkn_EvaGreen H08_katG 315_unkn_Cy5-2_rr
#>                         <num>                      <num>
#> 1:                   3090.593                   4676.935
#> 2:                   3083.679                   4626.677
#> 3:                   3077.828                   4575.587
#> 4:                   3071.916                   4524.437
#> 5:                   3066.526                   4472.843
#> 6:                   3061.000                   4421.124
#>    H08_katG 315_unkn_EvaGreen H09_H2O_ntc_Cy5-2_rr H09_H2O_ntc_EvaGreen
#>                         <num>                <num>                <num>
#> 1:                   3039.149             2820.862             3461.191
#> 2:                   3033.924             2817.041             3436.213
#> 3:                   3028.838             2815.319             3410.819
#> 4:                   3023.567             2813.531             3385.083
#> 5:                   3018.277             2812.587             3358.900
#> 6:                   3013.045             2811.805             3332.186
#>    H10_H2O_ntc_Cy5-2_rr H10_H2O_ntc_EvaGreen
#>                   <num>                <num>
#> 1:             3631.431             3802.533
#> 2:             3609.960             3782.780
#> 3:             3587.895             3762.903
#> 4:             3565.871             3742.670
#> 5:             3543.622             3721.994
#> 6:             3521.279             3700.934

For melting data, the coordinate is tmp.

Long-table output

For plotting and grouped statistical analysis, use longTable = TRUE.

ampLong <- getFData(lc96, longTable = TRUE)

head(ampLong)
#>                fdataName                                expId
#>                   <char>                               <char>
#> 1: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 2: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 3: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 4: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 5: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 6: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#>                                   runId reactId position    sample target
#>                                  <char>  <char>   <char>    <char> <char>
#> 1: 65aeb1ec-b377-4ef6-b03f-92898d47488b      39      D03 Sample 39    IPC
#> 2: 65aeb1ec-b377-4ef6-b03f-92898d47488b      39      D03 Sample 39    IPC
#> 3: 65aeb1ec-b377-4ef6-b03f-92898d47488b      39      D03 Sample 39    IPC
#> 4: 65aeb1ec-b377-4ef6-b03f-92898d47488b      39      D03 Sample 39    IPC
#> 5: 65aeb1ec-b377-4ef6-b03f-92898d47488b      39      D03 Sample 39    IPC
#> 6: 65aeb1ec-b377-4ef6-b03f-92898d47488b      39      D03 Sample 39    IPC
#>    targetDyeId sampleType    adp    mdp   cyc     tmp     fluor
#>         <char>     <char> <lgcl> <lgcl> <num>   <num>     <num>
#> 1:         Cy5        std   TRUE  FALSE     1 68.0022 0.0183606
#> 2:         Cy5        std   TRUE  FALSE     2 68.0213 0.0180048
#> 3:         Cy5        std   TRUE  FALSE     3 68.0291 0.0177101
#> 4:         Cy5        std   TRUE  FALSE     4 68.0300 0.0175328
#> 5:         Cy5        std   TRUE  FALSE     5 68.0198 0.0173891
#> 6:         Cy5        std   TRUE  FALSE     6 68.0231 0.0172979

The long representation combines fluorescence points with the metadata from the request table.

Filtering curves before extraction

Because request is an ordinary table, it can be filtered before calling getFData().

stdRequest <- request[
  request$sampleType == "std",
]

stdAmp <- getFData(
  lc96,
  request = stdRequest,
  dpType = "adp",
  longTable = TRUE
)

head(stdAmp)
#>                fdataName                                expId
#>                   <char>                               <char>
#> 1: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 2: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 3: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 4: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 5: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#> 6: D03_Sample 39_std_IPC ca1eb225-ecea-4793-9804-87bfbb45f81d
#>                                   runId position reactId    sample sampleType
#>                                  <char>   <char>  <char>    <char>     <char>
#> 1: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03      39 Sample 39        std
#> 2: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03      39 Sample 39        std
#> 3: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03      39 Sample 39        std
#> 4: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03      39 Sample 39        std
#> 5: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03      39 Sample 39        std
#> 6: 65aeb1ec-b377-4ef6-b03f-92898d47488b      D03      39 Sample 39        std
#>    target  sampleTarget   cyc     tmp     fluor
#>    <char>        <char> <num>   <num>     <num>
#> 1:    IPC Sample 39:IPC     1 68.0022 0.0183606
#> 2:    IPC Sample 39:IPC     2 68.0213 0.0180048
#> 3:    IPC Sample 39:IPC     3 68.0291 0.0177101
#> 4:    IPC Sample 39:IPC     4 68.0300 0.0175328
#> 5:    IPC Sample 39:IPC     5 68.0198 0.0173891
#> 6:    IPC Sample 39:IPC     6 68.0231 0.0172979

This replaces the original workflow in which AsTable() output was passed to GetFData().

Plotting with ggplot2

library(ggplot2)

ggplot(
  stdAmp,
  aes(
    x = cyc,
    y = fluor,
    group = fdataName,
    colour = target
  )
) +
  geom_line()

Adding or replacing fluorescence data

RDML7 uses value semantics. setFData() returns the modified object, so assign it back:

x <- setFData(x, ...)

Do not rely on the in-place mutation semantics of the old R6 implementation.

setFData() accepts:

The fluorescence table has a first coordinate column (cyc or tmp) and one column per fluorescence series. The series names must match description$fdataName.

Creating an RDML object from tables

A minimal RDML object can be created with rdmlType() and populated with setFData().

fdata <- data.frame(
  cyc = 1:6,
  c1 = c(10, 11, 13, 18, 35, 80),
  c2 = c(9, 10, 12, 16, 29, 65)
)

description <- data.frame(
  fdataName = c("c1", "c2"),
  expId = c("exp1", "exp1"),
  runId = c("run1", "run1"),
  reactId = c("1", "2"),
  sample = c("sample1", "sample2"),
  sampleType = c("unkn", "unkn"),
  target = c("gene1", "gene1"),
  targetDyeId = c("FAM", "FAM"),
  stringsAsFactors = FALSE
)

sim <- rdmlType()

sim <- setFData(
  sim,
  fdata = fdata,
  description = description,
  fdataType = "adp"
)

asTable(sim)
#> Ключ: «fdataName»
#>                 fdataName  expId  runId reactId position  sample target
#>                    <char> <char> <char>  <char>   <char>  <char> <char>
#> 1: A01_sample1_unkn_gene1   exp1   run1       1      A01 sample1  gene1
#> 2: A02_sample2_unkn_gene1   exp1   run1       2      A02 sample2  gene1
#>    targetDyeId sampleType    adp    mdp
#>         <char>     <char> <lgcl> <lgcl>
#> 1:         FAM       unkn   TRUE  FALSE
#> 2:         FAM       unkn   TRUE  FALSE

The required description columns are:

fdataName
expId
runId
reactId
target

Sample and target metadata are used when new schema objects are created. Missing experiments, runs, reactions, samples, targets, and dyes are created as needed.

For conflicting existing data:

sim <- setFData(
  sim,
  fdata,
  description,
  fdataType = "adp",
  conflict = "replace"
)

The conflict policies are:

Importing vendor and table data

File import is now handled by readRDML() and the format registry.

For example:

x <- readRDML("instrument-export-file")

If automatic detection is ambiguous:

x <- readRDML(
  "instrument-export-file",
  format = "registered-format-name"
)

Always use:

listRDMLFormats()
#>         format    extensions read write
#> 1          abi           eds TRUE FALSE
#> 2    rotorgene           rex TRUE FALSE
#> 3        excel     xlsx, xls TRUE FALSE
#> 4          csv           csv TRUE FALSE
#> 5      dtprime           r96 TRUE FALSE
#> 6          fqd           txt TRUE FALSE
#> 7  innova-qstd          qstd TRUE FALSE
#> 8         rdes tsv, csv, txt TRUE  TRUE
#> 9     rdml-xml           xml TRUE  TRUE
#> 10        rdml     rdml, rdm TRUE  TRUE
#> 11  roche-lc96         lc96p TRUE FALSE

to inspect the format names actually registered in the installed version of RDML7.

The source tree includes importers for RDML/XML, RDES, ABI, Roche, Rotor-Gene, DTprime, FQD, CSV, Excel/table data, and other supported input families. The registry is the authoritative runtime list.

RDES import and export

RDML7 includes RDES support. The example data installed with the package include amplification and melting RDES TSV files.

rdesAmp <- system.file(
  "extdata",
  "RDES_v1_0_example_amplification.tsv",
  package = "RDML7"
)

rdesMelt <- system.file(
  "extdata",
  "RDES_v1_0_example_melting.tsv",
  package = "RDML7"
)

c(rdesAmp, rdesMelt)
#> [1] "C:/tmp/RtmpKYPudu/Rinst45747907173b/RDML7/extdata/RDES_v1_0_example_amplification.tsv"
#> [2] "C:/tmp/RtmpKYPudu/Rinst45747907173b/RDML7/extdata/RDES_v1_0_example_melting.tsv"

Import uses the same generic entry point:

rdes <- readRDML(rdesAmp)

Export is likewise routed through writeRDML() according to the requested output format.

Merging RDML objects

Use mergeRDMLs() to combine RDML objects.

file1 <- system.file(
  "extdata",
  "lc96_bACTXY.rdml",
  package = "RDML7"
)

file2 <- system.file(
  "extdata",
  "stepone_std.rdml",
  package = "RDML7"
)

base <- readRDML(file1)
incoming <- readRDML(file2)

merged <- mergeRDMLs(
  base,
  incoming,
  dataConflict = "error"
)

asDendrogram(merged)

dataConflict controls what happens when equivalent data locations contain different values:

For merging independent files, "error" is useful while developing a pipeline because unexpected ID collisions become visible immediately.

A directory of files can be processed in a list:

files <- list.files(
  system.file("extdata", package = "RDML7"),
  full.names = TRUE
)

files <- files[!file.info(files)$isdir]

rdmls <- lapply(files, function(file) readRDML(file))
merged <- mergeRDMLs(
    rdmls,
    dataConflict = "error"
  )

merged <- mergeRDMLs(
    rdmls
  )
rm(rdmls)

asDendrogram(merged)
rm(merged)

If experiment identifiers are not unique between input files, rename them before merging.

Serialising and writing RDML

asXML()

Use asXML() when the XML representation itself is needed:

xml <- asXML(r96)

cat(
  substr(
    xml,
    1,
    500
  )
)

writeRDML()

Use writeRDML() for normal file output:

writeRDML(
  lc96,
  "lc96-copy.rdml",
  overwrite = TRUE
)

For RDML archives, the package writes the XML payload as rdml_data.xml inside the archive.

The writer is selected through the same registry used by readRDML().

Validation before export

A useful export workflow is:

validation <- validateRDML(lc96)

if (isValidRDML(lc96)) {
  writeRDML(
    lc96,
    "validated.rdml",
    overwrite = TRUE
  )
}

Extending the file-format registry

One of the major additions in RDML7 is a public plugin API for file formats.

Registering a custom format

A format can provide:

The public registration function is:

registerRDMLFormat(
  name,
  extensions = character(),
  reader = NULL,
  writer = NULL,
)

For example, a third-party package could register a reader:

# simple-qpcr-reader.R -----------------------------------------------------
#
# Example third-party reader for RDML7's minimal format registry.

readSimpleQPCR <- function(fileName, ...) {
  lines <- readLines(
    fileName,
    warn = FALSE,
    encoding = "UTF-8"
  )

  section <- function(name) {
    start <- match(
      paste0("[", name, "]"),
      lines
    )

    if (is.na(start)) {
      stop(
        "Missing [",
        name,
        "] section",
        call. = FALSE
      )
    }

    nextSections <- grep(
      "^\\[[^]]+\\]$",
      lines
    )

    end <- nextSections[
      nextSections > start
    ]

    end <- if (length(end)) {
      end[[1L]] - 1L
    } else {
      length(lines)
    }

    out <- lines[
      seq.int(
        start + 1L,
        end
      )
    ]

    out[nzchar(trimws(out))]
  }

  # [meta] ---------------------------------------------------------------
  metaLines <- section("meta")

  meta <- strsplit(
    metaLines,
    "=",
    fixed = TRUE
  )

  meta <- stats::setNames(
    vapply(
      meta,
      function(x) {
        paste(
          x[-1L],
          collapse = "="
        )
      },
      character(1)
    ),
    vapply(
      meta,
      `[[`,
      character(1),
      1L
    )
  )

  requiredMeta <- c(
    "experiment",
    "run",
    "target",
    "dye"
  )

  missingMeta <- setdiff(
    requiredMeta,
    names(meta)
  )

  if (length(missingMeta)) {
    stop(
      "Missing metadata: ",
      paste(
        missingMeta,
        collapse = ", "
      ),
      call. = FALSE
    )
  }

  # [reactions] ----------------------------------------------------------
  reactions <- utils::read.delim(
    text = paste(
      section("reactions"),
      collapse = "\n"
    ),
    stringsAsFactors = FALSE,
    check.names = FALSE
  )

  requiredReactions <- c(
    "fdataName",
    "reactId",
    "sample",
    "sampleType",
    "quantity"
  )

  missingReactions <- setdiff(
    requiredReactions,
    names(reactions)
  )

  if (length(missingReactions)) {
    stop(
      "Missing reaction columns: ",
      paste(
        missingReactions,
        collapse = ", "
      ),
      call. = FALSE
    )
  }

  # [adp] ----------------------------------------------------------------
  fdata <- utils::read.delim(
    text = paste(
      section("adp"),
      collapse = "\n"
    ),
    stringsAsFactors = FALSE,
    check.names = FALSE
  )

  if (!identical(names(fdata)[[1L]], "cyc")) {
    stop(
      "First [adp] column must be `cyc`",
      call. = FALSE
    )
  }

  missingCurves <- setdiff(
    reactions$fdataName,
    names(fdata)
  )

  if (length(missingCurves)) {
    stop(
      "Missing fluorescence columns: ",
      paste(
        missingCurves,
        collapse = ", "
      ),
      call. = FALSE
    )
  }

  description <- data.frame(
    fdataName = reactions$fdataName,
    expId = meta[["experiment"]],
    runId = meta[["run"]],
    reactId = as.character(
      reactions$reactId
    ),
    sample = reactions$sample,
    sampleType = reactions$sampleType,
    target = meta[["target"]],
    targetDyeId = meta[["dye"]],
    quantity = as.numeric(
      reactions$quantity
    ),
    stringsAsFactors = FALSE
  )

  buildRDMLFromFData(
    fdata = fdata,
    description = description,
    fdataType = "adp",
    publisher = "simpleqpcr"
  )
}


registerRDMLFormat(
  name = "simpleqpcr",
  extensions = "sqpcr",
  reader = readSimpleQPCR
)

The new format then participates in normal dispatch:

sqpcr <- system.file(
  "extdata/sqpcr",
  "cd53_efficiency.sqpcr",
  package = "RDML7"
)
sqpcr <- readRDML(sqpcr)
summary(sqpcr)
#> $version
#> [1] "1.3"
#> 
#> $dateMade
#> [1] NA
#> 
#> $dateUpdated
#> [1] NA
#> 
#> $totalExperiments
#> [1] 1
#> 
#> $totalRuns
#> [1] 1
#> 
#> $totalReacts
#> [1] 12
#> 
#> $totalData
#> [1] 12
#> 
#> $totalSamples
#> [1] 4
#> 
#> $totalTargets
#> [1] 1
#> 
#> $totalDyes
#> [1] 1
#> 
#> $totalThermalCyclingConditions
#> [1] 0
#> 
#> $totalAdp
#> [1] 12
#> 
#> $totalMdp
#> [1] 0
#> 
#> attr(,"class")
#> [1] "summary.rdmlType" "list"

To remove a third-party format:

unregisterRDMLFormat("simpleqpcr")

Built-in handlers are protected from accidental removal.

Canonical importer API

For custom file formats, see the Writing custom RDML importers vignette.

Loss-aware conversions

Different external formats do not necessarily represent every RDML feature. The importer/exporter API can describe known information loss using newRDMLLossRecord().

When developing a format plugin, declare known lossy transformations rather than silently discarding unsupported information.

This allows higher-level I/O code to decide whether loss should produce a warning, an error, or be explicitly allowed.

editRDML()

editRDML() remains available as an editing interface.

editRDML(lc96)

For reproducible analyses, scripted operations with asTable(), getFData(), setFData(), and the S7 object model should remain the primary workflow, while an editor is useful for interactive inspection and manual changes.

Working with external analysis packages

RDML7 intentionally keeps fluorescence extraction separate from downstream qPCR analysis. Once curves have been obtained with getFData(), they can be passed to other R packages.

For example:

curves <- getFData(
  lc96,
  dpType = "adp",
  longTable = FALSE
)

# The first column contains cycle coordinates.
cycles <- curves[[1]]

# Remaining columns contain fluorescence series and can be passed to
# modelling, preprocessing, Cq estimation, or visualisation code.

This separation allows RDML7 to focus on data interchange, metadata, validation, conversion, and object manipulation while specialised packages perform curve analysis.

See details and examples in Using RDML with other R packages vignette.

Legacy API compatibility

Legacy wrappers are exported to ease migration of existing analysis scripts.

Examples include:

AsTable        -> asTable
GetFData       -> getFData
SetFData       -> setFData
AsDendrogram   -> asDendrogram
asXML          -> asXML
MergeRDMLs     -> mergeRDMLs

Some old dotted argument names are also translated by compatibility code to their CamelCase equivalents, for example:

show.progress   -> showProgress
conditions.sep  -> conditionsSep
fdata.type      -> fdataType
serial.number   -> serialNumber
name.pattern    -> namePattern
long.table      -> longTable
sample.type     -> sampleType
target.dyeId    -> targetDyeId
dp.type         -> dpType
plot.dendrogram -> plotDendrogram
data.conflict   -> dataConflict

Compatibility wrappers are intended to keep old scripts usable. New code and new documentation should use the canonical names.

Function overview

The following groups summarise the most important public RDML7 functionality.

Core object and schema

rdmlType()
idType()
idReferenceType()
experimentType()
runType()
reactType()
dataType()
sampleType()
targetType()
dyeType()
thermalCyclingConditionsType()
...

File I/O

readRDML()
writeRDML()
asXML()
detectRDMLFormat()
listRDMLFormats()

Metadata and fluorescence

asTable()
getFData()
setFData()
buildRDMLFromFData()

Inspection and validation

summary()
validateRDML()
isValidRDML()
asDendrogram()

Manipulation

mergeRDMLs()
editRDML()

Extensible import/export

registerRDMLFormat()
unregisterRDMLFormat()
loadRDMLModule()
newRDMLImportSeries()
newRDMLImportData()
buildRDMLImport()
newRDMLLossRecord()

RDML7 public API naming convention rules

Summary

RDML7 keeps the main ideas of the original RDML package—structured qPCR metadata, fluorescence extraction, table-based filtering, creation of RDML objects from user data, and interoperability with other R analysis packages—but substantially changes the implementation and public API.

The central concepts for new code are:

  1. read files with readRDML();
  2. treat rdmlType and nested schema elements as S7 objects;
  3. use asTable() as the metadata/curve index;
  4. use {field} templates for readable and documented fdataName generation;
  5. add custom asTable() columns directly through ...;
  6. use getFData() and setFData() for fluorescence;
  7. remember that modification uses value semantics;
  8. validate with validateRDML() / isValidRDML();
  9. write through writeRDML();
  10. discover and extend formats through the format registry.

For the current runtime list of supported file handlers, always prefer:

listRDMLFormats()

over a hard-coded list in analysis code.

Further reading