STATS_MA – Moving average in Google Sheets™
What it does
Creates new columns with the moving average of each selected column.
The window is counted over the rows of the table: each average stands on the row it belongs to, so the result lines up with the source rows.
A window can require a minimum of values ("3:2") and can be trailing, prior (SPSS PMA) or centered (SPSS MA) — see windowSize.
Syntax
=STATS_MA(columns, windowSize, colFilter, postFix)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 columnsA1:C200— use 3 columnsA:F— use 6 columnsA1: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.
windowSize — required, default 3
Moving average window: "w", "w:min" or "w:min:kind". Empty: the window size from Settings (3 by default).
- w — window size in rows (a whole number of at least 1, not larger than the number of rows).
- min — how many values the window needs to give an average. Empty: all w values; a missing value in the window then gives ".". With "3:2" a window of 3 rows is averaged over its values as soon as at least 2 are there.
- kind — 0 or empty: trailing window, the average is on the last row of the window (the current row and the w − 1 rows before it); 1: prior window, the w rows before the current row (SPSS PMA); 2: centered window around the current row (SPSS MA; an even window averages two neighbouring spans, as SPSS does).
Rows where the window is not complete show ".": the first w − 1 rows for the trailing window, the first w rows for the prior window, both ends for the centered window.
Settings keep only the window size; a minimum or a kind is written in the formula.
Should be a whole number of at least 1
Examples:
3— trailing window of 3 rows"3:2"— window of 3 rows, averaged when at least 2 values are there"5::2"— centered window of 5 rows, all 5 values needed"3:3:1"— the 3 rows before the current row (SPSS PMA)
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):
- By name - exact header text (case-sensitive)
- "age,score,total"
- Negative rules - exclude by name (prefix with
-)- "-id,-notes" (include all except
idandnotes)
- "-id,-notes" (include all except
- 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.
- "/^q/" (all headers starting with
- 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.
postFix — optional
Suffix for new column names.
Each selected column produces a new column named <originalName><postFix>.
Examples:
"-w""recoded""_bin"
Examples
- — the sample data (50 rows) all examples use
- — trailing window of 3 rows
=STATS_MA(Data!A1:T51, 3, "x,x1,x2", "-ma") - — window of 3 rows, averaged when at least 2 values are there
=STATS_MA(Data!A1:T51, "3:2", "x") - — centered window of 5 rows (SPSS MA)
=STATS_MA(Data!A1:T51, "5::2", "x")
What the result contains
2D array with moving average columns. First row for names (source name + postFix, "-ma" by default; an empty postFix replaces the source column).
The block is as tall as the data rows of the range (an empty tail is dropped). Rows where the window is not complete, or holds a missing value without a minimum, show ".".
Errors
A window that is not a whole number of at least 1: "windowSize must be a positive integer".
A window larger than the number of rows: "window size cant be bigger than sample size".
A minimum outside 1…w: "window minimum must be an integer from 1 to window size".
A kind other than 0, 1 or 2: "window kind must be 0 (trailing), 1 (prior, SPSS PMA) or 2 (centered, SPSS MA), got 3".
A text column without value labels: "Moving average needs numeric columns, text columns: c (add value labels or recode to numbers)". Text with value labels is averaged by its codes.
About STATS_MA
STATS_MA adds a moving average column for each selected column. Each average stands on the row it belongs to, so the result lines up with the source rows and can be placed right next to them. The new columns are named after the source columns with the suffix "-ma" by default, and they recalculate by themselves when new responses arrive from Google Forms.
The window is written as "w:min:kind". w is the number of rows. kind chooses a trailing window (the current row and the rows before it, the default), a prior window of the rows before the current one (SPSS PMA) or a centered window (SPSS MA). With min, the average is given as soon as that many values are in the window, so one missing answer does not break the series.
Rows where the window is not complete show ".", for example the first w − 1 rows of a trailing window. Without min, a window with a missing value shows "." too. A text column without value labels is an error; text with labels is averaged by its codes.