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