svelteplot 0.4.6 → 0.4.7
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/helpers/scales.d.ts +1 -1
- package/dist/helpers/scales.js +21 -10
- package/dist/marks/helpers/Regression.svelte +1 -1
- package/dist/regression/exponential.d.ts +17 -0
- package/dist/regression/exponential.js +56 -0
- package/dist/regression/index.d.ts +10 -0
- package/dist/regression/index.js +10 -0
- package/dist/regression/linear.d.ts +17 -0
- package/dist/regression/linear.js +59 -0
- package/dist/regression/loess.d.ts +14 -0
- package/dist/regression/loess.js +122 -0
- package/dist/regression/logarithmic.d.ts +20 -0
- package/dist/regression/logarithmic.js +60 -0
- package/dist/regression/polynomial.d.ts +21 -0
- package/dist/regression/polynomial.js +160 -0
- package/dist/regression/power.d.ts +18 -0
- package/dist/regression/power.js +56 -0
- package/dist/regression/quadratic.d.ts +19 -0
- package/dist/regression/quadratic.js +70 -0
- package/dist/regression/types.d.ts +4 -0
- package/dist/regression/types.js +1 -0
- package/dist/regression/utils/determination.d.ts +6 -0
- package/dist/regression/utils/determination.js +16 -0
- package/dist/regression/utils/geometry.d.ts +9 -0
- package/dist/regression/utils/geometry.js +12 -0
- package/dist/regression/utils/interpose.d.ts +6 -0
- package/dist/regression/utils/interpose.js +37 -0
- package/dist/regression/utils/median.d.ts +4 -0
- package/dist/regression/utils/median.js +8 -0
- package/dist/regression/utils/ols.d.ts +6 -0
- package/dist/regression/utils/ols.js +9 -0
- package/dist/regression/utils/points.d.ts +11 -0
- package/dist/regression/utils/points.js +45 -0
- package/dist/types/scale.d.ts +6 -0
- package/package.json +1 -2
package/dist/helpers/scales.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export declare function createScale<T extends ScaleOptions>(name: ScaleName, sca
|
|
|
28
28
|
* Infer a scale type based on the scale name, the data values mapped to it and
|
|
29
29
|
* the mark types that are bound to the scale
|
|
30
30
|
*/
|
|
31
|
-
export declare function inferScaleType(name: ScaleName, dataValues: RawValue[], markTypes: Set<MarkType>): ScaleType;
|
|
31
|
+
export declare function inferScaleType(name: ScaleName, dataValues: RawValue[], markTypes: Set<MarkType>, scaleOptions?: Partial<ScaleOptions>): ScaleType;
|
|
32
32
|
/**
|
|
33
33
|
* Mark channels can explicitly or implicitly be exempt from being
|
|
34
34
|
* mapped to a scale, so everywhere where values are being mapped to
|
package/dist/helpers/scales.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { extent, ascending } from 'd3-array';
|
|
2
|
-
import { isColorOrNull, isDateOrNull, isNumberOrNull, isNumberOrNullOrNaN, isStringOrNull } from './typeChecks.js';
|
|
2
|
+
import { isColorOrNull, isDate, isDateOrNull, isNumberOrNull, isNumberOrNullOrNaN, isStringOrNull } from './typeChecks.js';
|
|
3
3
|
import { CHANNEL_SCALE, VALID_SCALE_TYPES } from '../constants.js';
|
|
4
4
|
import { isSymbolOrNull } from './typeChecks.js';
|
|
5
5
|
import { resolveProp, toChannelOption } from './resolve.js';
|
|
@@ -146,7 +146,7 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
|
|
|
146
146
|
// construct domain from data values
|
|
147
147
|
const valueArr = [...dataValues.values(), ...(scaleOptions.domain || [])].filter((d) => d != null);
|
|
148
148
|
const type = scaleOptions.type === 'auto'
|
|
149
|
-
? inferScaleType(name, valueArr, markTypes)
|
|
149
|
+
? inferScaleType(name, valueArr, markTypes, scaleOptions)
|
|
150
150
|
: scaleOptions.type;
|
|
151
151
|
if (VALID_SCALE_TYPES[name] && !VALID_SCALE_TYPES[name].has(type)) {
|
|
152
152
|
throw new Error(`Invalid scale type ${type} for scale
|
|
@@ -217,11 +217,15 @@ function domainFromInterval(domain, interval, name) {
|
|
|
217
217
|
const out = interval_.range(lo, interval_.offset(hi));
|
|
218
218
|
return name === 'y' ? out.toReversed() : out;
|
|
219
219
|
}
|
|
220
|
+
const markTypesWithBandDefault = {
|
|
221
|
+
x: new Set(['barY', 'cell', 'tickY']),
|
|
222
|
+
y: new Set(['barX', 'cell', 'tickX'])
|
|
223
|
+
};
|
|
220
224
|
/**
|
|
221
225
|
* Infer a scale type based on the scale name, the data values mapped to it and
|
|
222
226
|
* the mark types that are bound to the scale
|
|
223
227
|
*/
|
|
224
|
-
export function inferScaleType(name, dataValues, markTypes) {
|
|
228
|
+
export function inferScaleType(name, dataValues, markTypes, scaleOptions = {}) {
|
|
225
229
|
if (name === 'color') {
|
|
226
230
|
if (!dataValues.length)
|
|
227
231
|
return 'ordinal';
|
|
@@ -235,15 +239,22 @@ export function inferScaleType(name, dataValues, markTypes) {
|
|
|
235
239
|
}
|
|
236
240
|
if (name === 'symbol')
|
|
237
241
|
return 'ordinal';
|
|
238
|
-
// for positional scales, try to pick a scale that's required by the mark types
|
|
239
242
|
if (name === 'x' || name === 'y') {
|
|
240
|
-
if
|
|
241
|
-
|
|
242
|
-
return '
|
|
243
|
-
if (
|
|
244
|
-
(
|
|
245
|
-
|
|
243
|
+
// if for a positional scale we may infer the scale type from the scale options
|
|
244
|
+
if (scaleOptions.nice || scaleOptions.zero)
|
|
245
|
+
return 'linear';
|
|
246
|
+
if (scaleOptions.domain && scaleOptions.domain.length === 2) {
|
|
247
|
+
if (scaleOptions.domain.every(Number.isFinite))
|
|
248
|
+
return 'linear';
|
|
249
|
+
if (scaleOptions.domain.every(isDate))
|
|
250
|
+
return 'time';
|
|
251
|
+
}
|
|
246
252
|
}
|
|
253
|
+
// for positional scales, try to pick a scale that's required by the mark types
|
|
254
|
+
if (name === 'y' && Array.from(markTypes).some((d) => markTypesWithBandDefault.y.has(d)))
|
|
255
|
+
return 'band';
|
|
256
|
+
if (name === 'x' && Array.from(markTypes).some((d) => markTypesWithBandDefault.x.has(d)))
|
|
257
|
+
return 'band';
|
|
247
258
|
if (!dataValues.length)
|
|
248
259
|
return 'linear';
|
|
249
260
|
if (dataValues.length === 1)
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
regressionLog,
|
|
44
44
|
regressionPow,
|
|
45
45
|
regressionLoess
|
|
46
|
-
} from '
|
|
46
|
+
} from '../../regression/index.js';
|
|
47
47
|
import { resolveChannel } from '../../helpers/resolve.js';
|
|
48
48
|
import { confidenceInterval } from '../../helpers/math.js';
|
|
49
49
|
import callWithProps from '../../helpers/callWithProps.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PredictFunction, Domain, DataPoint, Accessor } from './types.js';
|
|
2
|
+
export type ExponentialOutput = [DataPoint, DataPoint] & {
|
|
3
|
+
a: number;
|
|
4
|
+
b: number;
|
|
5
|
+
predict: PredictFunction;
|
|
6
|
+
rSquared: number;
|
|
7
|
+
};
|
|
8
|
+
export interface ExponentialRegression<T> {
|
|
9
|
+
(data: T[]): ExponentialOutput;
|
|
10
|
+
domain(): Domain;
|
|
11
|
+
domain(arr: Domain): this;
|
|
12
|
+
x(): Accessor<T>;
|
|
13
|
+
x(fn: Accessor<T>): this;
|
|
14
|
+
y(): Accessor<T>;
|
|
15
|
+
y(fn: Accessor<T>): this;
|
|
16
|
+
}
|
|
17
|
+
export default function exponential<T = DataPoint>(): ExponentialRegression<T>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
import { determination } from './utils/determination.js';
|
|
5
|
+
import { interpose } from './utils/interpose.js';
|
|
6
|
+
import { ols } from './utils/ols.js';
|
|
7
|
+
import { visitPoints } from './utils/points.js';
|
|
8
|
+
export default function exponential() {
|
|
9
|
+
let y = (d) => d[1], x = (d) => d[0], domain;
|
|
10
|
+
const exponentialRegression = function (data) {
|
|
11
|
+
let n = 0, Y = 0, YL = 0, XY = 0, XYL = 0, X2Y = 0, xmin = domain ? +domain[0] : Infinity, xmax = domain ? +domain[1] : -Infinity;
|
|
12
|
+
visitPoints(data, x, y, (dx, dy) => {
|
|
13
|
+
const ly = Math.log(dy), xy = dx * dy;
|
|
14
|
+
++n;
|
|
15
|
+
Y += (dy - Y) / n;
|
|
16
|
+
XY += (xy - XY) / n;
|
|
17
|
+
X2Y += (dx * xy - X2Y) / n;
|
|
18
|
+
YL += (dy * ly - YL) / n;
|
|
19
|
+
XYL += (xy * ly - XYL) / n;
|
|
20
|
+
if (!domain) {
|
|
21
|
+
if (dx < xmin)
|
|
22
|
+
xmin = dx;
|
|
23
|
+
if (dx > xmax)
|
|
24
|
+
xmax = dx;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
let [a, b] = ols(XY / Y, YL / Y, XYL / Y, X2Y / Y);
|
|
28
|
+
a = Math.exp(a);
|
|
29
|
+
const fn = (xx) => a * Math.exp(b * xx);
|
|
30
|
+
const out = interpose(xmin, xmax, fn);
|
|
31
|
+
out.a = a;
|
|
32
|
+
out.b = b;
|
|
33
|
+
out.predict = fn;
|
|
34
|
+
out.rSquared = determination(data, x, y, Y, fn);
|
|
35
|
+
return out;
|
|
36
|
+
};
|
|
37
|
+
exponentialRegression.domain = function (arr) {
|
|
38
|
+
if (!arguments.length)
|
|
39
|
+
return domain;
|
|
40
|
+
domain = arr;
|
|
41
|
+
return exponentialRegression;
|
|
42
|
+
};
|
|
43
|
+
exponentialRegression.x = function (fn) {
|
|
44
|
+
if (!arguments.length)
|
|
45
|
+
return x;
|
|
46
|
+
x = fn;
|
|
47
|
+
return exponentialRegression;
|
|
48
|
+
};
|
|
49
|
+
exponentialRegression.y = function (fn) {
|
|
50
|
+
if (!arguments.length)
|
|
51
|
+
return y;
|
|
52
|
+
y = fn;
|
|
53
|
+
return exponentialRegression;
|
|
54
|
+
};
|
|
55
|
+
return exponentialRegression;
|
|
56
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
export { default as regressionLinear } from './linear.js';
|
|
5
|
+
export { default as regressionPoly } from './polynomial.js';
|
|
6
|
+
export { default as regressionExp } from './exponential.js';
|
|
7
|
+
export { default as regressionLog } from './logarithmic.js';
|
|
8
|
+
export { default as regressionPow } from './power.js';
|
|
9
|
+
export { default as regressionQuad } from './quadratic.js';
|
|
10
|
+
export { default as regressionLoess } from './loess.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
export { default as regressionLinear } from './linear.js';
|
|
5
|
+
export { default as regressionPoly } from './polynomial.js';
|
|
6
|
+
export { default as regressionExp } from './exponential.js';
|
|
7
|
+
export { default as regressionLog } from './logarithmic.js';
|
|
8
|
+
export { default as regressionPow } from './power.js';
|
|
9
|
+
export { default as regressionQuad } from './quadratic.js';
|
|
10
|
+
export { default as regressionLoess } from './loess.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PredictFunction, Accessor, DataPoint, Domain } from './types.js';
|
|
2
|
+
export type LinearOutput = [DataPoint, DataPoint] & {
|
|
3
|
+
a: number;
|
|
4
|
+
b: number;
|
|
5
|
+
predict: PredictFunction;
|
|
6
|
+
rSquared: number;
|
|
7
|
+
};
|
|
8
|
+
export interface LinearRegression<T> {
|
|
9
|
+
(data: T[]): LinearOutput;
|
|
10
|
+
domain(): Domain;
|
|
11
|
+
domain(arr: Domain): this;
|
|
12
|
+
x(): Accessor<T>;
|
|
13
|
+
x(fn: Accessor<T>): this;
|
|
14
|
+
y(): Accessor<T>;
|
|
15
|
+
y(fn: Accessor<T>): this;
|
|
16
|
+
}
|
|
17
|
+
export default function linear<T = DataPoint>(): LinearRegression<T>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
import { determination } from './utils/determination.js';
|
|
5
|
+
import { ols } from './utils/ols.js';
|
|
6
|
+
import { visitPoints } from './utils/points.js';
|
|
7
|
+
export default function linear() {
|
|
8
|
+
let x = (d) => d[0], y = (d) => d[1], domain;
|
|
9
|
+
const linearRegression = function (data) {
|
|
10
|
+
let n = 0, X = 0, // sum of x
|
|
11
|
+
Y = 0, // sum of y
|
|
12
|
+
XY = 0, // sum of x*y
|
|
13
|
+
X2 = 0, // sum of x*x
|
|
14
|
+
xmin = domain ? +domain[0] : Infinity, xmax = domain ? +domain[1] : -Infinity;
|
|
15
|
+
visitPoints(data, x, y, (dx, dy) => {
|
|
16
|
+
++n;
|
|
17
|
+
X += (dx - X) / n;
|
|
18
|
+
Y += (dy - Y) / n;
|
|
19
|
+
XY += (dx * dy - XY) / n;
|
|
20
|
+
X2 += (dx * dx - X2) / n;
|
|
21
|
+
if (!domain) {
|
|
22
|
+
if (dx < xmin)
|
|
23
|
+
xmin = dx;
|
|
24
|
+
if (dx > xmax)
|
|
25
|
+
xmax = dx;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const [intercept, slope] = ols(X, Y, XY, X2);
|
|
29
|
+
const fn = (xx) => slope * xx + intercept;
|
|
30
|
+
const out = [
|
|
31
|
+
[xmin, fn(xmin)],
|
|
32
|
+
[xmax, fn(xmax)]
|
|
33
|
+
];
|
|
34
|
+
out.a = slope;
|
|
35
|
+
out.b = intercept;
|
|
36
|
+
out.predict = fn;
|
|
37
|
+
out.rSquared = determination(data, x, y, Y, fn);
|
|
38
|
+
return out;
|
|
39
|
+
};
|
|
40
|
+
linearRegression.domain = function (arr) {
|
|
41
|
+
if (!arguments.length)
|
|
42
|
+
return domain;
|
|
43
|
+
domain = arr;
|
|
44
|
+
return linearRegression;
|
|
45
|
+
};
|
|
46
|
+
linearRegression.x = function (fn) {
|
|
47
|
+
if (!arguments.length)
|
|
48
|
+
return x;
|
|
49
|
+
x = fn;
|
|
50
|
+
return linearRegression;
|
|
51
|
+
};
|
|
52
|
+
linearRegression.y = function (fn) {
|
|
53
|
+
if (!arguments.length)
|
|
54
|
+
return y;
|
|
55
|
+
y = fn;
|
|
56
|
+
return linearRegression;
|
|
57
|
+
};
|
|
58
|
+
return linearRegression;
|
|
59
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
import type { Accessor, DataPoint } from './types.js';
|
|
5
|
+
export interface LoessRegression<T> {
|
|
6
|
+
(data: T[]): DataPoint[];
|
|
7
|
+
bandwidth(): number;
|
|
8
|
+
bandwidth(bw: number): this;
|
|
9
|
+
x(): Accessor<T>;
|
|
10
|
+
x(fn: Accessor<T>): this;
|
|
11
|
+
y(): Accessor<T>;
|
|
12
|
+
y(fn: Accessor<T>): this;
|
|
13
|
+
}
|
|
14
|
+
export default function loess<T = DataPoint>(): LoessRegression<T>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
// Adapted from science.js by Jason Davies
|
|
5
|
+
// License: https://github.com/jasondavies/science.js/blob/master/LICENSE
|
|
6
|
+
// Source: https://github.com/jasondavies/science.js/blob/master/src/stats/loess.js
|
|
7
|
+
// Adapted from vega-statistics by Jeffrey Heer
|
|
8
|
+
// License: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/LICENSE
|
|
9
|
+
// Source: https://github.com/vega/vega/blob/f21cb8792b4e0cbe2b1a3fd44b0f5db370dbaadb/packages/vega-statistics/src/regression/loess.js
|
|
10
|
+
import { median } from './utils/median.js';
|
|
11
|
+
import { ols } from './utils/ols.js';
|
|
12
|
+
import { points } from './utils/points.js';
|
|
13
|
+
const maxiters = 2, epsilon = 1e-12;
|
|
14
|
+
export default function loess() {
|
|
15
|
+
let x = (d) => d[0], y = (d) => d[1], bandwidth = 0.3;
|
|
16
|
+
const loessRegression = function loessRegression(data) {
|
|
17
|
+
const [xv, yv, ux, uy] = points(data, (dd) => x(dd), (dd) => y(dd), true);
|
|
18
|
+
const n = xv.length;
|
|
19
|
+
const bw = Math.max(2, ~~(bandwidth * n)); // # of nearest neighbors
|
|
20
|
+
const yhat = new Float64Array(n);
|
|
21
|
+
const residuals = new Float64Array(n);
|
|
22
|
+
const robustWeights = new Float64Array(n).fill(1);
|
|
23
|
+
for (let iter = -1; ++iter <= maxiters;) {
|
|
24
|
+
const interval = [0, bw - 1];
|
|
25
|
+
for (let i = 0; i < n; ++i) {
|
|
26
|
+
const dx = xv[i];
|
|
27
|
+
const i0 = interval[0];
|
|
28
|
+
const i1 = interval[1];
|
|
29
|
+
const edge = dx - xv[i0] > xv[i1] - dx ? i0 : i1;
|
|
30
|
+
let W = 0, X = 0, Y = 0, XY = 0, X2 = 0;
|
|
31
|
+
const denom = 1 / Math.abs(xv[edge] - dx || 1);
|
|
32
|
+
for (let k = i0; k <= i1; ++k) {
|
|
33
|
+
const xk = xv[k];
|
|
34
|
+
const yk = yv[k];
|
|
35
|
+
const w = tricube(Math.abs(dx - xk) * denom) * robustWeights[k];
|
|
36
|
+
const xkw = xk * w;
|
|
37
|
+
W += w;
|
|
38
|
+
X += xkw;
|
|
39
|
+
Y += yk * w;
|
|
40
|
+
XY += yk * xkw;
|
|
41
|
+
X2 += xk * xkw;
|
|
42
|
+
}
|
|
43
|
+
// Linear regression fit
|
|
44
|
+
const [a, b] = ols(X / W, Y / W, XY / W, X2 / W);
|
|
45
|
+
yhat[i] = a + b * dx;
|
|
46
|
+
residuals[i] = Math.abs(yv[i] - yhat[i]);
|
|
47
|
+
updateInterval(xv, i + 1, interval);
|
|
48
|
+
}
|
|
49
|
+
if (iter === maxiters) {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
const medianResidual = median(residuals);
|
|
53
|
+
if (Math.abs(medianResidual) < epsilon)
|
|
54
|
+
break;
|
|
55
|
+
for (let i = 0, arg, w; i < n; ++i) {
|
|
56
|
+
arg = residuals[i] / (6 * medianResidual);
|
|
57
|
+
// Default to epsilon (rather than zero) for large deviations
|
|
58
|
+
// Keeping weights tiny but non-zero prevents singularites
|
|
59
|
+
robustWeights[i] = arg >= 1 ? epsilon : (w = 1 - arg * arg) * w;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return output(xv, yhat, ux, uy);
|
|
63
|
+
};
|
|
64
|
+
loessRegression.bandwidth = function (bw) {
|
|
65
|
+
if (!arguments.length)
|
|
66
|
+
return bandwidth;
|
|
67
|
+
bandwidth = bw;
|
|
68
|
+
return loessRegression;
|
|
69
|
+
};
|
|
70
|
+
loessRegression.x = function (fn) {
|
|
71
|
+
if (!arguments.length)
|
|
72
|
+
return x;
|
|
73
|
+
x = fn;
|
|
74
|
+
return loessRegression;
|
|
75
|
+
};
|
|
76
|
+
loessRegression.y = function (fn) {
|
|
77
|
+
if (!arguments.length)
|
|
78
|
+
return y;
|
|
79
|
+
y = fn;
|
|
80
|
+
return loessRegression;
|
|
81
|
+
};
|
|
82
|
+
return loessRegression;
|
|
83
|
+
}
|
|
84
|
+
// Weighting kernel for local regression
|
|
85
|
+
function tricube(x) {
|
|
86
|
+
return (x = 1 - x * x * x) * x * x;
|
|
87
|
+
}
|
|
88
|
+
// Advance sliding window interval of nearest neighbors
|
|
89
|
+
function updateInterval(xv, i, interval) {
|
|
90
|
+
let val = xv[i], left = interval[0], right = interval[1] + 1;
|
|
91
|
+
if (right >= xv.length)
|
|
92
|
+
return;
|
|
93
|
+
// Step right if distance to new right edge is <= distance to old left edge
|
|
94
|
+
// Step when distance is equal to ensure movement over duplicate x values
|
|
95
|
+
while (i > left && xv[right] - val <= val - xv[left]) {
|
|
96
|
+
interval[0] = ++left;
|
|
97
|
+
interval[1] = right;
|
|
98
|
+
++right;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Generate smoothed output points
|
|
102
|
+
// Average points with repeated x values
|
|
103
|
+
function output(xv, yhat, ux, uy) {
|
|
104
|
+
const n = xv.length, out = [];
|
|
105
|
+
let i = 0, cnt = 0, prev = [], v;
|
|
106
|
+
for (; i < n; ++i) {
|
|
107
|
+
v = xv[i] + ux;
|
|
108
|
+
if (prev[0] === v) {
|
|
109
|
+
// Average output values via online update
|
|
110
|
+
prev[1] += (yhat[i] - prev[1]) / ++cnt;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Add new output point
|
|
114
|
+
cnt = 0;
|
|
115
|
+
prev[1] += uy;
|
|
116
|
+
prev = [v, yhat[i]];
|
|
117
|
+
out.push(prev);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
prev[1] += uy;
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PredictFunction, DataPoint, Accessor, Domain } from './types.js';
|
|
2
|
+
type LogarithmicOutput = [DataPoint, DataPoint] & {
|
|
3
|
+
a: number;
|
|
4
|
+
b: number;
|
|
5
|
+
predict: PredictFunction;
|
|
6
|
+
rSquared: number;
|
|
7
|
+
};
|
|
8
|
+
export interface LogarithmicRegression<T> {
|
|
9
|
+
(data: T[]): LogarithmicOutput;
|
|
10
|
+
domain(): Domain;
|
|
11
|
+
domain(arr: Domain): this;
|
|
12
|
+
x(): Accessor<T>;
|
|
13
|
+
x(fn: Accessor<T>): this;
|
|
14
|
+
y(): Accessor<T>;
|
|
15
|
+
y(fn: Accessor<T>): this;
|
|
16
|
+
base(): number;
|
|
17
|
+
base(b: number): this;
|
|
18
|
+
}
|
|
19
|
+
export default function logarithmic<T = DataPoint>(): LogarithmicRegression<T>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
import { determination } from './utils/determination.js';
|
|
5
|
+
import { interpose } from './utils/interpose.js';
|
|
6
|
+
import { ols } from './utils/ols.js';
|
|
7
|
+
import { visitPoints } from './utils/points.js';
|
|
8
|
+
export default function logarithmic() {
|
|
9
|
+
let x = (d) => d[0], y = (d) => d[1], base = Math.E, domain;
|
|
10
|
+
const logarithmicRegression = function (data) {
|
|
11
|
+
let n = 0, X = 0, Y = 0, XY = 0, X2 = 0, xmin = domain ? +domain[0] : Infinity, xmax = domain ? +domain[1] : -Infinity, lb = Math.log(base);
|
|
12
|
+
visitPoints(data, x, y, (dx, dy) => {
|
|
13
|
+
const lx = Math.log(dx) / lb;
|
|
14
|
+
++n;
|
|
15
|
+
X += (lx - X) / n;
|
|
16
|
+
Y += (dy - Y) / n;
|
|
17
|
+
XY += (lx * dy - XY) / n;
|
|
18
|
+
X2 += (lx * lx - X2) / n;
|
|
19
|
+
if (!domain) {
|
|
20
|
+
if (dx < xmin)
|
|
21
|
+
xmin = dx;
|
|
22
|
+
if (dx > xmax)
|
|
23
|
+
xmax = dx;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const [intercept, slope] = ols(X, Y, XY, X2);
|
|
27
|
+
const fn = (xx) => (slope * Math.log(xx)) / lb + intercept;
|
|
28
|
+
const out = interpose(xmin, xmax, fn);
|
|
29
|
+
out.a = slope;
|
|
30
|
+
out.b = intercept;
|
|
31
|
+
out.predict = fn;
|
|
32
|
+
out.rSquared = determination(data, x, y, Y, fn);
|
|
33
|
+
return out;
|
|
34
|
+
};
|
|
35
|
+
logarithmicRegression.domain = function (arr) {
|
|
36
|
+
if (!arguments.length)
|
|
37
|
+
return domain;
|
|
38
|
+
domain = arr;
|
|
39
|
+
return logarithmicRegression;
|
|
40
|
+
};
|
|
41
|
+
logarithmicRegression.x = function (fn) {
|
|
42
|
+
if (!arguments.length)
|
|
43
|
+
return x;
|
|
44
|
+
x = fn;
|
|
45
|
+
return logarithmicRegression;
|
|
46
|
+
};
|
|
47
|
+
logarithmicRegression.y = function (fn) {
|
|
48
|
+
if (!arguments.length)
|
|
49
|
+
return y;
|
|
50
|
+
y = fn;
|
|
51
|
+
return logarithmicRegression;
|
|
52
|
+
};
|
|
53
|
+
logarithmicRegression.base = function (b) {
|
|
54
|
+
if (!arguments.length)
|
|
55
|
+
return base;
|
|
56
|
+
base = b;
|
|
57
|
+
return logarithmicRegression;
|
|
58
|
+
};
|
|
59
|
+
return logarithmicRegression;
|
|
60
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
import type { PredictFunction, Accessor, DataPoint, Domain } from './types';
|
|
5
|
+
export type PolynomialOutput = [DataPoint, DataPoint] & {
|
|
6
|
+
coefficients: number[];
|
|
7
|
+
predict: PredictFunction;
|
|
8
|
+
rSquared: number;
|
|
9
|
+
};
|
|
10
|
+
export interface PolynomialRegression<T> {
|
|
11
|
+
(data: T[]): PolynomialOutput;
|
|
12
|
+
domain(): Domain;
|
|
13
|
+
domain(domain?: Domain): this;
|
|
14
|
+
x(): Accessor<T>;
|
|
15
|
+
x(x: Accessor<T>): this;
|
|
16
|
+
y(): Accessor<T>;
|
|
17
|
+
y(y: Accessor<T>): this;
|
|
18
|
+
order(): number;
|
|
19
|
+
order(order: number): this;
|
|
20
|
+
}
|
|
21
|
+
export default function polynomial<T = DataPoint>(): PolynomialRegression<T>;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
// Adapted from regression-js by Tom Alexander
|
|
5
|
+
// Source: https://github.com/Tom-Alexander/regression-js/blob/master/src/regression.js#L246
|
|
6
|
+
// License: https://github.com/Tom-Alexander/regression-js/blob/master/LICENSE
|
|
7
|
+
// ...with ideas from vega-statistics by Jeffrey Heer
|
|
8
|
+
// Source: https://github.com/vega/vega/blob/f21cb8792b4e0cbe2b1a3fd44b0f5db370dbaadb/packages/vega-statistics/src/regression/poly.js
|
|
9
|
+
// License: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/LICENSE
|
|
10
|
+
import { determination } from './utils/determination';
|
|
11
|
+
import { interpose } from './utils/interpose';
|
|
12
|
+
import { points, visitPoints } from './utils/points';
|
|
13
|
+
import linear from './linear';
|
|
14
|
+
import quad from './quadratic';
|
|
15
|
+
export default function polynomial() {
|
|
16
|
+
let x = (d) => d[0], y = (d) => d[1], order = 3, domain;
|
|
17
|
+
const polynomialRegression = function polynomialRegression(data) {
|
|
18
|
+
// Shortcut for lower-order polynomials:
|
|
19
|
+
if (order === 1) {
|
|
20
|
+
const o = linear().x(x).y(y).domain(domain)(data);
|
|
21
|
+
const result = [o[0], o[1]];
|
|
22
|
+
result.coefficients = [o.b, o.a];
|
|
23
|
+
result.predict = o.predict;
|
|
24
|
+
result.rSquared = o.rSquared;
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
if (order === 2) {
|
|
28
|
+
const o = quad().x(x).y(y).domain(domain)(data);
|
|
29
|
+
const result = [o[0], o[1]];
|
|
30
|
+
result.coefficients = [o.c, o.b, o.a];
|
|
31
|
+
result.predict = o.predict;
|
|
32
|
+
result.rSquared = o.rSquared;
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
const [xv, yv, ux, uy] = points(data, x, y);
|
|
36
|
+
const n = xv.length;
|
|
37
|
+
const k = order + 1;
|
|
38
|
+
const lhs = [];
|
|
39
|
+
const rhs = [];
|
|
40
|
+
let Y = 0, n0 = 0, xmin = domain ? +domain[0] : Infinity, xmax = domain ? +domain[1] : -Infinity;
|
|
41
|
+
visitPoints(data, x, y, (dx, dy) => {
|
|
42
|
+
n0++;
|
|
43
|
+
Y += (dy - Y) / n0;
|
|
44
|
+
if (!domain) {
|
|
45
|
+
if (dx < xmin)
|
|
46
|
+
xmin = dx;
|
|
47
|
+
if (dx > xmax)
|
|
48
|
+
xmax = dx;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// Build normal equations
|
|
52
|
+
for (let i = 0; i < k; i++) {
|
|
53
|
+
// LHS
|
|
54
|
+
let v = 0;
|
|
55
|
+
for (let l = 0; l < n; l++) {
|
|
56
|
+
v += Math.pow(xv[l], i) * yv[l];
|
|
57
|
+
}
|
|
58
|
+
lhs.push(v);
|
|
59
|
+
// RHS
|
|
60
|
+
const c = new Float64Array(k);
|
|
61
|
+
for (let j = 0; j < k; j++) {
|
|
62
|
+
let v2 = 0;
|
|
63
|
+
for (let l = 0; l < n; l++) {
|
|
64
|
+
v2 += Math.pow(xv[l], i + j);
|
|
65
|
+
}
|
|
66
|
+
c[j] = v2;
|
|
67
|
+
}
|
|
68
|
+
rhs.push(c);
|
|
69
|
+
}
|
|
70
|
+
rhs.push(new Float64Array(lhs));
|
|
71
|
+
const coef = gaussianElimination(rhs);
|
|
72
|
+
const fn = (xx) => {
|
|
73
|
+
let shifted = xx - ux;
|
|
74
|
+
let val = uy + coef[0];
|
|
75
|
+
for (let i = 1; i < k; i++) {
|
|
76
|
+
val += coef[i] * Math.pow(shifted, i);
|
|
77
|
+
}
|
|
78
|
+
return val;
|
|
79
|
+
};
|
|
80
|
+
const out = interpose(xmin, xmax, fn);
|
|
81
|
+
out.coefficients = uncenter(k, coef, -ux, uy);
|
|
82
|
+
out.predict = fn;
|
|
83
|
+
out.rSquared = determination(data, x, y, Y, fn);
|
|
84
|
+
return out;
|
|
85
|
+
};
|
|
86
|
+
polynomialRegression.domain = function (arr) {
|
|
87
|
+
if (!arguments.length)
|
|
88
|
+
return domain;
|
|
89
|
+
domain = arr;
|
|
90
|
+
return polynomialRegression;
|
|
91
|
+
};
|
|
92
|
+
polynomialRegression.x = function (fn) {
|
|
93
|
+
if (!arguments.length)
|
|
94
|
+
return x;
|
|
95
|
+
x = fn;
|
|
96
|
+
return polynomialRegression;
|
|
97
|
+
};
|
|
98
|
+
polynomialRegression.y = function (fn) {
|
|
99
|
+
if (!arguments.length)
|
|
100
|
+
return y;
|
|
101
|
+
y = fn;
|
|
102
|
+
return polynomialRegression;
|
|
103
|
+
};
|
|
104
|
+
polynomialRegression.order = function (n) {
|
|
105
|
+
if (!arguments.length)
|
|
106
|
+
return order;
|
|
107
|
+
order = n;
|
|
108
|
+
return polynomialRegression;
|
|
109
|
+
};
|
|
110
|
+
return polynomialRegression;
|
|
111
|
+
}
|
|
112
|
+
function uncenter(k, a, x, y) {
|
|
113
|
+
const z = new Array(k).fill(0);
|
|
114
|
+
for (let i = k - 1; i >= 0; --i) {
|
|
115
|
+
let v = a[i];
|
|
116
|
+
z[i] += v;
|
|
117
|
+
let c = 1;
|
|
118
|
+
for (let j = 1; j <= i; ++j) {
|
|
119
|
+
c *= (i + 1 - j) / j; // binomial coefficient
|
|
120
|
+
z[i - j] += v * Math.pow(x, j) * c;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// bias term
|
|
124
|
+
z[0] += y;
|
|
125
|
+
return z;
|
|
126
|
+
}
|
|
127
|
+
// Solve A * x = b using Gaussian elimination
|
|
128
|
+
function gaussianElimination(matrix) {
|
|
129
|
+
const n = matrix.length - 1;
|
|
130
|
+
const coef = new Array(n);
|
|
131
|
+
for (let i = 0; i < n; i++) {
|
|
132
|
+
let r = i;
|
|
133
|
+
// find pivot row
|
|
134
|
+
for (let j = i + 1; j < n; j++) {
|
|
135
|
+
if (Math.abs(matrix[i][j]) > Math.abs(matrix[i][r])) {
|
|
136
|
+
r = j;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// swap columns
|
|
140
|
+
for (let k = i; k < n + 1; k++) {
|
|
141
|
+
const t = matrix[k][i];
|
|
142
|
+
matrix[k][i] = matrix[k][r];
|
|
143
|
+
matrix[k][r] = t;
|
|
144
|
+
}
|
|
145
|
+
// reduce
|
|
146
|
+
for (let j = i + 1; j < n; j++) {
|
|
147
|
+
for (let k = n; k >= i; k--) {
|
|
148
|
+
matrix[k][j] -= (matrix[k][i] * matrix[i][j]) / matrix[i][i];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
for (let j = n - 1; j >= 0; j--) {
|
|
153
|
+
let t = 0;
|
|
154
|
+
for (let k = j + 1; k < n; k++) {
|
|
155
|
+
t += matrix[k][j] * coef[k];
|
|
156
|
+
}
|
|
157
|
+
coef[j] = (matrix[n][j] - t) / matrix[j][j];
|
|
158
|
+
}
|
|
159
|
+
return coef;
|
|
160
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PredictFunction, DataPoint, Accessor, Domain } from './types.js';
|
|
2
|
+
export type PowerOutput = [DataPoint, DataPoint] & {
|
|
3
|
+
a: number;
|
|
4
|
+
b: number;
|
|
5
|
+
predict: PredictFunction;
|
|
6
|
+
rSquared: number;
|
|
7
|
+
};
|
|
8
|
+
interface PowerRegression<T> {
|
|
9
|
+
(data: T[]): PowerOutput;
|
|
10
|
+
domain(): Domain;
|
|
11
|
+
domain(domain?: Domain): this;
|
|
12
|
+
x(): Accessor<T>;
|
|
13
|
+
x(x: Accessor<T>): this;
|
|
14
|
+
y(): Accessor<T>;
|
|
15
|
+
y(y: Accessor<T>): this;
|
|
16
|
+
}
|
|
17
|
+
export default function power<T = DataPoint>(): PowerRegression<T>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
import { determination } from './utils/determination.js';
|
|
5
|
+
import { interpose } from './utils/interpose.js';
|
|
6
|
+
import { ols } from './utils/ols.js';
|
|
7
|
+
import { visitPoints } from './utils/points.js';
|
|
8
|
+
export default function power() {
|
|
9
|
+
let x = (d) => d[0], y = (d) => d[1], domain;
|
|
10
|
+
const powerRegression = function powerRegression(data) {
|
|
11
|
+
let n = 0, X = 0, Y = 0, XY = 0, X2 = 0, YS = 0, xmin = domain ? +domain[0] : Infinity, xmax = domain ? +domain[1] : -Infinity;
|
|
12
|
+
visitPoints(data, x, y, (dx, dy) => {
|
|
13
|
+
const lx = Math.log(dx), ly = Math.log(dy);
|
|
14
|
+
++n;
|
|
15
|
+
X += (lx - X) / n;
|
|
16
|
+
Y += (ly - Y) / n;
|
|
17
|
+
XY += (lx * ly - XY) / n;
|
|
18
|
+
X2 += (lx * lx - X2) / n;
|
|
19
|
+
YS += (dy - YS) / n;
|
|
20
|
+
if (!domain) {
|
|
21
|
+
if (dx < xmin)
|
|
22
|
+
xmin = dx;
|
|
23
|
+
if (dx > xmax)
|
|
24
|
+
xmax = dx;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
let [a, b] = ols(X, Y, XY, X2);
|
|
28
|
+
a = Math.exp(a);
|
|
29
|
+
const fn = (xx) => a * Math.pow(xx, b);
|
|
30
|
+
const out = interpose(xmin, xmax, fn);
|
|
31
|
+
out.a = a;
|
|
32
|
+
out.b = b;
|
|
33
|
+
out.predict = fn;
|
|
34
|
+
out.rSquared = determination(data, x, y, YS, fn);
|
|
35
|
+
return out;
|
|
36
|
+
};
|
|
37
|
+
powerRegression.domain = function (arr) {
|
|
38
|
+
if (!arguments.length)
|
|
39
|
+
return domain;
|
|
40
|
+
domain = arr;
|
|
41
|
+
return powerRegression;
|
|
42
|
+
};
|
|
43
|
+
powerRegression.x = function (fn) {
|
|
44
|
+
if (!arguments.length)
|
|
45
|
+
return x;
|
|
46
|
+
x = fn;
|
|
47
|
+
return powerRegression;
|
|
48
|
+
};
|
|
49
|
+
powerRegression.y = function (fn) {
|
|
50
|
+
if (!arguments.length)
|
|
51
|
+
return y;
|
|
52
|
+
y = fn;
|
|
53
|
+
return powerRegression;
|
|
54
|
+
};
|
|
55
|
+
return powerRegression;
|
|
56
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Accessor, DataPoint, PredictFunction, Domain } from './types.js';
|
|
2
|
+
export type QuadraticOutput = [DataPoint, DataPoint] & {
|
|
3
|
+
a: number;
|
|
4
|
+
b: number;
|
|
5
|
+
c: number;
|
|
6
|
+
predict: PredictFunction;
|
|
7
|
+
rSquared: number;
|
|
8
|
+
};
|
|
9
|
+
interface QuadraticRegression<T> {
|
|
10
|
+
(data: T[]): QuadraticOutput;
|
|
11
|
+
domain(): Domain;
|
|
12
|
+
domain(domain?: Domain): this;
|
|
13
|
+
x(): Accessor<T>;
|
|
14
|
+
x(x: Accessor<T>): this;
|
|
15
|
+
y(): Accessor<T>;
|
|
16
|
+
y(y: Accessor<T>): this;
|
|
17
|
+
}
|
|
18
|
+
export default function quadratic<T = DataPoint>(): QuadraticRegression<T>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/HarryStevens/d3-regression
|
|
3
|
+
*/
|
|
4
|
+
import { determination } from './utils/determination.js';
|
|
5
|
+
import { interpose } from './utils/interpose.js';
|
|
6
|
+
import { points, visitPoints } from './utils/points.js';
|
|
7
|
+
export default function quadratic() {
|
|
8
|
+
let x = (d) => d[0], y = (d) => d[1], domain;
|
|
9
|
+
const quadraticRegression = function quadraticRegression(data) {
|
|
10
|
+
const [xv, yv, ux, uy] = points(data, (dd) => x(dd), (dd) => y(dd));
|
|
11
|
+
const n = xv.length;
|
|
12
|
+
let X2 = 0, X3 = 0, X4 = 0, XY = 0, X2Y = 0, i, dx, dy, x2;
|
|
13
|
+
for (i = 0; i < n;) {
|
|
14
|
+
dx = xv[i];
|
|
15
|
+
dy = yv[i++];
|
|
16
|
+
x2 = dx * dx;
|
|
17
|
+
X2 += (x2 - X2) / i;
|
|
18
|
+
X3 += (x2 * dx - X3) / i;
|
|
19
|
+
X4 += (x2 * x2 - X4) / i;
|
|
20
|
+
XY += (dx * dy - XY) / i;
|
|
21
|
+
X2Y += (x2 * dy - X2Y) / i;
|
|
22
|
+
}
|
|
23
|
+
let Y = 0, n0 = 0, xmin = domain ? +domain[0] : Infinity, xmax = domain ? +domain[1] : -Infinity;
|
|
24
|
+
visitPoints(data, x, y, (dx2, dy2) => {
|
|
25
|
+
n0++;
|
|
26
|
+
Y += (dy2 - Y) / n0;
|
|
27
|
+
if (!domain) {
|
|
28
|
+
if (dx2 < xmin)
|
|
29
|
+
xmin = dx2;
|
|
30
|
+
if (dx2 > xmax)
|
|
31
|
+
xmax = dx2;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const X2X2 = X4 - X2 * X2;
|
|
35
|
+
const d = X2 * X2X2 - X3 * X3;
|
|
36
|
+
const a = (X2Y * X2 - XY * X3) / d;
|
|
37
|
+
const b = (XY * X2X2 - X2Y * X3) / d;
|
|
38
|
+
const c = -a * X2;
|
|
39
|
+
const fn = (xx) => {
|
|
40
|
+
const shifted = xx - ux;
|
|
41
|
+
return a * shifted * shifted + b * shifted + c + uy;
|
|
42
|
+
};
|
|
43
|
+
const out = interpose(xmin, xmax, fn);
|
|
44
|
+
out.a = a;
|
|
45
|
+
out.b = b - 2 * a * ux;
|
|
46
|
+
out.c = c - b * ux + a * ux * ux + uy;
|
|
47
|
+
out.predict = fn;
|
|
48
|
+
out.rSquared = determination(data, x, y, Y, fn);
|
|
49
|
+
return out;
|
|
50
|
+
};
|
|
51
|
+
quadraticRegression.domain = function (arr) {
|
|
52
|
+
if (!arguments.length)
|
|
53
|
+
return domain;
|
|
54
|
+
domain = arr;
|
|
55
|
+
return quadraticRegression;
|
|
56
|
+
};
|
|
57
|
+
quadraticRegression.x = function (fn) {
|
|
58
|
+
if (!arguments.length)
|
|
59
|
+
return x;
|
|
60
|
+
x = fn;
|
|
61
|
+
return quadraticRegression;
|
|
62
|
+
};
|
|
63
|
+
quadraticRegression.y = function (fn) {
|
|
64
|
+
if (!arguments.length)
|
|
65
|
+
return y;
|
|
66
|
+
y = fn;
|
|
67
|
+
return quadraticRegression;
|
|
68
|
+
};
|
|
69
|
+
return quadraticRegression;
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Accessor, PredictFunction } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Given a dataset, x- and y-accessors, the mean center of the y-values (uY),
|
|
4
|
+
* and a prediction function, return the coefficient of determination, R^2.
|
|
5
|
+
*/
|
|
6
|
+
export declare function determination<T>(data: T[], x: Accessor<T>, y: Accessor<T>, uY: number, predict: PredictFunction): number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { visitPoints } from './points';
|
|
2
|
+
/**
|
|
3
|
+
* Given a dataset, x- and y-accessors, the mean center of the y-values (uY),
|
|
4
|
+
* and a prediction function, return the coefficient of determination, R^2.
|
|
5
|
+
*/
|
|
6
|
+
export function determination(data, x, y, uY, predict) {
|
|
7
|
+
let SSE = 0, // Sum of Squared Errors
|
|
8
|
+
SST = 0; // Total Sum of Squares
|
|
9
|
+
visitPoints(data, x, y, (dx, dy) => {
|
|
10
|
+
const sse = dy - predict(dx);
|
|
11
|
+
const sst = dy - uY;
|
|
12
|
+
SSE += sse * sse;
|
|
13
|
+
SST += sst * sst;
|
|
14
|
+
});
|
|
15
|
+
return 1 - SSE / SST;
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DataPoint } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the angle of a line in degrees.
|
|
4
|
+
*/
|
|
5
|
+
export declare function angle(line: [DataPoint, DataPoint]): number;
|
|
6
|
+
/**
|
|
7
|
+
* Returns the midpoint of a line.
|
|
8
|
+
*/
|
|
9
|
+
export declare function midpoint(line: [DataPoint, DataPoint]): DataPoint;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the angle of a line in degrees.
|
|
3
|
+
*/
|
|
4
|
+
export function angle(line) {
|
|
5
|
+
return Math.atan2(line[1][1] - line[0][1], line[1][0] - line[0][0]) * (180 / Math.PI);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Returns the midpoint of a line.
|
|
9
|
+
*/
|
|
10
|
+
export function midpoint(line) {
|
|
11
|
+
return [(line[0][0] + line[1][0]) / 2, (line[0][1] + line[1][1]) / 2];
|
|
12
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PredictFunction, DataPoint } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Given a start point (xmin), an end point (xmax),
|
|
4
|
+
* and a prediction function, returns a smooth line.
|
|
5
|
+
*/
|
|
6
|
+
export declare function interpose(xmin: number, xmax: number, predict: PredictFunction): [DataPoint, DataPoint];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { angle, midpoint } from './geometry';
|
|
2
|
+
/**
|
|
3
|
+
* Given a start point (xmin), an end point (xmax),
|
|
4
|
+
* and a prediction function, returns a smooth line.
|
|
5
|
+
*/
|
|
6
|
+
export function interpose(xmin, xmax, predict) {
|
|
7
|
+
const l = (Math.log(xmax - xmin) * Math.LOG10E + 1) | 0;
|
|
8
|
+
const precision = Math.pow(10, -l / 2 - 1);
|
|
9
|
+
const maxIter = 1e4;
|
|
10
|
+
let points = [px(xmin), px(xmax)];
|
|
11
|
+
let iter = 0;
|
|
12
|
+
while (find(points) && iter < maxIter)
|
|
13
|
+
;
|
|
14
|
+
return points;
|
|
15
|
+
function px(x) {
|
|
16
|
+
return [x, predict(x)];
|
|
17
|
+
}
|
|
18
|
+
function find(points) {
|
|
19
|
+
iter++;
|
|
20
|
+
const n = points.length;
|
|
21
|
+
let found = false;
|
|
22
|
+
for (let i = 0; i < n - 1; i++) {
|
|
23
|
+
const p0 = points[i];
|
|
24
|
+
const p1 = points[i + 1];
|
|
25
|
+
const m = midpoint([p0, p1]);
|
|
26
|
+
const mp = px(m[0]);
|
|
27
|
+
const a0 = angle([p0, m]);
|
|
28
|
+
const a1 = angle([p0, mp]);
|
|
29
|
+
const a = Math.abs(a0 - a1);
|
|
30
|
+
if (a > precision) {
|
|
31
|
+
points.splice(i + 1, 0, mp);
|
|
32
|
+
found = true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return found;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordinary Least Squares from vega-statistics by Jeffrey Heer
|
|
3
|
+
* License: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/LICENSE
|
|
4
|
+
* Source: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/packages/vega-statistics/src/regression/ols.js
|
|
5
|
+
*/
|
|
6
|
+
export declare function ols(uX: number, uY: number, uXY: number, uX2: number): [number, number];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordinary Least Squares from vega-statistics by Jeffrey Heer
|
|
3
|
+
* License: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/LICENSE
|
|
4
|
+
* Source: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/packages/vega-statistics/src/regression/ols.js
|
|
5
|
+
*/
|
|
6
|
+
export function ols(uX, uY, uXY, uX2) {
|
|
7
|
+
const delta = uX2 - uX * uX, slope = Math.abs(delta) < 1e-24 ? 0 : (uXY - uX * uY) / delta, intercept = uY - slope * uX;
|
|
8
|
+
return [intercept, slope];
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Accessor } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Adapted from vega-statistics by Jeffrey Heer
|
|
4
|
+
* License: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/LICENSE
|
|
5
|
+
* Source: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/packages/vega-statistics/src/regression/points.js
|
|
6
|
+
*/
|
|
7
|
+
export declare function points<T>(data: T[], x: Accessor<T>, y: Accessor<T>, sort?: boolean): [Float64Array, Float64Array, number, number];
|
|
8
|
+
/**
|
|
9
|
+
* Iterates over valid data points, invoking a callback for each.
|
|
10
|
+
*/
|
|
11
|
+
export declare function visitPoints<T>(data: T[], x: Accessor<T>, y: Accessor<T>, cb: (dx: number, dy: number, index: number) => void): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from vega-statistics by Jeffrey Heer
|
|
3
|
+
* License: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/LICENSE
|
|
4
|
+
* Source: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/packages/vega-statistics/src/regression/points.js
|
|
5
|
+
*/
|
|
6
|
+
export function points(data, x, y, sort) {
|
|
7
|
+
data = data.filter((d, i) => {
|
|
8
|
+
let u = x(d, i), v = y(d, i);
|
|
9
|
+
return u != null && isFinite(u) && v != null && isFinite(v);
|
|
10
|
+
});
|
|
11
|
+
if (sort) {
|
|
12
|
+
data.sort((a, b) => x(a) - x(b));
|
|
13
|
+
}
|
|
14
|
+
const n = data.length, X = new Float64Array(n), Y = new Float64Array(n);
|
|
15
|
+
// extract values, calculate means
|
|
16
|
+
let ux = 0, uy = 0, xv, yv, d;
|
|
17
|
+
for (let i = 0; i < n;) {
|
|
18
|
+
d = data[i];
|
|
19
|
+
X[i] = xv = +x(d, i, data);
|
|
20
|
+
Y[i] = yv = +y(d, i, data);
|
|
21
|
+
++i;
|
|
22
|
+
ux += (xv - ux) / i;
|
|
23
|
+
uy += (yv - uy) / i;
|
|
24
|
+
}
|
|
25
|
+
// mean center the data
|
|
26
|
+
for (let i = 0; i < n; ++i) {
|
|
27
|
+
X[i] -= ux;
|
|
28
|
+
Y[i] -= uy;
|
|
29
|
+
}
|
|
30
|
+
return [X, Y, ux, uy];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Iterates over valid data points, invoking a callback for each.
|
|
34
|
+
*/
|
|
35
|
+
export function visitPoints(data, x, y, cb) {
|
|
36
|
+
let iterations = 0;
|
|
37
|
+
for (let i = 0; i < data.length; i++) {
|
|
38
|
+
const d = data[i];
|
|
39
|
+
const dx = +x(d, i, data);
|
|
40
|
+
const dy = +y(d, i, data);
|
|
41
|
+
if (dx != null && isFinite(dx) && dy != null && isFinite(dy)) {
|
|
42
|
+
cb(dx, dy, iterations++);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
package/dist/types/scale.d.ts
CHANGED
|
@@ -24,7 +24,13 @@ export type ScaleOptions = {
|
|
|
24
24
|
label?: string | false;
|
|
25
25
|
interval?: string | number;
|
|
26
26
|
clamp: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Extend the domain to nice round numbers (applicable to quantitative scales only)
|
|
29
|
+
*/
|
|
27
30
|
nice: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Include zero in the scale domain (applicable to quantitative scales only)
|
|
33
|
+
*/
|
|
28
34
|
zero: boolean;
|
|
29
35
|
round: boolean;
|
|
30
36
|
percent: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteplot",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Gregor Aisch",
|
|
@@ -104,7 +104,6 @@
|
|
|
104
104
|
"d3-path": "^3.1.0",
|
|
105
105
|
"d3-quadtree": "^3.0.1",
|
|
106
106
|
"d3-random": "^3.0.1",
|
|
107
|
-
"@gka/d3-regression": "^1.3.10-pr48",
|
|
108
107
|
"d3-scale": "^4.0.2",
|
|
109
108
|
"d3-scale-chromatic": "^3.1.0",
|
|
110
109
|
"d3-shape": "^3.2.0",
|