scran_qc
Simple quality control on single-cell data
Loading...
Searching...
No Matches
per_cell_qc_metrics.hpp
Go to the documentation of this file.
1#ifndef SCRAN_QC_PER_CELL_QC_METRICS_HPP
2#define SCRAN_QC_PER_CELL_QC_METRICS_HPP
3
4#include <vector>
5#include <algorithm>
6#include <limits>
7#include <cstddef>
8
9#include "tatami/tatami.hpp"
10#include "tatami_stats/tatami_stats.hpp"
11#include "sanisizer/sanisizer.hpp"
12
13#include "utils.hpp"
14
20namespace scran_qc {
21
30 bool compute_sum = true;
31
36 bool compute_detected = true;
37
42 bool compute_max_value = true;
43
48 bool compute_max_index = true;
49
54 bool compute_subset_sum = true;
55
61
66 int num_threads = 1;
67};
68
79template<typename Sum_, typename Detected_, typename Value_, typename Index_>
84 PerCellQcMetricsBuffers() = default;
85
86 PerCellQcMetricsBuffers(const std::size_t nsubsets) :
87 subset_sum(sanisizer::cast<I<decltype(subset_sum.size())> >(nsubsets), NULL),
88 subset_detected(sanisizer::cast<I<decltype(subset_detected.size())> >(nsubsets), NULL)
89 {}
98 Sum_* sum = NULL;
99
104 Detected_* detected = NULL;
105
110 Index_* max_index = NULL;
111
116 Value_* max_value = NULL;
117
124 std::vector<Sum_*> subset_sum;
125
132 std::vector<Detected_*> subset_detected;
133};
134
138namespace internal {
139
140template<typename Value_, typename Index_, typename Subset_, typename Sum_, typename Detected_>
141void compute_qc_direct_dense(
143 const std::vector<Subset_>& subsets,
145 const int num_threads)
146{
147 std::vector<std::vector<Index_> > subset_indices;
148 if (!output.subset_sum.empty() || !output.subset_detected.empty()) {
149 if constexpr(std::is_pointer<Subset_>::value) {
150 const auto nsubsets = subsets.size();
151 sanisizer::resize(subset_indices, nsubsets);
152 const auto NR = mat.nrow();
153
154 for (I<decltype(nsubsets)> s = 0; s < nsubsets; ++s) {
155 auto& current = subset_indices[s];
156 const auto& source = subsets[s];
157 for (I<decltype(NR)> i = 0; i < NR; ++i) {
158 if (source[i]) {
159 current.push_back(i);
160 }
161 }
162 }
163 }
164 }
165
166 tatami::parallelize([&](const int, const Index_ start, const Index_ length) -> void {
167 const auto NR = mat.nrow();
168 auto ext = tatami::consecutive_extractor<false>(mat, false, start, length);
170
171 const bool do_max = output.max_index || output.max_value;
172
173 const auto nsubsets = subsets.size();
174
175 for (Index_ c = start, end = start + length; c < end; ++c) {
176 auto ptr = ext->fetch(c, vbuffer.data());
177
178 if (output.sum) {
179 output.sum[c] = std::accumulate(ptr, ptr + NR, static_cast<Sum_>(0));
180 }
181
182 if (output.detected) {
183 Detected_ count = 0;
184 for (I<decltype(NR)> r = 0; r < NR; ++r) {
185 count += (ptr[r] != 0);
186 }
187 output.detected[c] = count;
188 }
189
190 if (do_max) {
191 Index_ max_index = 0;
192 Value_ max_value = 0;
193
194 if (NR) {
195 max_value = ptr[0];
196 for (I<decltype(NR)> r = 1; r < NR; ++r) {
197 if (max_value < ptr[r]) {
198 max_value = ptr[r];
199 max_index = r;
200 }
201 }
202 }
203
204 if (output.max_index) {
205 output.max_index[c] = max_index;
206 }
207 if (output.max_value) {
208 output.max_value[c] = max_value;
209 }
210 }
211
212 if (!output.subset_sum.empty() || !output.subset_detected.empty()) { // protect against accessing an empty subset_indices.
213 for (I<decltype(nsubsets)> s = 0; s < nsubsets; ++s) {
214 const auto& sub = [&]() -> const auto& {
215 if constexpr(std::is_pointer<Subset_>::value) {
216 return subset_indices[s];
217 } else {
218 return subsets[s];
219 }
220 }();
221
222 if (!output.subset_sum.empty() && output.subset_sum[s]) {
223 Sum_ current = 0;
224 for (const auto r : sub) {
225 current += ptr[r];
226 }
227 output.subset_sum[s][c] = current;
228 }
229
230 if (!output.subset_detected.empty() && output.subset_detected[s]) {
231 Detected_ current = 0;
232 for (const auto r : sub) {
233 current += ptr[r] != 0;
234 }
235 output.subset_detected[s][c] = current;
236 }
237 }
238 }
239 }
240 }, mat.ncol(), num_threads);
241}
242
243template<typename Index_, typename Subset_, typename Sum_, typename Detected_, typename Value_>
244std::vector<std::vector<unsigned char> > boolify_subsets(const Index_ NR, const std::vector<Subset_>& subsets, const PerCellQcMetricsBuffers<Sum_, Detected_, Value_, Index_>& output) {
245 std::vector<std::vector<unsigned char> > is_in_subset;
246
247 if (!output.subset_sum.empty() || !output.subset_detected.empty()) {
248 if constexpr(!std::is_pointer<Subset_>::value) {
249 const auto nsubsets = subsets.size();
250 for (I<decltype(nsubsets)> s = 0; s < nsubsets; ++s) {
251 is_in_subset.emplace_back(NR);
252 auto& last = is_in_subset.back();
253 for (const auto i : subsets[s]) {
254 last[i] = 1;
255 }
256 }
257 }
258 }
259
260 return is_in_subset;
261}
262
263template<typename Value_, typename Index_, typename Subset_, typename Sum_, typename Detected_>
264void compute_qc_direct_sparse(
266 const std::vector<Subset_>& subsets,
267 const PerCellQcMetricsBuffers<Sum_, Detected_, Value_, Index_>& output,
268 const int num_threads)
269{
270 const auto is_in_subset = boolify_subsets(mat.nrow(), subsets, output);
271
272 tatami::parallelize([&](const int, const Index_ start, const Index_ length) -> void {
273 const auto NR = mat.nrow();
274 auto ext = tatami::consecutive_extractor<true>(mat, false, start, length);
277
278 const bool do_max = output.max_index || output.max_value;
279
280 const auto nsubsets = subsets.size();
281
282 for (Index_ c = start, end = start + length; c < end; ++c) {
283 auto range = ext->fetch(vbuffer.data(), ibuffer.data());
284
285 if (output.sum) {
286 output.sum[c] = std::accumulate(range.value, range.value + range.number, static_cast<Sum_>(0));
287 }
288
289 if (output.detected) {
290 Detected_ current = 0;
291 for (Index_ i = 0; i < range.number; ++i) {
292 current += (range.value[i] != 0);
293 }
294 output.detected[c] = current;
295 }
296
297 if (do_max) {
298 Index_ max_index = 0;
299 Value_ max_value = 0;
300
301 if (range.number) {
302 max_value = range.value[0];
303 max_index = range.index[0];
304 for (Index_ i = 1; i < range.number; ++i) {
305 if (max_value < range.value[i]) {
306 max_value = range.value[i];
307 max_index = range.index[i];
308 }
309 }
310
311 if (max_value <= 0 && range.number < NR) {
312 if (output.max_index) {
313 // Figuring out the index of the first zero, assuming range.index is sorted.
314 Index_ last = 0;
315 for (Index_ i = 0; i < range.number; ++i) {
316 if (range.index[i] > last) { // must be at least one intervening structural zero.
317 break;
318 } else if (range.value[i] == 0) { // appears earlier than any structural zero, so it's already the maximum.
319 break;
320 }
321 last = range.index[i] + 1;
322 }
323 max_index = last;
324 }
325 max_value = 0;
326 }
327 } else if (NR) {
328 max_value = 0;
329 }
330
331 if (output.max_index) {
332 output.max_index[c] = max_index;
333 }
334 if (output.max_value) {
335 output.max_value[c] = max_value;
336 }
337 }
338
339 if (!output.subset_sum.empty() || !output.subset_detected.empty()) { // protect against accessing an empty is_in_subset.
340 for (I<decltype(nsubsets)> s = 0; s < nsubsets; ++s) {
341 const auto& sub = [&]() -> const auto& {
342 if constexpr(std::is_pointer<Subset_>::value) {
343 return subsets[s];
344 } else {
345 return is_in_subset[s];
346 }
347 }();
348
349 if (!output.subset_sum.empty() && output.subset_sum[s]) {
350 Sum_ current = 0;
351 for (Index_ i = 0; i < range.number; ++i) {
352 current += (sub[range.index[i]] != 0) * range.value[i];
353 }
354 output.subset_sum[s][c] = current;
355 }
356
357 if (!output.subset_detected.empty() && output.subset_detected[s]) {
358 Detected_ current = 0;
359 for (Index_ i = 0; i < range.number; ++i) {
360 current += (sub[range.index[i]] != 0) * (range.value[i] != 0);
361 }
362 output.subset_detected[s][c] = current;
363 }
364 }
365 }
366 }
367 }, mat.ncol(), num_threads);
368}
369
370template<typename Sum_, typename Detected_, typename Value_, typename Index_>
371class PerCellQcMetricsRunningBuffers {
372public:
373 PerCellQcMetricsRunningBuffers(const PerCellQcMetricsBuffers<Sum_, Detected_, Value_, Index_>& output, const int thread, const Index_ start, const Index_ len) {
374 if (output.sum) {
375 my_sum = tatami_stats::LocalOutputBuffer<Sum_>(thread, start, len, output.sum);
376 }
377
378 if (output.detected) {
379 my_detected = tatami_stats::LocalOutputBuffer<Detected_>(thread, start, len, output.detected);
380 }
381
382 if (output.max_value) {
383 my_max_value = tatami_stats::LocalOutputBuffer<Value_>(thread, start, len, output.max_value);
384 } else if (output.max_index) {
385 tatami::resize_container_to_Index_size(my_holding_max_value, len);
386 }
387
388 if (output.max_index) {
389 my_max_index = tatami_stats::LocalOutputBuffer<Index_>(thread, start, len, output.max_index);
390 }
391
392 {
393 const auto nsubsets = output.subset_sum.size();
394 sanisizer::resize(my_subset_sum, nsubsets);
395 for (I<decltype(nsubsets)> s = 0; s < nsubsets; ++s) {
396 if (output.subset_sum[s]) {
397 my_subset_sum[s] = tatami_stats::LocalOutputBuffer<Sum_>(thread, start, len, output.subset_sum[s]);
398 }
399 }
400 }
401
402 {
403 const auto nsubsets = output.subset_detected.size();
404 sanisizer::resize(my_subset_detected, nsubsets);
405 for (I<decltype(nsubsets)> s = 0; s < nsubsets; ++s) {
406 if (output.subset_detected[s]) {
407 my_subset_detected[s] = tatami_stats::LocalOutputBuffer<Detected_>(thread, start, len, output.subset_detected[s]);
408 }
409 }
410 }
411 }
412
413private:
414 tatami_stats::LocalOutputBuffer<Sum_> my_sum;
415 tatami_stats::LocalOutputBuffer<Detected_> my_detected;
416
417 tatami_stats::LocalOutputBuffer<Value_> my_max_value;
418 std::vector<Value_> my_holding_max_value;
419 tatami_stats::LocalOutputBuffer<Index_> my_max_index;
420
421 std::vector<tatami_stats::LocalOutputBuffer<Sum_> > my_subset_sum;
422 std::vector<tatami_stats::LocalOutputBuffer<Detected_> > my_subset_detected;
423
424public:
425 Sum_* sum_data() {
426 return my_sum.data();
427 }
428
429 Detected_* detected_data() {
430 return my_detected.data();
431 }
432
433 Value_* max_value_data() {
434 auto dptr = my_max_value.data();
435 return (dptr ? dptr : my_holding_max_value.data());
436 }
437
438 Index_* max_index_data() {
439 return my_max_index.data();
440 }
441
442 std::vector<Sum_*> subset_sum_data() {
443 std::vector<Sum_*> output;
444 output.reserve(my_subset_sum.size());
445 for (auto& s : my_subset_sum) {
446 output.push_back(s.data());
447 }
448 return output;
449 }
450
451 std::vector<Detected_*> subset_detected_data() {
452 std::vector<Detected_*> output;
453 output.reserve(my_subset_detected.size());
454 for (auto& s : my_subset_detected) {
455 output.push_back(s.data());
456 }
457 return output;
458 }
459
460 void transfer() {
461 if (my_sum.data()) {
462 my_sum.transfer();
463 }
464 if (my_detected.data()) {
465 my_detected.transfer();
466 }
467
468 if (my_max_value.data()) {
469 my_max_value.transfer();
470 }
471 if (my_max_index.data()) {
472 my_max_index.transfer();
473 }
474
475 for (auto& s : my_subset_sum) {
476 if (s.data()) {
477 s.transfer();
478 }
479 }
480
481 for (auto& s : my_subset_detected) {
482 if (s.data()) {
483 s.transfer();
484 }
485 }
486 }
487};
488
489template<typename Value_, typename Index_, typename Subset_, typename Sum_, typename Detected_>
490void compute_qc_running_dense(
492 const std::vector<Subset_>& subsets,
493 const PerCellQcMetricsBuffers<Sum_, Detected_, Value_, Index_>& output,
494 const int num_threads)
495{
496 const auto is_in_subset = boolify_subsets(mat.nrow(), subsets, output);
497
498 tatami::parallelize([&](int thread, Index_ start, Index_ len) -> void {
499 const auto NR = mat.nrow();
500 auto ext = tatami::consecutive_extractor<false>(mat, true, static_cast<Index_>(0), NR, start, len);
502
503 PerCellQcMetricsRunningBuffers<Sum_, Detected_, Value_, Index_> locals(output, thread, start, len);
504 const auto outt = locals.sum_data();
505 const auto outd = locals.detected_data();
506 const auto outmi = locals.max_index_data();
507 const auto outmc = locals.max_value_data();
508 const bool do_max = (outmi || outmc);
509 const auto outst = locals.subset_sum_data();
510 const auto outsd = locals.subset_detected_data();
511
512 const auto nsubsets = subsets.size();
513
514 for (Index_ r = 0; r < NR; ++r) {
515 auto ptr = ext->fetch(vbuffer.data());
516
517 if (outt) {
518 for (Index_ i = 0; i < len; ++i) {
519 outt[i] += ptr[i];
520 }
521 }
522
523 if (outd) {
524 for (Index_ i = 0; i < len; ++i) {
525 outd[i] += (ptr[i] != 0);
526 }
527 }
528
529 if (do_max) {
530 if (r == 0) {
531 std::copy_n(ptr, len, outmc);
532 if (outmi) {
533 std::fill_n(outmi, len, 0);
534 }
535 } else {
536 for (Index_ i = 0; i < len; ++i) {
537 auto& curmax = outmc[i];
538 if (curmax < ptr[i]) {
539 curmax = ptr[i];
540 if (outmi) {
541 outmi[i] = r;
542 }
543 }
544 }
545 }
546 }
547
548 if (!outst.empty() || !outsd.empty()) { // protect against accessing an empty is_in_subset.
549 for (I<decltype(nsubsets)> s = 0; s < nsubsets; ++s) {
550 const auto& sub = [&]() -> const auto& {
551 if constexpr(std::is_pointer<Subset_>::value) {
552 return subsets[s];
553 } else {
554 return is_in_subset[s];
555 }
556 }();
557 if (sub[r] == 0) {
558 continue;
559 }
560
561 if (!outst.empty() && outst[s]) {
562 auto current = outst[s];
563 for (Index_ i = 0; i < len; ++i) {
564 current[i] += ptr[i];
565 }
566 }
567
568 if (!outsd.empty() && outsd[s]) {
569 auto current = outsd[s];
570 for (Index_ i = 0; i < len; ++i) {
571 current[i] += (ptr[i] != 0);
572 }
573 }
574 }
575 }
576 }
577
578 locals.transfer();
579 }, mat.ncol(), num_threads);
580}
581
582template<typename Value_, typename Index_, typename Subset_, typename Sum_, typename Detected_>
583void compute_qc_running_sparse(
585 const std::vector<Subset_>& subsets,
586 const PerCellQcMetricsBuffers<Sum_, Detected_, Value_, Index_>& output,
587 const int num_threads)
588{
589 tatami::Options opt;
590 opt.sparse_ordered_index = false;
591 const auto is_in_subset = boolify_subsets(mat.nrow(), subsets, output);
592
593 tatami::parallelize([&](int thread, Index_ start, Index_ len) -> void {
594 const auto NR = mat.nrow();
595 auto ext = tatami::consecutive_extractor<true>(mat, true, static_cast<Index_>(0), NR, start, len, opt);
598
599 PerCellQcMetricsRunningBuffers<Sum_, Detected_, Value_, Index_> locals(output, thread, start, len);
600 const auto outt = locals.sum_data();
601 const auto outd = locals.detected_data();
602 const auto outmi = locals.max_index_data();
603 const auto outmc = locals.max_value_data();
604 const bool do_max = (outmi || outmc);
605 const auto outst = locals.subset_sum_data();
606 const auto outsd = locals.subset_detected_data();
607
608 const auto nsubsets = subsets.size();
609
610 std::vector<Index_> last_consecutive_nonzero;
611 if (do_max) {
612 tatami::resize_container_to_Index_size(last_consecutive_nonzero, len);
613 }
614
615 for (Index_ r = 0; r < NR; ++r) {
616 auto range = ext->fetch(vbuffer.data(), ibuffer.data());
617
618 if (outt) {
619 for (Index_ i = 0; i < range.number; ++i) {
620 outt[range.index[i] - start] += range.value[i];
621 }
622 }
623
624 if (outd) {
625 for (Index_ i = 0; i < range.number; ++i) {
626 outd[range.index[i] - start] += (range.value[i] != 0);
627 }
628 }
629
630 if (do_max) {
631 if (r == 0) {
632 std::fill_n(outmc, len, 0);
633 for (Index_ i = 0; i < range.number; ++i) {
634 auto j = range.index[i] - start;
635 outmc[j] = range.value[i];
636 last_consecutive_nonzero[j] = 1; // see below
637 }
638 if (outmi) {
639 std::fill_n(outmi, len, 0);
640 }
641
642 } else {
643 for (Index_ i = 0; i < range.number; ++i) {
644 auto j = range.index[i] - start;
645 auto& curmax = outmc[j];
646
647 auto val = range.value[i];
648 if (curmax < val) {
649 curmax = val;
650 if (outmi) {
651 outmi[j] = r;
652 }
653 }
654
655 // Getting the index of the last consecutive structural
656 // non-zero, so that we can check if zero is the max
657 // and gets its first occurrence, if necessary.
658 auto& last = last_consecutive_nonzero[j];
659 if (last == r) {
660 ++last;
661 }
662 }
663 }
664 }
665
666 if (!outst.empty() || !outsd.empty()) { // protect against accessing an empty is_in_subset.
667 for (I<decltype(nsubsets)> s = 0; s < nsubsets; ++s) {
668 const auto& sub = [&]() -> const auto& {
669 if constexpr(std::is_pointer<Subset_>::value) {
670 return subsets[s];
671 } else {
672 return is_in_subset[s];
673 }
674 }();
675 if (sub[r] == 0) {
676 continue;
677 }
678
679 if (!outst.empty() && outst[s]) {
680 auto current = outst[s];
681 for (Index_ i = 0; i < range.number; ++i) {
682 current[range.index[i] - start] += range.value[i];
683 }
684 }
685
686 if (!outsd.empty() && outsd[s]) {
687 auto current = outsd[s];
688 for (Index_ i = 0; i < range.number; ++i) {
689 current[range.index[i] - start] += (range.value[i] != 0);
690 }
691 }
692 }
693 }
694 }
695
696 if (do_max) {
697 const auto NR = mat.nrow();
698
699 // Checking anything with non-positive maximum, and replacing it
700 // with zero if there are any structural zeros.
701 for (Index_ c = 0; c < len; ++c) {
702 auto last_nz = last_consecutive_nonzero[c];
703 if (last_nz == NR) { // i.e., no structural zeros.
704 continue;
705 }
706
707 auto& current = outmc[c];
708 if (current > 0) { // doesn't defeat the current maximum.
709 continue;
710 }
711
712 current = 0;
713 if (outmi) {
714 outmi[c] = last_nz;
715 }
716 }
717 }
718
719 locals.transfer();
720 }, mat.ncol(), num_threads);
721}
722
723}
741template<typename Sum_, typename Detected_, typename Value_, typename Index_>
746 PerCellQcMetricsResults() = default;
747
748 PerCellQcMetricsResults(const std::size_t nsubsets) :
749 subset_sum(sanisizer::cast<I<decltype(subset_sum.size())> >(nsubsets)),
750 subset_detected(sanisizer::cast<I<decltype(subset_detected.size())> >(nsubsets))
751 {}
760 std::vector<Sum_> sum;
761
766 std::vector<Detected_> detected;
767
773 std::vector<Index_> max_index;
774
779 std::vector<Value_> max_value;
780
786 std::vector<std::vector<Sum_> > subset_sum;
787
793 std::vector<std::vector<Detected_> > subset_detected;
794};
795
833template<typename Value_, typename Index_, typename Subset_, typename Sum_, typename Detected_>
836 const std::vector<Subset_>& subsets,
838 const PerCellQcMetricsOptions& options)
839{
840 if (mat.sparse()) {
841 if (mat.prefer_rows()) {
842 internal::compute_qc_running_sparse(mat, subsets, output, options.num_threads);
843 } else {
844 internal::compute_qc_direct_sparse(mat, subsets, output, options.num_threads);
845 }
846 } else {
847 if (mat.prefer_rows()) {
848 internal::compute_qc_running_dense(mat, subsets, output, options.num_threads);
849 } else {
850 internal::compute_qc_direct_dense(mat, subsets, output, options.num_threads);
851 }
852 }
853}
854
876template<typename Sum_ = double, typename Detected_ = int, typename Value_, typename Index_, typename Subset_>
879 const std::vector<Subset_>& subsets,
880 const PerCellQcMetricsOptions& options)
881{
884 const auto ncells = mat.ncol();
885
886 if (options.compute_sum) {
888#ifdef SCRAN_QC_TEST_INIT
889 , SCRAN_QC_TEST_INIT
890#endif
891 );
892 buffers.sum = output.sum.data();
893 }
894 if (options.compute_detected) {
896#ifdef SCRAN_QC_TEST_INIT
897 , SCRAN_QC_TEST_INIT
898#endif
899 );
900 buffers.detected = output.detected.data();
901 }
902 if (options.compute_max_index) {
904#ifdef SCRAN_QC_TEST_INIT
905 , SCRAN_QC_TEST_INIT
906#endif
907 );
908 buffers.max_index = output.max_index.data();
909 }
910 if (options.compute_max_value) {
912#ifdef SCRAN_QC_TEST_INIT
913 , SCRAN_QC_TEST_INIT
914#endif
915 );
916 buffers.max_value = output.max_value.data();
917 }
918
919 const auto nsubsets = subsets.size();
920
921 if (options.compute_subset_sum) {
922 sanisizer::resize(output.subset_sum, nsubsets);
923 sanisizer::resize(buffers.subset_sum, nsubsets);
924 for (I<decltype(nsubsets)> s = 0; s < nsubsets; ++s) {
926#ifdef SCRAN_QC_TEST_INIT
927 , SCRAN_QC_TEST_INIT
928#endif
929 );
930 buffers.subset_sum[s] = output.subset_sum[s].data();
931 }
932 }
933
934 if (options.compute_subset_detected) {
935 sanisizer::resize(output.subset_detected, nsubsets);
936 sanisizer::resize(buffers.subset_detected, nsubsets);
937 for (I<decltype(nsubsets)> s = 0; s < nsubsets; ++s) {
939#ifdef SCRAN_QC_TEST_INIT
940 , SCRAN_QC_TEST_INIT
941#endif
942 );
943 buffers.subset_detected[s] = output.subset_detected[s].data();
944 }
945 }
946
947 per_cell_qc_metrics(mat, subsets, buffers, options);
948 return output;
949}
950
951}
952
953#endif
virtual Index_ ncol() const=0
virtual Index_ nrow() const=0
virtual bool prefer_rows() const=0
virtual std::unique_ptr< MyopicSparseExtractor< Value_, Index_ > > sparse(bool row, const Options &opt) const=0
Simple quality control for single-cell data.
Definition adt_quality_control.hpp:23
void per_cell_qc_metrics(const tatami::Matrix< Value_, Index_ > &mat, const std::vector< Subset_ > &subsets, const PerCellQcMetricsBuffers< Sum_, Detected_, Value_, Index_ > &output, const PerCellQcMetricsOptions &options)
Definition per_cell_qc_metrics.hpp:834
void resize_container_to_Index_size(Container_ &container, const Index_ x, Args_ &&... args)
int parallelize(Function_ fun, const Index_ tasks, const int workers)
Container_ create_container_of_Index_size(const Index_ x, Args_ &&... args)
auto consecutive_extractor(const Matrix< Value_, Index_ > &matrix, const bool row, const Index_ iter_start, const Index_ iter_length, Args_ &&... args)
Buffers for per_cell_qc_metrics().
Definition per_cell_qc_metrics.hpp:80
Value_ * max_value
Definition per_cell_qc_metrics.hpp:116
Index_ * max_index
Definition per_cell_qc_metrics.hpp:110
Sum_ * sum
Definition per_cell_qc_metrics.hpp:98
Detected_ * detected
Definition per_cell_qc_metrics.hpp:104
std::vector< Detected_ * > subset_detected
Definition per_cell_qc_metrics.hpp:132
std::vector< Sum_ * > subset_sum
Definition per_cell_qc_metrics.hpp:124
Options for per_cell_qc_metrics().
Definition per_cell_qc_metrics.hpp:25
bool compute_sum
Definition per_cell_qc_metrics.hpp:30
bool compute_subset_sum
Definition per_cell_qc_metrics.hpp:54
bool compute_max_value
Definition per_cell_qc_metrics.hpp:42
bool compute_subset_detected
Definition per_cell_qc_metrics.hpp:60
bool compute_detected
Definition per_cell_qc_metrics.hpp:36
bool compute_max_index
Definition per_cell_qc_metrics.hpp:48
int num_threads
Definition per_cell_qc_metrics.hpp:66
Result store for QC metric calculations.
Definition per_cell_qc_metrics.hpp:742
std::vector< Index_ > max_index
Definition per_cell_qc_metrics.hpp:773
std::vector< Value_ > max_value
Definition per_cell_qc_metrics.hpp:779
std::vector< std::vector< Detected_ > > subset_detected
Definition per_cell_qc_metrics.hpp:793
std::vector< Detected_ > detected
Definition per_cell_qc_metrics.hpp:766
std::vector< Sum_ > sum
Definition per_cell_qc_metrics.hpp:760
std::vector< std::vector< Sum_ > > subset_sum
Definition per_cell_qc_metrics.hpp:786
bool sparse_ordered_index