1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 00:47:45 +00:00

Spreadsheet: Update statistical functions to take variadic arguments

This commit is contained in:
Eli Youngs 2022-04-30 13:22:36 -07:00 committed by Linus Groh
parent 92f4408d66
commit 21c605bfda

View file

@ -435,34 +435,41 @@ function numericResolve(cells) {
} }
function resolve(cells) { function resolve(cells) {
if (!(cells instanceof Array)) {
cells = [cells];
}
return cells.map(resolveRange).flat();
}
function resolveRange(cells) {
const isRange = cells instanceof CommonRange; const isRange = cells instanceof CommonRange;
return isRange ? cells.toArray().map(cell => cell.value()) : cells; return isRange ? cells.toArray().map(cell => cell.value()) : cells;
} }
// Statistics // Statistics
function sum(cells) { function sum(...cells) {
return numericReduce((acc, x) => acc + x, 0, cells); return numericReduce((acc, x) => acc + x, 0, cells);
} }
function sumIf(condition, cells) { function sumIf(condition, ...cells) {
return numericReduce((acc, x) => (condition(x) ? acc + x : acc), 0, cells); return numericReduce((acc, x) => (condition(x) ? acc + x : acc), 0, cells);
} }
function count(cells) { function count(...cells) {
return reduce((acc, x) => acc + 1, 0, cells); return reduce((acc, x) => acc + 1, 0, cells);
} }
function countIf(condition, cells) { function countIf(condition, ...cells) {
return reduce((acc, x) => (condition(x) ? acc + 1 : acc), 0, cells); return reduce((acc, x) => (condition(x) ? acc + 1 : acc), 0, cells);
} }
function average(cells) { function average(...cells) {
const sumAndCount = numericReduce((acc, x) => [acc[0] + x, acc[1] + 1], [0, 0], cells); const sumAndCount = numericReduce((acc, x) => [acc[0] + x, acc[1] + 1], [0, 0], cells);
return sumAndCount[0] / sumAndCount[1]; return sumAndCount[0] / sumAndCount[1];
} }
function averageIf(condition, cells) { function averageIf(condition, ...cells) {
const sumAndCount = numericReduce( const sumAndCount = numericReduce(
(acc, x) => (condition(x) ? [acc[0] + x, acc[1] + 1] : acc), (acc, x) => (condition(x) ? [acc[0] + x, acc[1] + 1] : acc),
[0, 0], [0, 0],
@ -471,20 +478,20 @@ function averageIf(condition, cells) {
return sumAndCount[0] / sumAndCount[1]; return sumAndCount[0] / sumAndCount[1];
} }
function maxIf(condition, cells) { function maxIf(condition, ...cells) {
return Math.max(...numericResolve(cells).filter(condition)); return Math.max(...numericResolve(cells).filter(condition));
} }
function max(cells) { function max(...cells) {
return maxIf(() => true, cells); return maxIf(() => true, ...cells);
} }
function minIf(condition, cells) { function minIf(condition, ...cells) {
return Math.min(...numericResolve(cells).filter(condition)); return Math.min(...numericResolve(cells).filter(condition));
} }
function min(cells) { function min(...cells) {
return minIf(() => true, cells); return minIf(() => true, ...cells);
} }
function sumProductIf(condition, rangeOne, rangeTwo) { function sumProductIf(condition, rangeOne, rangeTwo) {
@ -501,7 +508,7 @@ function sumProduct(rangeOne, rangeTwo) {
return sumProductIf(() => true, rangeOne, rangeTwo); return sumProductIf(() => true, rangeOne, rangeTwo);
} }
function median(cells) { function median(...cells) {
const values = numericResolve(cells); const values = numericResolve(cells);
if (values.length === 0) return 0; if (values.length === 0) return 0;
@ -526,7 +533,7 @@ function median(cells) {
return (qselect(values, values.length / 2) + qselect(values, values.length / 2 - 1)) / 2; return (qselect(values, values.length / 2) + qselect(values, values.length / 2 - 1)) / 2;
} }
function variance(cells) { function variance(...cells) {
const sumsAndSquaresAndCount = numericReduce( const sumsAndSquaresAndCount = numericReduce(
(acc, x) => [acc[0] + x, acc[1] + x * x, acc[2] + 1], (acc, x) => [acc[0] + x, acc[1] + x * x, acc[2] + 1],
[0, 0, 0], [0, 0, 0],
@ -539,7 +546,7 @@ function variance(cells) {
return (count * squares - sums * sums) / count; return (count * squares - sums * sums) / count;
} }
function mode(cells) { function mode(...cells) {
const counts = numericReduce( const counts = numericReduce(
(map, x) => { (map, x) => {
if (!map.has(x)) map.set(x, 0); if (!map.has(x)) map.set(x, 0);
@ -562,8 +569,8 @@ function mode(cells) {
return mostCommonValue; return mostCommonValue;
} }
function stddev(cells) { function stddev(...cells) {
return Math.sqrt(variance(cells)); return Math.sqrt(variance(...cells));
} }
// Lookup // Lookup
@ -786,19 +793,20 @@ numericReduce.__documentation = JSON.stringify({
sum.__documentation = JSON.stringify({ sum.__documentation = JSON.stringify({
name: "sum", name: "sum",
argc: 1, argc: 1,
argnames: ["cell names"], argnames: ["numbers or cell names"],
doc: "Calculates the sum of the values in `cells`", doc: "Calculates the total of the numbers or cell values in `numbers or cell names`",
examples: { examples: {
"sum(R`A0:C3`)": "sum(R`A0:C3`)":
"Calculate the sum of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)", "Calculate the sum of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)",
"sum(1, 2, 3)": "Calculate the sum of 1, 2, and 3 (Sum = 6)",
}, },
}); });
sumIf.__documentation = JSON.stringify({ sumIf.__documentation = JSON.stringify({
name: "sumIf", name: "sumIf",
argc: 2, argc: 2,
argnames: ["condition", "cell names"], argnames: ["condition", "numbers or cell names"],
doc: "Calculates the sum of cells the value of which evaluates to true when passed to `condition`", doc: "Calculates the sum of all numbers or cell values which evaluate to true when passed to `condition`",
examples: { examples: {
"sumIf(x => x instanceof Number, R`A1:C4`)": "sumIf(x => x instanceof Number, R`A1:C4`)":
"Calculates the sum of all numbers within A1:C4", "Calculates the sum of all numbers within A1:C4",
@ -808,8 +816,8 @@ sumIf.__documentation = JSON.stringify({
count.__documentation = JSON.stringify({ count.__documentation = JSON.stringify({
name: "count", name: "count",
argc: 1, argc: 1,
argnames: ["cell names"], argnames: ["numbers or cell names"],
doc: "Counts the number of cells in the given range", doc: "Counts the number of inputs or cells in a given range",
examples: { examples: {
"count(R`A0:C3`)": "count(R`A0:C3`)":
"Count the number of cells in A0:C3, [Click to view](spreadsheet://example/variance#simple)", "Count the number of cells in A0:C3, [Click to view](spreadsheet://example/variance#simple)",
@ -819,8 +827,8 @@ count.__documentation = JSON.stringify({
countIf.__documentation = JSON.stringify({ countIf.__documentation = JSON.stringify({
name: "countIf", name: "countIf",
argc: 2, argc: 2,
argnames: ["condition", "cell names"], argnames: ["condition", "numbers or cell names"],
doc: "Counts cells the value of which evaluates to true when passed to `condition`", doc: "Counts inputs or cell values which evaluate to true when passed to `condition`",
examples: { examples: {
"countIf(x => x instanceof Number, R`A1:C3`)": "countIf(x => x instanceof Number, R`A1:C3`)":
"Count the number of cells which have numbers within A1:C3", "Count the number of cells which have numbers within A1:C3",
@ -830,40 +838,42 @@ countIf.__documentation = JSON.stringify({
average.__documentation = JSON.stringify({ average.__documentation = JSON.stringify({
name: "average", name: "average",
argc: 1, argc: 1,
argnames: ["cell names"], argnames: ["numbers or cell names"],
doc: "Calculates the average of the values in `cells`", doc: "Calculates the average of the numbers or cell values in `numbers or cell names`",
examples: { examples: {
"average(R`A0:C3`)": "average(R`A0:C3`)":
"Calculate the average of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)", "Calculate the average of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)",
"average(4, 6)": "Calculate the average of 4 and 6 (Average = 5)",
}, },
}); });
averageIf.__documentation = JSON.stringify({ averageIf.__documentation = JSON.stringify({
name: "averageIf", name: "averageIf",
argc: 2, argc: 2,
argnames: ["condition", "cell names"], argnames: ["condition", "numbers or cell names"],
doc: "Calculates the average of cells the value of which evaluates to true when passed to `condition`", doc: "Calculates the average of all numbers or cell values which evaluate to true when passed to `condition`",
examples: { examples: {
"averageIf(x => x > 4, R`A1:C4`)": "averageIf(x => x > 4, R`A1:C4`)":
"Calculate the sum of all numbers larger then 4 within A1:C4", "Calculate the average of all numbers larger then 4 within A1:C4",
}, },
}); });
max.__documentation = JSON.stringify({ max.__documentation = JSON.stringify({
name: "max", name: "max",
argc: 1, argc: 1,
argnames: ["the range"], argnames: ["numbers or cell names"],
doc: "Gets the largest cell's value in the range", doc: "Calculates the largest number or cell value in `numbers or cell names`",
examples: { examples: {
"max(R`A1:C4`)": "Finds the largest number within A1:C4", "max(R`A1:C4`)": "Finds the largest number within A1:C4",
"max(1, 2, 3)": "Returns the largest of 1, 2, and 3 (Max = 3)",
}, },
}); });
maxIf.__documentation = JSON.stringify({ maxIf.__documentation = JSON.stringify({
name: "max", name: "max",
argc: 1, argc: 1,
argnames: ["condition", "the range"], argnames: ["condition", "numbers or cell names"],
doc: "Gets the largest cell's value in the range which evaluates to true when passed to `condition`", doc: "Calculates the largest of all numbers or cell values which evaluate to true when passed to `condition`",
examples: { examples: {
"maxIf(x => x > 4, R`A1:C4`)": "maxIf(x => x > 4, R`A1:C4`)":
"Finds the largest number within A1:C4 that is greater than 4", "Finds the largest number within A1:C4 that is greater than 4",
@ -873,18 +883,19 @@ maxIf.__documentation = JSON.stringify({
min.__documentation = JSON.stringify({ min.__documentation = JSON.stringify({
name: "min", name: "min",
argc: 1, argc: 1,
argnames: ["the range"], argnames: ["numbers or cell names"],
doc: "Gets the smallest cell's value in the range", doc: "Calculates the smallest number or cell value in `numbers or cell names`",
examples: { examples: {
"min(R`A1:C4`)": "Finds the smallest number within A1:C4", "min(R`A1:C4`)": "Finds the smallest number within A1:C4",
"min(1, 2, 3)": "Returns the smallest of 1, 2, and 3 (Min = 1)",
}, },
}); });
minIf.__documentation = JSON.stringify({ minIf.__documentation = JSON.stringify({
name: "min", name: "min",
argc: 1, argc: 1,
argnames: ["condition", "the range"], argnames: ["condition", "numbers or cell names"],
doc: "Gets the smallest cell's value in the range which evaluates to true when passed to `condition`", doc: "Calculates the smallest of all numbers or cell values which evaluate to true when passed to `condition`",
examples: { examples: {
"minIf(x => x > 4, R`A1:C4`)": "minIf(x => x > 4, R`A1:C4`)":
"Finds the smallest number within A1:C4 that is greater than 4", "Finds the smallest number within A1:C4 that is greater than 4",
@ -960,19 +971,20 @@ sumProductIf.__documentation = JSON.stringify({
median.__documentation = JSON.stringify({ median.__documentation = JSON.stringify({
name: "median", name: "median",
argc: 1, argc: 1,
argnames: ["cell names"], argnames: ["numbers or cell names"],
doc: "Calculates the median of the numeric values in the given range of cells", doc: "Calculates the median number or cell value in `numbers or cell names`",
examples: { examples: {
"median(R`A0:C3`)": "median(R`A0:C3`)":
"Calculate the median of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)", "Calculate the median of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)",
"median(1, 2, 5)": "Calculate the median of 1, 2, and 5 (Median = 2)",
}, },
}); });
variance.__documentation = JSON.stringify({ variance.__documentation = JSON.stringify({
name: "variance", name: "variance",
argc: 1, argc: 1,
argnames: ["cell names"], argnames: ["numbers or cell names"],
doc: "Calculates the variance of the numeric values in the given range of cells", doc: "Calculates the variance of the numbers or cell values in `numbers or cell names`",
examples: { examples: {
"variance(R`A0:C3`)": "variance(R`A0:C3`)":
"Calculate the variance of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)", "Calculate the variance of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)",
@ -1076,11 +1088,12 @@ variance.__documentation = JSON.stringify({
mode.__documentation = JSON.stringify({ mode.__documentation = JSON.stringify({
name: "mode", name: "mode",
argc: 1, argc: 1,
argnames: ["cell names"], argnames: ["numbers or cell names"],
doc: "Calculates the mode of the numeric values in the given range of cells, i.e. the value that appears most often", doc: "Calculates the mode (most common value) of the numbers or cell values in `numbers or cell names`",
examples: { examples: {
"mode(R`A2:A14`)": "mode(R`A2:A14`)":
"Calculate the mode of the values in A2:A14, [Click to view](spreadsheet://example/variance#simple)", "Calculate the mode of the values in A2:A14, [Click to view](spreadsheet://example/variance#simple)",
"mode(1, 2, 2)": "Calculate the mode of 1, 2, and 2 (Mode = 2)",
}, },
}); });
@ -1088,7 +1101,7 @@ stddev.__documentation = JSON.stringify({
name: "stddev", name: "stddev",
argc: 1, argc: 1,
argnames: ["cell names"], argnames: ["cell names"],
doc: "Calculates the standard deviation of the numeric values in the given range of cells", doc: "Calculates the standard deviation (square root of variance) of the numbers or cell values in `numbers or cell names`",
examples: { examples: {
"stddev(R`A0:C3`)": "stddev(R`A0:C3`)":
"Calculate the standard deviation of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)", "Calculate the standard deviation of the values in A0:C3, [Click to view](spreadsheet://example/variance#simple)",