Contents

1 Introduction

Nucleoli serve as major organizing hubs for the three-dimensional structure of mammalian heterochromatin1. Specific loci, termed nucleolar-associated domains (NADs), form frequent three-dimensional associations with nucleoli2. Early mammalian development is a critical period to study NAD biological function, because interactions between pericentromeric chromatin and perinucleolar regions are particularly dynamic during embryonic development4. Here, we developed a Bioconductor package NADfinder for bioinformatic analysis of the NAD-seq data, including normalization, smoothing, peak calling, peak trimming and annotation. We demonstrate the application of NADfinder in mapping the NADs in the mouse genome, determining how these associations are altered during embryonic stem cell (ESC) differentiation, and develop tools for study of these higher-order chromosome interactions in fixed and live single cells (Fig. 1).

Fig. 1 Workflow for NAD peak identification. Read counts are summarized for each 50kb moving window with step size of 1kb for nucleolar and genomic samples. Log2 ratio between nucleolar and genomic samples was computed for each window, followed by local background correction, smoothing, peak calling, filtering and annotation.

Fig. 1 Workflow for NAD peak identification. Read counts are summarized for each 50kb moving window with step size of 1kb for nucleolar and genomic samples. Log2 ratio between nucleolar and genomic samples was computed for each window, followed by local background correction, smoothing, peak calling, filtering and annotation.

2 Single pair of nucleoleus associated DNA and whole genomic DNA sequencing

samples
Here is an example to use NADfinder for peak calling.

2.1 Coverage calculation

Here is the code snippet for calculating coverage with a sliding window in a given step along the genome using a pair of bam files from genomic sample as background and purified nucleoleus-associated DNA as target signal.

## load the library
library(NADfinder)
library(SummarizedExperiment)
## test bam files in the package
path <- system.file("extdata", package = "NADfinder", lib.loc = NULL,
            mustWork = TRUE)
bamFiles <- dir(path, ".bam$")

## window size for tile of genome. Here we set it to a big window size,
## ie., 50k because of the following two reasons:
## 1. peaks of NADs are wide;
## 2. data will be smoothed better with bigger window size.
ws <- 50000
## step for tile of genome
step <- 5000
## Set the background. 
## 0.25 means 25% of the lower ratios will be used for background training.
backgroundPercentage <- 0.25
## Count the reads for each window with a given step in the genome.
## The output will be a GRanges object.
library(BSgenome.Mmusculus.UCSC.mm10)
se <- tileCount(reads=file.path(path, bamFiles), 
                genome=Mmusculus, 
                windowSize=ws, 
                step=step,
                mode = IntersectionNotStrict,
                dataOverSamples = FALSE)

Here we load the pre-computed coverage data single.count to save build time.

data(single.count)
se <- single.count

For quality asessment, cumulativePercentage can be used to plot the cumulative sums of sorted read counts for nucleolus-associated DNA and whole genomic DNA. We expect the cumulative sum in the genomic DNA to be close to a straight line because the coverage for the genomic DNA sample should be uniformly distributed. Unlike ChIP-seq data, the cumulative sum for the nucleoleus sample will not exhibit sharp changes because most of NADs are broad regions as wide as 100 kb. However, we should observe clear differences between the two curves.

## Calculate ratios for peak calling. We use nucleoleus vs genomic DNA.
dat <- log2se(se, nucleolusCols = "N18.subsampled.srt.bam", 
              genomeCols ="G18.subsampled.srt.bam",
              transformation = "log2CPMRatio")
## Smooth the ratios for each chromosome.
## We found that for each chromosome, the ratios are higher in 5'end than 3'end.
datList <- smoothRatiosByChromosome(dat, chr = c("chr18", "chr19"), N = 100)
## check the difference between the cumulative percentage tag allocation 
## in genome and nucleoleus samples.
cumulativePercentage(datList[["chr18"]])

2.2 Call peaks

Before peak calling, the function smoothRatiosByChromosome is used for log ratios calculation, normalization and smoothing.

