Skip to content

moarstats: --pct-thresholds silently yields 0 for any percentile not in the default --percentile-list #4455

Description

@jqnatividad

Summary

qsv moarstats --use-percentiles --pct-thresholds <lower>,<upper> silently produces wrong winsorized/trimmed statistics — 0 in the worst case — whenever the requested thresholds are not among the percentiles stats actually computed. It exits 0 with no warning.

The cause is that moarstats invokes stats itself without forwarding a matching --percentile-list, so stats always computes the default 5,10,40,60,90,95. moarstats then looks up the requested threshold as a key in the resulting percentiles cell. Any threshold outside that default is simply not found.

Reproduction

printf 'n\n' > d.csv && seq 1 100 >> d.csv
qsv moarstats --use-percentiles --pct-thresholds 7,93 d.csv
# inspect winsorized_mean_7pct in d.stats.csv

Column n is 1..100, so any winsorized mean must be near 50.5.

--pct-thresholds column emitted value correct?
5,95 winsorized_mean_5pct 50.45 ✅ (5 and 95 are in the default list)
10,90 winsorized_mean_10pct 50.4 ✅ (both in the default list)
0.5,95.5 winsorized_mean_0pct 50.35 ❌ lower bound not applied (truncates to 0, absent)
7,93 winsorized_mean_7pct 0 ❌ neither bound found

7,93 are plain integers — nothing to do with fractional input or with the 0 edge case. Any pair outside 5,10,40,60,90,95 reproduces it. When one bound resolves and the other does not, the result is a partially-winsorized value (wrong but plausible-looking); when neither resolves, it collapses to 0.

Confirmation of the cause

Pre-building the stats cache with a matching percentile list fixes it, with no other change to the command:

printf 'n\n' > d.csv && seq 1 100 >> d.csv

# without a matching cache
qsv moarstats --use-percentiles --pct-thresholds 7,93 d.csv
#   percentiles      = 5: 5|10: 10|40: 40|60: 60|90: 90|95: 95
#   winsorized_mean_7pct = 0

# with a matching cache
qsv stats --everything --percentiles --percentile-list 7,93 --output /dev/null d.csv
qsv moarstats --use-percentiles --pct-thresholds 7,93 d.csv
#   percentiles      = 7: 8|93: 93
#   winsorized_mean_7pct = 50.5

Suggested fix

Forward the thresholds to the stats run that moarstats performs — i.e. pass --percentile-list containing (at least) the two requested percentiles — so the values moarstats looks up are guaranteed to exist.

Validating harder at the front instead would be the wrong fix: it would reject legitimate requests like 7,93 that stats is perfectly capable of computing.

Whatever the fix, the "requested percentile not present in the cell" path should not stay silent. Today it returns 0/partial at exit 0, which is indistinguishable from a real result.

Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions