scran_markers
Marker detection for single-cell data
Loading...
Searching...
No Matches
score_markers_best.hpp
Go to the documentation of this file.
1#ifndef SCRAN_MARKERS_SCORE_MARKERS_BEST_HPP
2#define SCRAN_MARKERS_SCORE_MARKERS_BEST_HPP
3
4#include <vector>
5#include <cstddef>
6#include <optional>
7
9#include "tatami/tatami.hpp"
10#include "sanisizer/sanisizer.hpp"
11#include "topicks/topicks.hpp"
12#include "quickstats/quickstats.hpp"
13
14#include "scan_matrix.hpp"
15#include "average_group_stats.hpp"
16#include "block_averages.hpp"
17#include "create_combinations.hpp"
18#include "cohens_d.hpp"
19#include "simple_diff.hpp"
20#include "utils.hpp"
21
27namespace scran_markers {
28
38 double threshold = 0;
39
44 int num_threads = 1;
45
49 bool compute_group_mean = true;
50
55
59 bool compute_cohens_d = true;
60
64 bool compute_auc = true;
65
69 bool compute_delta_mean = true;
70
75
80 bool largest_cohens_d = true;
81
86 bool largest_auc = true;
87
92 bool largest_delta_mean = true;
93
99
105 std::optional<double> threshold_cohens_d = 0;
106
112 std::optional<double> threshold_auc = 0.5;
113
119 std::optional<double> threshold_delta_mean = 0;
120
126 std::optional<double> threshold_delta_detected = 0;
127
131 bool keep_ties = false;
132
138 BlockAveragePolicy block_average_policy = BlockAveragePolicy::MEAN;
139
152 scran_blocks::WeightPolicy block_weight_policy = scran_blocks::WeightPolicy::VARIABLE;
153
160
165 double block_quantile = 0.5;
166};
167
173template<typename Stat_, typename Index_>
179 std::vector<std::vector<Stat_> > mean;
180
185 std::vector<std::vector<Stat_> > detected;
186
200 std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > > cohens_d;
201
215 std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > > auc;
216
230 std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > > delta_mean;
231
245 std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > > delta_detected;
246};
247
260template<typename Stat_, typename Index_>
261std::vector<std::vector<std::vector<std::pair<Index_, Stat_> > > > queues_to_vectors(std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > >& queued) {
262 std::vector<std::vector<std::vector<std::pair<Index_, Stat_> > > > output;
263 const auto num_groups = queued.size();
264 sanisizer::resize(output, num_groups);
265
266 for (I<decltype(num_groups)> g1 = 0; g1 < num_groups; ++g1) {
267 sanisizer::resize(output[g1], num_groups);
268 for (I<decltype(num_groups)> g2 = 0; g2 < num_groups; ++g2) {
269 if (g1 == g2) {
270 continue;
271 }
272
273 auto& current_in = queued[g1][g2];
274 auto& current_out = output[g1][g2];
275 current_out.reserve(current_in.size());
276
277 while (!current_in.empty()) {
278 const auto& best = current_in.top();
279 current_out.emplace_back(best.second, best.first);
280 current_in.pop();
281 }
282 std::reverse(current_out.begin(), current_out.end()); // earliest element should have the strongest effect sizes.
283 }
284 }
285
286 return output;
287}
288
292namespace internal {
293
294template<typename Stat_, typename Index_>
295using PairwiseTopQueues = std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > >;
296
297template<typename Stat_, typename Index_>
298void allocate_best_top_queues(
299 PairwiseTopQueues<Stat_, Index_>& pqueues,
300 const std::size_t num_groups,
301 const Index_ top,
302 const bool larger,
303 const bool keep_ties,
304 const std::optional<Stat_>& bound
305) {
307 opt.check_nan = true;
308 opt.keep_ties = keep_ties;
309 if (bound.has_value()) {
310 opt.bound = *bound;
311 }
312
313 sanisizer::resize(pqueues, num_groups);
314 for (I<decltype(num_groups)> g1 = 0; g1 < num_groups; ++g1) {
315 auto& x = pqueues[g1];
316 x.reserve(num_groups);
317 for (I<decltype(num_groups)> g2 = 0; g2 < num_groups; ++g2) {
318 if (g1 == g2) {
319 x.emplace_back(0, larger, opt);
320 } else {
321 x.emplace_back(top, larger, opt);
322 }
323 }
324 }
325}
326
327template<typename Stat_, typename Index_>
328void add_best_top_queues(
329 PairwiseTopQueues<Stat_, Index_>& pqueues,
330 const Index_ gene,
331 std::size_t num_groups,
332 const std::vector<Stat_>& effects
333) {
334 for (I<decltype(num_groups)> g1 = 0; g1 < num_groups; ++g1) {
335 for (I<decltype(num_groups)> g2 = 0; g2 < num_groups; ++g2) {
336 const auto val = effects[sanisizer::nd_offset<std::size_t>(g2, num_groups, g1)];
337 if (g1 != g2) {
338 pqueues[g1][g2].emplace(val, gene);
339 }
340 }
341 }
342}
343
344template<typename Stat_, typename Index_>
345void report_best_top_queues(
346 std::vector<std::optional<PairwiseTopQueues<Stat_, Index_> > >& pqueues,
347 std::size_t num_groups,
348 std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > >& output
349) {
350 // We know it fits into an 'int' as this is what we got originally.
351 const int num_available = pqueues.size();
352
353 // If it's empty, we just create empty vectors and move on.
354 if (num_available == 0) {
355 sanisizer::resize(output, num_groups);
356 topicks::TopQueue<Stat_, Index_> placeholder(0, false, {});
357 for (I<decltype(num_groups)> g1 = 0; g1 < num_groups; ++g1) {
358 sanisizer::resize(output[g1], num_groups, placeholder);
359 }
360 return;
361 }
362
363 // Consolidating all of the thread-specific queues into a single queue.
364 auto& true_pqueue = *(pqueues.front());
365 for (int t = 1; t < num_available; ++t) {
366 auto& current_pqueue = *(pqueues[t]);
367 for (I<decltype(num_groups)> g1 = 0; g1 < num_groups; ++g1) {
368 for (I<decltype(num_groups)> g2 = 0; g2 < num_groups; ++g2) {
369 auto& current_in = current_pqueue[g1][g2];
370 auto& current_out = true_pqueue[g1][g2];
371 while (!current_in.empty()) {
372 current_out.push(current_in.top());
373 current_in.pop();
374 }
375 }
376 }
377 }
378
379 output = std::move(true_pqueue);
380}
381
382template<typename Index_, typename Stat_>
383void find_best_simple_best_effects(
384 const Index_ ngenes,
385 const std::size_t num_groups,
386 const std::size_t num_blocks,
387 const std::size_t num_combos,
388 const std::vector<Stat_>& combo_means,
389 const std::vector<Stat_>& combo_vars,
390 const std::vector<Stat_>& combo_detected,
391 const BlockAverageInfo<Stat_>& average_info,
392 const Index_ top,
393 const ScoreMarkersBestOptions& options,
394 ScoreMarkersBestResults<Stat_, Index_>& output
395) {
396 std::optional<std::vector<Stat_> > total_weights_per_group;
397 const Stat_* total_weights_ptr = NULL;
398 if (average_info.use_mean()) {
399 if (options.compute_group_mean || options.compute_group_detected) {
400 if (num_blocks > 1) {
401 total_weights_per_group = compute_total_weight_per_group(num_groups, num_blocks, average_info.combo_weights().data());
402 total_weights_ptr = total_weights_per_group->data();
403 } else {
404 total_weights_ptr = average_info.combo_weights().data();
405 }
406 }
407 }
408
409 std::vector<Stat_*> mptrs;
410 if (options.compute_group_mean) {
411 mptrs.reserve(num_groups);
412 sanisizer::resize(output.mean, num_groups);
413 for (auto& x : output.mean) {
414 sanisizer::resize(x, ngenes);
415 mptrs.push_back(x.data());
416 }
417 }
418
419 std::vector<Stat_*> dptrs;
420 if (options.compute_group_detected) {
421 dptrs.reserve(num_groups);
422 sanisizer::resize(output.detected, num_groups);
423 for (auto& x : output.detected) {
424 sanisizer::resize(x, ngenes);
425 dptrs.push_back(x.data());
426 }
427 }
428
429 std::optional<PrecomputedPairwiseWeights<Stat_> > preweights;
430 if (average_info.use_mean()) {
431 if (options.compute_cohens_d || options.compute_delta_mean || options.compute_delta_detected) {
432 preweights.emplace(num_groups, num_blocks, average_info.combo_weights().data());
433 }
434 }
435
436 // Setting up the output queues.
437 std::optional<std::vector<std::optional<PairwiseTopQueues<Stat_, Index_> > > > threaded_cohens_d_queues, threaded_delta_detected_queues, threaded_delta_mean_queues;
438 if (options.compute_cohens_d) {
439 threaded_cohens_d_queues.emplace(sanisizer::cast<I<decltype(threaded_cohens_d_queues->size())> >(options.num_threads));
440 }
441 if (options.compute_delta_mean) {
442 threaded_delta_mean_queues.emplace(sanisizer::cast<I<decltype(threaded_delta_mean_queues->size())> >(options.num_threads));
443 }
444 if (options.compute_delta_detected) {
445 threaded_delta_detected_queues.emplace(sanisizer::cast<I<decltype(threaded_delta_detected_queues->size())> >(options.num_threads));
446 }
447
448 const auto num_groups2 = sanisizer::product<typename std::vector<Stat_>::size_type>(num_groups, num_groups);
449
450 int num_used = tatami::parallelize([&](const int t, const Index_ start, const Index_ length) -> void {
451 std::optional<PairwiseTopQueues<Stat_, Index_> > local_cohens_d_queue, local_delta_mean_queue, local_delta_detected_queue;
452 if (options.compute_cohens_d) {
453 local_cohens_d_queue.emplace();
454 allocate_best_top_queues(*local_cohens_d_queue, num_groups, top, options.largest_cohens_d, options.keep_ties, options.threshold_cohens_d);
455 }
456 if (options.compute_delta_mean) {
457 local_delta_mean_queue.emplace();
458 allocate_best_top_queues(*local_delta_mean_queue, num_groups, top, options.largest_delta_mean, options.keep_ties, options.threshold_delta_mean);
459 }
460 if (options.compute_delta_detected) {
461 local_delta_detected_queue.emplace();
462 allocate_best_top_queues(*local_delta_detected_queue, num_groups, top, options.largest_delta_detected, options.keep_ties, options.threshold_delta_detected);
463 }
464
465 std::vector<Stat_> buffer;
466 if (options.compute_cohens_d || options.compute_delta_mean || options.compute_delta_detected) {
467 buffer.resize(num_groups2);
468 }
469
470 std::optional<std::vector<Stat_> > qbuffer, qrevbuffer;
471 std::optional<quickstats::SingleQuantileVariableNumber<Stat_> > qcalc;
472 if (!average_info.use_mean()) {
473 qbuffer.emplace();
474 qrevbuffer.emplace();
475 qcalc.emplace(num_blocks, average_info.quantile());
476 }
477
478 for (Index_ gene = start, end = start + length; gene < end; ++gene) {
479 auto in_offset = sanisizer::product_unsafe<std::size_t>(gene, num_combos);
480
481 if (options.compute_group_mean) {
482 const auto tmp_means = combo_means.data() + in_offset;
483 if (average_info.use_mean()) {
484 average_group_stats_blockmean(gene, num_groups, num_blocks, tmp_means, average_info.combo_weights().data(), total_weights_ptr, mptrs);
485 } else {
486 average_group_stats_blockquantile(gene, num_groups, num_blocks, tmp_means, *qbuffer, *qcalc, mptrs);
487 }
488 }
489
490 if (options.compute_group_detected) {
491 const auto tmp_detected = combo_detected.data() + in_offset;
492 if (average_info.use_mean()) {
493 average_group_stats_blockmean(gene, num_groups, num_blocks, tmp_detected, average_info.combo_weights().data(), total_weights_ptr, dptrs);
494 } else {
495 average_group_stats_blockquantile(gene, num_groups, num_blocks, tmp_detected, *qbuffer, *qcalc, dptrs);
496 }
497 }
498
499 // Computing the effect sizes.
500 if (options.compute_cohens_d) {
501 const auto tmp_means = combo_means.data() + in_offset;
502 const auto tmp_variances = combo_vars.data() + in_offset;
503 if (average_info.use_mean()) {
504 compute_pairwise_cohens_d_blockmean(tmp_means, tmp_variances, num_groups, num_blocks, options.threshold, *preweights, buffer.data());
505 } else {
506 compute_pairwise_cohens_d_blockquantile(tmp_means, tmp_variances, num_groups, num_blocks, options.threshold, *qbuffer, *qrevbuffer, *qcalc, buffer.data());
507 }
508 add_best_top_queues(*local_cohens_d_queue, gene, num_groups, buffer);
509 }
510
511 if (options.compute_delta_mean) {
512 const auto tmp_means = combo_means.data() + in_offset;
513 if (average_info.use_mean()) {
514 compute_pairwise_simple_diff_blockmean(tmp_means, num_groups, num_blocks, *preweights, buffer.data());
515 } else {
516 compute_pairwise_simple_diff_blockquantile(tmp_means, num_groups, num_blocks, *qbuffer, *qcalc, buffer.data());
517 }
518 add_best_top_queues(*local_delta_mean_queue, gene, num_groups, buffer);
519 }
520
521 if (options.compute_delta_detected) {
522 const auto tmp_detected = combo_detected.data() + in_offset;
523 if (average_info.use_mean()) {
524 compute_pairwise_simple_diff_blockmean(tmp_detected, num_groups, num_blocks, *preweights, buffer.data());
525 } else {
526 compute_pairwise_simple_diff_blockquantile(tmp_detected, num_groups, num_blocks, *qbuffer, *qcalc, buffer.data());
527 }
528 add_best_top_queues(*local_delta_detected_queue, gene, num_groups, buffer);
529 }
530 }
531
532 // Only moving it into the shared buffer at the end to minimize false sharing.
533 if (options.compute_cohens_d) {
534 (*threaded_cohens_d_queues)[t] = std::move(local_cohens_d_queue);
535 }
536 if (options.compute_delta_mean) {
537 (*threaded_delta_mean_queues)[t] = std::move(local_delta_mean_queue);
538 }
539 if (options.compute_delta_detected) {
540 (*threaded_delta_detected_queues)[t] = std::move(local_delta_detected_queue);
541 }
542 }, ngenes, options.num_threads);
543
544 // Now figuring out which of these are the top dogs.
545 if (options.compute_cohens_d) {
546 threaded_cohens_d_queues->resize(num_used);
547 report_best_top_queues(*threaded_cohens_d_queues, num_groups, output.cohens_d);
548 }
549 if (options.compute_delta_mean) {
550 threaded_delta_mean_queues->resize(num_used);
551 report_best_top_queues(*threaded_delta_mean_queues, num_groups, output.delta_mean);
552 }
553 if (options.compute_delta_detected) {
554 threaded_delta_detected_queues->resize(num_used);
555 report_best_top_queues(*threaded_delta_detected_queues, num_groups, output.delta_detected);
556 }
557}
558
559template<
560 bool single_block_,
561 typename Stat_,
562 typename Value_,
563 typename Index_,
564 typename Group_,
565 typename Block_
566>
567ScoreMarkersBestResults<Stat_, Index_> score_markers_best(
568 const tatami::Matrix<Value_, Index_>& matrix,
569 const Group_* const group,
570 const std::size_t num_groups,
571 const Block_* const block,
572 const std::size_t num_blocks,
573 const std::size_t* const combo,
574 const std::size_t num_combos,
575 const std::vector<Index_>& combo_sizes,
576 const Index_ top,
577 const ScoreMarkersBestOptions& options
578) {
579 const auto ngenes = matrix.nrow();
580 const auto payload_size = sanisizer::product<typename std::vector<Stat_>::size_type>(ngenes, num_combos);
581 std::vector<Stat_> combo_means, combo_vars, combo_detected;
582 if (options.compute_group_mean || options.compute_cohens_d || options.compute_delta_mean) {
583 combo_means.resize(payload_size);
584 }
585 if (options.compute_cohens_d) {
586 combo_vars.resize(payload_size);
587 }
588 if (options.compute_group_detected || options.compute_delta_detected) {
589 combo_detected.resize(payload_size);
590 }
591
592 // For a single block, this usually doesn't really matter, but we do it for consistency with the multi-block case,
593 // and to account for variable weighting where non-zero block sizes get zero weight.
594 BlockAverageInfo<Stat_> average_info;
595 if (options.block_average_policy == BlockAveragePolicy::MEAN) {
596 average_info = BlockAverageInfo<Stat_>(
598 combo_sizes,
599 options.block_weight_policy,
600 options.variable_block_weight_parameters
601 )
602 );
603 } else {
604 average_info = BlockAverageInfo<Stat_>(options.block_quantile);
605 }
606
607 ScoreMarkersBestResults<Stat_, Index_> output;
608
609 if (options.compute_auc) {
610 auto auc_queues = sanisizer::create<std::vector<std::optional<PairwiseTopQueues<Stat_, Index_> > > >(options.num_threads);
611
612 struct AucResultWorkspace {
613 AucResultWorkspace(const std::size_t num_groups) : pairwise_buffer(sanisizer::product<typename std::vector<Stat_>::size_type>(num_groups, num_groups)) {};
614 std::vector<Stat_> pairwise_buffer;
615 PairwiseTopQueues<Stat_, Index_> queue;
616 };
617
618 const auto num_used = scan_matrix_by_row_custom_auc<single_block_>(
619 matrix,
620 group,
621 num_groups,
622 block,
623 num_blocks,
624 combo,
625 num_combos,
626 combo_sizes,
627 average_info,
628 combo_means,
629 combo_vars,
630 combo_detected,
631 /* do_auc = */ true,
632 /* auc_result_initialize = */ [&](const int) -> AucResultWorkspace {
633 AucResultWorkspace res_work(num_groups);
634 allocate_best_top_queues(res_work.queue, num_groups, top, options.largest_auc, options.keep_ties, options.threshold_auc);
635 return res_work;
636 },
637 /* auc_result_process = */ [&](const Index_ gene, AucScanWorkspace<Value_, Group_, Stat_, Index_>& auc_work, AucResultWorkspace& res_work) -> void {
638 process_auc_for_rows(auc_work, num_groups, num_blocks, options.threshold, res_work.pairwise_buffer.data());
639 add_best_top_queues(res_work.queue, gene, num_groups, res_work.pairwise_buffer);
640 },
641 /* auc_result_finalize = */ [&](const int t, AucResultWorkspace& res_work) -> void {
642 auc_queues[t] = std::move(res_work.queue);
643 },
644 options.num_threads
645 );
646
647 auc_queues.resize(num_used);
648 report_best_top_queues(auc_queues, num_groups, output.auc);
649
650 } else if (matrix.prefer_rows()) {
651 scan_matrix_by_row_full_auc<single_block_>(
652 matrix,
653 group,
654 num_groups,
655 block,
656 num_blocks,
657 combo,
658 num_combos,
659 combo_sizes,
660 average_info,
661 combo_means,
662 combo_vars,
663 combo_detected,
664 static_cast<Stat_*>(NULL),
665 options.threshold,
666 options.num_threads
667 );
668
669 } else {
670 scan_matrix_by_column(
671 matrix,
672 [&]{
673 if constexpr(single_block_) {
674 return group;
675 } else {
676 return combo;
677 }
678 }(),
679 [&]{
680 if constexpr(single_block_) {
681 return num_groups;
682 } else {
683 return num_combos;
684 }
685 }(),
686 combo_sizes,
687 combo_means,
688 combo_vars,
689 combo_detected,
690 options.num_threads
691 );
692 }
693
694 find_best_simple_best_effects(
695 matrix.nrow(),
696 num_groups,
697 num_blocks,
698 num_combos,
699 combo_means,
700 combo_vars,
701 combo_detected,
702 average_info,
703 top,
704 options,
705 output
706 );
707
708 return output;
709}
710
711}
738template<typename Stat_, typename Value_, typename Index_, typename Group_>
740 const tatami::Matrix<Value_, Index_>& matrix,
741 const Group_* const group,
742 const std::size_t num_groups,
743 const Index_ top,
744 const ScoreMarkersBestOptions& options
745) {
746 const auto group_sizes = tabulate_groups(matrix.ncol(), group, num_groups);
747 return internal::score_markers_best<true, Stat_>(
748 matrix,
749 group,
750 num_groups,
751 static_cast<int*>(NULL),
752 1,
753 static_cast<std::size_t*>(NULL),
754 num_groups,
755 group_sizes,
756 top,
757 options
758 );
759}
760
787template<typename Stat_, typename Value_, typename Index_, typename Group_, typename Block_>
789 const tatami::Matrix<Value_, Index_>& matrix,
790 const Group_* const group,
791 const std::size_t num_groups,
792 const Block_* const block,
793 const std::size_t num_blocks,
794 const Index_ top,
795 const ScoreMarkersBestOptions& options
796) {
797 const auto combo_out = create_combinations(matrix.ncol(), group, num_groups, block, num_blocks);
798 return internal::score_markers_best<false, Stat_>(
799 matrix,
800 group,
801 num_groups,
802 block,
803 num_blocks,
804 combo_out.combinations.data(),
805 combo_out.num_combinations,
806 combo_out.frequencies,
807 top,
808 options
809 );
810}
811
812}
813
814#endif
Averaging statistics over blocks.
virtual Index_ ncol() const=0
virtual Index_ nrow() const=0
virtual bool prefer_rows() const=0
void compute_weights(const std::size_t num_blocks, const Size_ *const sizes, const WeightPolicy policy, const VariableWeightParameters &variable, Weight_ *const weights)
Marker detection for single-cell data.
Definition score_markers_pairwise.hpp:26
std::vector< std::vector< std::vector< std::pair< Index_, Stat_ > > > > queues_to_vectors(std::vector< std::vector< topicks::TopQueue< Stat_, Index_ > > > &queued)
Definition score_markers_best.hpp:261
ScoreMarkersBestResults< Stat_, Index_ > score_markers_best_blocked(const tatami::Matrix< Value_, Index_ > &matrix, const Group_ *const group, const std::size_t num_groups, const Block_ *const block, const std::size_t num_blocks, const Index_ top, const ScoreMarkersBestOptions &options)
Definition score_markers_best.hpp:788
BlockAveragePolicy
Definition block_averages.hpp:27
ScoreMarkersBestResults< Stat_, Index_ > score_markers_best(const tatami::Matrix< Value_, Index_ > &matrix, const Group_ *const group, const std::size_t num_groups, const Index_ top, const ScoreMarkersBestOptions &options)
Definition score_markers_best.hpp:739
int parallelize(Function_ fun, const Index_ tasks, const int workers)
Options for score_markers_best() and friends.
Definition score_markers_best.hpp:32
bool compute_cohens_d
Definition score_markers_best.hpp:59
std::optional< double > threshold_delta_mean
Definition score_markers_best.hpp:119
double threshold
Definition score_markers_best.hpp:38
bool compute_auc
Definition score_markers_best.hpp:64
bool largest_cohens_d
Definition score_markers_best.hpp:80
BlockAveragePolicy block_average_policy
Definition score_markers_best.hpp:138
bool compute_delta_mean
Definition score_markers_best.hpp:69
bool largest_delta_mean
Definition score_markers_best.hpp:92
bool compute_group_detected
Definition score_markers_best.hpp:54
bool compute_group_mean
Definition score_markers_best.hpp:49
double block_quantile
Definition score_markers_best.hpp:165
std::optional< double > threshold_cohens_d
Definition score_markers_best.hpp:105
std::optional< double > threshold_delta_detected
Definition score_markers_best.hpp:126
bool largest_delta_detected
Definition score_markers_best.hpp:98
bool largest_auc
Definition score_markers_best.hpp:86
scran_blocks::WeightPolicy block_weight_policy
Definition score_markers_best.hpp:152
std::optional< double > threshold_auc
Definition score_markers_best.hpp:112
int num_threads
Definition score_markers_best.hpp:44
scran_blocks::VariableWeightParameters variable_block_weight_parameters
Definition score_markers_best.hpp:159
bool compute_delta_detected
Definition score_markers_best.hpp:74
bool keep_ties
Definition score_markers_best.hpp:131
Results for score_markers_best() and friends.
Definition score_markers_best.hpp:174
std::vector< std::vector< topicks::TopQueue< Stat_, Index_ > > > delta_detected
Definition score_markers_best.hpp:245
std::vector< std::vector< topicks::TopQueue< Stat_, Index_ > > > auc
Definition score_markers_best.hpp:215
std::vector< std::vector< topicks::TopQueue< Stat_, Index_ > > > cohens_d
Definition score_markers_best.hpp:200
std::vector< std::vector< Stat_ > > mean
Definition score_markers_best.hpp:179
std::vector< std::vector< topicks::TopQueue< Stat_, Index_ > > > delta_mean
Definition score_markers_best.hpp:230
std::vector< std::vector< Stat_ > > detected
Definition score_markers_best.hpp:185
std::optional< Stat_ > bound