GenomicDataStream
A scalable interface between data and analysis
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1
2#ifndef DISABLE_RCPP
3#include <RcppArmadillo.h>
4#endif
5
6#ifndef DISABLE_EIGEN
7// #include <Eigen/Core>
8#include <RcppEigen.h>
9#endif
10
11#include <vector>
12#include <random>
13#include <algorithm>
14#include <unordered_set>
15#include <regex>
16#include <string>
17
18#ifndef UTILS_H_
19#define UTILS_H_
20
21namespace gds {
22
23/* Replacement for boost::erase_all() since
24 it causes issues on Windows
25*/
26static void erase_all(string& input, const string& pattern)
27{
28 if (pattern.empty())
29 return;
30
31 string::size_type position = 0;
32
33 while ((position = input.find(pattern, position)) !=
34 string::npos) {
35 input.erase(position, pattern.size());
36 }
37}
38
39static string erase_all_copy(string input,
40 const string& pattern)
41{
42 erase_all(input, pattern);
43 return input;
44}
45
46/* Replacement for boost::split() since
47 it causes issues on Windows
48*/
49static vector<string> split_any_of(
50 const string& input,
51 const string& delimiters,
52 bool compress = false)
53{
54 vector<string> output;
55 string::size_type begin = 0;
56
57 while (true) {
58 const auto separator = input.find_first_of(delimiters, begin);
59
60 if (separator == string::npos) {
61 output.emplace_back(input.substr(begin));
62 break;
63 }
64
65 output.emplace_back(input.substr(begin, separator - begin));
66
67 if (!compress) {
68 begin = separator + 1;
69 continue;
70 }
71
72 begin = input.find_first_not_of(delimiters, separator);
73
74 // Preserve the trailing empty field.
75 if (begin == string::npos) {
76 output.emplace_back();
77 break;
78 }
79 }
80
81 return output;
82}
83
88
89template<typename T>
90static vector<double> GP_to_dosage( const vector<T> &v, const bool &missingToMean) {
91 vector<double> res( v.size() / 3.0);
92 vector<int> missing;
93
94 // initialize
95 int runningSum = 0, nValid = 0;
96 double value;
97
98 // for each entry in result
99 // use two adjacent values
100 for(int i=0; i<res.size(); i++){
101 // compute dosage from genotype probabilties
102 value = v[3*i]*0 + v[3*i+1]*1 + v[3*i+2]*2;
103
104 // -9 is the missing value, so -18 is diploid
105 if( value == -18){
106 // if missing, set to NaN
107 value = std::numeric_limits<double>::quiet_NaN();
108 missing.push_back(i);
109 }else{
110 // for computing mean
111 runningSum += value;
112 nValid++;
113 }
114
115 // set dosage value
116 res[i] = value;
117 }
118
119 // mean excluding NaNs
120 double mu = runningSum / (double) nValid;
121
122 // if missing values should be set to mean
123 if( missingToMean ){
124 // for each entry with a missing value, set to mean
125 for(const int& i : missing) res[i] = mu;
126 }
127
128 return res;
129}
130
135static vector<double> intToDosage( const vector<int> &v, const bool &missingToMean) {
136
137 // store and return result
138 vector<double> res( v.size() / 2.0);
139 vector<int> missing;
140
141 // initialize
142 int runningSum = 0, nValid = 0;
143 double value;
144
145 // for each entry in result
146 // use two adjacent values
147 for(int i=0; i<res.size(); i++){
148 value = v[2*i] + v[2*i+1];
149
150 // -9 is the missing value, so -18 is diploid
151 if( value == -18){
152 // if missing, set to NaN
153 value = std::numeric_limits<double>::quiet_NaN();
154 missing.push_back(i);
155 }else{
156 // for computing mean
157 runningSum += value;
158 nValid++;
159 }
160
161 // set dosage value
162 res[i] = value;
163 }
164
165 // mean excluding NaNs
166 double mu = runningSum / (double) nValid;
167
168 // if missing values should be set to mean
169 if( missingToMean ){
170 // for each entry with a missing value, set to mean
171 for(const int& i : missing) res[i] = mu;
172 }
173
174 return res;
175}
176
180template<typename T>
181static size_t removeDuplicates(vector<T>& vec){
182 unordered_set<T> seen;
183
184 auto newEnd = remove_if(vec.begin(), vec.end(), [&seen](const T& value)
185 {
186 if (seen.find(value) != end(seen))
187 return true;
188
189 seen.insert(value);
190 return false;
191 });
192
193 vec.erase(newEnd, vec.end());
194
195 return vec.size();
196}
197
198
202[[maybe_unused]]
203static arma::vec colSums( const arma::mat &X){
204
205 // row vector of 1's
206 arma::rowvec ONE(X.n_rows, arma::fill::ones);
207
208 // matrix multiplication to get sums
209 arma::mat tmp = ONE * X;
210
211 return arma::conv_to<arma::vec>::from( tmp );
212}
213
214
222[[maybe_unused]]
223static void standardize( arma::mat &X, const bool &center = true, const bool &scale = true, const double tol = 1e-10 ){
224
225 double sqrt_rdf = sqrt(X.n_rows - 1.0);
226
227 // if center, subtract mean of each column
228 // if scale, divide by sd of each column
229 // Note, norm() does not center the column
230 // this give results consistent with base::scale()
231 // when scale is FALSE
232 for(size_t j=0; j<X.n_cols; j++){
233 if( center ) X.col(j) -= mean(X.col(j));
234 double sd = norm(X.col(j)) / sqrt_rdf;
235 if( scale && sd > tol ) X.col(j) /= sd;
236 }
237}
238
239#ifndef DISABLE_EIGEN
240[[maybe_unused]]
241static void standardize( Eigen::MatrixXd &X, const bool &center = true, const bool &scale = true, const double tol = 1e-10 ){
242
243 double sqrt_rdf = sqrt(X.rows() - 1.0);
244 if(center) X.rowwise() -= X.colwise().mean(); // centering
245 if(scale) {
246 // if X is centered, then we can convert norm to sd
247 for(size_t j=0; j<X.cols(); j++) {
248 double sd = X.col(j).norm() / sqrt_rdf ; // sd
249 if(sd > tol) X.col(j) /= sd;
250 }
251 }
252}
253
254[[maybe_unused]]
255static void standardize_rows( Eigen::MatrixXd &X, const bool &center = true, const bool &scale = true, const double tol = 1e-10 ){
256
257 double sqrt_rdf = sqrt(X.cols() - 1.0);
258 if(center) X.colwise() -= X.rowwise().mean(); // centering
259 if(scale) {
260 // if X is centered, then we can convert norm to sd
261 for(size_t j=0; j<X.rows(); j++) {
262 double sd = X.row(j).norm() / sqrt_rdf ; // sd
263 if(sd > tol) X.row(j) /= sd;
264 }
265 }
266}
267#endif
268
271static bool isOnlyDigits(const std::string& s){
272 int n = count_if(s.begin(), s.end(),
273 [](unsigned char c){ return isdigit(c); }
274 );
275 return( n == s.size());
276}
277
278
281static void nanToMean( arma::vec & v){
282 // get indeces of finite elements
283 arma::uvec idx = arma::find_finite(v);
284
285 // if number of finite elements is less than the total
286 if( idx.n_elem < v.n_elem ){
287 // compute mean from finite elements
288 double mu = arma::mean( v.elem(idx));
289
290 // replace nan with mu
291 v.replace(arma::datum::nan, mu);
292 }
293}
294
295
307
310static string toString( FileType x){
311
312 switch(x){
313 case VCF: return "vcf";
314 case VCFGZ: return "vcf.gz";
315 case BCF: return "bcf";
316 case BGEN: return "bgen";
317 case PGEN: return "pgen";
318 case PBED: return "bed";
319 case OTHER: return "other";
320 default: return "other";
321 }
322}
323
324
325
328static FileType getFileType( const string &file ){
329
330 FileType ft = OTHER;
331
332 if( regex_search( file, regex("\\.vcf$")) ){
333 ft = VCF;
334 }else if( regex_search( file, regex("\\.vcf\\.gz$")) ){
335 ft = VCFGZ;
336 }else if( regex_search( file, regex("\\.bcf$")) ) {
337 ft = BCF;
338 }else if( regex_search( file, regex("\\.bgen$")) ){
339 ft = BGEN;
340 }else if( regex_search( file, regex("\\.pgen$")) ){
341 ft = PGEN;
342 } if( regex_search( file, regex("\\.bed$")) ){
343 ft = PBED;
344 }
345
346 return ft;
347}
348
349
350
353template<typename T>
354static vector<T> cast_elements( const vector<string> &v ){
355 vector<T> output(0, v.size());
356
357 for (auto &s : v) {
358 stringstream parser(s);
359 T x = 0;
360 parser >> x;
361 output.push_back(x);
362 }
363 return output;
364}
365
366
369static vector<int> stoi_vec( const vector<string> &v ){
370
371 vector<int> output;
372 output.reserve(v.size());
373
374 for (const string& s : v) {
375 output.push_back(std::stoi(s));
376 }
377 return output;
378}
379
380
381
387static vector<string> splitRegionString( string regionString){
388
389 // regionString is string of chr:start-end delim by "\t,\n"
390 // remove spaces, then split based on delim
391 erase_all(regionString, " ");
392 vector<string> regions = split_any_of(regionString, "\t,\n");
393
394 // remove duplicate regions, but preserve order
395 removeDuplicates( regions );
396
397 return regions;
398}
399
400
403template<typename T, typename T2>
404static vector<T> subset_vector(const vector<T> &x, const vector<T2> &idx){
405
406 // initialize x_subset to have size idx.size()
407 vector<T> x_subset;
408 x_subset.reserve(idx.size());
409
410 for(auto i: idx){
411 if( i > x.size() ){
412 throw std::out_of_range("Index is out of bounds");
413 }
414 x_subset.push_back( x[i] );
415 }
416
417 return x_subset;
418}
419
420
421template<typename T>
422static void print_vec(const string &title, const vector<T> &x){
423 Rcpp::Rcout << title << "\n";
424 for(auto a: x) Rcpp::Rcout << a << " ";
425 Rcpp::Rcout << endl;
426}
427
428
431template<typename T>
432std::vector<std::vector<T>> chunk_vector(const std::vector<T>& vec, int k) {
433 std::vector<std::vector<T> > result;
434 int size = vec.size();
435 int chunk_size = size / k;
436 int remainder = size % k;
437
438 int start = 0;
439 for (int i = 0; i < k; ++i) {
440 int current_chunk_size = chunk_size;
441 if (i < remainder) {
442 current_chunk_size++;
443 }
444 std::vector<T> chunk(vec.begin() + start, vec.begin() + start + current_chunk_size);
445 result.push_back(chunk);
446 start += current_chunk_size;
447 }
448 return result;
449}
450
453template<typename T>
454static vector<unsigned int> which_in( const vector<T> &v1, const vector<T> &v2){
455
456 // Create a hash map (unordered_map) of elements in v2 for efficient lookups
457 unordered_map<T, bool> v2_elements;
458 for (auto &x : v2) {
459 v2_elements[x] = true;
460 }
461
462 // Iterate through v1 and check if the element exists in the hash map
463 vector<unsigned int> shared_indices;
464 for (int i = 0; i < v1.size(); ++i) {
465 // Check if the element exists in the map
466 if (v2_elements.count(v1[i])) {
467 // Add the index from v1
468 shared_indices.push_back(i);
469 }
470 }
471
472 return shared_indices;
473}
474
475
476
477
478
479
480} // end namespace
481#endif
Definition bgenstream.h:33
FileType
Definition utils.h:298
@ OTHER
Definition utils.h:305
@ BGEN
Definition utils.h:302
@ BCF
Definition utils.h:301
@ PBED
Definition utils.h:304
@ PGEN
Definition utils.h:303
@ VCFGZ
Definition utils.h:300
@ VCF
Definition utils.h:299
std::vector< std::vector< T > > chunk_vector(const std::vector< T > &vec, int k)
Definition utils.h:432