On this page

Distribution analysis – method notes

The decisions behind the distribution analysis module – which convention, rule, floor or fallback stands behind each column, test and plot, and the numbers that validate them, each computed on the example dataset (cafe.csv, its spec beside it). Sections follow the manual’s.

Frequency tables

Frequency table options

The cumulative columns run over the valid observations, and hide under a count sort. A cumulative percentage that counted the missing rows would stop short of 100% by the missing share, and by a different amount in every table, so the running count and the running share are taken over the non-missing rows alone – the convention SPSS follows – and the last valid row lands on exactly 100% however many rows are blank. The share is recomputed at every row from the running count over the valid total rather than by adding the rows’ own rounded percentages, which would drift off 100 by the rounding. A count-based sort scrambles the value axis into a sequence a running total means nothing along, so the two columns are withheld under it, with a note under the table naming the sort that brings them back, rather than printed as numbers that would be read as cumulative.

The Total row blanks every cell that has no subtotal. The row counts every row of the data, missing included, so its count and its percentage are the whole and 100%; its valid percentage is 100% only when nothing is missing, since with missing rows the value rows’ cells already sum to 100% without it; and its cumulative cells are always blank, because the total is not a step of the running count over the valid rows and printing the last value again would make it look like one. The plain percentage column is dropped whenever the valid one is shown and the variable has no missing rows, where the two columns would be identical digit for digit.

Count sorts fall back to value order on a compressed table. The ranges of a compressed table form a contiguous axis, and ordering them by count would put the fourth range above the first with nothing on the row saying so; the table keeps value order and a note names the coercion, while an uncompressed table – a categorical variable, or a numeric one under the Maximum categories threshold – sorts by count as asked, ties broken in value order so the order is reproducible.

Equal-count bins are cut between distinct values, so a tie never straddles two ranges. The boundaries are not the sample quantiles themselves: the sorted values are walked in tie blocks, and a range closes at the first distinct value after the running count has reached the next multiple of n ⁄ k, so an identical value always sits in one range and the ranges are only about equal in count. Heavily tied data can exhaust the values before k boundaries are placed; the duplicates are collapsed, the table shows the ranges that remain, and a note under it gives the requested and the actual count rather than padding the table with empty ranges that would misreport the data. Equal-width ranges are the same cut the histogram makes, so a table and a histogram of one variable agree; an empty equal-width range is kept, since dropping it would break the axis.

Validation. Ten equal-count ranges of the café’s 185 waiting times hold 19, 18, 23, 17, 19, 15, 19, 18, 20 and 17 members – about 18.5 each, the ties deciding the rest – and read 0.3..1.9, 2.0..2.6, 2.7..3.5 and so on to 9.2..19.9.

A numeric variable is tallied value by value until compression is turned on, and the table is never truncated. A continuous variable then produces a table as long as its distinct-value count, and the alternatives – binning it unasked, or cutting the table off – would each hand back something other than what was asked for; the table is printed in full and a note under it reports the distinct-value count and names the option that groups them. The note reads the Maximum categories box even while compression is off, so it fires past 20 distinct values by default. Under compression a variable with at most that many distinct values stays value by value, so a Likert item or a count is not binned into ranges no one would read; the threshold’s default of 20 is a readability judgement, not a statistical one.

Validation. Uncompressed, the café’s waiting times list 82 rows and the note reports 82 distinct values; the 0–10 overall score has 10 distinct values and is listed value by value under compression at the default threshold.

Reading a frequency table

Range labels step to the next value at the data’s own resolution, and fall back to ..< when the display precision cannot show it. A label such as 170..178 names the values a range holds inclusively, and the next range starts at 179, so no boundary is printed twice and a reader never asks which range a value on the edge belongs to; the step is the fewest decimals at which every value of the variable prints exactly, so one-decimal data steps by 0.1. That needs the precision setting to cover the data’s decimals: when the data is finer than the budget, three-decimal values under a two-decimal display, a snapped label could show two ranges meeting at the same printed value, so the label carries the exclusivity instead – 170.00..<180.00, up to but not including the upper edge – at the display precision, floored to whatever keeps the narrowest range’s edges distinct. The last range is closed either way, since its top is the true maximum.

