STATS_NORMALITY – Shapiro–Wilk test of normality in Google Sheets™

What it does

Shapiro–Wilk test of normality for every selected column — the Shapiro–Wilk part of the SPSS EXAMINE table "Tests of Normality" (without its Kolmogorov–Smirnov test).

One of the checks before a t-test / ANOVA (they assume normal data) or a rank test (STATS_MANN_WHITNEY, STATS_WILCOXON, STATS_KRUSKAL): test what the analysis needs normal — each group, or the differences of paired measurements.

Statistic is W, between 0 and 1: the closer to 1, the closer the data are to a normal distribution. Sig. below 0.05 rejects normality; a large Sig. is no proof of normality, only no reason to reject it.

df is the number of values the column holds after the empty cells and the sign "." are left out, not the number of rows.

W and Sig. are the Royston algorithm, as scipy.stats.shapiro. SPSS derives Sig. differently and prints ">0.99" or "<0.01" in the tails; here the cell always holds a number, so far in the tails the two differ.

A text column without value labels is skipped with the line "Text columns skipped: …"; text with value labels is tested by its codes.

Syntax

=STATS_NORMALITY(columns, colFilter)

Parameters

columns — required

Cell range with data, e.g., A1:D100 or A:D.

Usually the first row should contain column names.

You can also add labels as the first row and column names as the second row.

Labels should use formats like "male:1,female:2" or "low:1:3,mid:3:5,high:5:7".

Examples:

  • A1:D100 — use 4 columns
  • A1:C200 — use 3 columns
  • A:F — use 6 columns
  • A1:D100 where row 1 contains labels and row 2 contains column names — use labels from the sheet

If every non-empty cell of the first row holds at least two label rules ("low:1,high:2"), it is the labels row, and the next row holds the column names; an empty cell there is a column without labels.

One rule in a cell ("yes:1") is read as a name, so a header such as "T:1", "Ratio 1:2" or "Note:" stays the names row. Give such a column a second rule, or pass one label in the labels parameter, where one rule is valid.

If the first row contains ordinary text, it is treated as the column names row as before.

A number, a date or a checkbox in the names row is a name: 2025, 2025-01-01, TRUE.

Column names must be unique: two identical headers are an error "Duplicate column name: x".

Every column with values needs a name: an empty header cell over values is an error "The name of column 2 is empty: a column with values needs a name", even when colFilter does not select that column. A column with neither a name nor values (the empty right edge of a range such as A:Z) is dropped without an error.

