scran_markers
Marker detection for single-cell data
Loading...
Searching...
No Matches
score_markers_pairwise.hpp
Go to the documentation of this file.
1#ifndef SCRAN_MARKERS_SCORE_MARKERS_PAIRWISE_HPP
2#define SCRAN_MARKERS_SCORE_MARKERS_PAIRWISE_HPP
3
4#include <vector>
5#include <cstddef>
6#include <optional>
7
9#include "tatami/tatami.hpp"
10#include "sanisizer/sanisizer.hpp"
11#include "quickstats/quickstats.hpp"
12
13#include "scan_matrix.hpp"
14#include "average_group_stats.hpp"
15#include "block_averages.hpp"
16#include "create_combinations.hpp"
17#include "cohens_d.hpp"
18#include "simple_diff.hpp"
19#include "utils.hpp"
20
26namespace scran_markers {
27
37 double threshold = 0;
38
43 int num_threads = 1;
44
49 bool compute_group_mean = true;
50
56
61 bool compute_cohens_d = true;
62
67 bool compute_auc = true;
68
73 bool compute_delta_mean = true;
74
80
86 BlockAveragePolicy block_average_policy = BlockAveragePolicy::MEAN;
87
100 scran_blocks::WeightPolicy block_weight_policy = scran_blocks::WeightPolicy::VARIABLE;
101
108
113 double block_quantile = 0.5;
114};
115
120template<typename Stat_>
129 std::vector<Stat_*> mean;
130
138 std::vector<Stat_*> detected;
139
155 Stat_* cohens_d = NULL;
156
169 Stat_* auc = NULL;
170
178 Stat_* delta_mean = NULL;
179
187 Stat_* delta_detected = NULL;
188};
189
193namespace internal {
194
195template<typename Index_, typename Stat_>
196void process_simple_pairwise_effects(
197 const Index_ ngenes,
198 const std::size_t num_groups,
199 const std::size_t num_blocks,
200 const std::size_t num_combos,
201 const std::vector<Stat_>& combo_means,
202 const std::vector<Stat_>& combo_vars,
203 const std::vector<Stat_>& combo_detected,
204 const double threshold,
205 const BlockAverageInfo<Stat_>& average_info,
207 const int num_threads
208) {
209 const Stat_* total_weights_ptr = NULL;
210 std::optional<std::vector<Stat_> > total_weights_per_group;
211 if (average_info.use_mean()) {
212 if (!output.mean.empty() || !output.detected.empty()) {
213 if (num_blocks > 1) {
214 total_weights_per_group = compute_total_weight_per_group(num_groups, num_blocks, average_info.combo_weights().data());
215 total_weights_ptr = total_weights_per_group->data();
216 } else {
217 total_weights_ptr = average_info.combo_weights().data();
218 }
219 }
220 }
221
222 std::optional<PrecomputedPairwiseWeights<Stat_> > preweights;
223 if (average_info.use_mean()) {
224 if (output.cohens_d != NULL || output.delta_mean != NULL || output.delta_detected != NULL) {
225 preweights.emplace(num_groups, num_blocks, average_info.combo_weights().data());
226 }
227 }
228
229 tatami::parallelize([&](const int, const Index_ start, const Index_ length) -> void {
230 std::optional<std::vector<Stat_> > qbuffer, qrevbuffer;
231 std::optional<quickstats::SingleQuantileVariableNumber<Stat_> > qcalc;
232 if (!average_info.use_mean()) {
233 qbuffer.emplace();
234 qrevbuffer.emplace();
235 qcalc.emplace(num_blocks, average_info.quantile());
236 }
237
238 for (Index_ gene = start, end = start + length; gene < end; ++gene) {
239 auto in_offset = sanisizer::product_unsafe<std::size_t>(gene, num_combos);
240
241 if (!output.mean.empty()) {
242 const auto tmp_means = combo_means.data() + in_offset;
243 if (average_info.use_mean()) {
244 average_group_stats_blockmean(gene, num_groups, num_blocks, tmp_means, average_info.combo_weights().data(), total_weights_ptr, output.mean);
245 } else {
246 average_group_stats_blockquantile(gene, num_groups, num_blocks, tmp_means, *qbuffer, *qcalc, output.mean);
247 }
248 }
249
250 if (!output.detected.empty()) {
251 const auto tmp_detected = combo_detected.data() + in_offset;
252 if (average_info.use_mean()) {
253 average_group_stats_blockmean(gene, num_groups, num_blocks, tmp_detected, average_info.combo_weights().data(), total_weights_ptr, output.detected);
254 } else {
255 average_group_stats_blockquantile(gene, num_groups, num_blocks, tmp_detected, *qbuffer, *qcalc, output.detected);
256 }
257 }
258
259 // Computing the effect sizes.
260 const auto out_offset = sanisizer::product_unsafe<std::size_t>(gene, num_groups, num_groups);
261
262 if (output.cohens_d != NULL) {
263 const auto tmp_means = combo_means.data() + in_offset;
264 const auto tmp_variances = combo_vars.data() + in_offset;
265 const auto outptr = output.cohens_d + out_offset;
266 if (average_info.use_mean()) {
267 compute_pairwise_cohens_d_blockmean(tmp_means, tmp_variances, num_groups, num_blocks, threshold, *preweights, outptr);
268 } else {
269 compute_pairwise_cohens_d_blockquantile(tmp_means, tmp_variances, num_groups, num_blocks, threshold, *qbuffer, *qrevbuffer, *qcalc, outptr);
270 }
271 }
272
273 if (output.delta_detected != NULL) {
274 const auto tmp_detected = combo_detected.data() + in_offset;
275 const auto outptr = output.delta_detected + out_offset;
276 if (average_info.use_mean()) {
277 compute_pairwise_simple_diff_blockmean(tmp_detected, num_groups, num_blocks, *preweights, outptr);
278 } else {
279 compute_pairwise_simple_diff_blockquantile(tmp_detected, num_groups, num_blocks, *qbuffer, *qcalc, outptr);
280 }
281 }
282
283 if (output.delta_mean != NULL) {
284 const auto tmp_means = combo_means.data() + in_offset;
285 const auto outptr = output.delta_mean + out_offset;
286 if (average_info.use_mean()) {
287 compute_pairwise_simple_diff_blockmean(tmp_means, num_groups, num_blocks, *preweights, outptr);
288 } else {
289 compute_pairwise_simple_diff_blockquantile(tmp_means, num_groups, num_blocks, *qbuffer, *qcalc, outptr);
290 }
291 }
292 }
293 }, ngenes, num_threads);
294}
295
296template<
297 bool single_block_,
298 typename Value_,
299 typename Index_,
300 typename Group_,
301 typename Block_,
302 typename Stat_
303>
304void score_markers_pairwise(
305 const tatami::Matrix<Value_, Index_>& matrix,
306 const Group_* const group,
307 const std::size_t num_groups,
308 const Block_* const block,
309 const std::size_t num_blocks,
310 const std::size_t* const combo,
311 const std::size_t num_combos,
312 const std::vector<Index_>& combo_sizes,
313 const ScoreMarkersPairwiseOptions& options,
314 const ScoreMarkersPairwiseBuffers<Stat_>& output
315) {
316 const auto ngenes = matrix.nrow();
317 const auto payload_size = sanisizer::product<typename std::vector<Stat_>::size_type>(ngenes, num_combos);
318 std::vector<Stat_> combo_means, combo_vars, combo_detected;
319 if (!output.mean.empty() || output.cohens_d != NULL || output.delta_mean != NULL) {
320 combo_means.resize(payload_size);
321 }
322 if (output.cohens_d != NULL) {
323 combo_vars.resize(payload_size);
324 }
325 if (!output.detected.empty() || output.delta_detected != NULL) {
326 combo_detected.resize(payload_size);
327 }
328
329 // For a single block, this usually doesn't really matter, but we do it for consistency with the multi-block case,
330 // and to account for variable weighting where non-zero block sizes get zero weight.
331 BlockAverageInfo<Stat_> average_info;
332 if (options.block_average_policy == BlockAveragePolicy::MEAN) {
333 average_info = BlockAverageInfo<Stat_>(
335 combo_sizes,
336 options.block_weight_policy,
337 options.variable_block_weight_parameters
338 )
339 );
340 } else {
341 average_info = BlockAverageInfo<Stat_>(options.block_quantile);
342 }
343
344 if (output.auc != NULL || matrix.prefer_rows()) {
345 scan_matrix_by_row_full_auc<single_block_>(
346 matrix,
347 group,
348 num_groups,
349 block,
350 num_blocks,
351 combo,
352 num_combos,
353 combo_sizes,
354 average_info,
355 combo_means,
356 combo_vars,
357 combo_detected,
358 output.auc,
359 options.threshold,
360 options.num_threads
361 );
362
363 } else {
364 scan_matrix_by_column(
365 matrix,
366 [&]{
367 if constexpr(single_block_) {
368 return group;
369 } else {
370 return combo;
371 }
372 }(),
373 [&]{
374 if constexpr(single_block_) {
375 return num_groups;
376 } else {
377 return num_combos;
378 }
379 }(),
380 combo_sizes,
381 combo_means,
382 combo_vars,
383 combo_detected,
384 options.num_threads
385 );
386 }
387
388 process_simple_pairwise_effects(
389 matrix.nrow(),
390 num_groups,
391 num_blocks,
392 num_combos,
393 combo_means,
394 combo_vars,
395 combo_detected,
396 options.threshold,
397 average_info,
398 output,
399 options.num_threads
400 );
401}
402
403}
479template<typename Value_, typename Index_, typename Group_, typename Stat_>
481 const tatami::Matrix<Value_, Index_>& matrix,
482 const Group_* const group,
483 const std::size_t num_groups,
484 const ScoreMarkersPairwiseOptions& options,
486) {
487 const auto group_sizes = tabulate_groups(matrix.ncol(), group, num_groups);
488 internal::score_markers_pairwise<true>(
489 matrix,
490 group,
491 num_groups,
492 static_cast<int*>(NULL),
493 1,
494 static_cast<std::size_t*>(NULL),
495 num_groups,
496 group_sizes,
497 options,
498 output
499 );
500}
501
543template<typename Value_, typename Index_, typename Group_, typename Block_, typename Stat_>
545 const tatami::Matrix<Value_, Index_>& matrix,
546 const Group_* const group,
547 const std::size_t num_groups,
548 const Block_* const block,
549 const std::size_t num_blocks,
550 const ScoreMarkersPairwiseOptions& options,
552) {
553 const auto combo_out = create_combinations(matrix.ncol(), group, num_groups, block, num_blocks);
554 internal::score_markers_pairwise<false>(
555 matrix,
556 group,
557 num_groups,
558 block,
559 num_blocks,
560 combo_out.combinations.data(),
561 combo_out.num_combinations,
562 combo_out.frequencies,
563 options,
564 output
565 );
566}
567
572template<typename Stat_>
580 std::vector<std::vector<Stat_> > mean;
581
588 std::vector<std::vector<Stat_> > detected;
589
597 std::vector<Stat_> cohens_d;
598
606 std::vector<Stat_> auc;
607
615 std::vector<Stat_> delta_mean;
616
624 std::vector<Stat_> delta_detected;
625};
626
630template<typename Index_, typename Stat_>
631ScoreMarkersPairwiseBuffers<Stat_> preallocate_pairwise_results(
632 const Index_ ngenes,
633 const std::size_t num_groups,
636) {
638
639 if (opt.compute_group_mean) {
640 internal::preallocate_average_results(ngenes, num_groups, store.mean, output.mean);
641 }
642 if (opt.compute_group_detected) {
643 internal::preallocate_average_results(ngenes, num_groups, store.detected, output.detected);
644 }
645
646 const auto num_effect_sizes = sanisizer::product<typename std::vector<Stat_>::size_type>(ngenes, num_groups, num_groups);
647
648 if (opt.compute_cohens_d) {
649 store.cohens_d.resize(num_effect_sizes
650#ifdef SCRAN_MARKERS_TEST_INIT
651 , SCRAN_MARKERS_TEST_INIT
652#endif
653 );
654 output.cohens_d = store.cohens_d.data();
655 }
656 if (opt.compute_auc) {
657 store.auc.resize(num_effect_sizes
658#ifdef SCRAN_MARKERS_TEST_INIT
659 , SCRAN_MARKERS_TEST_INIT
660#endif
661 );
662 output.auc = store.auc.data();
663 }
664 if (opt.compute_delta_mean) {
665 store.delta_mean.resize(num_effect_sizes
666#ifdef SCRAN_MARKERS_TEST_INIT
667 , SCRAN_MARKERS_TEST_INIT
668#endif
669 );
670 output.delta_mean = store.delta_mean.data();
671 }
672 if (opt.compute_delta_detected) {
673 store.delta_detected.resize(num_effect_sizes
674#ifdef SCRAN_MARKERS_TEST_INIT
675 , SCRAN_MARKERS_TEST_INIT
676#endif
677 );
678 output.delta_detected = store.delta_detected.data();
679 }
680
681 return output;
682}
704template<typename Stat_ = double, typename Value_, typename Index_, typename Group_>
706 const tatami::Matrix<Value_, Index_>& matrix,
707 const Group_* const group,
708 const std::size_t num_groups,
709 const ScoreMarkersPairwiseOptions& options
710) {
712 auto buffers = preallocate_pairwise_results(matrix.nrow(), num_groups, res, options);
713 score_markers_pairwise(matrix, group, num_groups, options, buffers);
714 return res;
715}
716
738template<typename Stat_ = double, typename Value_, typename Index_, typename Group_, typename Block_>
740 const tatami::Matrix<Value_, Index_>& matrix,
741 const Group_* const group,
742 const std::size_t num_groups,
743 const Block_* const block,
744 const std::size_t num_blocks,
745 const ScoreMarkersPairwiseOptions& options
746) {
748 const auto buffers = preallocate_pairwise_results(matrix.nrow(), num_groups, res, options);
749 score_markers_pairwise_blocked(matrix, group, num_groups, block, num_blocks, options, buffers);
750 return res;
751}
752
753}
754
755#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
void score_markers_pairwise(const tatami::Matrix< Value_, Index_ > &matrix, const Group_ *const group, const std::size_t num_groups, const ScoreMarkersPairwiseOptions &options, const ScoreMarkersPairwiseBuffers< Stat_ > &output)
Definition score_markers_pairwise.hpp:480
void score_markers_pairwise_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 ScoreMarkersPairwiseOptions &options, const ScoreMarkersPairwiseBuffers< Stat_ > &output)
Definition score_markers_pairwise.hpp:544
BlockAveragePolicy
Definition block_averages.hpp:27
int parallelize(Function_ fun, const Index_ tasks, const int workers)
Buffers for score_markers_pairwise() and friends.
Definition score_markers_pairwise.hpp:121
Stat_ * delta_mean
Definition score_markers_pairwise.hpp:178
Stat_ * cohens_d
Definition score_markers_pairwise.hpp:155
std::vector< Stat_ * > detected
Definition score_markers_pairwise.hpp:138
Stat_ * delta_detected
Definition score_markers_pairwise.hpp:187
Stat_ * auc
Definition score_markers_pairwise.hpp:169
std::vector< Stat_ * > mean
Definition score_markers_pairwise.hpp:129
Options for score_markers_pairwise() and friends.
Definition score_markers_pairwise.hpp:31
bool compute_group_mean
Definition score_markers_pairwise.hpp:49
scran_blocks::WeightPolicy block_weight_policy
Definition score_markers_pairwise.hpp:100
int num_threads
Definition score_markers_pairwise.hpp:43
bool compute_delta_mean
Definition score_markers_pairwise.hpp:73
double block_quantile
Definition score_markers_pairwise.hpp:113
double threshold
Definition score_markers_pairwise.hpp:37
bool compute_group_detected
Definition score_markers_pairwise.hpp:55
BlockAveragePolicy block_average_policy
Definition score_markers_pairwise.hpp:86
bool compute_cohens_d
Definition score_markers_pairwise.hpp:61
bool compute_auc
Definition score_markers_pairwise.hpp:67
bool compute_delta_detected
Definition score_markers_pairwise.hpp:79
scran_blocks::VariableWeightParameters variable_block_weight_parameters
Definition score_markers_pairwise.hpp:107
Results for score_markers_pairwise() and friends.
Definition score_markers_pairwise.hpp:573
std::vector< Stat_ > delta_mean
Definition score_markers_pairwise.hpp:615
std::vector< std::vector< Stat_ > > detected
Definition score_markers_pairwise.hpp:588
std::vector< Stat_ > cohens_d
Definition score_markers_pairwise.hpp:597
std::vector< Stat_ > auc
Definition score_markers_pairwise.hpp:606
std::vector< Stat_ > delta_detected
Definition score_markers_pairwise.hpp:624
std::vector< std::vector< Stat_ > > mean
Definition score_markers_pairwise.hpp:580