stats is the numbers-about-numbers machine. Give it a list of
values and it tells you where they sit (Mean,
Median, Modes), how spread out they are
(StdDev, Iqr, Mad), what shape they
make (Skewness, Kurtosis), and how two lists move
together (Correl, LinFit). Then it tells you
whether what you're seeing is real: t-tests, chi-square tests,
ANOVA, and confidence intervals — a confidence interval being a range
the true answer very likely falls inside. Each test reports an honest
p-value: the chance that luck alone would produce a gap this large. That
p-value is computed from the actual distribution — the curve that says
how likely each outcome is — not looked up in a table. It is pure
Shoddy from top to bottom, built over four runtime builtins:
Sort, and the three special functions (Erf,
GammaP, BetaI) that give the normal, chi-square,
t, and F curves their native precision.
A Brief History of Student
The t-test in this machine was invented in a brewery, and published under
a false name. William Sealy Gosset was a chemist at Guinness in Dublin. His
questions were industrial ones: which barley strain yields more, does this
batch differ from that one? His samples were always a handful, because
experiments cost money. The statistics of the day assumed samples large
enough that such questions answered themselves. So in 1908 Gosset worked out
exactly how much less a small sample can be trusted — the
t-distribution. Guinness regarded its use of statistics as a trade secret
and let him publish only under a pseudonym. He chose "Student", and the name
stuck to the method forever. It is the founding story of practical
statistics. The tests in this machine exist not to adorn research papers but
to answer a brewer's kind of question — sixteen measurements, two
batches, is the difference real? — with an honest p-value computed
from the actual distribution. That is precisely what Gosset built.
Why It's Useful
The moment a program measures anything — test scores, reaction
times, plant heights, coin flips — the questions start. What's typical?
How much do they vary? Are these two groups actually different, or did
chance just make them look that way? Those questions have canonical,
centuries-polished answers, and stats gives them their Shoddy
names. It's the difference between eyeballing two averages and being able to
say "p = 0.02: a gap this large happens by luck about one time in
fifty." Every convention is the standard classroom one: sample variance
divides by n−1, quantiles interpolate the way R and Excel do, and
p-values are two-sided. So answers here match answers from R, scipy, or a
textbook worked example.
One scale note: Shoddy lists are linked lists, and most of these words
walk them more than once. That's perfect for classroom-sized data —
dozens to thousands of values — and wrong for bulk analytics. Know
which one you're doing.
User's Guide
Include the file (it brings seq and
dict along) and ask your questions:
Include "stats.shoddy"
Def Main()
Let scores = { 72, 85, 91, 68, 77, 85, 94, 81 }
Print(Mean(scores)) ' 81.625
Print(Median(scores)) ' 83
Print(StdDev(scores)) ' 8.94... (sample, n-1)
Print(Percentile(scores, 90)) ' the 90th percentile
Print(Outliers(scores)) ' [ ] — nobody past the fences
' Did the class really improve after the new textbook?
Let before = { 71, 78, 82, 69, 75 }
Let after = { 76, 84, 88, 71, 82 }
Let t = TTestPaired(before, after)
Print(PVal(t)) ' small p: yes, it's real
' How tight is our estimate of the mean?
Let ci = MeanCI(scores, 0.95)
Print(Str(Fst(ci)) & " TO " & Str(Snd(ci)))
A few things worth remembering:
Sample vs. population.Var,
StdDev, and StdErr divide by n−1. That is
the right choice when your data is a sample standing in for something
bigger, which is almost always. When your data is the whole
population, use VarP and StdDevP.
Tests hand back records. Every test returns a
TestResult with three fields: Stat (the t, z, F,
or chi-square statistic), Df (degrees of freedom —
roughly, how many values were free to vary; 0 where none apply), and
PVal (always two-sided). Regression returns a
Fit with Slope, Intercept, and
R2. Confidence intervals return a Pair of
(lo, hi).
Quantiles interpolate.Quantile,
Median, and Percentile use linear interpolation
between order statistics (R's type 7, Excel's
PERCENTILE.INC). So Median({ 1, 2, 3, 4 })
is 2.5, not a coin-flip between 2 and 3.
Modes returns them all. If 2 and 3 both
appear most often, you get { 2, 3 } — a tie is reported,
never silently broken. (If every value is unique, every value is a
mode.)
Real p-values, no tables. The CDF words
(NormCdf, TCdf, Chi2Cdf,
FCdf) compute the actual probability through the special
functions. (A CDF is a cumulative distribution function: the running total
of probability up to a given value.) NormInv/TInv
invert them by bisection — so confidence intervals use the exact
critical value, not the nearest row of a printed table.
Shuffling lives elsewhere. Random sampling and
shuffling are impure (two calls, two answers), so they belong to
random, which owns the Rnd edge.
Everything in stats is pure.
Want pictures?plotter
draws the histogram, bar, pie, box, and scatter charts straight from the
same lists, with this machine doing the arithmetic underneath.
Builtins
The three special functions the runtime dispatches — not
defined here, documented here
These are not stats's Defs. The engine dispatches
them, and a Def whose name is a builtin is refused. They are
listed on this page because stats is the machine whose domain they
belong to, and they need no Include. The same three are
documented in machines/stats.shoddy's own header block.
Three functions the .NET base library does not have,
implemented in the engine because they need native precision. Under the hood
they use the classic Lanczos log-gamma, power series, and modified-Lentz
continued fractions, accurate to about 1e−14 relative over the ranges used
here. They are what makes every p-value on this page a real p-value
rather than a lookup in a printed table. They are also the reason this
machine needs the runtime at all — everything else in the file is ordinary
Shoddy. All three are pure, and all three abort outside their domain. The
CDF words below check before they call.
Word
Description
Erf(x)
The error function: odd, and bounded strictly between
−1 and 1. NormCdf below is this, shifted and scaled.
GammaP(a, x)
The regularized lower incomplete gamma
P(a, x). Aborts unless a is positive and x
is not negative. Chi2Cdf is this one.
BetaI(a, b, x)
The regularized incomplete beta
Ix(a, b). Aborts unless a and b
are both positive and x is between 0 and 1. TCdf and
FCdf are both this one.
Sort is not documented here, though this
machine leans on it for every median, quantile and rank. It is a sequence
operation and seq is the sequences machine, so its entry
is there — this machine is a consumer of it, not its home.
Word Reference
Every word, plus the Fit and TestResult types
Aggregation poly — List or Array
Word
Description
Sum(xs)
Adds every number together. Empty list totals
0.
Product(xs)
Multiplies every number together. Empty list
gives 1.
Maximum(xs)
The largest value. Needs at least one
item.
Minimum(xs)
The smallest value. Needs at least one
item.
Central tendency
Word
Description
Mean(xs)
The arithmetic mean. Average is kept
as an everyday alias.
WeightedMean(xs, ws)
Mean with per-value weights —
grades with different credit hours, say.
GeoMean(xs)
Geometric mean, for rates and growth factors.
All values must be positive.
HarmonicMean(xs)
Harmonic mean — the right average
for rates like speeds over a fixed distance.
Median(xs)
The middle value (interpolated for even
counts).
Modes(xs)
ALL of the most frequent values, in first-seen
order. Works on any =-comparable values, not just
numbers.
Quantiles and spread
Word
Description
Quantile(xs, p)
The value a fraction p (0 to 1)
of the way through the sorted data, linearly interpolated (R type 7 / Excel
PERCENTILE.INC).
Percentile(xs, k)
The same, taking 0..100:
Percentile(xs, 75) is the third quartile.
Iqr(xs)
Interquartile range: Q3 − Q1, the box-plot
box.
RangeOf(xs)
Max − min. (Not called "Range" —
that's the builtin that builds { a .. b }.)
Var(xs) / VarP(xs)
Variance: sample (n−1) and
population (n) forms, two-pass for stability.
StdDev(xs) / StdDevP(xs)
Standard deviation — the
typical distance of a value from the mean. Same two forms.
StdErr(xs)
Standard error of the mean:
StdDev / √n.
Mad(xs)
Median absolute deviation — a robust spread
measure outliers can't drag around.
SqDevSum(xs)
Sum of squared deviations from the mean
— the shared core under the variance words.
Shape, z-scores, outliers
Word
Description
Skewness(xs)
Which way the distribution leans (moment form,
as scipy with bias=True).
Kurtosis(xs)
How heavy the tails are — excess form,
so the normal distribution scores 0.
ZScore(x, m, s)
How many SDs x sits from mean
m: (x - m) / s.
ZScores(xs)
The whole list standardized against its own
mean and sample SD.
Outliers(xs)
Values beyond the 1.5×IQR box-plot
fences.
OutliersZ(xs, z)
Values more than z sample SDs
from the mean.
Frequencies dict association lists
Word
Description
Freq(xs)
Value → count as a
dict, keys in first-seen order. Read it with
DictGet and friends.
RelFreq(xs)
Value → proportion of the whole (multiply
by 100 for percentages).
Ecdf(xs, x)
Empirical CDF: the proportion of the data at or
below x.
Correlation and regression
Word
Description
Cov(xs, ys)
Sample covariance (n−1) — the
primitive under the next two.
Correl(xs, ys)
Pearson correlation, −1 to 1.
Spearman(xs, ys)
Rank correlation — Pearson on the
ranks; catches any monotone relationship, straight or not.
RankOf(xs, x) / Ranks(xs)
Average ranks (ties share), the
machinery under Spearman.
LinFit(xs, ys)
Least-squares line →
Fit(Slope, Intercept, R2): y ≈ Slope·x +
Intercept, with R2 the share of variance explained.
Distributions
Word
Description
NormPdf(z) / NormCdf(z)
Standard normal density and CDF
(via Erf). For general x, standardize with
ZScore first.
Chi2Cdf(x, df)
Chi-square CDF (via
GammaP).
TCdf(t, df)
Student's t CDF (via
BetaI).
FCdf(f, d1, d2)
F CDF (via BetaI).
NormInv(p) / TInv(p, df)
The inverse CDFs (quantile
functions), found by bisection — what turns "95% confident" into an
exact critical value.
Inference all p-values two-sided
Word
Description
TTest1(xs, mu0)
One-sample t-test: is the mean plausibly
mu0? → TestResult(Stat, Df, PVal).
TTest2(xs, ys)
Two-sample t-test for independent groups
— pooled/equal-variance, the textbook form (R's
var.equal = TRUE).
TTestPaired(xs, ys)
Paired t-test: a one-sample test on the
before/after differences.
ZTest1(xs, mu0, sigma)
One-sample z-test when the
population SD is known. Df is 0.
Anova(gs)
One-way ANOVA over a list of groups (a list of
lists). Stat is F; Df is the between-groups df;
within = total n − groups.
ChiSqGof(obs, exp)
Chi-square goodness of fit: observed
counts against expected counts.
ChiSqTest(rows)
Chi-square independence on a contingency
table given as a list of rows.
TwoPropZTest(k1, n1, k2, n2)
Two-proportion z-test:
k1 successes of n1 against k2 of
n2, pooled SE.
TwoSidedTP(t, df)
Two-sided p for a t statistic — the
helper the t-tests share.
Intervals and effect sizes
Word
Description
MeanCI(xs, conf)
t-based confidence interval for the mean
→ Pair(lo, hi). conf is e.g.
0.95.
PropCI(k, n, conf)
Confidence interval for a proportion
(normal approximation), k successes of n →
Pair(lo, hi).
CohenD(xs, ys)
Cohen's d: the two means' difference in
pooled-SD units — how big the effect is, not just whether it's
there.
PooledVar(xs, ys)
Pooled variance of two samples —
shared by TTest2 and CohenD.