@odoo/o-spreadsheet 16.5.0-alpha.7 → 16.5.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/o-spreadsheet.cjs.js +638 -58
- package/dist/o-spreadsheet.d.ts +7 -10
- package/dist/o-spreadsheet.esm.js +638 -58
- package/dist/o-spreadsheet.iife.js +638 -58
- package/dist/o-spreadsheet.iife.min.js +315 -314
- package/package.json +1 -2
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
|
+
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
+
* @version 16.5.0-alpha.8
|
|
6
|
+
* @date 2023-09-21T15:59:10.959Z
|
|
7
|
+
* @hash 0b7527d
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
import { Component, useRef, onMounted, onPatched, onWillPatch, useState, onWillUpdateProps, useComponent, useEffect, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
|
|
2
11
|
|
|
3
12
|
// define a mock translation function, when o-spreadsheet runs in standalone it doesn't translate any string
|
|
@@ -231,13 +240,26 @@ const MENU_ITEM_PADDING_HORIZONTAL = 11;
|
|
|
231
240
|
const MENU_ITEM_PADDING_VERTICAL = 4;
|
|
232
241
|
const MENU_SEPARATOR_BORDER_WIDTH = 1;
|
|
233
242
|
const MENU_SEPARATOR_PADDING = 5;
|
|
243
|
+
// Style
|
|
244
|
+
const DEFAULT_STYLE = {
|
|
245
|
+
align: "left",
|
|
246
|
+
verticalAlign: "bottom",
|
|
247
|
+
wrapping: "overflow",
|
|
248
|
+
bold: false,
|
|
249
|
+
italic: false,
|
|
250
|
+
strikethrough: false,
|
|
251
|
+
underline: false,
|
|
252
|
+
fontSize: 10,
|
|
253
|
+
fillColor: "",
|
|
254
|
+
textColor: "#000000",
|
|
255
|
+
};
|
|
256
|
+
const DEFAULT_VERTICAL_ALIGN = DEFAULT_STYLE.verticalAlign;
|
|
257
|
+
const DEFAULT_WRAPPING_MODE = DEFAULT_STYLE.wrapping;
|
|
234
258
|
// Fonts
|
|
235
259
|
const DEFAULT_FONT_WEIGHT = "400";
|
|
236
|
-
const DEFAULT_FONT_SIZE =
|
|
260
|
+
const DEFAULT_FONT_SIZE = DEFAULT_STYLE.fontSize;
|
|
237
261
|
const HEADER_FONT_SIZE = 11;
|
|
238
262
|
const DEFAULT_FONT = "'Roboto', arial";
|
|
239
|
-
const DEFAULT_VERTICAL_ALIGN = "bottom";
|
|
240
|
-
const DEFAULT_WRAPPING_MODE = "overflow";
|
|
241
263
|
// Borders
|
|
242
264
|
const DEFAULT_BORDER_DESC = { style: "thin", color: "#000000" };
|
|
243
265
|
const DEFAULT_FILTER_BORDER_DESC = { style: "thin", color: FILTERS_COLOR };
|
|
@@ -615,13 +637,17 @@ function getAddHeaderStartIndex(position, base) {
|
|
|
615
637
|
return position === "after" ? base + 1 : base;
|
|
616
638
|
}
|
|
617
639
|
/**
|
|
618
|
-
*
|
|
640
|
+
* Compares two objects.
|
|
619
641
|
*/
|
|
620
642
|
function deepEquals(o1, o2) {
|
|
621
643
|
if (o1 === o2)
|
|
622
644
|
return true;
|
|
623
645
|
if ((o1 && !o2) || (o2 && !o1))
|
|
624
646
|
return false;
|
|
647
|
+
if (typeof o1 !== typeof o2)
|
|
648
|
+
return false;
|
|
649
|
+
if (typeof o1 !== "object")
|
|
650
|
+
return o1 === o2;
|
|
625
651
|
// Objects can have different keys if the values are undefined
|
|
626
652
|
const keys = new Set();
|
|
627
653
|
Object.keys(o1).forEach((key) => keys.add(key));
|
|
@@ -737,6 +763,10 @@ function insertItemsAtIndex(array, items, index) {
|
|
|
737
763
|
newArray.splice(index, 0, ...items);
|
|
738
764
|
return newArray;
|
|
739
765
|
}
|
|
766
|
+
function trimContent(content) {
|
|
767
|
+
const contentLines = content.split("\n");
|
|
768
|
+
return contentLines.map((line) => line.replace(/\s+/g, " ").trim()).join("\n");
|
|
769
|
+
}
|
|
740
770
|
|
|
741
771
|
const RBA_REGEX = /rgba?\(|\s+|\)/gi;
|
|
742
772
|
const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
|
|
@@ -4403,6 +4433,16 @@ function toNumber(value, locale) {
|
|
|
4403
4433
|
return 0;
|
|
4404
4434
|
}
|
|
4405
4435
|
}
|
|
4436
|
+
function tryCastAsNumberMatrix(data, argName) {
|
|
4437
|
+
data.forEach((row) => {
|
|
4438
|
+
row.forEach((cell) => {
|
|
4439
|
+
if (typeof cell !== "number") {
|
|
4440
|
+
throw new Error(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", typeof cell, argName));
|
|
4441
|
+
}
|
|
4442
|
+
});
|
|
4443
|
+
});
|
|
4444
|
+
return data;
|
|
4445
|
+
}
|
|
4406
4446
|
function strictToNumber(value, locale) {
|
|
4407
4447
|
if (value === "") {
|
|
4408
4448
|
throw new Error(expectNumberValueError(value));
|
|
@@ -5347,6 +5387,7 @@ css /* scss */ `
|
|
|
5347
5387
|
padding: 10px;
|
|
5348
5388
|
width: ${ERROR_TOOLTIP_WIDTH}px;
|
|
5349
5389
|
box-sizing: border-box !important;
|
|
5390
|
+
overflow-wrap: break-word;
|
|
5350
5391
|
}
|
|
5351
5392
|
`;
|
|
5352
5393
|
class ErrorToolTip extends Component {
|
|
@@ -8352,43 +8393,53 @@ var math = /*#__PURE__*/Object.freeze({
|
|
|
8352
8393
|
TRUNC: TRUNC
|
|
8353
8394
|
});
|
|
8354
8395
|
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8396
|
+
function assertSameNumberOfElements(...args) {
|
|
8397
|
+
const dims = args[0].length;
|
|
8398
|
+
args.forEach((arg, i) => assert(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
|
|
8399
|
+
}
|
|
8400
|
+
|
|
8401
|
+
function filterAndFlatData(dataY, dataX) {
|
|
8402
|
+
const _flatDataY = [];
|
|
8403
|
+
const _flatDataX = [];
|
|
8359
8404
|
let lenY = 0;
|
|
8360
8405
|
let lenX = 0;
|
|
8361
8406
|
visitAny([dataY], (y) => {
|
|
8362
|
-
|
|
8407
|
+
_flatDataY.push(y);
|
|
8363
8408
|
lenY += 1;
|
|
8364
8409
|
});
|
|
8365
8410
|
visitAny([dataX], (x) => {
|
|
8366
|
-
|
|
8411
|
+
_flatDataX.push(x);
|
|
8367
8412
|
lenX += 1;
|
|
8368
8413
|
});
|
|
8369
|
-
assert(() => lenY === lenX, _t("[[FUNCTION_NAME]] has mismatched argument count %s vs %s.", lenY
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
let sumX = 0;
|
|
8414
|
+
assert(() => lenY === lenX, _t("[[FUNCTION_NAME]] has mismatched argument count %s vs %s.", lenY, lenX));
|
|
8415
|
+
const flatDataX = [];
|
|
8416
|
+
const flatDataY = [];
|
|
8373
8417
|
for (let i = 0; i < lenY; i++) {
|
|
8374
|
-
const valueY =
|
|
8375
|
-
const valueX =
|
|
8418
|
+
const valueY = _flatDataY[i];
|
|
8419
|
+
const valueX = _flatDataX[i];
|
|
8376
8420
|
if (typeof valueY === "number" && typeof valueX === "number") {
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
sumX += valueX;
|
|
8421
|
+
flatDataY.push(valueY);
|
|
8422
|
+
flatDataX.push(valueX);
|
|
8380
8423
|
}
|
|
8381
8424
|
}
|
|
8425
|
+
return { flatDataX, flatDataY };
|
|
8426
|
+
}
|
|
8427
|
+
// Note: dataY and dataX may not have the same dimension
|
|
8428
|
+
function covariance(dataY, dataX, isSample) {
|
|
8429
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
8430
|
+
const count = flatDataY.length;
|
|
8382
8431
|
assert(() => count !== 0 && (!isSample || count !== 1), _t(`Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error.`));
|
|
8432
|
+
let sumY = 0;
|
|
8433
|
+
let sumX = 0;
|
|
8434
|
+
for (let i = 0; i < count; i++) {
|
|
8435
|
+
sumY += flatDataY[i];
|
|
8436
|
+
sumX += flatDataX[i];
|
|
8437
|
+
}
|
|
8383
8438
|
const averageY = sumY / count;
|
|
8384
8439
|
const averageX = sumX / count;
|
|
8385
8440
|
let acc = 0;
|
|
8386
|
-
for (let i = 0; i <
|
|
8387
|
-
|
|
8388
|
-
const valueX = flatDataX[i];
|
|
8389
|
-
if (typeof valueY === "number" && typeof valueX === "number") {
|
|
8390
|
-
acc += (valueY - averageY) * (valueX - averageX);
|
|
8391
|
-
}
|
|
8441
|
+
for (let i = 0; i < count; i++) {
|
|
8442
|
+
acc += (flatDataY[i] - averageY) * (flatDataX[i] - averageX);
|
|
8392
8443
|
}
|
|
8393
8444
|
return acc / (count - (isSample ? 1 : 0));
|
|
8394
8445
|
}
|
|
@@ -8425,6 +8476,161 @@ function centile(data, percent, isInclusive, locale) {
|
|
|
8425
8476
|
}
|
|
8426
8477
|
return percentile(sortedArray, _percent, isInclusive);
|
|
8427
8478
|
}
|
|
8479
|
+
function prepareDataForRegression(X, Y, newX) {
|
|
8480
|
+
const _X = X.length ? X : [range(1, Y.flat().length + 1)];
|
|
8481
|
+
const nVar = _X.length;
|
|
8482
|
+
let _newX = newX.length ? newX : _X;
|
|
8483
|
+
_newX = _newX.length === nVar ? transposeMatrix(_newX) : _newX;
|
|
8484
|
+
return { _X, _newX };
|
|
8485
|
+
}
|
|
8486
|
+
/*
|
|
8487
|
+
* This function performs a linear regression on the data set. It returns an array with two elements.
|
|
8488
|
+
* The first element is the slope, and the second element is the intercept.
|
|
8489
|
+
* The linear regression line is: y = slope*x + intercept
|
|
8490
|
+
* The function use the least squares method to find the best fit for the data set :
|
|
8491
|
+
* see https://www.mathsisfun.com/data/least-squares-regression.html
|
|
8492
|
+
* https://www.statology.org/standard-error-of-estimate/
|
|
8493
|
+
* https://agronomy4future.org/?p=16670
|
|
8494
|
+
* https://vitalflux.com/interpreting-f-statistics-in-linear-regression-formula-examples/
|
|
8495
|
+
* https://web.ist.utl.pt/~ist11038/compute/errtheory/,regression/regrthroughorigin.pdf
|
|
8496
|
+
*/
|
|
8497
|
+
function fullLinearRegression(X, Y, computeIntercept = true, verbose = false) {
|
|
8498
|
+
const y = Y.flat();
|
|
8499
|
+
const n = y.length;
|
|
8500
|
+
let { _X } = prepareDataForRegression(X, Y, []);
|
|
8501
|
+
_X = _X.length === n ? transposeMatrix(_X) : _X.slice();
|
|
8502
|
+
assertSameNumberOfElements(_X[0], y);
|
|
8503
|
+
const nVar = _X.length;
|
|
8504
|
+
const nDeg = n - nVar - (computeIntercept ? 1 : 0);
|
|
8505
|
+
const yMatrix = [y];
|
|
8506
|
+
const xMatrix = transposeMatrix(_X.reverse());
|
|
8507
|
+
let avgX = [];
|
|
8508
|
+
for (let i = 0; i < nVar; i++) {
|
|
8509
|
+
avgX.push(0);
|
|
8510
|
+
if (computeIntercept) {
|
|
8511
|
+
for (const xij of _X[i]) {
|
|
8512
|
+
avgX[i] += xij;
|
|
8513
|
+
}
|
|
8514
|
+
avgX[i] /= n;
|
|
8515
|
+
}
|
|
8516
|
+
}
|
|
8517
|
+
let avgY = 0;
|
|
8518
|
+
if (computeIntercept) {
|
|
8519
|
+
for (const yi of y) {
|
|
8520
|
+
avgY += yi;
|
|
8521
|
+
}
|
|
8522
|
+
avgY /= n;
|
|
8523
|
+
}
|
|
8524
|
+
const redX = xMatrix.map((row) => row.map((value, i) => value - avgX[i]));
|
|
8525
|
+
if (computeIntercept) {
|
|
8526
|
+
xMatrix.forEach((row) => row.push(1));
|
|
8527
|
+
}
|
|
8528
|
+
const coeffs = getLMSCoefficients(xMatrix, yMatrix);
|
|
8529
|
+
if (!computeIntercept) {
|
|
8530
|
+
coeffs.push([0]);
|
|
8531
|
+
}
|
|
8532
|
+
if (!verbose) {
|
|
8533
|
+
return coeffs;
|
|
8534
|
+
}
|
|
8535
|
+
const dot1 = multiplyMatrices(redX, transposeMatrix(redX));
|
|
8536
|
+
const { inverted: dotInv } = invertMatrix(dot1);
|
|
8537
|
+
if (dotInv === undefined) {
|
|
8538
|
+
throw new Error(_t("Matrix is not invertible"));
|
|
8539
|
+
}
|
|
8540
|
+
let SSE = 0, SSR = 0;
|
|
8541
|
+
for (let i = 0; i < n; i++) {
|
|
8542
|
+
const yi = y[i] - avgY;
|
|
8543
|
+
let temp = 0;
|
|
8544
|
+
for (let j = 0; j < nVar; j++) {
|
|
8545
|
+
const xi = redX[i][j];
|
|
8546
|
+
temp += xi * coeffs[j][0];
|
|
8547
|
+
}
|
|
8548
|
+
const ei = yi - temp;
|
|
8549
|
+
SSE += ei * ei;
|
|
8550
|
+
SSR += temp * temp;
|
|
8551
|
+
}
|
|
8552
|
+
const RMSE = Math.sqrt(SSE / nDeg);
|
|
8553
|
+
const r2 = SSR / (SSR + SSE);
|
|
8554
|
+
const f_stat = SSR / nVar / (SSE / nDeg);
|
|
8555
|
+
const deltaCoeffs = [];
|
|
8556
|
+
for (let i = 0; i < nVar; i++) {
|
|
8557
|
+
deltaCoeffs.push(RMSE * Math.sqrt(dotInv[i][i]));
|
|
8558
|
+
}
|
|
8559
|
+
if (computeIntercept) {
|
|
8560
|
+
const dot2 = multiplyMatrices(dotInv, [avgX]);
|
|
8561
|
+
const dot3 = multiplyMatrices(transposeMatrix([avgX]), dot2);
|
|
8562
|
+
deltaCoeffs.push(RMSE * Math.sqrt(dot3[0][0] + 1 / y.length));
|
|
8563
|
+
}
|
|
8564
|
+
const returned = [
|
|
8565
|
+
[coeffs[0][0], deltaCoeffs[0], r2, f_stat, SSR],
|
|
8566
|
+
[coeffs[1][0], deltaCoeffs[1], RMSE, nDeg, SSE],
|
|
8567
|
+
];
|
|
8568
|
+
for (let i = 2; i < nVar; i++) {
|
|
8569
|
+
returned.push([coeffs[i][0], deltaCoeffs[i], "", "", ""]);
|
|
8570
|
+
}
|
|
8571
|
+
if (computeIntercept) {
|
|
8572
|
+
returned.push([coeffs[nVar][0], deltaCoeffs[nVar], "", "", ""]);
|
|
8573
|
+
}
|
|
8574
|
+
else {
|
|
8575
|
+
returned.push([0, "", "", "", ""]);
|
|
8576
|
+
}
|
|
8577
|
+
return returned;
|
|
8578
|
+
}
|
|
8579
|
+
/*
|
|
8580
|
+
This function performs a polynomial regression on the data set. It returns the coefficients of
|
|
8581
|
+
the polynomial function that best fits the data set.
|
|
8582
|
+
The polynomial function is: y = c0 + c1*x + c2*x^2 + ... + cn*x^n, where n is the order (degree)
|
|
8583
|
+
of the polynomial. The returned coefficients are then in the form: [c0, c1, c2, ..., cn]
|
|
8584
|
+
The function is based on the method of least squares :
|
|
8585
|
+
see: https://mathworld.wolfram.com/LeastSquaresFittingPolynomial.html
|
|
8586
|
+
*/
|
|
8587
|
+
function polynomialRegression(flatY, flatX, order, intercept) {
|
|
8588
|
+
assertSameNumberOfElements(flatX, flatY);
|
|
8589
|
+
assert(() => order >= 1, _t(`Function [[FUNCTION_NAME]] A regression of order less than 1 cannot be possible.`));
|
|
8590
|
+
const yMatrix = [flatY];
|
|
8591
|
+
const xMatrix = flatX.map((x) => range(0, order).map((i) => Math.pow(x, order - i)));
|
|
8592
|
+
if (intercept) {
|
|
8593
|
+
xMatrix.forEach((row) => row.push(1));
|
|
8594
|
+
}
|
|
8595
|
+
const coeffs = getLMSCoefficients(xMatrix, yMatrix);
|
|
8596
|
+
if (!intercept) {
|
|
8597
|
+
coeffs.push([0]);
|
|
8598
|
+
}
|
|
8599
|
+
return coeffs;
|
|
8600
|
+
}
|
|
8601
|
+
function getLMSCoefficients(xMatrix, yMatrix) {
|
|
8602
|
+
const xMatrixT = transposeMatrix(xMatrix);
|
|
8603
|
+
const dot1 = multiplyMatrices(xMatrix, xMatrixT);
|
|
8604
|
+
const { inverted: dotInv } = invertMatrix(dot1);
|
|
8605
|
+
if (dotInv === undefined) {
|
|
8606
|
+
throw new Error(_t("Matrix is not invertible"));
|
|
8607
|
+
}
|
|
8608
|
+
const dot2 = multiplyMatrices(xMatrix, yMatrix);
|
|
8609
|
+
return transposeMatrix(multiplyMatrices(dotInv, dot2));
|
|
8610
|
+
}
|
|
8611
|
+
function evaluatePolynomial(coeffs, x, order) {
|
|
8612
|
+
return coeffs.reduce((acc, coeff, i) => acc + coeff * Math.pow(x, order - i), 0);
|
|
8613
|
+
}
|
|
8614
|
+
function expM(M) {
|
|
8615
|
+
return M.map((col) => col.map((cell) => Math.exp(cell)));
|
|
8616
|
+
}
|
|
8617
|
+
function logM(M) {
|
|
8618
|
+
return M.map((col) => col.map((cell) => Math.log(cell)));
|
|
8619
|
+
}
|
|
8620
|
+
function predictLinearValues(Y, X, newX, computeIntercept) {
|
|
8621
|
+
const { _X, _newX } = prepareDataForRegression(X, Y, newX);
|
|
8622
|
+
const coeffs = fullLinearRegression(_X, Y, computeIntercept, false);
|
|
8623
|
+
const nVar = coeffs.length - 1;
|
|
8624
|
+
const newY = _newX.map((col) => {
|
|
8625
|
+
let value = 0;
|
|
8626
|
+
for (let i = 0; i < nVar; i++) {
|
|
8627
|
+
value += coeffs[i][0] * col[nVar - i - 1];
|
|
8628
|
+
}
|
|
8629
|
+
value += coeffs[nVar][0];
|
|
8630
|
+
return [value];
|
|
8631
|
+
});
|
|
8632
|
+
return newY.length === newX.length ? newY : transposeMatrix(newY);
|
|
8633
|
+
}
|
|
8428
8634
|
// -----------------------------------------------------------------------------
|
|
8429
8635
|
// AVEDEV
|
|
8430
8636
|
// -----------------------------------------------------------------------------
|
|
@@ -8709,6 +8915,56 @@ const COVARIANCE_S = {
|
|
|
8709
8915
|
isExported: true,
|
|
8710
8916
|
};
|
|
8711
8917
|
// -----------------------------------------------------------------------------
|
|
8918
|
+
// FORECAST
|
|
8919
|
+
// -----------------------------------------------------------------------------
|
|
8920
|
+
const FORECAST = {
|
|
8921
|
+
description: _t("Calculates the expected y-value for a specified x based on a linear regression of a dataset."),
|
|
8922
|
+
args: [
|
|
8923
|
+
arg("x (number, range<number>)", _t("The value(s) on the x-axis to forecast.")),
|
|
8924
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
8925
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
8926
|
+
],
|
|
8927
|
+
returns: ["NUMBER"],
|
|
8928
|
+
compute: function (x, dataY, dataX) {
|
|
8929
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
8930
|
+
return predictLinearValues([flatDataY], [flatDataX], matrixMap(toMatrix(x), (value) => toNumber(value, this.locale)), true);
|
|
8931
|
+
},
|
|
8932
|
+
isExported: true,
|
|
8933
|
+
};
|
|
8934
|
+
// -----------------------------------------------------------------------------
|
|
8935
|
+
// GROWTH
|
|
8936
|
+
// -----------------------------------------------------------------------------
|
|
8937
|
+
const GROWTH = {
|
|
8938
|
+
description: _t("Fits points to exponential growth trend."),
|
|
8939
|
+
args: [
|
|
8940
|
+
arg("known_data_y (range<number>)", _t("The array or range containing dependent (y) values that are already known, used to curve fit an ideal exponential growth curve.")),
|
|
8941
|
+
arg("known_data_x (range<number>, default={1;2;3;...})", _t("The values of the independent variable(s) corresponding with known_data_y.")),
|
|
8942
|
+
arg("new_data_x (any, range, default=known_data_x)", _t("The data points to return the y values for on the ideal curve fit.")),
|
|
8943
|
+
arg("b (boolean, default=TRUE)", _t("Given a general exponential form of y = b*m^x for a curve fit, calculates b if TRUE or forces b to be 1 and only calculates the m values if FALSE.")),
|
|
8944
|
+
],
|
|
8945
|
+
returns: ["NUMBER"],
|
|
8946
|
+
compute: function (knownDataY, knownDataX = [], newDataX = [], b = true) {
|
|
8947
|
+
return expM(predictLinearValues(logM(tryCastAsNumberMatrix(knownDataY, "the first argument (known_data_y)")), tryCastAsNumberMatrix(knownDataX, "the second argument (known_data_x)"), tryCastAsNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b)));
|
|
8948
|
+
},
|
|
8949
|
+
};
|
|
8950
|
+
// -----------------------------------------------------------------------------
|
|
8951
|
+
// INTERCEPT
|
|
8952
|
+
// -----------------------------------------------------------------------------
|
|
8953
|
+
const INTERCEPT = {
|
|
8954
|
+
description: _t("Compute the intercept of the linear regression."),
|
|
8955
|
+
args: [
|
|
8956
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
8957
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
8958
|
+
],
|
|
8959
|
+
returns: ["NUMBER"],
|
|
8960
|
+
compute: function (dataY, dataX) {
|
|
8961
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
8962
|
+
const [[], [intercept]] = fullLinearRegression([flatDataX], [flatDataY]);
|
|
8963
|
+
return intercept;
|
|
8964
|
+
},
|
|
8965
|
+
isExported: true,
|
|
8966
|
+
};
|
|
8967
|
+
// -----------------------------------------------------------------------------
|
|
8712
8968
|
// LARGE
|
|
8713
8969
|
// -----------------------------------------------------------------------------
|
|
8714
8970
|
const LARGE = {
|
|
@@ -8736,12 +8992,94 @@ const LARGE = {
|
|
|
8736
8992
|
});
|
|
8737
8993
|
const result = largests.shift();
|
|
8738
8994
|
assert(() => result !== undefined, _t(`[[FUNCTION_NAME]] has no valid input data.`));
|
|
8739
|
-
assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n
|
|
8995
|
+
assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
|
|
8740
8996
|
return result;
|
|
8741
8997
|
},
|
|
8742
8998
|
isExported: true,
|
|
8743
8999
|
};
|
|
8744
9000
|
// -----------------------------------------------------------------------------
|
|
9001
|
+
// LINEST
|
|
9002
|
+
// -----------------------------------------------------------------------------
|
|
9003
|
+
const LINEST = {
|
|
9004
|
+
description: _t("Compute the intercept of the linear regression."),
|
|
9005
|
+
args: [
|
|
9006
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9007
|
+
arg("data_x (range<number>, default={1;2;3;...})", _t("The range representing the array or matrix of independent data.")),
|
|
9008
|
+
arg("calculate_b (boolean, default=TRUE)", _t("A flag specifying wheter to compute the slope or not")),
|
|
9009
|
+
arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
|
|
9010
|
+
],
|
|
9011
|
+
returns: ["NUMBER"],
|
|
9012
|
+
compute: function (dataY, dataX = [], calculateB = true, verbose = false) {
|
|
9013
|
+
return fullLinearRegression(tryCastAsNumberMatrix(dataX, "the first argument (data_y)"), tryCastAsNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
|
|
9014
|
+
},
|
|
9015
|
+
isExported: true,
|
|
9016
|
+
};
|
|
9017
|
+
// -----------------------------------------------------------------------------
|
|
9018
|
+
// LOGEST
|
|
9019
|
+
// -----------------------------------------------------------------------------
|
|
9020
|
+
const LOGEST = {
|
|
9021
|
+
description: _t("Compute the intercept of the linear regression."),
|
|
9022
|
+
args: [
|
|
9023
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9024
|
+
arg("data_x (range<number>, optional, default={1;2;3;...})", _t("The range representing the array or matrix of independent data.")),
|
|
9025
|
+
arg("calculate_b (boolean, default=TRUE)", _t("A flag specifying wheter to compute the slope or not")),
|
|
9026
|
+
arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
|
|
9027
|
+
],
|
|
9028
|
+
returns: ["NUMBER"],
|
|
9029
|
+
compute: function (dataY, dataX = [], calculateB = true, verbose = false) {
|
|
9030
|
+
const coeffs = fullLinearRegression(tryCastAsNumberMatrix(dataX, "the second argument (data_x)"), logM(tryCastAsNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
|
|
9031
|
+
for (let i = 0; i < coeffs.length; i++) {
|
|
9032
|
+
coeffs[i][0] = Math.exp(coeffs[i][0]);
|
|
9033
|
+
}
|
|
9034
|
+
return coeffs;
|
|
9035
|
+
},
|
|
9036
|
+
isExported: true,
|
|
9037
|
+
};
|
|
9038
|
+
// -----------------------------------------------------------------------------
|
|
9039
|
+
// MATTHEWS
|
|
9040
|
+
// -----------------------------------------------------------------------------
|
|
9041
|
+
const MATTHEWS = {
|
|
9042
|
+
description: _t("Compute the Matthews correlation coefficient of a dataset."),
|
|
9043
|
+
args: [
|
|
9044
|
+
arg("data_x (range)", _t("The range representing the array or matrix of observed data.")),
|
|
9045
|
+
arg("data_y (range)", _t("The range representing the array or matrix of predicted data.")),
|
|
9046
|
+
],
|
|
9047
|
+
returns: ["NUMBER"],
|
|
9048
|
+
compute: function (dataX, dataY) {
|
|
9049
|
+
const flatX = dataX.flat();
|
|
9050
|
+
const flatY = dataY.flat();
|
|
9051
|
+
assertSameNumberOfElements(flatX, flatY);
|
|
9052
|
+
if (flatX.length === 0) {
|
|
9053
|
+
throw new Error(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
|
|
9054
|
+
}
|
|
9055
|
+
const n = flatX.length;
|
|
9056
|
+
let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
|
|
9057
|
+
for (let i = 0; i < n; ++i) {
|
|
9058
|
+
const isTrue1 = toBoolean(flatX[i]);
|
|
9059
|
+
const isTrue2 = toBoolean(flatY[i]);
|
|
9060
|
+
if (isTrue1 === isTrue2) {
|
|
9061
|
+
if (isTrue1) {
|
|
9062
|
+
trueP++;
|
|
9063
|
+
}
|
|
9064
|
+
else {
|
|
9065
|
+
trueN++;
|
|
9066
|
+
}
|
|
9067
|
+
}
|
|
9068
|
+
else {
|
|
9069
|
+
if (isTrue1) {
|
|
9070
|
+
falseN++;
|
|
9071
|
+
}
|
|
9072
|
+
else {
|
|
9073
|
+
falseP++;
|
|
9074
|
+
}
|
|
9075
|
+
}
|
|
9076
|
+
}
|
|
9077
|
+
return ((trueP * trueN - falseP * falseN) /
|
|
9078
|
+
Math.sqrt((trueP + falseP) * (trueP + falseN) * (trueN + falseP) * (trueN + falseN)));
|
|
9079
|
+
},
|
|
9080
|
+
isExported: false,
|
|
9081
|
+
};
|
|
9082
|
+
// -----------------------------------------------------------------------------
|
|
8745
9083
|
// MAX
|
|
8746
9084
|
// -----------------------------------------------------------------------------
|
|
8747
9085
|
const MAX = {
|
|
@@ -8896,6 +9234,41 @@ const MINIFS = {
|
|
|
8896
9234
|
isExported: true,
|
|
8897
9235
|
};
|
|
8898
9236
|
// -----------------------------------------------------------------------------
|
|
9237
|
+
// PEARSON
|
|
9238
|
+
// -----------------------------------------------------------------------------
|
|
9239
|
+
const PEARSON = {
|
|
9240
|
+
description: _t("Compute the Pearson product-moment correlation coefficient of a dataset."),
|
|
9241
|
+
args: [
|
|
9242
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9243
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9244
|
+
],
|
|
9245
|
+
returns: ["NUMBER"],
|
|
9246
|
+
compute: function (dataY, dataX) {
|
|
9247
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataX, dataY);
|
|
9248
|
+
if (flatDataX.length === 0) {
|
|
9249
|
+
throw new Error(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
|
|
9250
|
+
}
|
|
9251
|
+
if (flatDataX.length < 2) {
|
|
9252
|
+
throw new Error(_t("[[FUNCTION_NAME]] needs at least two values for both parameters"));
|
|
9253
|
+
}
|
|
9254
|
+
const n = flatDataX.length;
|
|
9255
|
+
let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0, sumYY = 0;
|
|
9256
|
+
for (let i = 0; i < n; i++) {
|
|
9257
|
+
const xij = flatDataX[i];
|
|
9258
|
+
const yij = flatDataY[i];
|
|
9259
|
+
sumX += xij;
|
|
9260
|
+
sumY += yij;
|
|
9261
|
+
sumXY += xij * yij;
|
|
9262
|
+
sumXX += xij * xij;
|
|
9263
|
+
sumYY += yij * yij;
|
|
9264
|
+
}
|
|
9265
|
+
return ((n * sumXY - sumX * sumY) / Math.sqrt((n * sumXX - sumX * sumX) * (n * sumYY - sumY * sumY)));
|
|
9266
|
+
},
|
|
9267
|
+
isExported: true,
|
|
9268
|
+
};
|
|
9269
|
+
// In GSheet, CORREL is just an alias to PEARSON
|
|
9270
|
+
const CORREL = PEARSON;
|
|
9271
|
+
// -----------------------------------------------------------------------------
|
|
8899
9272
|
// PERCENTILE
|
|
8900
9273
|
// -----------------------------------------------------------------------------
|
|
8901
9274
|
const PERCENTILE = {
|
|
@@ -8950,6 +9323,45 @@ const PERCENTILE_INC = {
|
|
|
8950
9323
|
isExported: true,
|
|
8951
9324
|
};
|
|
8952
9325
|
// -----------------------------------------------------------------------------
|
|
9326
|
+
// POLYFIT
|
|
9327
|
+
// -----------------------------------------------------------------------------
|
|
9328
|
+
const POLYFIT_COEFFS = {
|
|
9329
|
+
description: _t("Compute the coefficients of polynomial regression of the dataset."),
|
|
9330
|
+
args: [
|
|
9331
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9332
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9333
|
+
arg("order (number)", _t("The order of the polynomial to fit the data, between 1 and 6.")),
|
|
9334
|
+
arg("intercept (boolean, default=TRUE)", _t("A flag specifying whether to compute the intercept or not.")),
|
|
9335
|
+
],
|
|
9336
|
+
returns: ["RANGE<NUMBER>"],
|
|
9337
|
+
compute: function (dataY, dataX, order, intercept = true) {
|
|
9338
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
9339
|
+
return polynomialRegression(flatDataY, flatDataX, toNumber(order, this.locale), toBoolean(intercept));
|
|
9340
|
+
},
|
|
9341
|
+
isExported: false,
|
|
9342
|
+
};
|
|
9343
|
+
// -----------------------------------------------------------------------------
|
|
9344
|
+
// POLYFIT.FORECAST
|
|
9345
|
+
// -----------------------------------------------------------------------------
|
|
9346
|
+
const POLYFIT_FORECAST = {
|
|
9347
|
+
description: _t("Predict value by computing a polynomial regression of the dataset."),
|
|
9348
|
+
args: [
|
|
9349
|
+
arg("x (number, range<number>)", _t("The value(s) on the x-axis to forecast.")),
|
|
9350
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9351
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9352
|
+
arg("order (number)", _t("The order of the polynomial to fit the data, between 1 and 6.")),
|
|
9353
|
+
arg("intercept (boolean, default=TRUE)", _t("A flag specifying whether to compute the intercept or not.")),
|
|
9354
|
+
],
|
|
9355
|
+
returns: ["NUMBER"],
|
|
9356
|
+
compute: function (x, dataY, dataX, order, intercept = true) {
|
|
9357
|
+
const _order = toNumber(order, this.locale);
|
|
9358
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
9359
|
+
const coeffs = polynomialRegression(flatDataY, flatDataX, _order, toBoolean(intercept)).flat();
|
|
9360
|
+
return matrixMap(toMatrix(x), (xij) => evaluatePolynomial(coeffs, toNumber(xij, this.locale), _order));
|
|
9361
|
+
},
|
|
9362
|
+
isExported: false,
|
|
9363
|
+
};
|
|
9364
|
+
// -----------------------------------------------------------------------------
|
|
8953
9365
|
// QUARTILE
|
|
8954
9366
|
// -----------------------------------------------------------------------------
|
|
8955
9367
|
const QUARTILE = {
|
|
@@ -9006,6 +9418,39 @@ const QUARTILE_INC = {
|
|
|
9006
9418
|
isExported: true,
|
|
9007
9419
|
};
|
|
9008
9420
|
// -----------------------------------------------------------------------------
|
|
9421
|
+
// RSQ
|
|
9422
|
+
// -----------------------------------------------------------------------------
|
|
9423
|
+
const RSQ = {
|
|
9424
|
+
description: _t("Compute the square of r, the Pearson product-moment correlation coefficient of a dataset."),
|
|
9425
|
+
args: [
|
|
9426
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9427
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9428
|
+
],
|
|
9429
|
+
returns: ["NUMBER"],
|
|
9430
|
+
compute: function (dataY, dataX) {
|
|
9431
|
+
const pearson = PEARSON.compute.bind(this);
|
|
9432
|
+
return Math.pow(pearson(dataX, dataY), 2.0);
|
|
9433
|
+
},
|
|
9434
|
+
isExported: true,
|
|
9435
|
+
};
|
|
9436
|
+
// -----------------------------------------------------------------------------
|
|
9437
|
+
// SLOPE
|
|
9438
|
+
// -----------------------------------------------------------------------------
|
|
9439
|
+
const SLOPE = {
|
|
9440
|
+
description: _t("Compute the slope of the linear regression."),
|
|
9441
|
+
args: [
|
|
9442
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9443
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9444
|
+
],
|
|
9445
|
+
returns: ["NUMBER"],
|
|
9446
|
+
compute: function (dataY, dataX) {
|
|
9447
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
9448
|
+
const [[slope]] = fullLinearRegression([flatDataX], [flatDataY]);
|
|
9449
|
+
return slope;
|
|
9450
|
+
},
|
|
9451
|
+
isExported: true,
|
|
9452
|
+
};
|
|
9453
|
+
// -----------------------------------------------------------------------------
|
|
9009
9454
|
// SMALL
|
|
9010
9455
|
// -----------------------------------------------------------------------------
|
|
9011
9456
|
const SMALL = {
|
|
@@ -9033,12 +9478,39 @@ const SMALL = {
|
|
|
9033
9478
|
});
|
|
9034
9479
|
const result = largests.pop();
|
|
9035
9480
|
assert(() => result !== undefined, _t(`[[FUNCTION_NAME]] has no valid input data.`));
|
|
9036
|
-
assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n
|
|
9481
|
+
assert(() => count >= _n, _t("Function [[FUNCTION_NAME]] parameter 2 value (%s) is out of range.", _n));
|
|
9037
9482
|
return result;
|
|
9038
9483
|
},
|
|
9039
9484
|
isExported: true,
|
|
9040
9485
|
};
|
|
9041
9486
|
// -----------------------------------------------------------------------------
|
|
9487
|
+
// SPEARMAN
|
|
9488
|
+
// -----------------------------------------------------------------------------
|
|
9489
|
+
const SPEARMAN = {
|
|
9490
|
+
description: _t("Compute the Spearman rank correlation coefficient of a dataset."),
|
|
9491
|
+
args: [
|
|
9492
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9493
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9494
|
+
],
|
|
9495
|
+
returns: ["NUMBER"],
|
|
9496
|
+
compute: function (dataX, dataY) {
|
|
9497
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
9498
|
+
const n = flatDataX.length;
|
|
9499
|
+
const order = flatDataX.map((e, i) => [e, flatDataY[i]]);
|
|
9500
|
+
order.sort((a, b) => a[0] - b[0]);
|
|
9501
|
+
for (let i = 0; i < n; ++i) {
|
|
9502
|
+
order[i][0] = i;
|
|
9503
|
+
}
|
|
9504
|
+
order.sort((a, b) => a[1] - b[1]);
|
|
9505
|
+
let sum = 0.0;
|
|
9506
|
+
for (let i = 0; i < n; ++i) {
|
|
9507
|
+
sum += (order[i][0] - i) ** 2;
|
|
9508
|
+
}
|
|
9509
|
+
return 1 - (6 * sum) / (n ** 3 - n);
|
|
9510
|
+
},
|
|
9511
|
+
isExported: false,
|
|
9512
|
+
};
|
|
9513
|
+
// -----------------------------------------------------------------------------
|
|
9042
9514
|
// STDEV
|
|
9043
9515
|
// -----------------------------------------------------------------------------
|
|
9044
9516
|
const STDEV = {
|
|
@@ -9129,6 +9601,39 @@ const STDEVPA = {
|
|
|
9129
9601
|
isExported: true,
|
|
9130
9602
|
};
|
|
9131
9603
|
// -----------------------------------------------------------------------------
|
|
9604
|
+
// STEYX
|
|
9605
|
+
// -----------------------------------------------------------------------------
|
|
9606
|
+
const STEYX = {
|
|
9607
|
+
description: _t("Calculates the standard error of the predicted y-value for each x in the regression of a dataset."),
|
|
9608
|
+
args: [
|
|
9609
|
+
arg("data_y (range<number>)", _t("The range representing the array or matrix of dependent data.")),
|
|
9610
|
+
arg("data_x (range<number>)", _t("The range representing the array or matrix of independent data.")),
|
|
9611
|
+
],
|
|
9612
|
+
returns: ["NUMBER"],
|
|
9613
|
+
compute: function (dataY, dataX) {
|
|
9614
|
+
const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
|
|
9615
|
+
const data = fullLinearRegression([flatDataX], [flatDataY], true, true);
|
|
9616
|
+
return data[1][2];
|
|
9617
|
+
},
|
|
9618
|
+
isExported: true,
|
|
9619
|
+
};
|
|
9620
|
+
// -----------------------------------------------------------------------------
|
|
9621
|
+
// TREND
|
|
9622
|
+
// -----------------------------------------------------------------------------
|
|
9623
|
+
const TREND = {
|
|
9624
|
+
description: _t("Fits points to linear trend derived via least-squares."),
|
|
9625
|
+
args: [
|
|
9626
|
+
arg("known_data_y (number, range<number>)", _t("The array or range containing dependent (y) values that are already known, used to curve fit an ideal linear trend.")),
|
|
9627
|
+
arg("known_data_x (number, range<number>, optional, default={1;2;3;...})", _t("The values of the independent variable(s) corresponding with known_data_y.")),
|
|
9628
|
+
arg("new_data_x (number, range<number>, optional, default=known_data_x)", _t("The data points to return the y values for on the ideal curve fit.")),
|
|
9629
|
+
arg("b (boolean, optional, default=TRUE)", _t("Given a general linear form of y = m*x+b for a curve fit, calculates b if TRUE or forces b to be 0 and only calculates the m values if FALSE, i.e. forces the curve fit to pass through the origin.")),
|
|
9630
|
+
],
|
|
9631
|
+
returns: ["NUMBER"],
|
|
9632
|
+
compute: function (knownDataY, knownDataX = [], newDataX = [], b = true) {
|
|
9633
|
+
return predictLinearValues(tryCastAsNumberMatrix(knownDataY, "the first argument (known_data_y)"), tryCastAsNumberMatrix(knownDataX, "the second argument (known_data_x)"), tryCastAsNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b));
|
|
9634
|
+
},
|
|
9635
|
+
};
|
|
9636
|
+
// -----------------------------------------------------------------------------
|
|
9132
9637
|
// VAR
|
|
9133
9638
|
// -----------------------------------------------------------------------------
|
|
9134
9639
|
const VAR = {
|
|
@@ -9227,12 +9732,19 @@ var statistical = /*#__PURE__*/Object.freeze({
|
|
|
9227
9732
|
AVERAGEIF: AVERAGEIF,
|
|
9228
9733
|
AVERAGEIFS: AVERAGEIFS,
|
|
9229
9734
|
AVERAGE_WEIGHTED: AVERAGE_WEIGHTED,
|
|
9735
|
+
CORREL: CORREL,
|
|
9230
9736
|
COUNT: COUNT,
|
|
9231
9737
|
COUNTA: COUNTA,
|
|
9232
9738
|
COVAR: COVAR,
|
|
9233
9739
|
COVARIANCE_P: COVARIANCE_P,
|
|
9234
9740
|
COVARIANCE_S: COVARIANCE_S,
|
|
9741
|
+
FORECAST: FORECAST,
|
|
9742
|
+
GROWTH: GROWTH,
|
|
9743
|
+
INTERCEPT: INTERCEPT,
|
|
9235
9744
|
LARGE: LARGE,
|
|
9745
|
+
LINEST: LINEST,
|
|
9746
|
+
LOGEST: LOGEST,
|
|
9747
|
+
MATTHEWS: MATTHEWS,
|
|
9236
9748
|
MAX: MAX,
|
|
9237
9749
|
MAXA: MAXA,
|
|
9238
9750
|
MAXIFS: MAXIFS,
|
|
@@ -9240,19 +9752,27 @@ var statistical = /*#__PURE__*/Object.freeze({
|
|
|
9240
9752
|
MIN: MIN,
|
|
9241
9753
|
MINA: MINA,
|
|
9242
9754
|
MINIFS: MINIFS,
|
|
9755
|
+
PEARSON: PEARSON,
|
|
9243
9756
|
PERCENTILE: PERCENTILE,
|
|
9244
9757
|
PERCENTILE_EXC: PERCENTILE_EXC,
|
|
9245
9758
|
PERCENTILE_INC: PERCENTILE_INC,
|
|
9759
|
+
POLYFIT_COEFFS: POLYFIT_COEFFS,
|
|
9760
|
+
POLYFIT_FORECAST: POLYFIT_FORECAST,
|
|
9246
9761
|
QUARTILE: QUARTILE,
|
|
9247
9762
|
QUARTILE_EXC: QUARTILE_EXC,
|
|
9248
9763
|
QUARTILE_INC: QUARTILE_INC,
|
|
9764
|
+
RSQ: RSQ,
|
|
9765
|
+
SLOPE: SLOPE,
|
|
9249
9766
|
SMALL: SMALL,
|
|
9767
|
+
SPEARMAN: SPEARMAN,
|
|
9250
9768
|
STDEV: STDEV,
|
|
9251
9769
|
STDEVA: STDEVA,
|
|
9252
9770
|
STDEVP: STDEVP,
|
|
9253
9771
|
STDEVPA: STDEVPA,
|
|
9254
9772
|
STDEV_P: STDEV_P,
|
|
9255
9773
|
STDEV_S: STDEV_S,
|
|
9774
|
+
STEYX: STEYX,
|
|
9775
|
+
TREND: TREND,
|
|
9256
9776
|
VAR: VAR,
|
|
9257
9777
|
VARA: VARA,
|
|
9258
9778
|
VARP: VARP,
|
|
@@ -13887,7 +14407,7 @@ const TRIM = {
|
|
|
13887
14407
|
],
|
|
13888
14408
|
returns: ["STRING"],
|
|
13889
14409
|
compute: function (text) {
|
|
13890
|
-
return toString(text)
|
|
14410
|
+
return trimContent(toString(text));
|
|
13891
14411
|
},
|
|
13892
14412
|
isExported: true,
|
|
13893
14413
|
};
|
|
@@ -19436,6 +19956,12 @@ const removeDuplicates = {
|
|
|
19436
19956
|
env.openSidePanel("RemoveDuplicates", {});
|
|
19437
19957
|
},
|
|
19438
19958
|
};
|
|
19959
|
+
const trimWhitespace = {
|
|
19960
|
+
name: _t("Trim whitespace"),
|
|
19961
|
+
execute: (env) => {
|
|
19962
|
+
env.model.dispatch("TRIM_WHITESPACE");
|
|
19963
|
+
},
|
|
19964
|
+
};
|
|
19439
19965
|
const sortDescending = {
|
|
19440
19966
|
name: _t("Descending (Z ⟶ A)"),
|
|
19441
19967
|
execute: (env) => {
|
|
@@ -20795,6 +21321,10 @@ topbarMenuRegistry
|
|
|
20795
21321
|
.addChild("remove_duplicates", ["data", "data_cleanup"], {
|
|
20796
21322
|
...removeDuplicates,
|
|
20797
21323
|
sequence: 10,
|
|
21324
|
+
})
|
|
21325
|
+
.addChild("trim_whitespace", ["data", "data_cleanup"], {
|
|
21326
|
+
...trimWhitespace,
|
|
21327
|
+
sequence: 20,
|
|
20798
21328
|
})
|
|
20799
21329
|
.addChild("split_to_columns", ["data"], {
|
|
20800
21330
|
...splitToColumns,
|
|
@@ -25970,9 +26500,11 @@ function useCellHovered(env, gridRef, callback) {
|
|
|
25970
26500
|
}
|
|
25971
26501
|
}
|
|
25972
26502
|
function updateMousePosition(e) {
|
|
25973
|
-
|
|
25974
|
-
|
|
25975
|
-
|
|
26503
|
+
if (gridRef.el === e.target) {
|
|
26504
|
+
x = e.offsetX;
|
|
26505
|
+
y = e.offsetY;
|
|
26506
|
+
lastMoved = Date.now();
|
|
26507
|
+
}
|
|
25976
26508
|
}
|
|
25977
26509
|
function recompute() {
|
|
25978
26510
|
const { col, row } = getPosition();
|
|
@@ -32454,8 +32986,9 @@ class CellPlugin extends CorePlugin {
|
|
|
32454
32986
|
for (const position of positions) {
|
|
32455
32987
|
const cell = this.getters.getCell(position);
|
|
32456
32988
|
const xc = toXC(position.col, position.row);
|
|
32989
|
+
const style = this.removeDefaultStyleValues(cell.style);
|
|
32457
32990
|
cells[xc] = {
|
|
32458
|
-
style:
|
|
32991
|
+
style: Object.keys(style).length ? getItemId(style, styles) : undefined,
|
|
32459
32992
|
format: cell.format ? getItemId(cell.format, formats) : undefined,
|
|
32460
32993
|
content: cell.content || undefined,
|
|
32461
32994
|
};
|
|
@@ -32474,6 +33007,15 @@ class CellPlugin extends CorePlugin {
|
|
|
32474
33007
|
exportForExcel(data) {
|
|
32475
33008
|
this.export(data);
|
|
32476
33009
|
}
|
|
33010
|
+
removeDefaultStyleValues(style) {
|
|
33011
|
+
const cleanedStyle = { ...style };
|
|
33012
|
+
for (const [property, defaultValue] of Object.entries(DEFAULT_STYLE)) {
|
|
33013
|
+
if (cleanedStyle[property] === defaultValue) {
|
|
33014
|
+
delete cleanedStyle[property];
|
|
33015
|
+
}
|
|
33016
|
+
}
|
|
33017
|
+
return cleanedStyle;
|
|
33018
|
+
}
|
|
32477
33019
|
// ---------------------------------------------------------------------------
|
|
32478
33020
|
// GETTERS
|
|
32479
33021
|
// ---------------------------------------------------------------------------
|
|
@@ -39812,6 +40354,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
39812
40354
|
cells;
|
|
39813
40355
|
copiedTables;
|
|
39814
40356
|
zones;
|
|
40357
|
+
uuidGenerator = new UuidGenerator();
|
|
39815
40358
|
constructor(zones, operation, getters, dispatch, selection) {
|
|
39816
40359
|
super(operation, getters, dispatch, selection);
|
|
39817
40360
|
if (!zones.length) {
|
|
@@ -40264,8 +40807,9 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
40264
40807
|
this.adaptCFRules(origin.sheetId, cf, [xc], toRemoveRange);
|
|
40265
40808
|
}
|
|
40266
40809
|
else {
|
|
40267
|
-
this.adaptCFRules(target.sheetId, cf, [xc], []);
|
|
40268
40810
|
this.adaptCFRules(origin.sheetId, cf, [], toRemoveRange);
|
|
40811
|
+
const cfToCopyTo = this.getCFToCopyTo(target.sheetId, cf);
|
|
40812
|
+
this.adaptCFRules(target.sheetId, cfToCopyTo, [xc], []);
|
|
40269
40813
|
}
|
|
40270
40814
|
}
|
|
40271
40815
|
}
|
|
@@ -40289,6 +40833,12 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
|
|
|
40289
40833
|
sheetId,
|
|
40290
40834
|
});
|
|
40291
40835
|
}
|
|
40836
|
+
getCFToCopyTo(targetSheetId, originCF) {
|
|
40837
|
+
const cfInTarget = this.getters
|
|
40838
|
+
.getConditionalFormats(targetSheetId)
|
|
40839
|
+
.find((cf) => cf.stopIfTrue === originCF.stopIfTrue && deepEquals(cf.rule, originCF.rule));
|
|
40840
|
+
return cfInTarget ? cfInTarget : { ...originCF, id: this.uuidGenerator.uuidv4(), ranges: [] };
|
|
40841
|
+
}
|
|
40292
40842
|
}
|
|
40293
40843
|
|
|
40294
40844
|
class DataCleanupPlugin extends UIPlugin {
|
|
@@ -40307,6 +40857,9 @@ class DataCleanupPlugin extends UIPlugin {
|
|
|
40307
40857
|
case "REMOVE_DUPLICATES":
|
|
40308
40858
|
this.removeDuplicates(cmd.columns, cmd.hasHeader);
|
|
40309
40859
|
break;
|
|
40860
|
+
case "TRIM_WHITESPACE":
|
|
40861
|
+
this.trimWhitespace();
|
|
40862
|
+
break;
|
|
40310
40863
|
}
|
|
40311
40864
|
}
|
|
40312
40865
|
// ---------------------------------------------------------------------------
|
|
@@ -40427,6 +40980,35 @@ class DataCleanupPlugin extends UIPlugin {
|
|
|
40427
40980
|
}
|
|
40428
40981
|
return "Success" /* CommandResult.Success */;
|
|
40429
40982
|
}
|
|
40983
|
+
trimWhitespace() {
|
|
40984
|
+
const zones = this.getters.getSelectedZones();
|
|
40985
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
40986
|
+
let count = 0;
|
|
40987
|
+
for (const { col, row } of zones.map(positions).flat()) {
|
|
40988
|
+
const cell = this.getters.getCell({ col, row, sheetId });
|
|
40989
|
+
if (!cell) {
|
|
40990
|
+
continue;
|
|
40991
|
+
}
|
|
40992
|
+
const trimmedContent = trimContent(cell.content);
|
|
40993
|
+
if (trimmedContent !== cell.content) {
|
|
40994
|
+
count += 1;
|
|
40995
|
+
this.dispatch("UPDATE_CELL", {
|
|
40996
|
+
sheetId,
|
|
40997
|
+
col,
|
|
40998
|
+
row,
|
|
40999
|
+
content: trimmedContent,
|
|
41000
|
+
});
|
|
41001
|
+
}
|
|
41002
|
+
}
|
|
41003
|
+
const text = count
|
|
41004
|
+
? _t("Trimmed whitespace from %s cells.", count)
|
|
41005
|
+
: _t("No selected cells had whitespace trimmed.");
|
|
41006
|
+
this.ui.notifyUI({
|
|
41007
|
+
type: "info",
|
|
41008
|
+
text: text,
|
|
41009
|
+
sticky: false,
|
|
41010
|
+
});
|
|
41011
|
+
}
|
|
40430
41012
|
}
|
|
40431
41013
|
|
|
40432
41014
|
const BORDER_COLOR = "#8B008B";
|
|
@@ -43225,7 +43807,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
43225
43807
|
allowDispatch(cmd) {
|
|
43226
43808
|
switch (cmd.type) {
|
|
43227
43809
|
case "CUT":
|
|
43228
|
-
const zones =
|
|
43810
|
+
const zones = this.getters.getSelectedZones();
|
|
43229
43811
|
const state = this.getClipboardState(zones, cmd.type);
|
|
43230
43812
|
return state.isCutAllowed(zones);
|
|
43231
43813
|
case "PASTE":
|
|
@@ -43260,7 +43842,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
43260
43842
|
switch (cmd.type) {
|
|
43261
43843
|
case "COPY":
|
|
43262
43844
|
case "CUT":
|
|
43263
|
-
const zones =
|
|
43845
|
+
const zones = this.getters.getSelectedZones();
|
|
43264
43846
|
this.state = this.getClipboardState(zones, cmd.type);
|
|
43265
43847
|
this.status = "visible";
|
|
43266
43848
|
this.originSheetId = this.getters.getActiveSheetId();
|
|
@@ -44924,26 +45506,24 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
44924
45506
|
const isBasedBefore = cmd.base < start;
|
|
44925
45507
|
const deltaCol = isBasedBefore && isCol ? thickness : 0;
|
|
44926
45508
|
const deltaRow = isBasedBefore && !isCol ? thickness : 0;
|
|
44927
|
-
|
|
44928
|
-
|
|
44929
|
-
|
|
44930
|
-
|
|
44931
|
-
|
|
44932
|
-
|
|
44933
|
-
|
|
44934
|
-
|
|
44935
|
-
|
|
44936
|
-
|
|
44937
|
-
|
|
44938
|
-
|
|
44939
|
-
|
|
44940
|
-
|
|
44941
|
-
|
|
44942
|
-
|
|
44943
|
-
|
|
44944
|
-
|
|
44945
|
-
],
|
|
44946
|
-
});
|
|
45509
|
+
const target = [
|
|
45510
|
+
{
|
|
45511
|
+
left: isCol ? start + deltaCol : 0,
|
|
45512
|
+
right: isCol ? end + deltaCol : this.getters.getNumberCols(cmd.sheetId) - 1,
|
|
45513
|
+
top: !isCol ? start + deltaRow : 0,
|
|
45514
|
+
bottom: !isCol ? end + deltaRow : this.getters.getNumberRows(cmd.sheetId) - 1,
|
|
45515
|
+
},
|
|
45516
|
+
];
|
|
45517
|
+
const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
|
|
45518
|
+
const pasteTarget = [
|
|
45519
|
+
{
|
|
45520
|
+
left: isCol ? cmd.base : 0,
|
|
45521
|
+
right: isCol ? cmd.base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
|
|
45522
|
+
top: !isCol ? cmd.base : 0,
|
|
45523
|
+
bottom: !isCol ? cmd.base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
|
|
45524
|
+
},
|
|
45525
|
+
];
|
|
45526
|
+
state.paste(pasteTarget, { selectTarget: true });
|
|
44947
45527
|
const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
|
|
44948
45528
|
let currentIndex = cmd.base;
|
|
44949
45529
|
for (const element of toRemove) {
|
|
@@ -52260,6 +52840,6 @@ const constants = {
|
|
|
52260
52840
|
export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
|
|
52261
52841
|
|
|
52262
52842
|
|
|
52263
|
-
__info__.version =
|
|
52264
|
-
__info__.date =
|
|
52265
|
-
__info__.hash =
|
|
52843
|
+
__info__.version = "16.5.0-alpha.8";
|
|
52844
|
+
__info__.date = "2023-09-21T15:59:10.959Z";
|
|
52845
|
+
__info__.hash = "0b7527d";
|