Skip to content

dupRadar

In DNA sequencing, PCR duplicates are straightforward to identify and remove: reads with identical alignment coordinates are likely amplification artifacts. In RNA-seq, the situation is more nuanced.

Highly expressed genes produce many RNA fragments. When sequencing depth is sufficient, some of these independent fragments will share the same start and end positions purely by chance. These are natural duplicates — biologically real, not PCR artifacts. Removing them would distort expression estimates.

dupRadar addresses this by analyzing duplication rates in the context of expression level. Rather than applying a blanket duplicate removal, it models the expected relationship between how highly a gene is expressed and what fraction of its reads are marked as duplicates.

At low expression levels, duplicates are almost certainly PCR artifacts. At high expression levels, a baseline level of duplication is expected and biologically meaningful.

Duplicate rate analysis helps answer quality questions about an RNA-seq library:

  • Was the library over-amplified? If low-expression genes show high duplication rates, too many PCR cycles were used.
  • Is the library complex enough? Low-complexity libraries produce high duplication across all expression levels.
  • Can I trust the expression estimates? If duplication follows the expected expression-dependent pattern, the data is likely reliable.

This is useful for experiment QC, troubleshooting library preparation, and deciding whether to include a sample in downstream analysis.

dupRadar fits a logistic regression to the relationship between expression level and duplication rate. RustQC implements this same model using iteratively reweighted least squares (IRLS).

The model:

duplication_rate = 1 / (1 + exp(-(intercept + slope * x)))

where x is log10(reads per kilobase).

Intercept: Controls the baseline duplication rate at zero expression.

  • Values close to 0 (or negative) indicate good library quality — genes with few reads have few duplicates.
  • Values above 0.5 indicate significant PCR over-amplification — even lowly expressed genes have high duplication rates.

Slope: Controls how steeply duplication rises with expression.

  • Positive values mean duplication increases with expression, which is the expected biological pattern.
  • Very high slope values with a low intercept indicate a well-prepared library where duplication is driven by expression level rather than PCR artifacts.
  • A low slope combined with a high intercept suggests uniform over-amplification.
InterceptSlopeInterpretation
Low (~0)Moderate-highGood library. Duplicates are expression-dependent.
High (>0.5)LowOver-amplified. Duplicates everywhere.
High (>0.5)HighOver-amplified, but expression signal still visible.
Low (~0)Very lowGood complexity, very little duplication at any level.

All dupRadar output files use the BAM file stem as a prefix (e.g., sample.bam produces sample_dupMatrix.txt) and are written to a dupradar/ subdirectory under the output directory. Use --flat-output to write all files directly to the output directory instead. Each output can be individually enabled or disabled via the configuration file.

  • Directorydupradar/
    • sample_dupMatrix.txt Duplication matrix (core data table)
    • sample_intercept_slope.txt Fitted model intercept and slope
    • sample_duprateExpDens.png Density scatter plot
    • sample_duprateExpDens.svg
    • sample_duprateExpBoxplot.png Duplication rate boxplot
    • sample_duprateExpBoxplot.svg
    • sample_expressionHist.png Expression histogram
    • sample_expressionHist.svg
    • sample_dup_intercept_mqc.txt MultiQC general statistics
    • sample_duprateExpDensCurve_mqc.txt MultiQC fitted curve data

File: <sample>_dupMatrix.txt

A tab-separated file with one row per gene and 14 columns. This is the core data table from which all other dupRadar outputs are derived.

ColumnDescription
IDGene identifier from the GTF
geneLengthGene length in base pairs (non-overlapping exon bases)
allCountsMultiTotal read count including multi-mappers
filteredCountsMultiRead count excluding duplicates, including multi-mappers
dupRateMultiDuplication rate with multi-mappers: (all - filtered) / all
dupsPerIdMultiNumber of duplicate reads (multi-mapper inclusive)
RPKMultiReads per kilobase (multi-mapper inclusive)
RPKMMultiReads per kilobase per million mapped reads (multi-mapper inclusive)
allCountsTotal read count (unique mappers only)
filteredCountsRead count excluding duplicates (unique mappers only)
dupRateDuplication rate (unique mappers only)
dupsPerIdUniqueNumber of duplicate reads (unique mappers only)
RPKReads per kilobase (unique mappers only)
RPKMReads per kilobase per million mapped reads (unique mappers only)

Genes with zero reads have a duplication rate of 0 and RPK/RPKM values of 0. The format and values are identical to R dupRadar’s _dupMatrix.txt output.

File: <sample>_intercept_slope.txt

A two-line text file containing the intercept and slope of the fitted logistic regression model:

intercept 0.03312947025247037
slope 1.5522385655558626

These parameters define the fitted curve: y = 1 / (1 + exp(-(intercept + slope * x))), where x is log10(reads/kbp) and y is the duplication rate.

RustQC generates three diagnostic plots in both PNG and SVG formats. See the Interpreting plots section below for how to read each plot.

<sample>_duprateExpDens.{png,svg}

dupRadar (R)
RustQC
dupRadar (R) density scatter plot
RustQC density scatter plot

<sample>_duprateExpBoxplot.{png,svg}

dupRadar (R)
RustQC
dupRadar (R) duplication rate boxplot
RustQC duplication rate boxplot

<sample>_expressionHist.{png,svg}

dupRadar (R)
RustQC
dupRadar (R) expression histogram
RustQC expression histogram
  • <sample>_dup_intercept_mqc.txt — Intercept and slope values in MultiQC general statistics format.
  • <sample>_duprateExpDensCurve_mqc.txt — Fitted duplication rate curve data points for MultiQC line plot visualization.

These files integrate with MultiQC for inclusion in multi-sample QC reports.

