1#ifndef SCRAN_MARKERS_SCORE_MARKERS_BEST_HPP
2#define SCRAN_MARKERS_SCORE_MARKERS_BEST_HPP
10#include "sanisizer/sanisizer.hpp"
12#include "quickstats/quickstats.hpp"
14#include "scan_matrix.hpp"
15#include "average_group_stats.hpp"
17#include "create_combinations.hpp"
18#include "cohens_d.hpp"
19#include "simple_diff.hpp"
173template<
typename Stat_,
typename Index_>
179 std::vector<std::vector<Stat_> >
mean;
200 std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > >
cohens_d;
215 std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > >
auc;
230 std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > >
delta_mean;
260template<
typename Stat_,
typename Index_>
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);
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) {
273 auto& current_in = queued[g1][g2];
274 auto& current_out = output[g1][g2];
275 current_out.reserve(current_in.size());
277 while (!current_in.empty()) {
278 const auto& best = current_in.top();
279 current_out.emplace_back(best.second, best.first);
282 std::reverse(current_out.begin(), current_out.end());
294template<
typename Stat_,
typename Index_>
295using PairwiseTopQueues = std::vector<std::vector<topicks::TopQueue<Stat_, Index_> > >;
297template<
typename Stat_,
typename Index_>
298void allocate_best_top_queues(
299 PairwiseTopQueues<Stat_, Index_>& pqueues,
300 const std::size_t num_groups,
303 const bool keep_ties,
304 const std::optional<Stat_>& bound
309 if (bound.has_value()) {
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) {
319 x.emplace_back(0, larger, opt);
321 x.emplace_back(top, larger, opt);
327template<
typename Stat_,
typename Index_>
328void add_best_top_queues(
329 PairwiseTopQueues<Stat_, Index_>& pqueues,
331 std::size_t num_groups,
332 const std::vector<Stat_>& effects
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)];
338 pqueues[g1][g2].emplace(val, gene);
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,
351 const int num_available = pqueues.size();
354 if (num_available == 0) {
355 sanisizer::resize(output, num_groups);
357 for (I<
decltype(num_groups)> g1 = 0; g1 < num_groups; ++g1) {
358 sanisizer::resize(output[g1], num_groups, placeholder);
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());
379 output = std::move(true_pqueue);
382template<
typename Index_,
typename Stat_>
383void find_best_simple_best_effects(
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,
393 const ScoreMarkersBestOptions& options,
394 ScoreMarkersBestResults<Stat_, Index_>& output
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();
404 total_weights_ptr = average_info.combo_weights().data();
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());
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());
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());
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));
441 if (options.compute_delta_mean) {
442 threaded_delta_mean_queues.emplace(sanisizer::cast<I<
decltype(threaded_delta_mean_queues->size())> >(options.num_threads));
444 if (options.compute_delta_detected) {
445 threaded_delta_detected_queues.emplace(sanisizer::cast<I<
decltype(threaded_delta_detected_queues->size())> >(options.num_threads));
448 const auto num_groups2 = sanisizer::product<typename std::vector<Stat_>::size_type>(num_groups, num_groups);
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);
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);
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);
465 std::vector<Stat_> buffer;
466 if (options.compute_cohens_d || options.compute_delta_mean || options.compute_delta_detected) {
467 buffer.resize(num_groups2);
470 std::optional<std::vector<Stat_> > qbuffer, qrevbuffer;
471 std::optional<quickstats::SingleQuantileVariableNumber<Stat_> > qcalc;
472 if (!average_info.use_mean()) {
474 qrevbuffer.emplace();
475 qcalc.emplace(num_blocks, average_info.quantile());
478 for (Index_ gene = start, end = start + length; gene < end; ++gene) {
479 auto in_offset = sanisizer::product_unsafe<std::size_t>(gene, num_combos);
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);
486 average_group_stats_blockquantile(gene, num_groups, num_blocks, tmp_means, *qbuffer, *qcalc, mptrs);
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);
495 average_group_stats_blockquantile(gene, num_groups, num_blocks, tmp_detected, *qbuffer, *qcalc, dptrs);
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());
506 compute_pairwise_cohens_d_blockquantile(tmp_means, tmp_variances, num_groups, num_blocks, options.threshold, *qbuffer, *qrevbuffer, *qcalc, buffer.data());
508 add_best_top_queues(*local_cohens_d_queue, gene, num_groups, buffer);
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());
516 compute_pairwise_simple_diff_blockquantile(tmp_means, num_groups, num_blocks, *qbuffer, *qcalc, buffer.data());
518 add_best_top_queues(*local_delta_mean_queue, gene, num_groups, buffer);
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());
526 compute_pairwise_simple_diff_blockquantile(tmp_detected, num_groups, num_blocks, *qbuffer, *qcalc, buffer.data());
528 add_best_top_queues(*local_delta_detected_queue, gene, num_groups, buffer);
533 if (options.compute_cohens_d) {
534 (*threaded_cohens_d_queues)[t] = std::move(local_cohens_d_queue);
536 if (options.compute_delta_mean) {
537 (*threaded_delta_mean_queues)[t] = std::move(local_delta_mean_queue);
539 if (options.compute_delta_detected) {
540 (*threaded_delta_detected_queues)[t] = std::move(local_delta_detected_queue);
542 }, ngenes, options.num_threads);
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);
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);
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);
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,
577 const ScoreMarkersBestOptions& options
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);
585 if (options.compute_cohens_d) {
586 combo_vars.resize(payload_size);
588 if (options.compute_group_detected || options.compute_delta_detected) {
589 combo_detected.resize(payload_size);
594 BlockAverageInfo<Stat_> average_info;
595 if (options.block_average_policy == BlockAveragePolicy::MEAN) {
596 average_info = BlockAverageInfo<Stat_>(
599 options.block_weight_policy,
600 options.variable_block_weight_parameters
604 average_info = BlockAverageInfo<Stat_>(options.block_quantile);
607 ScoreMarkersBestResults<Stat_, Index_> output;
609 if (options.compute_auc) {
610 auto auc_queues = sanisizer::create<std::vector<std::optional<PairwiseTopQueues<Stat_, Index_> > > >(options.num_threads);
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;
618 const auto num_used = scan_matrix_by_row_custom_auc<single_block_>(
632 [&](
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);
637 [&](
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);
641 [&](
const int t, AucResultWorkspace& res_work) ->
void {
642 auc_queues[t] = std::move(res_work.queue);
647 auc_queues.resize(num_used);
648 report_best_top_queues(auc_queues, num_groups, output.auc);
651 scan_matrix_by_row_full_auc<single_block_>(
664 static_cast<Stat_*
>(NULL),
670 scan_matrix_by_column(
673 if constexpr(single_block_) {
680 if constexpr(single_block_) {
694 find_best_simple_best_effects(
738template<
typename Stat_,
typename Value_,
typename Index_,
typename Group_>
741 const Group_*
const group,
742 const std::size_t num_groups,
746 const auto group_sizes = tabulate_groups(matrix.
ncol(), group, num_groups);
747 return internal::score_markers_best<true, Stat_>(
751 static_cast<int*
>(NULL),
753 static_cast<std::size_t*
>(NULL),
787template<
typename Stat_,
typename Value_,
typename Index_,
typename Group_,
typename Block_>
790 const Group_*
const group,
791 const std::size_t num_groups,
792 const Block_*
const block,
793 const std::size_t num_blocks,
797 const auto combo_out = create_combinations(matrix.
ncol(), group, num_groups, block, num_blocks);
798 return internal::score_markers_best<false, Stat_>(
804 combo_out.combinations.data(),
805 combo_out.num_combinations,
806 combo_out.frequencies,
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)
std::optional< Stat_ > bound