NORMALITY
Normality tests assess whether sample values are plausibly drawn from a normal distribution.
Depending on the selected method, Pingouin applies tests such as Shapiro-Wilk or D’Agostino-Pearson. Results include test statistics, p-values, and a boolean decision at the specified significance level.
This wrapper accepts tabular data and returns Pingouin’s normality test table.
Excel Usage
=NORMALITY(data, dv, group, method, alpha)
data(list[list], required): Input table where the first row contains column names.dv(str, required): Name of the dependent-variable column.group(str, optional, default: ““): Grouping column name; use empty string to test without groups.method(str, optional, default: “shapiro”): Normality method (for example shapiro or normaltest).alpha(float, optional, default: 0.05): Significance level for decision threshold.
Returns (list[list]): 2D table containing normality test results.
Example 1: Grouped Shapiro tests
Inputs:
| data | dv | group | |
|---|---|---|---|
| score | grp | score | grp |
| 1.1 | A | ||
| 1.2 | A | ||
| 1.3 | A | ||
| 1.4 | A | ||
| 1.5 | A | ||
| 1.6 | A | ||
| 1.7 | A | ||
| 1.8 | A | ||
| 1.9 | A | ||
| 2 | A | ||
| 2.1 | A | ||
| 2.2 | A | ||
| 2.3 | A | ||
| 2.4 | A | ||
| 2.5 | A | ||
| 2.6 | A | ||
| 2.7 | A | ||
| 2.8 | A | ||
| 2.9 | A | ||
| 3 | A | ||
| 2.1 | B | ||
| 2.2 | B | ||
| 2.3 | B | ||
| 2.4 | B | ||
| 2.5 | B | ||
| 2.6 | B | ||
| 2.7 | B | ||
| 2.8 | B | ||
| 2.9 | B | ||
| 3 | B | ||
| 3.1 | B | ||
| 3.2 | B | ||
| 3.3 | B | ||
| 3.4 | B | ||
| 3.5 | B | ||
| 3.6 | B | ||
| 3.7 | B | ||
| 3.8 | B | ||
| 3.9 | B | ||
| 4 | B | ||
| 3.1 | C | ||
| 3.2 | C | ||
| 3.3 | C | ||
| 3.4 | C | ||
| 3.5 | C | ||
| 3.6 | C | ||
| 3.7 | C | ||
| 3.8 | C | ||
| 3.9 | C | ||
| 4 | C | ||
| 4.1 | C | ||
| 4.2 | C | ||
| 4.3 | C | ||
| 4.4 | C | ||
| 4.5 | C | ||
| 4.6 | C | ||
| 4.7 | C | ||
| 4.8 | C | ||
| 4.9 | C | ||
| 5 | C |
Excel formula:
=NORMALITY({"score","grp";1.1,"A";1.2,"A";1.3,"A";1.4,"A";1.5,"A";1.6,"A";1.7,"A";1.8,"A";1.9,"A";2,"A";2.1,"A";2.2,"A";2.3,"A";2.4,"A";2.5,"A";2.6,"A";2.7,"A";2.8,"A";2.9,"A";3,"A";2.1,"B";2.2,"B";2.3,"B";2.4,"B";2.5,"B";2.6,"B";2.7,"B";2.8,"B";2.9,"B";3,"B";3.1,"B";3.2,"B";3.3,"B";3.4,"B";3.5,"B";3.6,"B";3.7,"B";3.8,"B";3.9,"B";4,"B";3.1,"C";3.2,"C";3.3,"C";3.4,"C";3.5,"C";3.6,"C";3.7,"C";3.8,"C";3.9,"C";4,"C";4.1,"C";4.2,"C";4.3,"C";4.4,"C";4.5,"C";4.6,"C";4.7,"C";4.8,"C";4.9,"C";5,"C"}, "score", "grp")
Expected output:
| W | pval | normal |
|---|---|---|
| 0.960375 | 0.551372 | true |
| 0.960375 | 0.551372 | true |
| 0.960375 | 0.551372 | true |
Example 2: Overall Shapiro test without grouping
Inputs:
| data | dv |
|---|---|
| x | x |
| 1.1 | |
| 1.2 | |
| 1 | |
| 1.3 | |
| 1.1 | |
| 1.2 | |
| 1 | |
| 1.3 |
Excel formula:
=NORMALITY({"x";1.1;1.2;1;1.3;1.1;1.2;1;1.3}, "x")
Expected output:
| W | pval | normal |
|---|---|---|
| 0.897413 | 0.273806 | true |
Example 3: Grouped normaltest method
Inputs:
| data | dv | group | method | |
|---|---|---|---|---|
| value | batch | value | batch | normaltest |
| 1.1 | B1 | |||
| 1.2 | B1 | |||
| 1.3 | B1 | |||
| 1.4 | B1 | |||
| 1.5 | B1 | |||
| 1.6 | B1 | |||
| 1.7 | B1 | |||
| 1.8 | B1 | |||
| 1.9 | B1 | |||
| 2 | B1 | |||
| 2.1 | B1 | |||
| 2.2 | B1 | |||
| 2.3 | B1 | |||
| 2.4 | B1 | |||
| 2.5 | B1 | |||
| 2.6 | B1 | |||
| 2.7 | B1 | |||
| 2.8 | B1 | |||
| 2.9 | B1 | |||
| 3 | B1 | |||
| 2.1 | B2 | |||
| 2.2 | B2 | |||
| 2.3 | B2 | |||
| 2.4 | B2 | |||
| 2.5 | B2 | |||
| 2.6 | B2 | |||
| 2.7 | B2 | |||
| 2.8 | B2 | |||
| 2.9 | B2 | |||
| 3 | B2 | |||
| 3.1 | B2 | |||
| 3.2 | B2 | |||
| 3.3 | B2 | |||
| 3.4 | B2 | |||
| 3.5 | B2 | |||
| 3.6 | B2 | |||
| 3.7 | B2 | |||
| 3.8 | B2 | |||
| 3.9 | B2 | |||
| 4 | B2 | |||
| 3.1 | B3 | |||
| 3.2 | B3 | |||
| 3.3 | B3 | |||
| 3.4 | B3 | |||
| 3.5 | B3 | |||
| 3.6 | B3 | |||
| 3.7 | B3 | |||
| 3.8 | B3 | |||
| 3.9 | B3 | |||
| 4 | B3 | |||
| 4.1 | B3 | |||
| 4.2 | B3 | |||
| 4.3 | B3 | |||
| 4.4 | B3 | |||
| 4.5 | B3 | |||
| 4.6 | B3 | |||
| 4.7 | B3 | |||
| 4.8 | B3 | |||
| 4.9 | B3 | |||
| 5 | B3 |
Excel formula:
=NORMALITY({"value","batch";1.1,"B1";1.2,"B1";1.3,"B1";1.4,"B1";1.5,"B1";1.6,"B1";1.7,"B1";1.8,"B1";1.9,"B1";2,"B1";2.1,"B1";2.2,"B1";2.3,"B1";2.4,"B1";2.5,"B1";2.6,"B1";2.7,"B1";2.8,"B1";2.9,"B1";3,"B1";2.1,"B2";2.2,"B2";2.3,"B2";2.4,"B2";2.5,"B2";2.6,"B2";2.7,"B2";2.8,"B2";2.9,"B2";3,"B2";3.1,"B2";3.2,"B2";3.3,"B2";3.4,"B2";3.5,"B2";3.6,"B2";3.7,"B2";3.8,"B2";3.9,"B2";4,"B2";3.1,"B3";3.2,"B3";3.3,"B3";3.4,"B3";3.5,"B3";3.6,"B3";3.7,"B3";3.8,"B3";3.9,"B3";4,"B3";4.1,"B3";4.2,"B3";4.3,"B3";4.4,"B3";4.5,"B3";4.6,"B3";4.7,"B3";4.8,"B3";4.9,"B3";5,"B3"}, "value", "batch", "normaltest")
Expected output:
| W | pval | normal |
|---|---|---|
| 2.90979 | 0.233425 | true |
| 2.90979 | 0.233425 | true |
| 2.90979 | 0.233425 | true |
Example 4: Custom alpha threshold
Inputs:
| data | dv | group | alpha | |
|---|---|---|---|---|
| metric | type | metric | type | 0.01 |
| 1.1 | T1 | |||
| 1.2 | T1 | |||
| 1.3 | T1 | |||
| 1.4 | T1 | |||
| 1.5 | T1 | |||
| 1.6 | T1 | |||
| 1.7 | T1 | |||
| 1.8 | T1 | |||
| 1.9 | T1 | |||
| 2 | T1 | |||
| 2.1 | T1 | |||
| 2.2 | T1 | |||
| 2.3 | T1 | |||
| 2.4 | T1 | |||
| 2.5 | T1 | |||
| 2.6 | T1 | |||
| 2.7 | T1 | |||
| 2.8 | T1 | |||
| 2.9 | T1 | |||
| 3 | T1 | |||
| 2.1 | T2 | |||
| 2.2 | T2 | |||
| 2.3 | T2 | |||
| 2.4 | T2 | |||
| 2.5 | T2 | |||
| 2.6 | T2 | |||
| 2.7 | T2 | |||
| 2.8 | T2 | |||
| 2.9 | T2 | |||
| 3 | T2 | |||
| 3.1 | T2 | |||
| 3.2 | T2 | |||
| 3.3 | T2 | |||
| 3.4 | T2 | |||
| 3.5 | T2 | |||
| 3.6 | T2 | |||
| 3.7 | T2 | |||
| 3.8 | T2 | |||
| 3.9 | T2 | |||
| 4 | T2 | |||
| 3.1 | T3 | |||
| 3.2 | T3 | |||
| 3.3 | T3 | |||
| 3.4 | T3 | |||
| 3.5 | T3 | |||
| 3.6 | T3 | |||
| 3.7 | T3 | |||
| 3.8 | T3 | |||
| 3.9 | T3 | |||
| 4 | T3 | |||
| 4.1 | T3 | |||
| 4.2 | T3 | |||
| 4.3 | T3 | |||
| 4.4 | T3 | |||
| 4.5 | T3 | |||
| 4.6 | T3 | |||
| 4.7 | T3 | |||
| 4.8 | T3 | |||
| 4.9 | T3 | |||
| 5 | T3 |
Excel formula:
=NORMALITY({"metric","type";1.1,"T1";1.2,"T1";1.3,"T1";1.4,"T1";1.5,"T1";1.6,"T1";1.7,"T1";1.8,"T1";1.9,"T1";2,"T1";2.1,"T1";2.2,"T1";2.3,"T1";2.4,"T1";2.5,"T1";2.6,"T1";2.7,"T1";2.8,"T1";2.9,"T1";3,"T1";2.1,"T2";2.2,"T2";2.3,"T2";2.4,"T2";2.5,"T2";2.6,"T2";2.7,"T2";2.8,"T2";2.9,"T2";3,"T2";3.1,"T2";3.2,"T2";3.3,"T2";3.4,"T2";3.5,"T2";3.6,"T2";3.7,"T2";3.8,"T2";3.9,"T2";4,"T2";3.1,"T3";3.2,"T3";3.3,"T3";3.4,"T3";3.5,"T3";3.6,"T3";3.7,"T3";3.8,"T3";3.9,"T3";4,"T3";4.1,"T3";4.2,"T3";4.3,"T3";4.4,"T3";4.5,"T3";4.6,"T3";4.7,"T3";4.8,"T3";4.9,"T3";5,"T3"}, "metric", "type", 0.01)
Expected output:
| W | pval | normal |
|---|---|---|
| 0.960375 | 0.551372 | true |
| 0.960375 | 0.551372 | true |
| 0.960375 | 0.551372 | true |
Python Code
import pandas as pd
from pingouin import normality as pg_normality
def normality(data, dv, group='', method='shapiro', alpha=0.05):
"""
Test normality by group or overall using Pingouin.
See: https://pingouin-stats.org/build/html/generated/pingouin.normality.html
This example function is provided as-is without any representation of accuracy.
Args:
data (list[list]): Input table where the first row contains column names.
dv (str): Name of the dependent-variable column.
group (str, optional): Grouping column name; use empty string to test without groups. Default is ''.
method (str, optional): Normality method (for example shapiro or normaltest). Default is 'shapiro'.
alpha (float, optional): Significance level for decision threshold. Default is 0.05.
Returns:
list[list]: 2D table containing normality test results.
"""
try:
def to2d(x):
return [[x]] if not isinstance(x, list) else x
def build_dataframe(table):
table = to2d(table)
if not isinstance(table, list) or not table or not all(isinstance(row, list) for row in table):
return None, "Error: data must be a non-empty 2D list"
if len(table) < 2:
return None, "Error: data must include a header row and at least one data row"
headers = [str(h).strip() for h in table[0]]
if any(h == "" for h in headers):
return None, "Error: header row contains empty column names"
if len(set(headers)) != len(headers):
return None, "Error: header row contains duplicate column names"
rows = []
for row in table[1:]:
if len(row) != len(headers):
return None, "Error: all data rows must match header width"
rows.append([None if cell == "" else cell for cell in row])
return pd.DataFrame(rows, columns=headers), None
def dataframe_to_2d(df):
out = [list(df.columns)]
for values in df.itertuples(index=False, name=None):
row = []
for value in values:
if pd.isna(value):
row.append("")
elif isinstance(value, bool):
row.append(value)
elif isinstance(value, (int, float)):
row.append(float(value))
else:
row.append(str(value))
out.append(row)
return out
frame, error = build_dataframe(data)
if error:
return error
if dv not in frame.columns:
return f"Error: dv column '{dv}' not found"
if group is None or group == "":
# Pingouin's normality() has a bug where dv is provided without a group
# and it asserts that group is in the DataFrame columns (even when group is None).
# Workaround: call it on the Series for the dv column.
result = pg_normality(data=frame[dv], method=method, alpha=float(alpha))
else:
if group not in frame.columns:
return f"Error: group column '{group}' not found"
result = pg_normality(data=frame, dv=dv, group=group, method=method, alpha=float(alpha))
return dataframe_to_2d(result)
except Exception as e:
return f"Error: {str(e)}"Online Calculator
Input table where the first row contains column names.
Name of the dependent-variable column.
Grouping column name; use empty string to test without groups.
Normality method (for example shapiro or normaltest).
Significance level for decision threshold.