Normality tests

Available tests

Each test runs only inside the sample sizes its R implementation defines, and a constant variable is refused up front. The floors are where the R functions error or are undefined: shapiro.test takes 3 to 5,000 values, nortest::sf.test 5 to 5,000, nortest::ad.test and nortest::cvm.test 8 and up, nortest::lillie.test 5 and up; moments::agostino.test stops above 46,340, where its normal approximation gives out, and that caps the K² combination. Jarque–Bera’s floor of 4 is the exception: moments::jarque.test runs on anything past a single value, so the 4 is a reliability floor rather than a breaking point. A variable outside a test’s range gets a message in that test’s cell and the other tests still run; a test no selected variable qualifies for is not sent to R at all, which saves the package load and the per-variable failures it would produce. Every supported test needs spread and raises an opaque R error on constant input, so a variable whose valid values are all identical is flagged Zero variance for every test before R is reached.

Lilliefors stands in for the plain Kolmogorov–Smirnov test. The classic test’s p assumes the reference distribution was fixed in advance; with the mean and SD estimated from the sample – the only case a normality check ever meets – the fitted normal hugs the data and the plain p runs far too high, so the plain test is not offered and nortest::lillie.test supplies the corrected one.

D’Agostino–Pearson’s K² is assembled from the skewness and kurtosis tests, and both it and Jarque–Bera read as exploratory below n = 20. The moments package exports the two components but not the omnibus, so the app combines agostino.test’s skewness z and anscombe.test’s kurtosis z as K2=zs2+zk2 and takes its p from χ² on 2 degrees of freedom – the standard omnibus, and the reason the row cites both. Both tests rest on asymptotic normality of the two z’s, which sets in around twenty observations; they are enforced only down to the technical floors of 8 and 4, so the manual names 20 as the point below which a result is exploratory rather than refusing samples the functions can compute.

Validation. On the café’s 185 waiting times K² = 53.72, p < 0.001, and Jarque–Bera = 132.48; on the 200 pre-change spends, whose skewness is 0.45, K² = 6.88 (p = 0.032) and JB = 6.80 (p = 0.033).

Reading the normality table

The agreement summary is a tri-state verdict, not a vote. The selected tests examine one null on one sample, so their outcomes are strongly correlated and “four of seven rejected” is not a quantity with a distribution – a majority rule would present a coincidence of test sensitivities as evidence. The column therefore reports the tally and one of three verdicts: no test rejects, every computed test rejects, or the tests disagree, with the disagreement surfaced as mixed evidence rather than resolved; a test that could not run is counted apart and excluded from the tally.

Validation. On the café’s pre-change spends four of the seven tests reject at 0.05 – Shapiro–Wilk (W = 0.98, p = 0.013), Shapiro–Francia (p = 0.027), D’Agostino–Pearson and Jarque–Bera – while Anderson–Darling (p = 0.068), Lilliefors (p = 0.303) and Cramér–von Mises (p = 0.114) do not, and the column reads 4/7 tests: mixed evidence.

The verdict reads the assumption test significance level, not the main one. The tests’ null is “normal”, so the stricter check is the higher threshold: had the verdict followed the main α, a user lowering it to 0.01 for their hypothesis tests would have made the normality check more lenient, the opposite of the intent, and the assumption test significance level exists for exactly this reversed-null case. It is also the level every other assumption check in the app reads – comparison’s Check assumptions, the factor and SEM suitability tests – so a variable checked here and again there gets one verdict from one p. The p and statistic cells highlight and star at that same level, not the main one, so a row’s formatting agrees with its verdict however the two levels are set – which tables read which level is the significance formatting setting’s.

Distribution plots

Histogram

