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
17namespace scran_variances {
18
29 double minimum_mean = 0.1;
30
35 bool mean_filter = true;
36
41 bool transform = true;
42
47 double span = 0.3;
48
54 bool use_minimum_width = false;
55
61 double minimum_width = 1;
62
69
74 int num_threads = 1;
75};
76
82template<typename Float_>
88
89 std::vector<uint8_t> sort_workspace;
90
91 std::vector<Float_> xbuffer, ybuffer;
95};
96
122template<typename Float_>
123void fit_variance_trend(std::size_t n, const Float_* mean, const Float_* variance, Float_* fitted, Float_* residuals, FitVarianceTrendWorkspace<Float_>& workspace, const FitVarianceTrendOptions& options) {
124 auto& xbuffer = workspace.xbuffer;
125 xbuffer.resize(sanisizer::cast<decltype(xbuffer.size())>(n));
126 auto& ybuffer = workspace.ybuffer;
127 ybuffer.resize(sanisizer::cast<decltype(ybuffer.size())>(n));
128
129 auto quad = [](Float_ x) -> Float_ {
130 return x * x * x * x;
131 };
132
133 std::size_t counter = 0;
134 Float_ min_mean = options.minimum_mean;
135 for (decltype(n) i = 0; i < n; ++i) {
136 if (!options.mean_filter || mean[i] >= min_mean) {
137 xbuffer[counter] = mean[i];
138 if (options.transform) {
139 ybuffer[counter] = std::pow(variance[i], 0.25); // Using the same quarter-root transform that limma::voom uses.
140 } else {
141 ybuffer[counter] = variance[i];
142 }
143 ++counter;
144 }
145 }
146
147 if (counter < 2) {
148 throw std::runtime_error("not enough observations above the minimum mean");
149 }
150
151 auto& sorter = workspace.sorter;
152 sorter.set(counter, xbuffer.data());
153 auto& work = workspace.sort_workspace;
154 sorter.permute(std::array<Float_*, 2>{ xbuffer.data(), ybuffer.data() }, work);
155
157 if (options.use_minimum_width) {
158 smooth_opt.span = options.minimum_window_count;
159 smooth_opt.span_as_proportion = false;
160 smooth_opt.minimum_width = options.minimum_width;
161 } else {
162 smooth_opt.span = options.span;
163 }
164 smooth_opt.num_threads = options.num_threads;
165
166 // Using the residual array to store the robustness weights as a placeholder;
167 // we'll be overwriting this later.
168 WeightedLowess::compute(counter, xbuffer.data(), ybuffer.data(), fitted, residuals, smooth_opt);
169
170 // Determining the left edge before we unpermute.
171 Float_ left_x = xbuffer[0];
172 Float_ left_fitted = (options.transform ? quad(fitted[0]) : fitted[0]);
173
174 sorter.unpermute(fitted, work);
175
176 // Walking backwards to shift the elements back to their original position
177 // (i.e., before filtering on the mean) on the same array. We need to walk
178 // backwards to ensure that writing to the original position on this array
179 // doesn't clobber the first 'counter' positions containing the fitted
180 // values, at least not until each value is shifted to its original place.
181 for (auto i = n; i > 0; --i) {
182 auto j = i - 1;
183 if (!options.mean_filter || mean[j] >= min_mean) {
184 --counter;
185 fitted[j] = (options.transform ? quad(fitted[counter]) : fitted[counter]);
186 } else {
187 fitted[j] = mean[j] / left_x * left_fitted; // draw a y = x line to the origin from the left of the fitted trend.
188 }
189 }
190
191 for (decltype(n) i = 0; i < n; ++i) {
192 residuals[i] = variance[i] - fitted[i];
193 }
194 return;
195}
196
205template<typename Float_>
211
212 FitVarianceTrendResults(std::size_t n) :
213 fitted(sanisizer::cast<decltype(fitted.size())>(n)
214#ifdef SCRAN_VARIANCES_TEST_INIT
215 , SCRAN_VARIANCES_TEST_INIT
216#endif
217 ),
218 residuals(sanisizer::cast<decltype(residuals.size())>(n)
219#ifdef SCRAN_VARIANCES_TEST_INIT
220 , SCRAN_VARIANCES_TEST_INIT
221#endif
222 )
223 {}
231 std::vector<Float_> fitted;
232
236 std::vector<Float_> residuals;
237};
238
251template<typename Float_>
252FitVarianceTrendResults<Float_> fit_variance_trend(std::size_t n, const Float_* mean, const Float_* variance, const FitVarianceTrendOptions& options) {
255 fit_variance_trend(n, mean, variance, output.fitted.data(), output.residuals.data(), work, options);
256 return output;
257}
258
259}
260
261#endif
void compute(size_t num_points, const Data_ *x, const PrecomputedWindows< Data_ > &windows, const Data_ *y, Data_ *fitted, Data_ *robust_weights, const Options< Data_ > &opt)
Variance modelling for single-cell expression data.
Definition choose_highly_variable_genes.hpp:16
void fit_variance_trend(std::size_t n, const Float_ *mean, const Float_ *variance, Float_ *fitted, Float_ *residuals, FitVarianceTrendWorkspace< Float_ > &workspace, const FitVarianceTrendOptions &options)
Definition fit_variance_trend.hpp:123
Options for fit_variance_trend().
Definition fit_variance_trend.hpp:22
int num_threads
Definition fit_variance_trend.hpp:74
double minimum_width
Definition fit_variance_trend.hpp:61
bool use_minimum_width
Definition fit_variance_trend.hpp:54
double span
Definition fit_variance_trend.hpp:47
bool transform
Definition fit_variance_trend.hpp:41
int minimum_window_count
Definition fit_variance_trend.hpp:68
bool mean_filter
Definition fit_variance_trend.hpp:35
double minimum_mean
Definition fit_variance_trend.hpp:29
Results of fit_variance_trend().
Definition fit_variance_trend.hpp:206
std::vector< Float_ > residuals
Definition fit_variance_trend.hpp:236
std::vector< Float_ > fitted
Definition fit_variance_trend.hpp:231
Workspace for fit_variance_trend().
Definition fit_variance_trend.hpp:83