The peaks will be called if the ratios are significantly higher than chromosome-specific background determined by trimPeaks. The following figure shows the peaks (black rectangles) called using normalized (green curve) and smoothed (red curve) log2 ratios.

## check the results of smooth function
chr18 <- datList[["chr18"]] ## we only have reads in chr18 in test data.
chr18subset <- subset(chr18, seq.int(floor(length(chr18)/10))*10)
chr18 <- assays(chr18subset)
ylim <- range(c(chr18$ratio[, 1], 
                chr18$bcRatio[, 1], 
                chr18$smoothedRatio[, 1]))
plot(chr18$ratio[, 1], 
     ylim=ylim*c(.9, 1.1), 
     type="l", main="chr18")
abline(h=0, col="cyan", lty=2)
points(chr18$bcRatio[, 1], type="l", col="green")
points(chr18$smoothedRatio[, 1], type="l", col="red")
legend("topright", 
       legend = c("raw_ratio", "background_corrected", "smoothed"), 
       fill = c("black", "green", "red"))
## call peaks for each chromosome
peaks <- lapply(datList, trimPeaks, 
                backgroundPercentage=backgroundPercentage, 
                cutoffAdjPvalue=0.05, countFilter=1000)
## plot the peaks in "chr18"
peaks.subset <- countOverlaps(rowRanges(chr18subset), peaks$chr18)>=1
peaks.run <- rle(peaks.subset)
run <- cumsum(peaks.run$lengths)
run <- data.frame(x0=c(1, run[-length(run)]), x1=run)
#run <- run[peaks.run$values, , drop=FALSE]
rect(xleft = run$x0, ybottom = ylim[2]*.75, 
     xright = run$x1, ytop = ylim[2]*.8,
     col = "black")

## convert list to a GRanges object
peaks.gr <- unlist(GRangesList(peaks))

The following shows how to save or export the called peaks for downstream analysis.

## output the peaks
write.csv(as.data.frame(unname(peaks.gr)), "peaklist.csv", row.names=FALSE)
## export peaks to a bed file.
library(rtracklayer)
#export(peaks.gr, "peaklist.bed", "BED")

3 Samples with more than one replicate

Data analysis with multiple biological replicates follows the same steps as that of a single paired samples, i.e., coverage calculation, normalization and smoothing, and peak calling. The only difference is that limma is used to determine the statistical significance in peak calling.

library(NADfinder)
## bam file path
path <- system.file("extdata", package = "NADfinder", lib.loc = NULL,
            mustWork = TRUE)
bamFiles <- dir(path, ".bam$")

f <- bamFiles
ws <- 50000
step <- 5000
library(BSgenome.Mmusculus.UCSC.mm10)
se <- tileCount(reads=file.path(path, f), 
                genome=Mmusculus, 
                windowSize=ws, 
                step=step)

Load saved coverage.

data(triplicate.count)
se <- triplicate.count

## Calculate ratios for nucleoloar vs genomic samples.
se <- log2se(se, 
             nucleolusCols = c("N18.subsampled.srt-2.bam", 
                                "N18.subsampled.srt-3.bam",
                                "N18.subsampled.srt.bam" ),
             genomeCols = c("G18.subsampled.srt-2.bam",
                            "G18.subsampled.srt-3.bam",
                            "G18.subsampled.srt.bam"), 
             transformation = "log2CPMRatio")
seList<- smoothRatiosByChromosome(se, chr="chr18")
cumulativePercentage(seList[["chr18"]])

peaks <- lapply(seList, callPeaks, 
                cutoffAdjPvalue=0.05, countFilter=1)
