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