boxplot
boxplot(data)/boxplot(data1, data2, ...)
Input: data · Output: no output
What it is
One group per argument (each a Vec of numbers), placed at x = 0, 1, 2, ...; draws a Tukey five-number summary (min/Q1/median/Q3/max plus outliers) per group as a box-and-whisker.
Overloads
boxplot's argument count, not a keyword, decides how many groups are drawn side by side — the same pattern also used by spiderplot and raincloud, shown once here rather than three times.
2 forms. Each carries its own inputs, outputs and worked example — pick the tab for the one you are calling.
| Code | Input | Output | Description |
boxplot(data) | data Vecnot documented yet | no output | A single Vec argument draws one box. |
Examples
Written for this form and run by the build -- the output below is what the engine actually produced.
boxplot(randn(50, seed = 20))
| Code | Input | Output | Description |
boxplot(data1, data2, ...) | data1 Vecnot documented yetdata2 Vecnot documented yet... Vec, repeatableOne group per argument (each a Vec of numbers), placed at x = 0, 1, 2, .... | no output | Each further Vec argument adds its own box at the next integer x position. Look for three boxes side by side instead of one. |
Examples
Written for this form and run by the build -- the output below is what the engine actually produced.
boxplot(randn(50, seed = 20), randn(50, seed = 21) + 0.5, randn(50, seed = 22) - 0.3)
The line/scatter family covers continuous data; these five cover data that is naturally discrete or already summed. Reach for stem when each sample is its own event (a Kronecker-delta-like sequence, an impulse response) and a connecting line would misleadingly imply values in between; for stair when the value is genuinely constant between changes (a clock signal, a piecewise-constant control setpoint); for groupbar/stackbar when you have several numbers per category and want them either side-by-side (comparing magnitudes) or stacked (comparing a total and its composition); and for waterfall when you have several related traces (successive measurement passes, a parameter sweep) and want each to stay legible instead of drawing directly on top of the last. Six figures come out of this block — look for the stem's lollipops, the stair's right-angle corners, the two bar layouts' different use of the same three numbers, and the waterfall's traces climbing up and to the right of one another:
x = 0 to 9
y = sin(x / 2)
figure()
stem(x, y)
figure()
stair(x, y)
figure()
groupbar([1, 2, 3], [3, 5, 2])
figure()
stackbar([1, 2, 3], [3, 5, 2])
figure()
Z = [1, 2, 3, 4, 5; 2, 3, 4, 5, 6; 3, 4, 5, 6, 7]
waterfall(0 to 4, Z)
· stem: figure 14 (10 points) · stair: figure 15 (10 points) · groupbar: figure 16 (3 points) · stackbar: figure 17 (3 points)
These four fit or summarize data rather than drawing it raw. splineplot and pwl both take a handful of control points and turn them into a smooth curve, but for different reasons: a spline is for when the points *are* the truth and you want a visually smooth curve through all of them exactly, while pwl is for when the underlying process is closer to piecewise-linear and you want a *fit* — its four segments won't pass through every point. spiderplot and raincloud are both about a shape rather than a fit: spiderplot compares several quantities on one radar chart, raincloud shows the full distribution (not just mean and spread) of two or more samples side by side, with the boxplot's summary, the KDE cloud's shape, and the individual jittered points all in one figure. Watch for the spline bending smoothly through the same five points pwl approximates with straight segments, and the raincloud's two "clouds" leaning the same direction as their generating distribution:
figure()
splineplot([0, 1, 2, 3, 4], [0, 2, 1, 3, 2])
figure()
pwl([0, 1, 2, 3, 4, 5, 6], [0, 1, 1.5, 3, 3.2, 3.5, 5])
figure()
spiderplot([3, 4, 2, 5, 4])
figure()
raincloud(randn(60, seed = 6), randn(60, seed = 7))
Two different kinds of "more than one number per point" live here. errorbar and fill_between both communicate uncertainty around a central curve — a whisker per sample versus a continuous shaded band — and bubble communicates a third *dimension* by encoding it as marker size rather than position. donut and pie are the odd ones out: they show how a whole splits into parts, which is a fundamentally different question ("what fraction is this?") from everything else on this page ("how does this vary against that?"), which is also why pie/donut charts are worth reaching for sparingly. In the rendered figures, check that the error whiskers and the fill_between band both widen and narrow with the same 0.1/±0.2 envelope around the same sine wave, and that the donut's hollow center is the only visual difference from the plain pie beside it:
x = 0 to 9
y = sin(x / 2)
figure()
errorbar(x, y, 0.1 * ones(10))
figure()
fill_between(x, y - 0.2, y + 0.2)
figure()
bubble(x, y, abs(y) * 20 + 5)
figure()
donut([30, 20, 50], "a", "b", "c")
figure()
pie([30, 20, 50], "a", "b", "c")
Four unrelated needs share this block only because they all involve a transform of the axes or the data before plotting. semilogx/semilogy/ loglog are for data that spans decades — a Bode magnitude, an exponential growth curve — where a linear axis would crush most of the interesting structure into a few pixels at one end. hist turns raw samples into a distribution shape. contour/contourf need a full 2-D field: note that meshgrid returns one Record with .x/.y fields (not two separate values), and that contour/contourf always take three arguments — (X, Y, Z) — never Z alone, because Qu has no notion of "the implicit grid a matrix was measured on". corr_heatmap/corrplot close the block by turning a table's numeric columns into a single correlation matrix. In the output, the semilog plot should look straight (or straighten a curve that was exponential), the log-log Bode-style curve should show a shallow slope rather than a hooked one, and the correlation heatmap's diagonal should read as the same colour throughout — a column is always perfectly correlated with itself:
f = logspace(0, 3, 200)
mag = 1 ./ sqrt(1 + (f / 100) .^ 2)
semilogx(f, mag)
figure()
x = 1 to 50
semilogy(x, exp(0.1 * x))
figure()
loglog(f, mag)
figure()
samples = randn(500, seed = 2)
hist(samples, bins = 15)
figure()
g = meshgrid(-3 to 3 step 0.2, -3 to 3 step 0.2)
Z = sin(sqrt(g.x .^ 2 + g.y .^ 2))
contour(g.x, g.y, Z)
figure()
contourf(g.x, g.y, Z)
figure()
tbl = table(a = randn(30, seed = 3), b = randn(30, seed = 4), c = randn(30, seed = 5))
corr_heatmap(tbl)
figure()
corrplot(tbl)
· hist: figure 31 (500 points, 15 bins)
If your data already lives in a table(...) (a data frame, in other languages' terms), the sns_* family saves you the step of pulling columns out into loose vectors by hand — you name the columns and Qu does the grouping. sns_scatter colours points by a categorical column, sns_box draws one boxplot group per category, and sns_bar aggregates a numeric column within each category before plotting the result. This example builds one small table with a numeric x, y and a two-level categorical grp column, then feeds it to all three; look for the scatter's two colours ("a" vs "b"), the two boxplot groups, and the two bars showing each group's mean y:
df = table(x = randn(30, seed = 1), y = randn(30, seed = 2),
grp = ["a","b","a","b","a","b","a","b","a","b",
"a","b","a","b","a","b","a","b","a","b",
"a","b","a","b","a","b","a","b","a","b"])
sns_scatter(df, "x", "y", "grp")
figure()
sns_box(df, "grp", "y")
figure()
sns_bar(df, "grp", "y")
print("rows: " + str(rows(df)))
rows: 30
How it works
boxplot(data) (one group) or boxplot(data1, data2, ...) (one group per argument, placed at x = 0, 1, 2, ...) -- Tukey five-number summary per group (§ plotting::boxplot_stats).
From the note above this builtin in the interpreter, where the reasoning was written down as it was built.