Column names that start with "#" are reserved/unsupported in filters, because "#" is used for index-based colFilter rules (for example #1, #2-#5).

What counts as a number (the same in every function):

  • A column is numeric when it holds more numbers than text; with as many texts as numbers, or more, it is a text column.
  • Every cell is typed by itself: a number written as text ('10', '007') is the number, a checkbox is 1 (checked) or 0 (unchecked). The texts 'TRUE'/'FALSE' stay text.
  • A cell that does not match the type of its own column (text in a numeric column, a number in a text column) is missing for calculations and is counted in missing.
  • An empty cell is a missing answer; the sign "." means "not applicable" — it is not a missing answer, not a category and not counted in missing.
  • A text column with value labels (a labels row above the names) is analysed by the codes of its labels. A text column without labels is not analysed as numbers: lists of columns skip it with a note, tests on a pair of columns and models show an error.

An empty tail of the range (rows below the data) is dropped — only blank cells make it: a row of "." at the end stays and counts as not applicable, as in the middle. A column with neither a name nor values is dropped; a named empty column stays.

colFilter — optional, default "*"

Comma-separated column selector.

Use names ("age,score"), negative rules ("-id,-note"), a regex ("/^q/"), or index rules ("#N", "#A-#B", "-#N", "-#A-#B").

Case-sensitive; flags are not supported.

A name that does not exist is an error: "Column nope does not exist" (excluding it, "-nope", is ignored). Empty — all columns ("*").

Examples:

  • "age,score" — Names only (include only "age" and "score")
  • "-id,-group" — Exclude some (include all except "id" and "group")
  • "/^q/" — Regex only (all starts with "q")
  • "#1,#3-#5" — Index include rules (first, and range 3..5)
  • "-#1,-#3-#4" — Index exclude rules (drop first and 3..4)
  • "age,#2-#4,-#3" — Mixed name/index rules with deterministic order
  • "age,/^q/,-q5" — Mixed (include age, all starts with "q" except "q5")
  • "/^x1{1,2}$/" — Regex with a comma inside: x1 and x11

Choose which columns from columns to use - a comma-separated string of rules.

⚙️ Four rule types (you can mix them):

  1. By name - exact header text (case-sensitive)
    • "age,score,total"
  2. Negative rules - exclude by name (prefix with -)
    • "-id,-notes" (include all except id and notes)
  3. Regex - a regular expression between slashes /.../
    • "/^q/" (all headers starting with q), "/(score|age)/", "/^x1{1,2}$/" (commas inside /.../ do not split the filter)
    • ❗— case-sensitive
    • ❗— flags are not supported.
  4. Index rules - position-based rules with #
    • "#1" (first selected column), "#2-#5" (range), "-#1", "-#2-#4"

"*" alone selects every column; it cannot be combined with other rules ("*,-id" is an error) — write "-id" instead.

Rules are applied in the written order, and the selected columns keep that order ("/^q/,-q5,age" puts age last).

Index rules count the columns of the range: "#1" is its first column.

A name that does not exist is an error: "Column nope does not exist" — also in colName, yName, steps, rowCol, colCol and colNameForSplit. Excluding a name that does not exist ("-nope") is ignored.

A filter that selects no column — a regex without a match ("/^Q/" where the columns are q1, q2), index rules past the end, exclusions that remove every column — is an error that names the filter and the columns of the range: colFilter "/^Q/" selects no columns (columns: q1, q2, t). Empty and "*" select every column.

In STATS_ANALYZE, a regex with brackets or | needs the new syntax: PEARSON('/(score|age)/').

A colFilter saved in Settings applies to every formula without its own filter: on a range without those columns the formula shows the error above.

# prefix is reserved for index syntax and is not treated as a column-name filter token.

colFilter applies to all expressions in query. It does not create separate groups per expression.

Examples

  • — the sample data (50 rows) all examples use
  • — three columns, a row each =STATS_NORMALITY(Data!A1:T51, "x,x1,x2")
  • — every column whose name starts with q =STATS_NORMALITY(Data!A1:T51, "/^q/")

What the result contains

A report that spills below the formula cell: the title "Tests of Normality for …" and one table with a row per column — Column, Statistic (W), df, Sig.

How to read the result

Look at Sig. of each column: below 0.05 — evidence that it is not normal. Test what the analysis needs normal: each group for a t test or ANOVA, the differences for a paired t test; if they are clearly not normal, a rank test (Mann–Whitney, Wilcoxon, Kruskal–Wallis) is the safer choice.

At 0.05 or above — no evidence against normality.

With large samples small deviations are significant too: then weigh skewness and kurtosis (STATS_DESCRIPTIVE) as well.

In a report: "Scores were not normally distributed, W(50) = 0.86, p < .001, so the Mann–Whitney test was used."

What each number means

Column — Column

Read the numbers along the row for that column.

Statistic — Shapiro–Wilk W

Close to 1 is good, but read Sig.: with many rows even W = 0.98 can be significant.

df — Number of values

Shapiro–Wilk is computed for 3 to 5000 values; in a report: W(50) = 0.86.

Sig. — p of the Shapiro–Wilk test

Below 0.05 — evidence that the column is not normal; at 0.05 or above — no evidence against normality. It does not choose the test by itself: what should be normal is the distribution the test relies on — each group for a t test or ANOVA, the differences for a paired t test.

With large samples even small deviations become significant, so look at skewness, kurtosis or a histogram too. In a report: W(50) = 0.86, p < .001.

How it is computed

Every selected column is tested on its own valid values; text columns without value labels are skipped with a line.

The Shapiro–Wilk W and its p by Royston (algorithm AS R94), as SPSS EXAMINE and SciPy; computed for 3 to 5000 values.

Errors

A column that cannot be tested cancels the whole report with one cell that names it — on a sheet with mixed columns, list the numeric ones instead of relying on "*":

A column name that does not exist: "Column nope does not exist".

About STATS_NORMALITY

STATS_NORMALITY runs the Shapiro–Wilk test on every selected column: it checks whether the values could come from a normal (bell-shaped) distribution. The report is one table with a row per column — W, df and Sig. — the Shapiro–Wilk part of the SPSS EXAMINE table "Tests of Normality". It spills below the formula cell and recalculates by itself when new responses arrive from Google Forms.

Use it before a t test or ANOVA, which assume normal data, and test what the analysis needs normal: each group, or the differences of paired measurements. W lies between 0 and 1; the closer to 1, the closer the data are to normal. Sig. (the p-value) below 0.05 is evidence that a column is not normal, and then a rank test — Mann–Whitney, Wilcoxon or Kruskal–Wallis — is the safer choice. A large Sig. is no proof of normality, only no reason to reject it. df is the number of values in the column, not the number of rows; the test is computed for 3 to 5000 values.

Every number of the report is explained on this page in plain words, together with the line to write in your report, for example: Scores were not normally distributed, W(50) = 0.86, p < .001, so the Mann–Whitney test was used.