jared.andrews07 — Thank you for your input and for taking the time to review the code.
Based on my analysis of the deepTools code, I believe that, when invoking deepTools' bamCoverage from the command line, any custom scaling factor assigned through --scalingFactor ${value} (where variable value is a user-provided positive integer or float) is recognized by the function get_scale_factor() (which is called by bamCoverage.py) and thus included in any subsequent RPKM (or RPGC or CPM or BPM) calculation. This is because all command-line argument values, including ${value} from --scalingFactor, are assigned to the object args in bamCoverage.py; the object args is then passed to the get_scale_factor() function as an argument, meaning that args.scaleFactor is accessible to code within the function get_scale_factor().
Within getScaleFactor.py, we see
elif args.normalizeUsing == 'RPKM':
# Print output, since normalzation stuff isn't printed to stderr otherwise
sys.stderr.write("normalization: RPKM\n")
# the RPKM is the # reads per tile / \
# ( total reads (in millions) * tile length in Kb)
million_reads_mapped = float(bam_mapped) / 1e6
tile_len_in_kb = float(args.binSize) / 1000
scale_factor *= 1.0 / (million_reads_mapped * tile_len_in_kb)
if debug:
print("scale factor using RPKM is {0}".format(args.scaleFactor))
The scale_factor is multiplied by 1.0 / (million_reads_mapped * tile_len_in_kb), making a new "RPKM-transformed" scaling factor. Back in bamCoverage.py, this updated value for scale_factor is stored and made available to subsequent code. Ultimately, the scaling factor is applied to coverage/signal and written out to a bedGraph or a bigWig via the function writeBedGraph.scaleCoverage.
Anyway, I hope I'm not coming off as pedantic with all of this. The reason I bring this all up is because I work with researchers who have historically called bamCoverage like this:
bamCoverage \
-b "${bam}" \
--binSize "${bin_size}" \
--scaleFactor ${scaling_factor} \
--normalizeUsing RPKM \
-o "${bigwig}"
This seems weird to me and I want to check my understanding with people that have knowledge of and experience with calculating and plotting "coverage" for ChIP-seq (and other NGS) assays.
The reason this seems weird to me is that, in our specific analyses, we have calculated scaling factors based on spike-in reads. We use spike-ins because we expect genome-wide shifts in coverage between our control and experimental "models"; these shifts would not really be apparent without spike-in normalization.
- Wouldn't applying an additional normalization to the spike-in normalization skew the resulting coverage, potentially altering the appearance of genome-wide shifts?
- For (rough) inter-sample comparisons, would it not be preferable to work with counts following the application of the scaling factor rather than the counts following both a scaling-factor normalization and RPKM normalization?
Also, if it is valid to apply an additional normalization, I recognize that B/TPM normalization should be favored over R/FPKM normalization since, per this and other papers,
The intended meaning of [R/FPKM] is a measure of relative RNA molar concentration (rmc) of a transcript in a sample. If a measure of RNA abundance is proportional to rmc, then their average over genes within a sample should be a constant, namely the inverse of the number of transcripts mapped. Unfortunately, [R/FPKM] does not respect this invariance property and thus cannot be an accurate measure of rmc (Wagner et al. 2012). In fact, the average [R/FPKM] varies from sample to sample. Therefore, [B/TPM] ([bins/transcripts] per million), a slight modification of [R/FPKM], was proposed (Li and Dewey 2011; Wagner et al. 2012).
If you made it this far, thanks for reading it all.