peaks <- unlist(GRangesList(peaks))
peaks
## GRanges object with 552 ranges and 6 metadata columns:
##             seqnames            ranges strand | N18.subsampled.srt.2.bam
##                <Rle>         <IRanges>  <Rle> |                <numeric>
##     chr18.1    chr18   2956001-3138000      * |                  1.97569
##     chr18.2    chr18   3141001-3249000      * |                  2.15444
##     chr18.3    chr18   3302001-3381000      * |                  1.82705
##     chr18.4    chr18   3413001-3455000      * |                  1.91337
##     chr18.5    chr18   3480001-3510000      * |                  1.86436
##         ...      ...               ...    ... .                      ...
##   chr18.552    chr18 90001001-90086000      * |                 2.017107
##   chr18.553    chr18 90087001-90182000      * |                 2.055698
##   chr18.554    chr18 90291001-90421000      * |                 1.223125
##   chr18.555    chr18 90465001-90554000      * |                 0.849524
##   chr18.556    chr18 90645001-90649000      * |                 1.937102
##             N18.subsampled.srt.3.bam N18.subsampled.srt.bam    AveSig
##                            <numeric>              <numeric> <numeric>
##     chr18.1                  1.97569                1.97569   1.97569
##     chr18.2                  2.15444                2.15444   2.15444
##     chr18.3                  1.82705                1.82705   1.82705
##     chr18.4                  1.91337                1.91337   1.91337
##     chr18.5                  1.86436                1.86436   1.86436
##         ...                      ...                    ...       ...
##   chr18.552                 2.017107               2.017107  2.017107
##   chr18.553                 2.055698               2.055698  2.055698
##   chr18.554                 1.223125               1.223125  1.223125
##   chr18.555                 0.849524               0.849524  0.849524
##   chr18.556                 1.937102               1.937102  1.937102
##                 P.value   adj.P.Val
##               <numeric>   <numeric>
##     chr18.1 6.33384e-45 1.17421e-43
##     chr18.2 6.64233e-45 1.17421e-43
##     chr18.3 7.95083e-45 1.17421e-43
##     chr18.4 2.58488e-43 1.93759e-42
##     chr18.5 8.17230e-45 1.17421e-43
##         ...         ...         ...
##   chr18.552 6.62835e-45 1.17421e-43
##   chr18.553 6.91237e-45 1.17421e-43
##   chr18.554 9.04965e-45 1.17421e-43
##   chr18.555 1.00991e-44 1.25051e-43
##   chr18.556 2.61297e-43 1.93759e-42
##   -------
##   seqinfo: 66 sequences (1 circular) from mm10 genome

The peaks can be visualized along the ideogram using plotSig.

ideo <- readRDS(system.file("extdata", "ideo.mm10.rds", 
                            package = "NADfinder"))
plotSig(ideo=ideo, grList=GRangesList(peaks), mcolName="AveSig", 
        layout=list("chr18"), 
        parameterList=list(types="heatmap"))

4 Session Info

