scran_variances
Model per-gene variance in expression
Loading...
Searching...
No Matches
fit_variance_trend.hpp
Go to the documentation of this file.
1#ifndef SCRAN_VARIANCES_FIT_VARIANCE_TREND_H
2#define SCRAN_VARIANCES_FIT_VARIANCE_TREND_H
3
4#include <algorithm>
5#include <vector>
6#include <array>
7#include <cstddef>
8
10#include "sanisizer/sanisizer.hpp"
11
12#include "utils.hpp"
13
19namespace scran_variances {
20
35 bool mean_filter = true;
36
43 double minimum_mean = 0.1;
44
50 bool transform = true;
51
66 bool use_minimum_width = true;
67
73 double minimum_width = 1;
74
84
90 double span = 0.3;
91
96 int num_threads = 1;
97};
98
104template<typename Float_>
110
111 std::vector<unsigned char> sort_workspace;
112
113 std::vector<Float_> xbuffer, ybuffer;
117};
118
148template<typename Float_>
150 const std::size_t n,
151 const Float_* const mean,
152 const Float_* const variance,
153 Float_* const fitted,
154 Float_* const residual,
156 const FitVarianceTrendOptions& options
157) {
158 auto& xbuffer = workspace.xbuffer;
159 sanisizer::resize(xbuffer, n);
160 auto& ybuffer = workspace.ybuffer;
161 sanisizer::resize(ybuffer, n);
162
163 const auto quad = [](Float_ x) -> Float_ {
164 return x * x * x * x;
165 };
166
167 std::size_t counter = 0;
168 const Float_ min_mean = options.minimum_mean;
169 for (I<decltype(n)> i = 0; i < n; ++i) {
170 if (!options.mean_filter || mean[i] >= min_mean) {
171 xbuffer[counter] = mean[i];
172 if (options.transform) {
173 ybuffer[counter] = std::pow(variance[i], 0.25); // Using the same quarter-root transform that limma::voom uses.
174 } else {
175 ybuffer[counter] = variance[i];
176 }
177 ++counter;
178 }
179 }
180
181 if (counter < 2) {
182 throw std::runtime_error("not enough observations above the minimum mean");
183 }
184
185 auto& sorter = workspace.sorter;
186 sorter.set(counter, xbuffer.data());
187 auto& work = workspace.sort_workspace;
188 sorter.permute(std::array<Float_*, 2>{ xbuffer.data(), ybuffer.data() }, work);
189
191 if (options.use_minimum_width) {
192 smooth_opt.span = options.minimum_window_count;
193 smooth_opt.span_as_proportion = false;
194 smooth_opt.minimum_width = options.minimum_width;
195 } else {
196 smooth_opt.span = options.span;
197 }
198 smooth_opt.num_threads = options.num_threads;
199
200 // Using the residual array to store the robustness weights as a placeholder;
201 // we'll be overwriting this later.
202 WeightedLowess::compute(counter, xbuffer.data(), ybuffer.data(), fitted, residual, smooth_opt);
203
204 // Determining the left edge before we unpermute.
205 const Float_ left_x = xbuffer[0];
206 const Float_ left_fitted = (options.transform ? quad(fitted[0]) : fitted[0]);
207
208 sorter.unpermute(fitted, work);
209
210 // Walking backwards to shift the elements back to their original position
211 // (i.e., before filtering on the mean) on the same array. We need to walk
212 // backwards to ensure that writing to the original position on this array
213 // doesn't clobber the first 'counter' positions containing the fitted
214 // values, at least not until each value is shifted to its original place.
215 for (auto i = n; i > 0; --i) {
216 auto j = i - 1;
217 if (!options.mean_filter || mean[j] >= min_mean) {
218 --counter;
219 fitted[j] = (options.transform ? quad(fitted[counter]) : fitted[counter]);
220 } else {
221 fitted[j] = mean[j] / left_x * left_fitted; // draw a y = x line to the origin from the left of the fitted trend.
222 }
223 }
224
225 for (I<decltype(n)> i = 0; i < n; ++i) {
226 residual[i] = variance[i] - fitted[i];
227 }
228 return;
229}
230
239template<typename Float_>
245
246 FitVarianceTrendResults(const std::size_t n) :
247 fitted(sanisizer::cast<I<decltype(fitted.size())> >(n)
248#ifdef SCRAN_VARIANCES_TEST_INIT
249 , SCRAN_VARIANCES_TEST_INIT
250#endif
251 ),
252 residual(sanisizer::cast<I<decltype(residual.size())> >(n)
253#ifdef SCRAN_VARIANCES_TEST_INIT
254 , SCRAN_VARIANCES_TEST_INIT
255#endif
256 )
257 {}
265 std::vector<Float_> fitted;
266
270 std::vector<Float_> residual;
271};
272
285template<typename Float_>
286FitVarianceTrendResults<Float_> fit_variance_trend(const std::size_t n, const Float_* const mean, const Float_* const variance, const FitVarianceTrendOptions& options) {
289 fit_variance_trend(n, mean, variance, output.fitted.data(), output.residual.data(), work, options);
290 return output;
291}
292
293}
294
295#endif
void compute(const std::size_t num_points, const Data_ *const x, const PrecomputedWindows< Data_ > &windows, const Data_ *const y, Data_ *const fitted, Data_ *robust_weights, const Options< Data_ > &opt)
Variance modelling for single-cell expression data.
Definition choose_highly_variable_genes.hpp:15
void fit_variance_trend(const std::size_t n, const Float_ *const mean, const Float_ *const variance, Float_ *const fitted, Float_ *const residual, FitVarianceTrendWorkspace< Float_ > &workspace, const FitVarianceTrendOptions &options)
Definition fit_variance_trend.hpp:149
Options for fit_variance_trend().
Definition fit_variance_trend.hpp:24
int num_threads
Definition fit_variance_trend.hpp:96
double minimum_width
Definition fit_variance_trend.hpp:73
bool use_minimum_width
Definition fit_variance_trend.hpp:66
double span
Definition fit_variance_trend.hpp:90
bool transform
Definition fit_variance_trend.hpp:50
int minimum_window_count
Definition fit_variance_trend.hpp:83
bool mean_filter
Definition fit_variance_trend.hpp:35
double minimum_mean
Definition fit_variance_trend.hpp:43
Results of fit_variance_trend().
Definition fit_variance_trend.hpp:240
std::vector< Float_ > residual
Definition fit_variance_trend.hpp:270
std::vector< Float_ > fitted
Definition fit_variance_trend.hpp:265
Workspace for fit_variance_trend().
Definition fit_variance_trend.hpp:105