GenomicDataStream
A scalable interface between data and analysis
Loading...
Searching...
No Matches
bgenstream.h
Go to the documentation of this file.
1/***********************************************************************
2 * @file bgenstream.h
3 * @author Gabriel Hoffman
4 * @email gabriel.hoffman@mssm.edu
5 * @brief reads a BGEN into matrix in chunks, storing variants in columns
6 * Copyright (C) 2024 Gabriel Hoffman
7 ***********************************************************************/
8
9#ifndef BGEN_STREAM_H_
10#define BGEN_STREAM_H_
11
12#ifndef DISABLE_EIGEN
13// #include <Eigen/Sparse>
14#include <RcppEigen.h>
15#endif
16
17#include <string>
18
19#include "genfile/bgen/View.hpp"
20#include "genfile/bgen/IndexQuery.hpp"
21
22#include "VariantInfo.h"
24#include "GenomicRanges.h"
25#include "VariantSet.h"
26#include "bgen_load.h"
27#include "utils.h"
28
29using namespace std;
30using namespace arma;
31using namespace genfile::bgen;
32
33namespace gds {
34
39static genfile::bgen::View::UniquePtr construct_view(
40 const string & filename) {
41
42 if( ! filesystem::exists( filename ) ){
43 throw runtime_error("File does not exist: " + filename);
44 }
45
46 View::UniquePtr view = genfile::bgen::View::create( filename ) ;
47
48 return view ;
49}
50
51static IndexQuery::UniquePtr construct_query(const string &index_filename){
52
53 if( ! filesystem::exists( index_filename ) ){
54 throw runtime_error("File does not exist: " + index_filename);
55 }
56
57 IndexQuery::UniquePtr query = IndexQuery::create( index_filename ) ;
58 return query;
59}
60
61
62
70[[maybe_unused]]
71static genfile::bgen::View::UniquePtr construct_view(
72 const string & filename,
73 const string & index_filename,
74 const GenomicRanges & gr) {
75 // const vector<string> & rsids = vector<string>()) {
76
77 // create view of BGEN file
78 View::UniquePtr view = construct_view( filename );
79
80 // process region queries
81 if( gr.size() > 0){
82 IndexQuery::UniquePtr query = construct_query(index_filename);
83 for( int i = 0; i < gr.size(); i++ ) {
84 query->include_range( IndexQuery::GenomicRange( gr.get_chrom(i) , gr.get_start(i), gr.get_end(i) ) ) ;
85 }
86 // query->include_rsids( rsids ) ;
87 query->initialise() ;
88 view->set_query( query ) ;
89 }
90
91 return view ;
92}
93
94static std::shared_ptr<VariantSet> getVariantSet(
95 const string & index_filename){
96
97 // Initialize SQLite database
98 SqliteIndexQuery query(index_filename);
99
100 std::vector<std::string> rsid, chrom;
101 std::vector<int> position;
102
103 // populate rsid, chrom, position for all variants
104 query.get_variant_info(&rsid, &chrom, &position);
105
106 // construct VariantSet
107 VariantSet vs(chrom, position, rsid);
108
109 // return shared pointer
110 return std::make_shared<VariantSet>(vs);
111}
112
113
114
119 public GenomicDataStream {
120 public:
121
123
125
129
130 // Initialize view from just bgen file
131 view = construct_view( param.file ) ;
132
133 filenameIdxGlobal = param.file + ".bgi";
134 // queryGlobal = construct_query( filenameIdxGlobal );
135
136 // Read VariantSet from SQLite .bgi file
137 vs = getVariantSet( filenameIdxGlobal );
138
139 // apply region filters
141
142 // Filter samples
143 if( param.samples.compare("-") == 0 ){
144 get_all_samples( *view, &number_of_samples, &sampleNames, &requestedSamplesByIndexInDataIndex ) ;
145 }else{
146
147 // get subset of samples
148 vector<string> requestedSamples;
149
150
151 requestedSamples = split_any_of(
152 erase_all_copy(param.samples, " "),
153 "\t,\n");
154
155 get_requested_samples( *view, requestedSamples, &number_of_samples, &sampleNames, &requestedSamplesByIndexInDataIndex ) ;
156 }
157
158 vInfo = new VariantInfo( sampleNames );
159
160 // store probabilities
161 probs.reserve( n_samples() * param.chunkSize * max_entries_per_sample);
162
163 // store dosage
164 matDosage.reserve( n_samples() * param.chunkSize );
165 }
166
170 if( vInfo != nullptr) delete vInfo;
171 }
172
175 void setRegions(const vector<string> &regions) override {
176
177 GenomicRanges gr( regions );
178
179 auto query = construct_query( filenameIdxGlobal );
180
181 if( gr.size() > 0){
182
183 // get indeces of variants overlapping these regions
184 varIdx = vs->getIndeces( gr );
185
186 // get IDs of these variants
187 vector<string> variantIDs = vs->getVariantIDs(varIdx);
188
189 // query these variants from the BGEN file
190 query->include_rsids( variantIDs );
191 query->initialise() ;
192 view->set_query( query ) ;
193 }
194
195 // number of variants after filtering
196 n_variants_total = view->number_of_variants() ;
197
198 // set current position of index
199 variant_idx_start = 0;
200 }
201
204 int n_samples() override {
205 return number_of_samples;
206 }
207
210 vector<string> getSampleNames() override {
211 return vInfo->sampleNames;
212 }
213
216 string getStreamType() override {
217 return toString( param.fileType);
218 }
219
221 return vs->getChromRanges();
222 }
223
224 bool getNextChunk( DataChunk<arma::mat> & chunk, const bool &useFilter = true) override {
225
226 // Update matDosage and vInfo for the chunk
227 bool ret = getNextChunk_helper();
228
229 if( useFilter ){
230 // modifies matDosage and vInfo directly
231 applyVariantFilter(matDosage, vInfo, number_of_samples, getMAF(), getMinVariance() );
232 }
233
234 arma::mat M(matDosage.data(), number_of_samples, vInfo->size(), false, true);
235 chunk = DataChunk<arma::mat>( M, vInfo );
236
237 return ret;
238 }
239
240 bool getNextChunk( DataChunk<arma::sp_mat> & chunk, const bool &useFilter = true) override {
241
242 // Update matDosage and vInfo for the chunk
243 bool ret = getNextChunk_helper();
244
245 if( useFilter ){
246 // modifies matDosage and vInfo directly
247 applyVariantFilter(matDosage, vInfo, number_of_samples, getMAF(), getMinVariance() );
248 }
249
250 arma::mat M(matDosage.data(), number_of_samples, vInfo->size(), false, true);
251
252 chunk = DataChunk<arma::sp_mat>( arma::sp_mat(M), vInfo );
253
254 return ret;
255 }
256
257 #ifndef DISABLE_EIGEN
258 bool getNextChunk( DataChunk<Eigen::MatrixXd> & chunk, const bool &useFilter = true) override {
259
260 // Update matDosage and vInfo for the chunk
261 bool ret = getNextChunk_helper();
262
263 if( useFilter ){
264 // modifies matDosage and vInfo directly
265 applyVariantFilter(matDosage, vInfo, number_of_samples, getMAF(), getMinVariance() );
266 }
267
268 Eigen::MatrixXd M = Eigen::Map<Eigen::MatrixXd>(matDosage.data(), number_of_samples, vInfo->size());
269
270 chunk = DataChunk<Eigen::MatrixXd>( M, vInfo );
271
272 return ret;
273 }
274
275
276 bool getNextChunk( DataChunk<Eigen::SparseMatrix<double> > & chunk, const bool &useFilter = true) override {
277
278 // Update matDosage and vInfo for the chunk
279 bool ret = getNextChunk_helper();
280
281 if( useFilter ){
282 // modifies matDosage and vInfo directly
283 applyVariantFilter(matDosage, vInfo, number_of_samples, getMAF(), getMinVariance() );
284 }
285
286 Eigen::MatrixXd M = Eigen::Map<Eigen::MatrixXd>(matDosage.data(), number_of_samples, vInfo->size());
287
288 chunk = DataChunk<Eigen::SparseMatrix<double>>( M.sparseView(), vInfo );
289
290 return ret;
291 }
292 #endif
293
294 #ifndef DISABLE_RCPP
295 bool getNextChunk( DataChunk<Rcpp::NumericMatrix> & chunk, const bool &useFilter = true) override {
296
297 // Update matDosage and vInfo for the chunk
298 bool ret = getNextChunk_helper();
299
300 if( useFilter ){
301 // modifies matDosage and vInfo directly
302 applyVariantFilter(matDosage, vInfo, number_of_samples, getMAF(), getMinVariance() );
303 }
304
305 Rcpp::NumericMatrix M(number_of_samples, vInfo->size(), matDosage.data());
306 colnames(M) = Rcpp::wrap( vInfo->getFeatureNames() );
307 rownames(M) = Rcpp::wrap( vInfo->sampleNames );
308
309 chunk = DataChunk<Rcpp::NumericMatrix>( M, vInfo );
310
311 return ret;
312 }
313 #endif
314
315 bool getNextChunk( DataChunk<vector<double> > & chunk, const bool &useFilter = true) override {
316
317 // Update matDosage and vInfo for the chunk
318 bool ret = getNextChunk_helper();
319
320 if( useFilter ){
321 // modifies matDosage and vInfo directly
322 applyVariantFilter(matDosage, vInfo, number_of_samples, getMAF(), getMinVariance() );
323 }
324
325 chunk = DataChunk<vector<double> >( matDosage, vInfo );
326
327 return ret;
328 }
329
330 private:
331 View::UniquePtr view = nullptr;
332 // IndexQuery::UniquePtr queryGlobal = nullptr;
333 std::shared_ptr<VariantSet> vs;
334 size_t number_of_samples = 0;
335 vector<string> sampleNames;
336 map<size_t, size_t> requestedSamplesByIndexInDataIndex;
337 VariantInfo *vInfo = nullptr;
338 vector<double> probs;
339 vector<double> matDosage;
340 size_t max_entries_per_sample = 4;
341 int n_variants_total;
342 int variant_idx_start;
343 vector<int> varIdx;
344
345 bool getNextChunk_helper(){
346
347 // clear data, but keep allocated capacity
348 matDosage.clear();
349 vInfo->clear();
350
351 // number of variants in this chunk
352 int chunkSize = min(param.chunkSize, n_variants_total - variant_idx_start);
353 chunkSize = max(chunkSize, 0);
354
355 // if no variants remain, return false
356 if( chunkSize == 0) return false;
357
358 vector<int> data_dimension;
359 data_dimension.push_back(chunkSize);
360 data_dimension.push_back(number_of_samples);
361 data_dimension.push_back(max_entries_per_sample);
362
363 vector<int> ploidy_dimension;
364 ploidy_dimension.push_back( chunkSize ) ;
365 ploidy_dimension.push_back( number_of_samples ) ;
366
367 vector<int> ploidy(ploidy_dimension[0]*ploidy_dimension[1], numeric_limits<int>::quiet_NaN());
368 vector<bool> phased( chunkSize, numeric_limits<bool>::quiet_NaN() ) ;
369
370 string SNPID, rsid, chromosome;
371 genfile::bgen::uint32_t position;
372 vector<string> alleles;
373
374 // Iterate through variants
375 size_t k = 0;
376 for( size_t j = variant_idx_start; j < variant_idx_start + chunkSize; j++ ) {
377
378 // read variant information
379 view->read_variant( &SNPID, &rsid, &chromosome, &position, &alleles ) ;
380
381 // store variant info
382 vInfo->addVariant(chromosome, position, rsid, alleles[0], alleles[1] );
383
384 // read genotype probabilities into DataSetter object
385 DataSetter setter(
386 &ploidy, ploidy_dimension,
387 &probs, data_dimension,
388 &phased,
389 k++,
390 requestedSamplesByIndexInDataIndex
391 );
392
393 view->read_genotype_data_block( setter ) ;
394 }
395 // increament starting position to beginning of next chunk
396 variant_idx_start += chunkSize;
397
398 // Convert to dosage values stored in vector<double>
399 //------------------
400
401 // use probs to create Cube
402 cube C(probs.data(), chunkSize, number_of_samples, max_entries_per_sample, true, true);
403
404 // weight alleles by dosage
405 // With max_entries_per_sample = 4, the unphased coding is
406 // AA/AB/BB/NULL so use weights 0/1/2/0 to conver to dosage
407 // since the last entry doesn't encode valid information
408 // When phased, the coding is [a1 a2] / [a1 a2]
409 // so use weights 0/1/0/1
410 // vec w_unph = {0,1,2,0};
411 // vec w_ph = {0,1,0,1};
412 // compute dosages with weights depend on phasing
413 // w = phased[j] ? w_ph : w_unph;
414 // dsg = C.row_as_mat(j).t() * w;
415 // BUT !!!
416 // in unphased data, they last entry can be NaN
417 // and NaN * 0 is still NaN
418 // so need to drop the last entry _manually_
419
420 vec v, dsg;
421 vec w_unph = {0,1,2};
422 vec w_ph = {0,1,0,1};
423 mat m;
424
425 // compute dosage from Cube
426 // copy results of each variant to vector<double>
427 for(int j=0; j<chunkSize; j++){
428 if( phased[j] ){
429 dsg = C.row_as_mat(j).t() * w_ph;
430 }else{
431 // extract columns 0,1,2
432 // skip 3rd
433 m = C.row_as_mat(j).t();
434 dsg = m.cols(0,2) * w_unph;
435 }
436
437 // replace missing with mean
438 if( param.missingToMean ) nanToMean( dsg );
439
440 // save vector in matDosage
441 memcpy(matDosage.data() + number_of_samples*j, dsg.memptr(), number_of_samples*sizeof(double));
442 }
443
444 return true;
445 }
446};
447
448}
449
450#endif
Definition GenomicDataStream_virtual.h:40
double getMinVariance()
Definition GenomicDataStream_virtual.h:219
double getMAF() const
Definition GenomicDataStream_virtual.h:225
Param param
Definition GenomicDataStream_virtual.h:273
GenomicDataStream()
Definition GenomicDataStream_virtual.h:187
Definition GenomicRanges.h:25
vector< size_t > get_end() const
Definition GenomicRanges.h:55
vector< string > get_chrom() const
Definition GenomicRanges.h:53
vector< size_t > get_start() const
Definition GenomicRanges.h:54
const int size() const
Definition GenomicRanges.h:61
Definition VariantInfo.h:24
void clear()
Definition VariantInfo.h:142
void addVariant(const string &chr, const int &pos, const string &id, const string &allele1, const string &allele2)
Definition VariantInfo.h:38
VariantInfo()
Definition VariantInfo.h:28
Definition VariantSet.h:45
bool getNextChunk(DataChunk< Rcpp::NumericMatrix > &chunk, const bool &useFilter=true) override
Definition bgenstream.h:295
bool getNextChunk(DataChunk< arma::mat > &chunk, const bool &useFilter=true) override
Definition bgenstream.h:224
int n_samples() override
Definition bgenstream.h:204
bool getNextChunk(DataChunk< vector< double > > &chunk, const bool &useFilter=true) override
Definition bgenstream.h:315
bgenstream()
Definition bgenstream.h:124
bool getNextChunk(DataChunk< Eigen::MatrixXd > &chunk, const bool &useFilter=true) override
Definition bgenstream.h:258
bool getNextChunk(DataChunk< Eigen::SparseMatrix< double > > &chunk, const bool &useFilter=true) override
Definition bgenstream.h:276
GenomicRanges getChromRanges() override
Definition bgenstream.h:220
string getStreamType() override
Definition bgenstream.h:216
~bgenstream()
Definition bgenstream.h:169
bool getNextChunk(DataChunk< arma::sp_mat > &chunk, const bool &useFilter=true) override
Definition bgenstream.h:240
void setRegions(const vector< string > &regions) override
Definition bgenstream.h:175
string filenameIdxGlobal
Definition bgenstream.h:122
bgenstream(const Param &param)
Definition bgenstream.h:128
vector< string > getSampleNames() override
Definition bgenstream.h:210
Definition bgenstream.h:33
Definition GenomicDataStream_virtual.h:84
vector< string > regions
Definition GenomicDataStream_virtual.h:173
string samples
Definition GenomicDataStream_virtual.h:174
FileType fileType
Definition GenomicDataStream_virtual.h:178
string file
Definition GenomicDataStream_virtual.h:171
int chunkSize
Definition GenomicDataStream_virtual.h:176
bool missingToMean
Definition GenomicDataStream_virtual.h:177