sessionInfo()
## R version 4.2.1 (2022-06-23)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.5 LTS
## 
## Matrix products: default
## BLAS:   /home/biocbuild/bbs-3.16-bioc/R/lib/libRblas.so
## LAPACK: /home/biocbuild/bbs-3.16-bioc/R/lib/libRlapack.so
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_GB              LC_COLLATE=C              
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] BSgenome.Mmusculus.UCSC.mm10_1.4.3 BSgenome_1.66.0                   
##  [3] rtracklayer_1.58.0                 Biostrings_2.66.0                 
##  [5] XVector_0.38.0                     NADfinder_1.22.0                  
##  [7] SummarizedExperiment_1.28.0        Biobase_2.58.0                    
##  [9] MatrixGenerics_1.10.0              matrixStats_0.62.0                
## [11] GenomicRanges_1.50.0               GenomeInfoDb_1.34.0               
## [13] IRanges_2.32.0                     S4Vectors_0.36.0                  
## [15] BiocGenerics_0.44.0                BiocStyle_2.26.0                  
## 
## loaded via a namespace (and not attached):
##   [1] rappdirs_0.3.3                SparseM_1.81                 
##   [3] ATACseqQC_1.22.0              R.methodsS3_1.8.2            
##   [5] ggplot2_3.3.6                 csaw_1.32.0                  
##   [7] bit64_4.0.5                   knitr_1.40                   
##   [9] multcomp_1.4-20               DelayedArray_0.24.0          
##  [11] R.utils_2.12.1                data.table_1.14.4            
##  [13] rpart_4.1.19                  KEGGREST_1.38.0              
##  [15] TFBSTools_1.36.0              RCurl_1.98-1.9               
##  [17] AnnotationFilter_1.22.0       generics_0.1.3               
##  [19] metap_1.8                     GenomicFeatures_1.50.0       
##  [21] lambda.r_1.2.4                TH.data_1.1-1                
##  [23] RSQLite_2.2.18                bit_4.0.4                    
##  [25] tzdb_0.3.0                    mutoss_0.1-12                
##  [27] xml2_1.3.3                    httpuv_1.6.6                 
##  [29] assertthat_0.2.1              DirichletMultinomial_1.40.0  
##  [31] xfun_0.34                     hms_1.1.2                    
##  [33] jquerylib_0.1.4               evaluate_0.17                
##  [35] promises_1.2.0.1              fansi_1.0.3                  
##  [37] restfulr_0.0.15               progress_1.2.2               
##  [39] caTools_1.18.2                dbplyr_2.2.1                 
##  [41] Rgraphviz_2.42.0              DBI_1.1.3                    
##  [43] htmlwidgets_1.5.4             futile.logger_1.4.3          
##  [45] ellipsis_0.3.2                corrplot_0.92                
##  [47] dplyr_1.0.10                  backports_1.4.1              
##  [49] signal_0.7-7                  GenomicScores_2.10.0         
##  [51] bookdown_0.29                 annotate_1.76.0              
##  [53] biomaRt_2.54.0                deldir_1.0-6                 
##  [55] vctrs_0.5.0                   ensembldb_2.22.0             
##  [57] cachem_1.0.6                  Gviz_1.42.0                  
##  [59] checkmate_2.1.0               GenomicAlignments_1.34.0     
##  [61] prettyunits_1.1.1             mnormt_2.1.1                 
##  [63] cluster_2.1.4                 grImport_0.9-5               
##  [65] lazyeval_0.2.2                seqLogo_1.64.0               
##  [67] crayon_1.5.2                  edgeR_3.40.0                 
##  [69] pkgconfig_2.0.3               qqconf_1.3.0                 
##  [71] ProtGenerics_1.30.0           nnet_7.3-18                  
##  [73] rlang_1.0.6                   lifecycle_1.0.3              
##  [75] sandwich_3.0-2                filelock_1.0.2               
##  [77] BiocFileCache_2.6.0           mathjaxr_1.6-0               
##  [79] dichromat_2.0-0.1             AnnotationHub_3.6.0          
##  [81] VennDiagram_1.7.3             randomForest_4.7-1.1         
##  [83] graph_1.76.0                  Matrix_1.5-1                 
##  [85] Rhdf5lib_1.20.0               zoo_1.8-11                   
##  [87] base64enc_0.1-3               png_0.1-7                    
##  [89] rjson_0.2.21                  bitops_1.0-7                 
##  [91] R.oo_1.25.0                   KernSmooth_2.23-20           
##  [93] rhdf5filters_1.10.0           blob_1.2.3                   
##  [95] trackViewer_1.34.0            stringr_1.4.1                
##  [97] regioneR_1.30.0               readr_2.1.3                  
##  [99] jpeg_0.1-9                    CNEr_1.34.0                  
## [101] scales_1.2.1                  lpSolve_5.6.17               
## [103] memoise_2.0.1                 magrittr_2.0.3               
## [105] plyr_1.8.7                    zlibbioc_1.44.0              
## [107] compiler_4.2.1                BiocIO_1.8.0                 
## [109] RColorBrewer_1.1-3            plotrix_3.8-2                
## [111] Rsamtools_2.14.0              cli_3.4.1                    
## [113] ade4_1.7-19                   htmlTable_2.4.1              
## [115] formatR_1.12                  Formula_1.2-4                
## [117] MASS_7.3-58.1                 limSolve_1.5.6               
## [119] tidyselect_1.2.0              stringi_1.7.8                
## [121] highr_0.9                     yaml_2.3.6                   
## [123] locfit_1.5-9.6                ChIPpeakAnno_3.32.0          
## [125] latticeExtra_0.6-30           grid_4.2.1                   
## [127] VariantAnnotation_1.44.0      sass_0.4.2                   
## [129] polynom_1.4-1                 tools_4.2.1                  
## [131] parallel_4.2.1                rstudioapi_0.14              
## [133] TFMPvalue_0.0.9               foreign_0.8-83               
## [135] metapod_1.6.0                 gridExtra_2.3                
## [137] digest_0.6.30                 BiocManager_1.30.19          
## [139] shiny_1.7.3                   pracma_2.4.2                 
## [141] quadprog_1.5-8                Rcpp_1.0.9                   
## [143] BiocVersion_3.16.0            later_1.3.0                  
## [145] httr_1.4.4                    motifStack_1.42.0            
## [147] AnnotationDbi_1.60.0          biovizBase_1.46.0            
## [149] Rdpack_2.4                    colorspace_2.0-3             
## [151] XML_3.99-0.12                 splines_4.2.1                
## [153] RBGL_1.74.0                   sn_2.1.0                     
## [155] multtest_2.54.0               preseqR_4.0.0                
## [157] xtable_1.8-4                  jsonlite_1.8.3               
## [159] futile.options_1.0.1          poweRlaw_0.70.6              
## [161] EmpiricalBrownsMethod_1.26.0  R6_2.5.1                     
## [163] TFisher_0.2.0                 Hmisc_4.7-1                  
## [165] pillar_1.8.1                  htmltools_0.5.3              
## [167] mime_0.12                     glue_1.6.2                   
## [169] fastmap_1.1.0                 BiocParallel_1.32.0          
## [171] interactiveDisplayBase_1.36.0 codetools_0.2-18             
## [173] mvtnorm_1.1-3                 utf8_1.2.2                   
## [175] lattice_0.20-45               bslib_0.4.0                  
## [177] tibble_3.1.8                  numDeriv_2016.8-1.1          
## [179] baseline_1.3-4                curl_4.3.3                   
## [181] gtools_3.9.3                  magick_2.7.3                 
## [183] GO.db_3.16.0                  interp_1.1-3                 
## [185] survival_3.4-0                limma_3.54.0                 
## [187] rmarkdown_2.17                InteractionSet_1.26.0        
## [189] munsell_0.5.0                 rhdf5_2.42.0                 
## [191] GenomeInfoDbData_1.2.9        HDF5Array_1.26.0             
## [193] reshape2_1.4.4                gtable_0.3.1                 
## [195] rbibutils_2.2.9

References

1. Politz, J. C. R., Scalzo, D. & Groudine, M. The redundancy of the mammalian heterochromatic compartment. Current opinion in genetics & development 37, 1–8 (2016).

2. Németh, A. et al. Initial genomics of the human nucleolus. PLoS Genet 6, e1000889 (2010).

3. Koningsbruggen, S. van et al. High-resolution whole-genome sequencing reveals that specific chromatin domains from most human chromosomes associate with nucleoli. Molecular biology of the cell 21, 3735–3748 (2010).

4. Aguirre-Lavin, T. et al. 3D-fish analysis of embryonic nuclei in mouse highlights several abrupt changes of nuclear organization during preimplantation development. BMC developmental biology 12, 1 (2012).

5. Popken, J. et al. Reprogramming of fibroblast nuclei in cloned bovine embryos involves major structural remodeling with both striking similarities and differences to nuclear phenotypes of in vitro fertilized embryos. Nucleus 5, 555–589 (2014).