mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-24 02:12:09 -05:00
Spreadsheet: Add a whole bunch of basic statistical functions
This commit is contained in:
parent
facd7fe05b
commit
383ee279ee
1 changed files with 225 additions and 42 deletions
|
@ -54,24 +54,6 @@ function select(criteria, t, f) {
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sumIf(condition, cells) {
|
|
||||||
let sum = null;
|
|
||||||
for (let name of cells) {
|
|
||||||
let cell = thisSheet[name];
|
|
||||||
if (condition(cell)) sum = sum === null ? cell : sum + cell;
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
function countIf(condition, cells) {
|
|
||||||
let count = 0;
|
|
||||||
for (let name of cells) {
|
|
||||||
let cell = thisSheet[name];
|
|
||||||
if (condition(cell)) count++;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
function now() {
|
function now() {
|
||||||
return new Date();
|
return new Date();
|
||||||
}
|
}
|
||||||
|
@ -92,6 +74,104 @@ function sheet(name) {
|
||||||
return workbook.sheet(name);
|
return workbook.sheet(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reduce(op, accumulator, cells) {
|
||||||
|
for (let name of cells) {
|
||||||
|
let cell = thisSheet[name];
|
||||||
|
accumulator = op(accumulator, cell);
|
||||||
|
}
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
function numericReduce(op, accumulator, cells) {
|
||||||
|
return reduce((acc, x) => op(acc, Number(x)), accumulator, cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
function numericResolve(cells) {
|
||||||
|
const values = [];
|
||||||
|
for (let name of cells) values.push(Number(thisSheet[name]));
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolve(cells) {
|
||||||
|
const values = [];
|
||||||
|
for (let name of cells) values.push(thisSheet[name]);
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
|
||||||
|
function sum(cells) {
|
||||||
|
return numericReduce((acc, x) => acc + x, 0, cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sumIf(condition, cells) {
|
||||||
|
return numericReduce((acc, x) => (condition(x) ? acc + x : acc), 0, cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
function count(cells) {
|
||||||
|
return reduce((acc, x) => acc + 1, 0, cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
function countIf(condition, cells) {
|
||||||
|
return reduce((acc, x) => (condition(x) ? acc + 1 : acc), 0, cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
function average(cells) {
|
||||||
|
const sumAndCount = numericReduce((acc, x) => [acc[0] + x, acc[1] + 1], [0, 0], cells);
|
||||||
|
return sumAndCount[0] / sumAndCount[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function averageIf(condition, cells) {
|
||||||
|
const sumAndCount = numericReduce(
|
||||||
|
(acc, x) => (condition(x) ? [acc[0] + x, acc[1] + 1] : acc),
|
||||||
|
[0, 0],
|
||||||
|
cells
|
||||||
|
);
|
||||||
|
return sumAndCount[0] / sumAndCount[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function median(cells) {
|
||||||
|
const values = numericResolve(cells);
|
||||||
|
|
||||||
|
if (values.length == 0) return 0;
|
||||||
|
|
||||||
|
function qselect(arr, idx) {
|
||||||
|
if (arr.length == 1) return arr[0];
|
||||||
|
|
||||||
|
const pivot = arr[0];
|
||||||
|
const ls = arr.filter(x => x < pivot);
|
||||||
|
const hs = arr.filter(x => x > pivot);
|
||||||
|
const eqs = arr.filter(x => x === pivot);
|
||||||
|
|
||||||
|
if (idx < ls.length) return qselect(ls, k);
|
||||||
|
|
||||||
|
if (idx < ls.length + eqs.length) return pivot;
|
||||||
|
|
||||||
|
return qselect(hs, idx - ls.length - eqs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.length % 2) return qselect(values, values.length / 2);
|
||||||
|
|
||||||
|
return (qselect(values, values.length / 2) + qselect(values, values.length / 2 - 1)) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function variance(cells) {
|
||||||
|
const sumsAndSquaresAndCount = numericReduce(
|
||||||
|
(acc, x) => [acc[0] + x, acc[1] + x * x, acc[2] + 1],
|
||||||
|
[0, 0, 0],
|
||||||
|
cells
|
||||||
|
);
|
||||||
|
let sums = sumsAndSquaresAndCount[0];
|
||||||
|
let squares = sumsAndSquaresAndCount[1];
|
||||||
|
let count = sumsAndSquaresAndCount[2];
|
||||||
|
|
||||||
|
return (count * squares - sums * sums) / count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stddev(cells) {
|
||||||
|
return Math.sqrt(variance(cells));
|
||||||
|
}
|
||||||
|
|
||||||
// Cheat the system and add documentation
|
// Cheat the system and add documentation
|
||||||
range.__documentation = JSON.stringify({
|
range.__documentation = JSON.stringify({
|
||||||
name: "range",
|
name: "range",
|
||||||
|
@ -130,29 +210,6 @@ select.__documentation = JSON.stringify({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
sumIf.__documentation = JSON.stringify({
|
|
||||||
name: "sumIf",
|
|
||||||
argc: 2,
|
|
||||||
argnames: ["condition", "cell names"],
|
|
||||||
doc:
|
|
||||||
"Calculates the sum of cells the value of which evaluates to true when passed to `condition`",
|
|
||||||
examples: {
|
|
||||||
'sumIf(x => x instanceof Number, range("A1", "C4"))':
|
|
||||||
"Calculates the sum of all numbers within A1:C4",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
countIf.__documentation = JSON.stringify({
|
|
||||||
name: "countIf",
|
|
||||||
argc: 2,
|
|
||||||
argnames: ["condition", "cell names"],
|
|
||||||
doc: "Counts cells the value of which evaluates to true when passed to `condition`",
|
|
||||||
examples: {
|
|
||||||
'countIf(x => x instanceof Number, range("A1", "C4"))':
|
|
||||||
"Counts the number of cells which have numbers within A1:C4",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
now.__documentation = JSON.stringify({
|
now.__documentation = JSON.stringify({
|
||||||
name: "now",
|
name: "now",
|
||||||
argc: 0,
|
argc: 0,
|
||||||
|
@ -199,3 +256,129 @@ sheet.__documentation = JSON.stringify({
|
||||||
"sheet(0).A0 = 123": "Set the value of the cell A0 in the first sheet to 123",
|
"sheet(0).A0 = 123": "Set the value of the cell A0 in the first sheet to 123",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
reduce.__documentation = JSON.stringify({
|
||||||
|
name: "reduce",
|
||||||
|
argc: 3,
|
||||||
|
argnames: ["reduction function", "accumulator", "cells"],
|
||||||
|
doc:
|
||||||
|
"Reduces the entries in `cells` with repeated applications of the `reduction function` " +
|
||||||
|
"to the `accumulator`\n The `reduction function` should be a function of arity 2, taking " +
|
||||||
|
"first the accumulator, then the current value, and returning the new accumulator value\n\n" +
|
||||||
|
"Please keep in mind that this function respects the cell type, and can yield non-numeric " +
|
||||||
|
"values to the `curent value`.",
|
||||||
|
examples: {
|
||||||
|
'reduce((acc, x) => acc * x, 1, range("A0", "A5"))':
|
||||||
|
"Calculate the product of all values in the range A0:A5",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
numericReduce.__documentation = JSON.stringify({
|
||||||
|
name: "numericReduce",
|
||||||
|
argc: 3,
|
||||||
|
argnames: ["reduction function", "accumulator", "cells"],
|
||||||
|
doc:
|
||||||
|
"Reduces the entries in `cells` with repeated applications of the `reduction function` to the " +
|
||||||
|
"`accumulator`\n The `reduction function` should be a function of arity 2, taking first the " +
|
||||||
|
"accumulator, then the current value, and returning the new accumulator value\n\nThis function, " +
|
||||||
|
"unlike `reduce`, casts the values to a number before passing them to the `reduction function`.",
|
||||||
|
examples: {
|
||||||
|
'numericReduce((acc, x) => acc * x, 1, range("A0", "A5"))':
|
||||||
|
"Calculate the numeric product of all values in the range A0:A5",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
sum.__documentation = JSON.stringify({
|
||||||
|
name: "sum",
|
||||||
|
argc: 1,
|
||||||
|
argnames: ["cell names"],
|
||||||
|
doc: "Calculates the sum of the values in `cells`",
|
||||||
|
examples: {
|
||||||
|
'sum(range("A0", "C4"))': "Calculate the sum of the values in A0:C4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
sumIf.__documentation = JSON.stringify({
|
||||||
|
name: "sumIf",
|
||||||
|
argc: 2,
|
||||||
|
argnames: ["condition", "cell names"],
|
||||||
|
doc:
|
||||||
|
"Calculates the sum of cells the value of which evaluates to true when passed to `condition`",
|
||||||
|
examples: {
|
||||||
|
'sumIf(x => x instanceof Number, range("A1", "C4"))':
|
||||||
|
"Calculates the sum of all numbers within A1:C4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
count.__documentation = JSON.stringify({
|
||||||
|
name: "count",
|
||||||
|
argc: 1,
|
||||||
|
argnames: ["cell names"],
|
||||||
|
doc: "Counts the number of cells in the given range",
|
||||||
|
examples: {
|
||||||
|
'count(range("A0", "C4"))': "Count the number of cells in A0:C4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
countIf.__documentation = JSON.stringify({
|
||||||
|
name: "countIf",
|
||||||
|
argc: 2,
|
||||||
|
argnames: ["condition", "cell names"],
|
||||||
|
doc: "Counts cells the value of which evaluates to true when passed to `condition`",
|
||||||
|
examples: {
|
||||||
|
'countIf(x => x instanceof Number, range("A1", "C4"))':
|
||||||
|
"Count the number of cells which have numbers within A1:C4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
average.__documentation = JSON.stringify({
|
||||||
|
name: "average",
|
||||||
|
argc: 1,
|
||||||
|
argnames: ["cell names"],
|
||||||
|
doc: "Calculates the average of the values in `cells`",
|
||||||
|
examples: {
|
||||||
|
'average(range("A0", "C4"))': "Calculate the average of the values in A0:C4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
averageIf.__documentation = JSON.stringify({
|
||||||
|
name: "averageIf",
|
||||||
|
argc: 2,
|
||||||
|
argnames: ["condition", "cell names"],
|
||||||
|
doc:
|
||||||
|
"Calculates the average of cells the value of which evaluates to true when passed to `condition`",
|
||||||
|
examples: {
|
||||||
|
'averageIf(x => x > 4, range("A1", "C4"))':
|
||||||
|
"Calculate the sum of all numbers larger then 4 within A1:C4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
median.__documentation = JSON.stringify({
|
||||||
|
name: "median",
|
||||||
|
argc: 1,
|
||||||
|
argnames: ["cell names"],
|
||||||
|
doc: "Calculates the median of the numeric values in the given range of cells",
|
||||||
|
examples: {
|
||||||
|
'median(range("A0", "C4"))': "Calculate the median of the values in A0:C4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
variance.__documentation = JSON.stringify({
|
||||||
|
name: "variance",
|
||||||
|
argc: 1,
|
||||||
|
argnames: ["cell names"],
|
||||||
|
doc: "Calculates the variance of the numeric values in the given range of cells",
|
||||||
|
examples: {
|
||||||
|
'variance(range("A0", "C4"))': "Calculate the variance of the values in A0:C4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
stddev.__documentation = JSON.stringify({
|
||||||
|
name: "stddev",
|
||||||
|
argc: 1,
|
||||||
|
argnames: ["cell names"],
|
||||||
|
doc: "Calculates the standard deviation of the numeric values in the given range of cells",
|
||||||
|
examples: {
|
||||||
|
'stddev(range("A0", "C4"))': "Calculate the standard deviation of the values in A0:C4",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue