STATS_RECODE – recode ranges and exact values
What it does
Recode values by rule bins (ranges) or exact matches, or reverse a scale. New values can be numbers or text.
A pair is read by the type of the column: in a text column "Never:1" (or "1:Never") recodes Never -> 1 and "control:group_a" recodes text to text; in a numeric column two numbers are new:old and "low:1" recodes 1 -> low.
Applies to selected columns (via colFilter) and creates new recoded columns.
The last parameter may be either a suffix or a "|"-separated list of explicit output names.
"reverse" mirrors the scale: min + max − value, so a scale with a gap stays a mirror (1,2,3,5 → 5,4,3,1). "reverse:1:7" declares the scale when not every value is present. One scale for all selected columns: reverse columns with different scales in separate formulas.
Syntax
=STATS_RECODE(columns, ranges, colFilter, outputNames)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.
ranges — required
Comma-separated rules for recoding values, or the shortcut "reverse". Every rule works on its own; the first rule that matches a value wins.
Formats:
- Range: new:min:max (recodes any x with min <= x < max to NEW). An empty bound is open.
Example: "1:0:10" (0-10 -> 1), "3:20:" (x >= 20 -> 3), "9::0" (everything below 0 -> 9).
- Range to text: label:min:max gives the values of the range the text label ("kids:0:18,adult:18:"); label:code:min:max gives the code with a label ("kids:1:0:18").
- Pair: which side is the old value depends on the type of the column — the side of the column's type.
Two numbers are new:old: "1:0,0:1" (flip 0<->1), "1:7,2:6,3:5".
In a text column "Never:1" and "1:Never" both recode Never -> 1 (Never becomes the label of 1); "control:group_a" recodes text to text.
In a numeric column "low:1" recodes 1 -> low (the result mixes text and numbers).
- Reverse: "reverse" mirrors the scale, min + max − value (1,2,3,5 -> 5,4,3,1).
"reverse:1:7" declares the scale (useful when 1 or 7 is not in the data). Numbers only; one scale for all selected columns.
A list that starts with the bare word reverse is a reverse, and the other rules are dropped: "reverse,1:2" gives the same as "reverse". To recode and reverse, use two formulas.
A value that no rule matches stays as it is. An empty item (a trailing comma) is skipped. Letter case matters: "Never:1" does not touch "never".
Examples:
"1:0:10,2:10:20,3:20:""male:1,female:2""control:group_a""low:1:3,high:3:""1:7,2:6,3:5""reverse""reverse:1:5"
A wrong rule is an error of the whole formula that names the rule — nothing is recoded: no rules, one part ("5"), an empty side (":1"), a bound that is not a number ("1:a:3"), an empty range ("9:3:1"), more than four parts, two texts in a numeric column ("a:b"), the bare word reverse after other rules, "reverse:1" in a numeric column.
A pair with text over numeric and text columns at once is an error ("recode them separately"): recode them in separate formulas. Rules of numbers only work on such a selection and leave the text columns as they are.
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.
outputNames — optional
Controls names of new output columns.
You can pass a regular suffix, for example "_bin" or "-calc".
You can also pass explicit output names as a "|"-separated list.
In COMPUTE, names are assigned in the same order as expressions in query.
In RECODE, names are assigned in the same order as selected source columns.
In RECODE, template naming is supported, for example "code{1}" or "group{5}".
Short "|"-separated lists are allowed; a missing name becomes <column>_missing ("z|" on x1,x2 gives z and x2_missing).
One name without "|" is a suffix: "x2" turns x1 into x1x2.
New names must be different: a repeated name is an error "Duplicate column name: z" in STATS_COLUMNS, STATS_RECODE and STATS_COMPUTE (there also when the same expression is written twice without names).
Examples:
"-calc""_bin""ratio|row_mean|row_std""age_bin|income_bin""code{1}""new_a||new_c"
Examples
- — the sample data (50 rows) all examples use
- —
=STATS_RECODE(Data!A1:T51, "1:0:3,2:3:5,3:5:", "x,x2", "_bin") - —
=STATS_RECODE(Data!A1:T51, "1:0:2,2:2:4,3:4:", "/^q/") - — Use explicit names with |
=STATS_RECODE(Data!A1:T51, "1:0:3,2:3:5,3:5:", "x,x2", "x_bin|x2_bin") - — reverse a 1–9 scale
=STATS_RECODE(Data!A1:T51, "reverse:1:9", "q1") - — ranges to text: 1–2 is low, 3 and above is high
=STATS_RECODE(Data!A1:T51, "low:1:3,high:3:", "x", "_level")
What the result contains
A 2D array: first row contains new column names (name + "_recoded" by default); next rows are recoded values.
A value that no rule matches stays as it is; an empty cell stays empty.
Errors
Reverse on text: "Reverse works on numbers only, the order of text labels is unknown: a, b, c. Recode them to numbers first, for example 'Never:1, Sometimes:2, Often:3'".
A wrong rule is an error that names it, and nothing is recoded: 'recode: wrong rule "9:3:1" - the range [min, max) is empty: min must be below max'. No rules at all: "recode needs rules, e.g. …".
A pair with text over numeric and text columns at once: 'recode: rule "control:group_a" reads differently in numeric (n) and text (t) columns - recode them separately'.
The bare word reverse after other rules: 'recode: wrong rule "reverse" - reverse is a rule of its own …'. Recode and reverse in two formulas.
A repeated output name: "Duplicate column name: a".
A column name that does not exist: "Column nope does not exist".
About STATS_RECODE
STATS_RECODE turns the values of the selected columns into new codes and writes them as new columns; the source columns stay unchanged. The new columns are named with the suffix "_recoded" by default, with your own suffix, or with a list of names such as "x_bin|x2_bin". They recalculate by themselves when new responses arrive from Google Forms.
The rules are one text, separated by commas, and can be mixed. A range rule new:min:max puts values into bins, min included and max not: "1:0:10,2:10:20,3:20:" (an empty max has no upper bound). An equals rule new:old replaces exact values, for example "1:0,0:1" flips 0 and 1; a text rule text:new turns text answers into numbers, for example "male:1,female:2". A value that no rule matches stays as it is, and an empty cell stays empty.
To reverse a scale, write "reverse": each value becomes min + max − value, so 1, 2, 3, 5 become 5, 4, 3, 1. When an end of the scale may be missing from the data, declare it: "reverse:1:7". Reverse works on numbers only and uses one scale for all selected columns; reverse columns with different scales in separate formulas.