MIXED_ANOVA
Mixed ANOVA models one within-subject factor and one between-subject factor in a single design.
It tests the main effects and the interaction between factors while accounting for repeated measurements within subjects. The output includes F statistics, p-values, and effect sizes for each tested term.
This wrapper takes long-format tabular data and returns the mixed ANOVA result table.
Excel Usage
=MIXED_ANOVA(data, dv, within, subject, between, correction, effsize)
data(list[list], required): Input table where the first row contains column names.dv(str, required): Name of the dependent-variable column.within(str, required): Name of the within-subject factor column.subject(str, required): Name of the subject identifier column.between(str, required): Name of the between-subject factor column.correction(str, optional, default: “auto”): Sphericity correction option (for example auto, True, False).effsize(str, optional, default: “np2”): Effect size metric (for example, np2, ng2).
Returns (list[list]): 2D table containing mixed ANOVA results.
Example 1: Mixed design with two groups and two times
Inputs:
| data | dv | within | subject | between | |||
|---|---|---|---|---|---|---|---|
| subject | group | time | score | score | time | subject | group |
| S1 | G1 | T1 | 5 | ||||
| S1 | G1 | T2 | 5.7 | ||||
| S2 | G1 | T1 | 4.8 | ||||
| S2 | G1 | T2 | 5.4 | ||||
| S3 | G2 | T1 | 5.3 | ||||
| S3 | G2 | T2 | 6.1 | ||||
| S4 | G2 | T1 | 5.1 | ||||
| S4 | G2 | T2 | 5.9 |
Excel formula:
=MIXED_ANOVA({"subject","group","time","score";"S1","G1","T1",5;"S1","G1","T2",5.7;"S2","G1","T1",4.8;"S2","G1","T2",5.4;"S3","G2","T1",5.3;"S3","G2","T2",6.1;"S4","G2","T1",5.1;"S4","G2","T2",5.9}, "score", "time", "subject", "group")
Expected output:
| Source | SS | DF1 | DF2 | MS | F | p_unc | np2 | eps |
|---|---|---|---|---|---|---|---|---|
| group | 0.28125 | 1 | 2 | 0.28125 | 5.4878 | 0.143905 | 0.732899 | |
| time | 1.05125 | 1 | 2 | 1.05125 | 841 | 0.00118694 | 0.997628 | 1 |
| Interaction | 0.01125 | 1 | 2 | 0.01125 | 9 | 0.095466 | 0.818182 |
Example 2: Partial eta squared output
Inputs:
| data | dv | within | subject | between | effsize | |||
|---|---|---|---|---|---|---|---|---|
| id | cohort | session | y | y | session | id | cohort | np2 |
| A | C1 | pre | 2.1 | |||||
| A | C1 | post | 2.9 | |||||
| B | C1 | pre | 2 | |||||
| B | C1 | post | 2.7 | |||||
| C | C2 | pre | 2.4 | |||||
| C | C2 | post | 3.2 | |||||
| D | C2 | pre | 2.3 | |||||
| D | C2 | post | 3.1 |
Excel formula:
=MIXED_ANOVA({"id","cohort","session","y";"A","C1","pre",2.1;"A","C1","post",2.9;"B","C1","pre",2;"B","C1","post",2.7;"C","C2","pre",2.4;"C","C2","post",3.2;"D","C2","pre",2.3;"D","C2","post",3.1}, "y", "session", "id", "cohort", "np2")
Expected output:
| Source | SS | DF1 | DF2 | MS | F | p_unc | np2 | eps |
|---|---|---|---|---|---|---|---|---|
| cohort | 0.21125 | 1 | 2 | 0.21125 | 13 | 0.0690507 | 0.866667 | |
| session | 1.20125 | 1 | 2 | 1.20125 | 961 | 0.00103896 | 0.997923 | 1 |
| Interaction | 0.00125 | 1 | 2 | 0.00125 | 1 | 0.42265 | 0.333333 |
Example 3: Generalized eta squared output
Inputs:
| data | dv | within | subject | between | effsize | |||
|---|---|---|---|---|---|---|---|---|
| sid | grp | stage | metric | metric | stage | sid | grp | ng2 |
| P1 | A | one | 11.1 | |||||
| P1 | A | two | 11.9 | |||||
| P2 | A | one | 10.8 | |||||
| P2 | A | two | 11.6 | |||||
| P3 | B | one | 12 | |||||
| P3 | B | two | 12.8 | |||||
| P4 | B | one | 11.7 | |||||
| P4 | B | two | 12.6 |
Excel formula:
=MIXED_ANOVA({"sid","grp","stage","metric";"P1","A","one",11.1;"P1","A","two",11.9;"P2","A","one",10.8;"P2","A","two",11.6;"P3","B","one",12;"P3","B","two",12.8;"P4","B","one",11.7;"P4","B","two",12.6}, "metric", "stage", "sid", "grp", "ng2")
Expected output:
| Source | SS | DF1 | DF2 | MS | F | p_unc | ng2 | eps |
|---|---|---|---|---|---|---|---|---|
| grp | 1.71125 | 1 | 2 | 1.71125 | 22.4426 | 0.0417851 | 0.916946 | |
| stage | 1.36125 | 1 | 2 | 1.36125 | 1089 | 0.000917011 | 0.897774 | 1 |
| Interaction | 0.00125 | 1 | 2 | 0.00125 | 1 | 0.42265 | 0.008 |
Example 4: Correction value set true
Inputs:
| data | dv | within | subject | between | correction | |||
|---|---|---|---|---|---|---|---|---|
| sid | grp | wave | val | val | wave | sid | grp | true |
| U1 | X | w1 | 1.1 | |||||
| U1 | X | w2 | 1.4 | |||||
| U2 | X | w1 | 1 | |||||
| U2 | X | w2 | 1.3 | |||||
| U3 | Y | w1 | 1.5 | |||||
| U3 | Y | w2 | 1.9 | |||||
| U4 | Y | w1 | 1.4 | |||||
| U4 | Y | w2 | 1.8 |
Excel formula:
=MIXED_ANOVA({"sid","grp","wave","val";"U1","X","w1",1.1;"U1","X","w2",1.4;"U2","X","w1",1;"U2","X","w2",1.3;"U3","Y","w1",1.5;"U3","Y","w2",1.9;"U4","Y","w1",1.4;"U4","Y","w2",1.8}, "val", "wave", "sid", "grp", TRUE)
Expected output:
| Source | SS | DF1 | DF2 | MS | F | p_unc | np2 | eps |
|---|---|---|---|---|---|---|---|---|
| grp | 0.405 | 1 | 2 | 0.405 | 40.5 | 0.0238129 | 0.952941 | |
| wave | 0.245 | 1 | 2 | 0.245 | 8827060000000000 | 1.13288e-16 | 1 | 1 |
| Interaction | 0.005 | 1 | 2 | 0.005 | 180144000000000 | 5.55112e-15 | 1 |
Python Code
import pandas as pd
from pingouin import mixed_anova as pg_mixed_anova
def mixed_anova(data, dv, within, subject, between, correction='auto', effsize='np2'):
"""
Perform mixed ANOVA with within- and between-subject factors using Pingouin.
See: https://pingouin-stats.org/build/html/generated/pingouin.mixed_anova.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.
within (str): Name of the within-subject factor column.
subject (str): Name of the subject identifier column.
between (str): Name of the between-subject factor column.
correction (str, optional): Sphericity correction option (for example auto, True, False). Default is 'auto'.
effsize (str, optional): Effect size metric (for example, np2, ng2). Default is 'np2'.
Returns:
list[list]: 2D table containing mixed ANOVA 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
for col_name, col_label in [(dv, "dv"), (within, "within"), (subject, "subject"), (between, "between")]:
if col_name not in frame.columns:
return f"Error: {col_label} column '{col_name}' not found"
corr_value = correction
if isinstance(correction, str):
lower = correction.lower()
if lower == "true":
corr_value = True
elif lower == "false":
corr_value = False
result = pg_mixed_anova(
data=frame,
dv=dv,
within=within,
subject=subject,
between=between,
correction=corr_value,
effsize=effsize,
)
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.
Name of the within-subject factor column.
Name of the subject identifier column.
Name of the between-subject factor column.
Sphericity correction option (for example auto, True, False).
Effect size metric (for example, np2, ng2).