Auto binning is Freedman–Diaconis from 30 values and Sturges below, with Sturges the fallback and 1,000 bins the cap. Freedman–Diaconis sizes the bin from the IQR, so it survives an outlier that would stretch Scott’s SD-based width, but it needs a sample large enough for the IQR to be stable, hence the switch at 30. Scott and Freedman–Diaconis divide the range by a spread-driven width, which degenerates when the spread is zero with a non-zero range – a width of 0 and a bin count of infinity – and can demand millions of bins on an outlier-dominated range with a tiny IQR; a degenerate width falls back to Sturges, which cites Sturges as the rule that produced the bins, and the count is capped at 1,000, far beyond anything readable. The count the rule returns is a target: the bins are then placed at round values by the axis, so the drawn count can differ.

Validation. The café’s 185 waiting times, IQR 3.9, give Freedman–Diaconis a width of 2⋅3.9/1851/3=1.37 minutes and a target of 15 bins over the 19.6-minute range, drawn as 20 whole-minute bins; Sturges would ask for 9.

Few-valued integer data gets one bar per value under Auto, and an explicit rule overrides it with a note. Bins that straddle integers misrepresent a Likert item or a count – a bin of 2.5 to 3.5 holds the 3s and nothing else, and a bin of 1 to 2 merges two answers – so integer data with at most 50 distinct values is drawn one bar per value. The layout is to scale, one unit-width bar per integer with a zero-height gap at every integer no value takes, while the span is at most 50 and at least half its integers occur; past either limit the empty slots would swamp the plot, so each present value gets an equal-width bar spaced evenly and labelled, the axis is no longer to scale, and the density and normal curves – meaningless on an axis that is not – are withheld with a note. A rule picked by name is obeyed on integer data too, since a dropdown that silently reverted to Auto would be a lie; the note under the chart says the rule was applied and points back to Auto.

Validation. The café’s overall score, ten distinct integers from 1 to 10, draws ten bars to scale; the waiting times, one-decimal values, take the continuous path.

The density curve is a Gaussian kernel at the robust Silverman bandwidth, floored on dense integers, on an axis calibrated to the counts. Silverman’s rule of thumb, 0.9σn−1/5, is derived for a Gaussian kernel, so the kernel is Gaussian and no canonical-bandwidth conversion is needed; the spread is the smaller of the SD and IQR ⁄ 1.349, which keeps a heavy tail from over-smoothing the body. On dense integer data – the same predicate as the to-scale layout – the data-driven bandwidth can fall below the integer step and the curve collapses into a spike on every integer, so it is floored at 0.75 of a step; a bandwidth of zero (no spread, or a single value) skips the curve rather than divide by zero. The curve and the normal curve are drawn on the bars’ count axis through count = density × n × bin width, with the mean bin width so a narrower edge bin from the rounded axis does not under-scale them, and the right-hand density axis mirrors the count axis through the same factor – the two scales are calibrated, not arbitrary; the axis and the legend appear only when a curve was actually drawn.

Validation. The waiting times’ bandwidth is 0.92 minutes (spread 2.89, the IQR route, against an SD of 2.91); the overall score’s data-driven bandwidth would be at most 0.60 and is floored to 0.75.

Skewness and kurtosis are the bias-corrected G₁ and G₂. The same estimators as descriptive statistics, so the annotation matches the table and the packages a reader checks it in; the annotation is omitted, not printed as NaN, when either is undefined – fewer than four values, or no spread.

Validation. The waiting times annotate skew = 1.27, ex.kurt = 3.42, the overall score skew = −0.58, ex.kurt = 0.32, the table’s values.

Box plot

Quartiles are type 7, not Tukey’s hinges. R’s boxplot draws Tukey’s hinges, which differ from the linear-interpolation quartiles on small and even samples; the app draws the type-7 quartiles the descriptive table, the intervals and the outlier counts use, so a box, a table and an outlier count of one variable agree with each other, at the price of a box that can differ slightly from R’s. The whiskers and the outliers are Tukey’s 1.5 IQR fences on those quartiles, computed once per plot and reused on every resize. A variable with a single value or no spread is drawn as a flat box, since its five-number summary is defined – as R does – rather than dropped.

