pub struct ValueStats {
pub min: f32,
pub max: f32,
pub count: i64,
/* private fields */
}Expand description
What a reduction accumulates over a locus or a profile bin.
min and max are data values and stay f32; the accumulators do not.
Both start NaN, so an untouched region reports no extremes rather than
±inf.
§Why the sums are shifted
The obvious accumulator holds Σx and Σx² and computes the variance as
E[x²] − E[x]². That subtracts two nearly equal large numbers whenever the
values sit far from zero relative to their spread, which is the ordinary
case for coverage, CPM and log-ratio tracks — and it does not merely lose
a few bits. Measured against a float64 reference on normal(1e4, 1e-2)
data, the naive form reported a standard deviation of 1.9e-1 where the
truth was 9.8e-3: wrong by a factor of twenty, and in another case
wrong all the way to zero, the variance having gone negative and been
clamped.
So every sum here is taken relative to shift, the first value folded in:
Σ(x − k) and Σ(x − k)². The variance is then
[Σ(x−k)² − Σ(x−k)²/n] / n over numbers of the size of the spread
rather than of the mean, and the cancellation goes with it. A constant
column gives exactly zero, because every difference is exactly zero.
mean and sum are recovered by adding k back, which costs one
multiply and no accuracy.
This is the standard shifted-data algorithm. Welford’s would be equivalent
for values arriving one at a time, and cannot fold in a pre-aggregated zoom
record, which this has to do — see ValueStats::add_aggregate.
Accumulation order is behaviour. These are summed in the order the
extraction visits intervals, which is block order within a batch and batch
order across the output. That order is deterministic on purpose: it is what
makes an answer independent of parallel, which tests/roundtrip.rs and
tests/properties.rs both check.
Fields§
§min: f32§max: f32§count: i64Implementations§
Source§impl ValueStats
impl ValueStats
Sourcepub fn sum_squared(&self) -> f64
pub fn sum_squared(&self) -> f64
Σx², undoing the shift. Only L2Norm and the tests want this, and
it is the one quantity the shift makes less accurate — which is the
right trade, l2norm having no cancellation to suffer from.
Sourcepub fn add_repeated(&mut self, value: f32, bases: i64)
pub fn add_repeated(&mut self, value: f32, bases: i64)
Fold in bases bases all carrying value, as a wide interval does.
The f32::min/max NaN rule does the seeding: NAN.min(x) == x, so
the first value replaces the initial NaN without a branch.
Sourcepub fn add_aggregate(
&mut self,
min: f32,
max: f32,
sum: f64,
sum_squared: f64,
bases: i64,
)
pub fn add_aggregate( &mut self, min: f32, max: f32, sum: f64, sum_squared: f64, bases: i64, )
Fold in a pre-aggregated run: bases bases whose sum is sum and
whose sum of squares is sum_squared, with known extremes.
This is how a zoom record enters, prorated over the part of it a
window covers. The record’s own Σx² is used rather than its mean
squared: squaring the mean would keep only the variance between
records and drop the variance inside each, collapsing sd as the zoom
level rises.
Σ|x| cannot be recovered from a record in general — the format stores
no such field — so it is derived where the record’s sign is not in
doubt (min ≥ 0, or max ≤ 0, which is every non-negative track) and
approximated by |Σx| where the record straddles zero. That is a lower
bound, it is the only thing the format allows, and the README says so
under quantify’s reduce.
Sourcepub fn merge(&mut self, other: &ValueStats)
pub fn merge(&mut self, other: &ValueStats)
Fold another accumulator in, rebasing it onto this one’s shift.
Sourcepub fn reduce(&self, reduce: Reduce, def_value: f32) -> f32
pub fn reduce(&self, reduce: Reduce, def_value: f32) -> f32
Reduce to one number.
A region no data reached has no mean, no extremes and no spread, so
those keep def_value. Its count is not unknown but zero: leaving
def_value there would make count the one reduction unable to say
“nothing here”.
Trait Implementations§
Source§impl Clone for ValueStats
impl Clone for ValueStats
Source§fn clone(&self) -> ValueStats
fn clone(&self) -> ValueStats
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for ValueStats
Source§impl Debug for ValueStats
impl Debug for ValueStats
Source§impl Default for ValueStats
impl Default for ValueStats
Source§impl PartialEq for ValueStats
impl PartialEq for ValueStats
impl StructuralPartialEq for ValueStats
Auto Trait Implementations§
impl Freeze for ValueStats
impl RefUnwindSafe for ValueStats
impl Send for ValueStats
impl Sync for ValueStats
impl Unpin for ValueStats
impl UnsafeUnpin for ValueStats
impl UnwindSafe for ValueStats
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more