@@ -28,8 +28,9 @@ library(iscream)
2828
2929### Input BED files
3030
31- Running this vignette requires downloading 2GB of BED files and tabix indices
32- from this Zenodo record: < https://zenodo.org/records/14733834 > .
31+ Running this vignette requires downloading 2GB of single-cell whole genome
32+ bisulfite sequencing (WGBS) BED files and tabix indices from this Zenodo
33+ record: < https://zenodo.org/records/14733834 > .
3334
3435
3536``` r
@@ -87,22 +88,25 @@ head(regions)
8788
8889### Make tabix queries
8990
90- The ` tabix() ` function can be used to query regions from BED files much like the
91- * tabix* shell command. It returns the queried lines from the BED files, parsed
92- into columns, as a ` data.table ` . ` tabix() ` is a generic BED files query function
93- and is not restricted to methylation BED files. It has built-in support for
94- BISCUIT, Bismark, and BSBolt BED file column names, set with the ` aligner `
95- argument, but can take other column names with the ` col.names ` argument.
91+ The ` tabix() ` function can be used to query regions from BED files much like
92+ the * tabix* shell command. It returns the queried lines from the BED files,
93+ parsed into columns, as a ` data.table ` . ` tabix() ` is a generic BED files query
94+ function. It has support for the BISCUIT, Bismark, and BSBolt aligners, set
95+ with the ` aligner ` argument, to correctly set column names in the output.
9696
9797If multiple input files are provided, they are queried in parallel. If `raw =
9898TRUE` , ` tabix()` will return the same data as ` Rsamtools::scanTabix()` does - a
99- named list of strings.
99+ named list of strings. For large queries, ` tabix() ` may currently seem
100+ unresponsive, but a progress bar is in development to show its progress.
100101
101102
102103``` r
103104system.time(tbx_query <- tabix(bedfiles , regions , col.names = c(" beta" , " coverage" )))
104105# > user system elapsed
105- # > 32.184 5.368 11.000
106+ # > 29.272 7.398 10.178
107+ ```
108+
109+ ``` r
106110tbx_query
107111# > chr start end beta coverage file
108112# > <char> <int> <int> <num> <int> <char>
@@ -121,105 +125,101 @@ tbx_query
121125
122126### Get summary data
123127
124- To get a summary of the methylation information of the gene bodies use
125- ` summarize_regions ` , providing the gene name column as the feature column:
128+ To get a summary of the information of the gene bodies use ` summarize_regions ` ,
129+ providing the gene name column as the feature column:
126130
127131
128132``` r
129- system.time(summary_query <- summarize_meth_regions (
133+ system.time(summary_query <- summarize_regions (
130134 bedfiles ,
131135 regions ,
136+ columns = 4 ,
137+ col_names = " beta" ,
132138 feature_col = " gene" )
133139)
134- # > [11:20:22.814673 ] [iscream::summarize_regions] [info] Summarizing 5000 regions from 100 bedfiles
135- # > [11:20:22.814996 ] [iscream::summarize_regions] [info] using sum, mean, median, stddev, variance, min, max, range, count
136- # > [11:20:22.815005 ] [iscream::summarize_regions] [info] with columns 4, 5 as coverage, M
140+ # > [15:59:49.032602 ] [iscream::summarize_regions] [info] Summarizing 5000 regions from 100 bedfiles
141+ # > [15:59:49.032651 ] [iscream::summarize_regions] [info] using sum, mean, median, stddev, variance, min, max, range, count
142+ # > [15:59:49.032653 ] [iscream::summarize_regions] [info] with columns 4 as beta
137143# > user system elapsed
138- # > 24.616 0.618 4.913
144+ # > 27.525 0.536 5.107
145+ ```
146+
147+ ``` r
139148head(summary_query )
140- # > feature file coverage.sum M.sum coverage.mean M.mean
141- # > 1 ATAD3B bisc_SRR6911624 109 95 1.101010 0.9595960
142- # > 2 PRDM16 bisc_SRR6911624 1470 789 1.074561 0.5767544
143- # > 3 PEX10 bisc_SRR6911624 62 15 1.000000 0.2419355
144- # > 4 PEX14 bisc_SRR6911624 258 200 1.011765 0.7843137
145- # > 5 PLCH2 bisc_SRR6911624 272 195 1.066667 0.7647059
146- # > 6 SPSB1 bisc_SRR6911624 74 64 1.000000 0.8648649
147- # > coverage.median M.median coverage.stddev M.stddev coverage.variance
148- # > 1 1 1 0.3028757 0.4932026 0.09173366
149- # > 2 1 1 0.2892802 0.5888135 0.08368306
150- # > 3 1 0 0.0000000 0.4317514 0.00000000
151- # > 4 1 1 0.1080374 0.4307897 0.01167207
152- # > 5 1 1 0.2796698 0.5169655 0.07821522
153- # > 6 1 1 0.0000000 0.3442015 0.00000000
154- # > M.variance coverage.min M.min coverage.max M.max coverage.range M.range
155- # > 1 0.2432488 1 0 2 2 1 2
156- # > 2 0.3467014 1 0 3 3 2 3
157- # > 3 0.1864093 1 0 1 1 0 1
158- # > 4 0.1855797 1 0 2 2 1 2
159- # > 5 0.2672534 1 0 3 3 2 3
160- # > 6 0.1184746 1 0 1 1 0 1
161- # > cpg_count
162- # > 1 99
163- # > 2 1368
164- # > 3 62
165- # > 4 255
166- # > 5 255
167- # > 6 74
149+ # > feature file beta.sum beta.mean beta.median beta.stddev
150+ # > 1 ATAD3B bisc_SRR6911624 85.000 0.8585859 1 0.3502215
151+ # > 2 PRDM16 bisc_SRR6911624 723.500 0.5288743 1 0.4976973
152+ # > 3 PEX10 bisc_SRR6911624 15.000 0.2419355 0 0.4317514
153+ # > 4 PEX14 bisc_SRR6911624 198.000 0.7764706 1 0.4174294
154+ # > 5 PLCH2 bisc_SRR6911624 184.333 0.7228745 1 0.4474832
155+ # > 6 SPSB1 bisc_SRR6911624 64.000 0.8648649 1 0.3442015
156+ # > beta.variance beta.min beta.max beta.range count
157+ # > 1 0.1226551 0 1 1 99
158+ # > 2 0.2477026 0 1 1 1368
159+ # > 3 0.1864093 0 1 1 62
160+ # > 4 0.1742473 0 1 1 255
161+ # > 5 0.2002412 0 1 1 255
162+ # > 6 0.1184746 0 1 1 74
168163```
169164
170- Alternatively to get just the beta column use the general ` summarize_regions ()`
171- function, specifying the columns to summarize :
165+ Alternatively to use the methylation-specific ` summarize_meth_regions ()`
166+ function:
172167
173168
174169``` r
175- system.time(summary_query <- summarize_regions (
170+ system.time(summary_query <- summarize_meth_regions (
176171 bedfiles ,
177172 regions ,
178- columns = 4 ,
179- col_names = " beta" ,
180173 feature_col = " gene" )
181174)
182175```
183176
184177### Build matrices
185178
186- The ` make_bsseq_mat() ` function queries and stores every CpG in the input
187- regions. Unlike ` summarize_regions() ` the output matrix dimensions are unknown
188- at runtime. Although usually quite fast, if the CpG count is very large and
189- there are few overlaps in the CpGs between files, this can take a long time.
190- Here, gene bodies are large and the final matrix can contain millions of CpGs.
191- Further, with single-cell data, chances are new CpGs are found in every file.
179+ The ` make_mat() ` function queries and stores every locus within the input
180+ regions across input files. Unlike ` summarize_regions() ` the output matrix
181+ dimensions are unknown at runtime. Although usually quite fast, if the locus
182+ count is very large and there are few overlaps in the loci between files, this
183+ can take a long time. Here, gene bodies are large and the final matrix can
184+ contain millions of loci. Further, with sparse data, chances are new loci are
185+ found in every file.
192186
193187Preallocating the number of rows, however, can drastically reduce runtime.
194- Since we got 45 million CpGs from all the BED files with the tabix query above,
195- we can expect approximately between 5 and 10 million unique CpGs as single-cell
196- data has lower coverage than bulk. We already have the tabix query so we can get
197- the unique CpG count here and use it to preallocate the matrix, reducing the
198- number of matrix resizes. We'll add 100,000 extra on top of the existing count
199- to be safe since every avoided resize cuts off at least a couple seconds from
200- the runtime. Making tabix queries can be a relatively quick way to approximate
201- the CpG count of a dataset. If you haven't done a tabix query of the full
202- dataset, you can approximate how many CpGs to expect based on CpG counts in one
203- file and the coverage of your WGBS method.
188+ Since we got 45 million loci/CpGs from all the BED files with the tabix query
189+ above, we can expect approximately between 5 and 10 million unique loci as
190+ single-cell data has lower coverage than bulk. We already have the tabix query
191+ so we can get the unique CpG count here and use it to preallocate the matrix,
192+ reducing the number of matrix resizes. We'll add 100,000 extra on top of the
193+ existing count to be safe since every avoided resize cuts off at least a couple
194+ seconds from the runtime. Making tabix queries can be a relatively quick way to
195+ approximate the CpG count of a dataset. If you haven't done a tabix query of
196+ the full dataset, you can approximate how many CpGs to expect based on CpG
197+ counts in one file and the coverage of your WGBS method. Here we make a matrix
198+ of the beta-values in the 4th column:
199+
204200
205201``` r
206202cpg.count <- tbx_query $ start | > unique() | > length()
207- system.time(meth_mat <- make_bsseq_mat (
203+ system.time(meth_mat <- make_mat (
208204 bedfiles ,
209205 regions ,
206+ column = 4 ,
210207 sparse = TRUE ,
211208 prealloc = cpg.count + 1e5
212209))
213- # > [11:20:29.883091 ] [iscream::query_all] [info] Querying 5000 regions from 100 bedfiles
210+ # > [15:59:56.450308 ] [iscream::query_all] [info] Querying 5000 regions from 100 bedfiles
214211# >
215- # > [11:21:13.888292 ] [iscream::query_all] [info] Creating metadata vectors
216- # > [11:21:14.365486 ] [iscream::query_all] [info] 7276107 loci found - 16250 extra rows allocated with 0 resizes
217- # > [11:21:22.518141 ] [iscream::query_all] [info] Creating sparse matrix
212+ # > [16:00:40.571982 ] [iscream::query_all] [info] Creating metadata vectors
213+ # > [16:00:40.867558 ] [iscream::query_all] [info] 7276107 loci found - 16250 extra rows allocated with 0 resizes
214+ # > [16:00:44.217883 ] [iscream::query_all] [info] Creating sparse matrix
218215# > user system elapsed
219- # > 275.560 2.978 53.696
216+ # > 335.525 1.186 47.981
217+ ```
218+
219+ ``` r
220220str(meth_mat )
221- # > List of 5
222- # > $ M :Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
221+ # > List of 4
222+ # > $ value :Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
223223# > .. ..@ i : int [1:32627134] 9 11 12 13 14 15 16 17 18 19 ...
224224# > .. ..@ p : int [1:101] 0 157256 362760 626888 904511 1158005 1437240 1729046 2014168 2231683 ...
225225# > .. ..@ Dim : int [1:2] 7276107 100
@@ -228,73 +228,65 @@ str(meth_mat)
228228# > .. .. ..$ : chr [1:100] "bisc_SRR6911624" "bisc_SRR6911625" "bisc_SRR6911626" "bisc_SRR6911627" ...
229229# > .. ..@ x : num [1:32627134] 1 1 1 1 1 1 1 1 1 1 ...
230230# > .. ..@ factors : list()
231- # > $ Cov :Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
232- # > .. ..@ i : int [1:43227794] 0 1 2 3 4 5 6 7 8 9 ...
233- # > .. ..@ p : int [1:101] 0 217582 488919 850016 1220035 1545163 1920047 2322291 2713873 3020522 ...
234- # > .. ..@ Dim : int [1:2] 7276107 100
235- # > .. ..@ Dimnames:List of 2
236- # > .. .. ..$ : NULL
237- # > .. .. ..$ : chr [1:100] "bisc_SRR6911624" "bisc_SRR6911625" "bisc_SRR6911626" "bisc_SRR6911627" ...
238- # > .. ..@ x : num [1:43227794] 1 1 1 1 1 1 1 1 1 1 ...
239- # > .. ..@ factors : list()
240231# > $ pos : int [1:7276107] 1472309 1472386 1472390 1472394 1472407 1472414 1472439 1472489 1472503 1473922 ...
241232# > $ chr : chr [1:7276107] "chr1" "chr1" "chr1" "chr1" ...
242233# > $ sampleNames: chr [1:100] "bisc_SRR6911624" "bisc_SRR6911625" "bisc_SRR6911626" "bisc_SRR6911627" ...
243234```
244235
245- The output of ` make_bsseq_mat() ` is a named list containing matrices of coverage
246- values and M values and vectors of the sample names, chromosome names and
247- positions of the loci.
236+ The output of ` make_mat() ` is a named list containing a matrix of beta values
237+ and vectors of the sample names, chromosome names and positions of the loci.
248238
249- This list can be used to produce a BSseq object. However since BSseq cannot work
250- with sparse matrices, the two matrices would need to be converted to dense
251- matrices first.
252-
253- ``` r
254- bs <- do.call(BSseq , meth_mat )
255- ```
239+ If you want to make a BSseq object, you can use ` make_bsseq_mat() ` which makes
240+ both beta/M-value and coverage value matrices. Its output can be used to
241+ produce a BSseq object. However since BSseq cannot work with sparse matrices,
242+ the two matrices would need to be converted to dense matrices first.
256243
257- If you want just one column from the BED file as a matrix instead of the two
258- for BSseq, use ` make_mat() ` . For example, to get a matrix of beta values
259- (column 4 in the BISCUIT BED file) run
260244
261245``` r
262- make_mat(bedfiles , regions , col = 4 , mat_name = " beta" )
246+ meth_mat <- make_bsseq_mat(
247+ bedfiles ,
248+ regions ,
249+ sparse = TRUE ,
250+ prealloc = cpg.count + 1e5
251+ )
252+ bs <- do.call(BSseq , meth_mat )
263253```
264254
265255## Session info
266256
267257
268258``` r
269259sessionInfo()
270- # > R version 4.4.2 (2024-10-31 )
260+ # > R version 4.4.3 (2025-02-28 )
271261# > Platform: x86_64-pc-linux-gnu
272- # > Running under: AlmaLinux 9.5 (Teal Serval)
262+ # > Running under: Ubuntu 24.04.2 LTS
273263# >
274264# > Matrix products: default
275- # > BLAS/LAPACK: /usr/lib64/libopenblas-r0.3.26.so ; LAPACK version 3.12.0
265+ # > BLAS/LAPACK: /nix/store/6kknwpcf8fl7ihkkxmdb6p764kdn443n-blas-3/lib/libblas.so.3 ; LAPACK version 3.12.0
276266# >
277267# > locale:
278- # > [1] LC_CTYPE=C.utf8 LC_NUMERIC=C LC_TIME=C.UTF-8
279- # > [4] LC_COLLATE=C.utf8 LC_MONETARY=C.UTF-8 LC_MESSAGES=C.utf8
280- # > [7] LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C
281- # > [10] LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
268+ # > [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
269+ # > [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
270+ # > [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
271+ # > [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
272+ # > [9] LC_ADDRESS=C LC_TELEPHONE=C
273+ # > [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
282274# >
283- # > time zone: UTC
284- # > tzcode source: internal
275+ # > time zone: America/Detroit
276+ # > tzcode source: system (glibc)
285277# >
286278# > attached base packages:
287279# > [1] stats graphics grDevices utils datasets methods base
288280# >
289281# > other attached packages:
290- # > [1] data.table_1.17.0 iscream_0.0 .0.9000
282+ # > [1] data.table_1.17.0 iscream_0.1 .0.9000
291283# >
292284# > loaded via a namespace (and not attached):
293- # > [1] compiler_4.4.2 parallelly_1.43 .0 Matrix_1.7-3
294- # > [4] parallel_4.4.2 tools_4.4.2 Rcpp_1.0.14
295- # > [7] grid_4.4.2 knitr_1.50 xfun_0.51
285+ # > [1] compiler_4.4.3 parallelly_1.42 .0 Matrix_1.7-2
286+ # > [4] parallel_4.4.3 tools_4.4.3 Rcpp_1.0.14
287+ # > [7] grid_4.4.3 knitr_1.47 xfun_0.51
296288# > [10] RcppParallel_5.1.10 stringfish_0.16.0 lattice_0.22-6
297- # > [13] evaluate_1.0.3
289+ # > [13] evaluate_0.24.0
298290```
299291
300292<!-- vim: set filetype=rmd: -->
0 commit comments