ANOVA1S
Description
ANOVA1S performs one-factor analysis of variance using group statistics as inputs. The intent and output of the function are exactly the same as ANOVA; it is just the nature of the inputs that is different.
Syntax
ANOVA1S(n_i, xbar_i, s_i, [sb_stats]) where
n_iis a row or column vector of replicate counts for each groupxbar_iis a row or column vector of group meanss_iis a row or column vector of group standard deviationssb_statsis an optional boolean (defaultFALSE) that will display an additional column of information about
The output of the function is exactly the same as with ANOVA, a labeled table with 4 rows and either 4 or 6 columns (not counting the row labels) depending on the value of sb_stats. Please read the documentation for ANOVA for more information about the output.
Example
The example below shows the same data as in the ANOVA example but formatted differently. To perform ANOVA on the data you have two options: reformat the data using Unfold (described elsewhere) and use ANOVA, or calculate group statistics and use ANOVA1S. The latter approach is shown below.
There may be cases when the original measurements are not available but the group statistics are, in which case ANOVA1S is the only option.

Download: ANOVA1S.xlsx
Source Code
ANOVA1S = LAMBDA(
n_i, // vector of replicate counts
xbar_i, // vector of means within a group
s_i, // vector of within-group std deviations
[sb_stats], // add column of s_b info?
LET(
// before doing calcs convert all inputs to column vectors
n_i, TOCOL(n_i),
xbar_i, TOCOL(xbar_i),
s_i, TOCOL(s_i),
xbar_global, SUM(n_i*xbar_i)/sum(n_i),
sb_stats, IF(ISOMITTED(sb_stats), FALSE, sb_stats),
SS_b, SUM(n_i * (xbar_i - xbar_global) ^ 2),
// don't use groups with just one member to calculate SS_w (s_i doesn't exist)
SS_w, SUM(FILTER((n_i - 1) * s_i ^ 2, n_i > 1)),
SS_t, SS_b + SS_w,
DF_b, COUNT(n_i) - 1,
DF_w, SUM(n_i - 1),
DF_t, SUM(n_i) - 1,
MS_b, SS_b / DF_b,
MS_w, SS_w / DF_w,
MS_t, SS_t / DF_t,
s_w, SQRT(MS_w),
// if MS_b < MS_w then s_b is not defined
// output zero in that case
n0, (SUM(n_i) - SUMSQ(n_i) / SUM(n_i)) / (COUNT(n_i) - 1),
s_b, IFERROR(SQRT((MS_b - MS_w) / n0), 0),
s_r, SQRT(s_w ^ 2 + s_b ^ 2),
Fobs, MS_b / MS_w,
F_LL, Fobs / F.INV.RT(5%, DF_b, DF_w),
F_crit, F.INV.RT(5%, DF_b, DF_w),
P_obs, F.DIST.RT(Fobs, DF_b, DF_w),
results, VSTACK(
HSTACK("", "SS", "DF", "MS", "s"),
HSTACK("Within", SS_w, DF_w, MS_w, s_w),
HSTACK("Between", SS_b, DF_b, MS_b, s_b),
HSTACK("Total", SS_t, DF_t, MS_t, s_r),
HSTACK("F test", Fobs, F_LL, F_crit, P_obs)
),
// optional columns of s_b info, with their own IFERROR so a
// problem here can't wipe out the ANOVA table itself
sb_col, IFERROR(
IF(
MS_b > MS_w,
LET(
// Satterthwaite DOF for s_b^2 = (MS_b - MS_w)/n0 (a difference)
DF_sb, (MS_b - MS_w) ^ 2 / (MS_b ^ 2 / DF_b + MS_w ^ 2 / DF_w),
// GAMMA.INV = chi-square quantile that allows fractional DOF
// (CHISQ.INV truncates DOF to an integer)
LL, SQRT(DF_sb * s_b ^ 2 / GAMMA.INV(0.975, DF_sb / 2, 2)),
UL, SQRT(DF_sb * s_b ^ 2 / GAMMA.INV(0.025, DF_sb / 2, 2)),
VSTACK("s_b", DF_sb, LL, UL, "")
),
VSTACK("s_b", "n/a", 0, "n/a", "") // s_b clamped to 0: no CI
),
VSTACK("s_b", "error", "", "", "")
),
sb_labels, VSTACK("", "<-- s_b DOF", "<-- s_b 95% LL", "<-- s_b 95% UL", ""),
output, IF(sb_stats, HSTACK(results, sb_col, sb_labels), results),
IFERROR(output, "calc error")
)
);