1#ifndef SCRAN_QC_CHOOSE_FILTER_THRESHOLDS_HPP
2#define SCRAN_QC_CHOOSE_FILTER_THRESHOLDS_HPP
11#include "sanisizer/sanisizer.hpp"
12#include "quickstats/quickstats.hpp"
77template<
typename Float_>
97template<
typename Float_>
98Float_ unlog_threshold(
const Float_ val,
const bool was_logged) {
100 if (std::isinf(val)) {
105 return std::exp(val);
111template<
typename Float_>
112ChooseFilterThresholdsResults<Float_> choose_filter_thresholds_internal(
113 std::size_t num_cells,
115 const ChooseFilterThresholdsOptions& options
117 static_assert(std::is_floating_point<Float_>::value);
120 I<
decltype(num_cells)> lost = 0;
121 for (I<
decltype(num_cells)> i = 0; i < num_cells; ++i) {
122 if (std::isnan(metrics[i])) {
123 std::swap(metrics[i], metrics[lost]);
132 for (I<
decltype(num_cells)> i = 0; i < num_cells; ++i) {
133 auto& val = metrics[i];
136 }
else if (val == 0) {
137 val = -std::numeric_limits<double>::infinity();
139 throw std::runtime_error(
"cannot log-transform negative values");
144 ChooseFilterThresholdsResults<Float_> output;
145 Float_& lthresh = output.
lower;
146 Float_& uthresh = output.upper;
147 lthresh = -std::numeric_limits<Float_>::infinity();
148 uthresh = std::numeric_limits<Float_>::infinity();
150 quickstats::MedianOptions<Float_> medopt;
151 medopt.placeholder = std::numeric_limits<Float_>::quiet_NaN();
152 const auto median = quickstats::median<Float_>(num_cells, metrics, medopt);
154 quickstats::MadOptions<Float_> madopt;
155 madopt.placeholder = std::numeric_limits<Float_>::quiet_NaN();
156 madopt.difference_between_infinities_is_zero =
true;
157 const auto mad = quickstats::scale_mad_to_sd(quickstats::mad<Float_>(num_cells, metrics, median, madopt));
159 if (!std::isnan(mad)) {
160 const auto delta = std::max(
static_cast<Float_
>(options.min_diff),
static_cast<Float_
>(options.num_mads * mad));
162 const auto threshold = median - delta;
163 if (!std::isnan(threshold)) {
164 lthresh = unlog_threshold(threshold, options.log);
168 const auto threshold = median + delta;
169 if (!std::isnan(threshold)) {
170 uthresh = unlog_threshold(threshold, options.log);
199template<
typename Value_,
typename Float_>
201 const std::size_t num_cells,
202 const Value_*
const metrics,
203 Float_*
const buffer,
208 if constexpr(std::is_same<Value_, Float_>::value) {
209 if (metrics == buffer) {
213 std::copy_n(metrics, num_cells, buffer);
215 return choose_filter_thresholds_internal(num_cells, buffer, options);
225template<
typename Float_>
235 template<
typename Block_>
249 std::vector<Float_> buffer;
250 std::vector<std::size_t> block_starts;
251 std::vector<std::size_t> block_offsets;
271template<
typename Float_,
typename Block_>
274 const std::size_t num_cells,
275 const Block_*
const block,
276 const std::size_t num_blocks
278 work.block_starts.clear();
280 sanisizer::resize(work.block_starts, num_blocks);
281 for (I<
decltype(num_cells)> i = 0; i < num_cells; ++i) {
282 ++work.block_starts[block[i]];
285 std::size_t sofar = 0;
286 for (
auto& s : work.block_starts) {
287 const auto last = sofar;
292 sanisizer::resize(work.buffer, num_cells
293#ifdef SCRAN_QC_TEST_INIT
298 sanisizer::resize(work.block_offsets, num_blocks
299#ifdef SCRAN_QC_TEST_INIT
332template<
typename Value_,
typename Block_,
typename Float_>
334 const std::size_t num_cells,
335 const Value_*
const metrics,
336 const Block_*
const block,
337 const std::size_t num_blocks,
341 std::vector<ChooseFilterThresholdsResults<Float_> > output;
342 output.reserve(num_blocks);
343 process_blocks_for_choose_filter_thresholds(
349 [&](
const std::size_t len, Float_*
const ptr) ->
void {
350 output.push_back(choose_filter_thresholds_internal<Float_>(len, ptr, options));
359template<
typename Value_,
typename Block_,
typename Float_,
class Function_>
360void process_blocks_for_choose_filter_thresholds(
361 const std::size_t num_cells,
362 const Value_*
const metrics,
363 const Block_*
const block,
364 const std::size_t num_blocks,
365 ChooseFilterThresholdsBlockedWorkspace<Float_>& workspace,
368 assert(num_cells == workspace.buffer.size());
369 assert(num_blocks == workspace.block_starts.size());
371 auto& buffer = workspace.buffer;
372 const auto& starts = workspace.block_starts;
373 auto& offsets = workspace.block_offsets;
374 std::copy(starts.begin(), starts.end(), offsets.begin());
375 for (I<
decltype(num_cells)> i = 0; i < num_cells; ++i) {
376 auto& pos = offsets[block[i]];
377 buffer[pos] = metrics[i];
381 for (I<
decltype(num_blocks)> g = 0; g < num_blocks; ++g) {
382 fun(offsets[g] - starts[g], buffer.data() + starts[g]);
388template<
bool lower_,
typename Float_>
389std::vector<Float_> extract_filter_thresholds(
const std::vector<ChooseFilterThresholdsResults<Float_> >& res) {
390 std::vector<Float_> output;
391 output.reserve(res.size());
392 for (
const auto& r : res) {
393 if constexpr(lower_) {
394 output.push_back(r.lower);
396 output.push_back(r.upper);
Simple quality control for single-cell data.
Definition adt_quality_control.hpp:22
std::vector< ChooseFilterThresholdsResults< Float_ > > choose_filter_thresholds_blocked(const std::size_t num_cells, const Value_ *const metrics, const Block_ *const block, const std::size_t num_blocks, ChooseFilterThresholdsBlockedWorkspace< Float_ > &workspace, const ChooseFilterThresholdsOptions &options)
Definition choose_filter_thresholds.hpp:333
void reset_choose_filter_thresholds_blocked_workspace(ChooseFilterThresholdsBlockedWorkspace< Float_ > &work, const std::size_t num_cells, const Block_ *const block, const std::size_t num_blocks)
Definition choose_filter_thresholds.hpp:272
ChooseFilterThresholdsResults< Float_ > choose_filter_thresholds(const std::size_t num_cells, const Value_ *const metrics, Float_ *const buffer, const ChooseFilterThresholdsOptions &options)
Definition choose_filter_thresholds.hpp:200
Workspace for choose_filter_thresholds_blocked().
Definition choose_filter_thresholds.hpp:226
ChooseFilterThresholdsBlockedWorkspace()=default
ChooseFilterThresholdsBlockedWorkspace(const std::size_t num_cells, const Block_ *const block, const std::size_t num_blocks)
Definition choose_filter_thresholds.hpp:236
Options for choose_filter_thresholds().
Definition choose_filter_thresholds.hpp:26
bool upper
Definition choose_filter_thresholds.hpp:37
bool log
Definition choose_filter_thresholds.hpp:70
double num_mads
Definition choose_filter_thresholds.hpp:44
double min_diff
Definition choose_filter_thresholds.hpp:51
bool lower
Definition choose_filter_thresholds.hpp:31
Results of compute_adt_qc_metrics().
Definition choose_filter_thresholds.hpp:78
Float_ upper
Definition choose_filter_thresholds.hpp:91
Float_ lower
Definition choose_filter_thresholds.hpp:84