Validation. The café’s waiting times give a box from 3.0 to 6.9 around a median of 4.9, fences at −2.85 and 12.75, whiskers ending at 0.3 and 11.9, and the members at 16.3 and 19.9 as outliers – the numbers the descriptive table’s quartiles and outlier count report.

The notch is the interpolated median interval, clamped to the hinges. The interval is the Hettmansperger–Sheather one from the order statistics, distribution-free and asymmetric on skewed data – the construction descriptive statistics offers as its interpolated option, and the reason that option exists – at the confidence level in Settings rather than the fixed ±1.58 IQR ⁄ √n of R’s notch, which assumes normality. An interval wider than the box would draw the notch outside the hinges, an inverted shape that reads as nothing, so the notch is clamped to the hinges and the groups it happened to are named in a note; a group too small for any pair of order statistics to reach the level gets no notch, also named. The interval is computed lazily, on the first notched draw, since it is O(n log n) per group.

Validation. The waiting times’ 95% interval runs 4.3 to 5.3, inside the box, so the notch is drawn unclamped.

Outliers stay among the jittered points. With data points on, every observation is drawn at a jittered position – the jitter a deterministic hash of the index and the group, so a redraw moves nothing and side-by-side groups do not share a stripe pattern – and the outliers keep their diamond marker at their own jittered place rather than being stacked on the centre line, which would overplot a heavy tail into one mark; the outlier option then controls the marker alone, never whether the value is drawn.

Q-Q plot

Points sit at R’s plotting positions, the line is R’s qqline, and the exponential line runs through the origin. The ith sorted value is placed at the quantile of (i−a)/(n+1−2a) with Blom’s a = 3⁄8 up to ten values and Hazen’s ½ above – R’s ppoints – so a plot matches qqnorm at any n. The reference line passes through the first and third quartiles of both axes, as qqline does: robust to the tails, and the basis the confidence band is built on. The exponential family is scale-only, with no location, so a quartile line would carry a spurious intercept; its line runs through the origin with the sample mean as its slope, the moment and maximum-likelihood estimate of the scale. Three values with some spread are the floor: the quartile slope needs them, and a constant sample gives 0 ⁄ 0.

Validation. On the café’s waiting times the quartile line has slope 2.89 and intercept 4.95 – near the SD of 2.91 and the median of 4.9, and unmoved by the two members past 16 minutes.

The band is the pointwise order-statistic envelope, drawn across the observed range only. The standard error of the ith order statistic is approximated as SE≈|slope|/f(F−1(p))⋅p(1−p)/n, with f the reference density in closed form for every family – the normal density for the normal and the log-transformed lognormal, Student’s t, 1 − p for the exponential and 1 for the uniform – and the band is the line ± z × SE at the confidence level in Settings. It is evaluated between the plotting positions of the smallest and the largest value rather than across the axis, since a rounded axis reaches into the tails where f → 0 and the SE balloons into a flare that says nothing about the data; what survives is clamped to the plot rectangle, because a clipped path still inflates the exported figure’s bounding box. In detrended mode the band sits symmetrically around zero.

Student’s t degrees of freedom are floored at 2, and empty means n − 1. Below 2 the t inverse CDF and density used by the quantiles and the band are not both well defined, so a typed value under 2 is raised to it and an empty box takes max(2, n − 1), the natural reference for a sample of n.

Lognormal is a normal reference on log(x), and out-of-support values are dropped one at a time. Only a location-scale family draws a straight reference line, and the lognormal is not one on the raw scale, so the sample is log-transformed and plotted against the normal, which also restores the closed-form band; the axes say so. Its support excludes zero and the negatives, and the exponential’s excludes the negatives alone, so each family drops the values outside its support individually – a note gives the count, and the corner n shrinks with it – rather than refusing the whole plot over one stray value; the wording is per family, since calling a dropped value “non-positive” on the exponential branch would tell a user with zeros that their zeros were dropped when they were kept.