RustQC generates the same three visualizations as the original dupRadar R package and follows the same conventions.

File: <sample>_duprateExpDens.png / .svg

This is the primary diagnostic plot, showing the relationship between gene expression level and duplication rate.

  • X-axis: Expression level as log10(reads per kilobase). Each point is one gene.
  • Y-axis: Duplication rate, ranging from 0 (no duplicates) to 1 (all reads are duplicates).
  • Color: Point density — brighter colors indicate more genes at that position. This helps reveal where the majority of genes fall, even when thousands of points overlap.
  • Red curve: The fitted logistic regression model.

Expected patterns in a well-prepared library:

  • Genes with low expression cluster at the bottom-left with duplication rates near zero. At low sequencing depth, it is unlikely for independent fragments to share identical start/end positions.
  • Genes with high expression have moderately higher duplication rates. This is expected — highly expressed genes produce many fragments, and some will share positions by chance alone.
  • The fitted curve rises gradually from left to right, staying below 0.5 for most of the expression range.

File: <sample>_duprateExpBoxplot.png / .svg

Shows the distribution of duplication rates across expression-level bins, providing a complementary view to the density scatter plot.

  • X-axis: Expression bins (log10 reads per kilobase).
  • Y-axis: Duplication rate.
  • Boxes: The interquartile range (25th to 75th percentile) of duplication rates for genes within each bin.
  • Horizontal line in each box: Median duplication rate for that bin.
  • Whiskers: Extend to the most extreme points within 1.5 times the IQR.

Boxes should be near zero at low expression and gradually rise at higher expression levels. Very wide boxes at low expression suggest inconsistent library preparation. The median line within each box should track the general trend seen in the density scatter plot. This plot helps determine whether high duplication rates are expression-dependent (expected) or uniform across all expression levels (problematic).

File: <sample>_expressionHist.png / .svg

A histogram of gene expression levels across all genes in the annotation.

  • X-axis: Expression level as log10(reads per kilobase).
  • Y-axis: Number of genes.

A bimodal distribution is common: one peak near zero (unexpressed or very lowly expressed genes) and another peak at moderate expression levels. The position and height of the peaks indicate the dynamic range of the experiment. A narrow distribution shifted to the left suggests low overall sequencing depth or poor library complexity.

If genes with few reads already show high duplication rates, this indicates technical PCR duplication rather than biological signal. The density scatter plot will show points clustered in the upper-left region, and the fitted curve will have a high intercept.

If the duplication rate is uniformly high across all expression levels (a flat, high curve), this suggests a severe PCR amplification issue — the library has very low complexity and most reads are duplicates regardless of gene expression.

Duplication rates near zero across all expression levels, including highly expressed genes, may indicate that duplicates were not properly marked in the BAM file. Verify that a duplicate-marking tool was run before RustQC.

IndicatorGood libraryProblematic library
InterceptClose to 0Above 0.5
Low-expression genesNear-zero dup rateElevated dup rate
Curve shapeGradual riseFlat and high, or steep early rise
Boxplot spreadTight boxes at low expressionWide boxes everywhere

RustQC produces output matching R dupRadar to floating-point precision. All 14 output columns are identical, and the fitted model parameters agree to 9-10 significant figures. The benchmarks below quantify the performance difference on real RNA-seq data, measured on AWS (2026-03-09).

Small dataset (~52K reads, chr6)
ToolRuntimeMax RSS
R dupRadar7.2s219.1 MB
RustQC (all tools)25.9s182.1 MB
Large dataset (GM12878 REP1, ~186M reads)
ToolRuntimeMax RSS
R dupRadar2h 14m 24s491 MB
RustQC (all tools)14m 54s11.4 GB

Note: RustQC runtime shown is for all tools combined in a single pass. See Benchmark Details for a full breakdown.

Small dataset
MetricdupRadar (R)RustQCMatch
Intercept0.0331294701670.033129470252~9e-11 diff
Slope1.5522385678391.552238565556~2e-9 diff
Genes total2,9052,905Exact
Genes with reads (unique)620620Exact
Genes with reads (multi)640640Exact
Genes with duplicates203203Exact
allCounts (sum)21,26221,262Exact
filteredCounts (sum)18,60918,609Exact
allCountsMulti (sum)23,68523,685Exact
filteredCountsMulti (sum)20,82020,820Exact
Total values compared37,76537,765
Value mismatches0
Large dataset
MetricdupRadar (R)RustQCMatch
Intercept0.8191598522420.819159852329~9e-11 diff
Slope1.5368568679021.536856867835~7e-11 diff
Genes total63,67763,677Exact
Genes with reads (unique)23,85523,855Exact
Genes with reads (multi)24,95224,952Exact
allCounts (sum)65,153,06765,153,067Exact
filteredCounts (sum)15,207,23115,207,231Exact
allCountsMulti (sum)75,766,52975,766,529Exact
filteredCountsMulti (sum)21,986,61221,986,612Exact
Total values compared827,801827,801
Value mismatches0

The intercept and slope differences are at the 9th-10th significant figure, arising purely from floating-point arithmetic order-of-operations variance. All count columns are integer-identical. Model fit parameters match to 9-10 significant figures across all 63,677 genes, including integer count columns, duplication rates, and RPKM-normalised values.

Each output file can be individually enabled or disabled. See the Configuration page for details.

  • dupRadar: Sayols S, Scherzinger D, Klein H. dupRadar: a Bioconductor package for the assessment of PCR artifacts in RNA-Seq data. BMC Bioinformatics. 2016;17(1):428. Bioconductor page
  • featureCounts: Liao Y, Smyth GK, Shi W. featureCounts: an efficient general purpose program for assigning sequence reads to genomic features. Bioinformatics. 2014;30(7):923-930. Subread/featureCounts