In overlay mode each variable is standardized by its own reference line. Variables on different scales cannot share a sample axis, so each is first mapped through its own line – the quartile line, or the through-the-origin line for the exponential, after the log for the lognormal – to (value − intercept) ⁄ slope, on which the reference is y = x for every family; the band is then drawn per variable with slope 1 and intercept 0 around that line, and detrending subtracts it. A variable whose slope is zero or undefined – a constant sample, or an all-zero exponential one – cannot be standardized and is dropped with a note, as is one with fewer than two values after the support filter.

Detrending is a change of view, not of data. The residual of each point from the reference line is plotted against the same theoretical quantile, so the line becomes y = 0 and the y-axis relabels to residuals; the tooltip labels the y-value a residual for the same reason. The y-domain is extended to include zero, so a sample whose residuals all sit on one side does not lose its reference line off the axis.

Violin plot

The violin is the histogram’s kernel density trimmed to the data and scaled to a common width. The outline is the same Gaussian kernel at the same robust Silverman bandwidth, with the same integer floor, evaluated on 101 points from the group’s smallest to its largest value – ggplot’s trim = TRUE – because evaluating into the padded axis range and zero-padding the ends fabricated density where there is no data and an artificial cliff at small n. Every violin is scaled so its own peak fills the same width: shapes stay comparable between groups where an area scaling would shrink a small group into a sliver, and the size cue the scaling gives up is carried by the corner n. A group with fewer than two values has no bandwidth and one with no spread a bandwidth of zero, so both are left out with a note rather than drawn as a spike; the box plot and the ECDF on the same card keep them, and the colour scale is built on the full group list so a dropped group does not shift its neighbours’ colours. The inner box is the type-7 box plot’s summary, whiskers included.

Validation. On the café’s waiting times the violin’s bandwidth is the histogram’s 0.92 minutes, and the outline runs from 0.3 to 19.9.

ECDF plot

The Wilson band is bounded to the observed values; the DKW band runs the whole padded range. At each step the Wilson score interval treats the share at or below the value as a binomial proportion, which narrows toward 0% and 100% as a proportion’s interval does; at p = 0 its centre is z² ⁄ 2n > 0, so extended to the axis’s padding it drew a visibly fat shoulder outside the data, and it is therefore evaluated at the observed values alone. The Dvoretzky–Kiefer–Wolfowitz band is a constant ε=ln⁡(2/α)/(2n) either side of the curve, the one construction whose coverage is simultaneous over the whole curve, and it is extended across the padded range including the 0 and 1 sentinels so it tracks the tails of the drawn curve. Wilson is the default because a per-value interval is what most readers mean by a band around a curve; DKW is offered for a claim about the distribution as a whole.

Validation. For the café’s 185 waiting times at 95%, the Wilson band is ±7.1 percentage points at the median and runs 2.7% to 9.2% around the 5% point, where the DKW band is ±10.0 points everywhere.

Bar chart

The rare tail folds into “Other” past 25 bars, and 10 slices. Past those counts the tail is unreadable and crowds out what the figure is for; the bar chart tolerates more than the pie because its tick labels tilt, where a pie has to fit a legend row and a readable wedge per category. The folded categories are summed into one entry and a note says how many it stands for, so the count on the figure still adds up. Categories are ordered by count with ties broken alphabetically, so a redraw is reproducible.

Pie chart

A numeric pie is cut the histogram’s way, with empty bins dropped. Under Auto, integer data with at most 50 distinct values gets one slice per value and anything else one slice per bin from the histogram’s rule at the histogram’s setting, so a pie and a histogram of the same variable agree slice for bar; a zero-count bin draws nothing yet would take a legend row, so it is left out. A percentage is printed inside a wedge only from 15° of arc, below which the label lands on its neighbours, and the legend names every slice with its count regardless.