xy-scale 1.4.44 → 1.4.46
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/README.md +2 -1
- package/dist/xy-scale.min.js +1 -1
- package/index.js +2 -1
- package/package.json +1 -1
- package/src/datasets.js +106 -22
- package/src/validators.js +22 -0
- package/src/zscore.js +145 -0
- package/test/test.js +6 -1
package/README.md
CHANGED
|
@@ -29,7 +29,8 @@ Builds supervised-learning datasets and splits them into training and testing ar
|
|
|
29
29
|
#### Parameters
|
|
30
30
|
|
|
31
31
|
- `arrObj` (Array<Object>): Source dataset.
|
|
32
|
-
- `
|
|
32
|
+
- `trainSize` (Number, required).
|
|
33
|
+
- `testSize` (Number, required).
|
|
33
34
|
- `yCallbackFunc` (Function, optional): Builds the output object for each row. Returning `null` or `undefined` skips the row.
|
|
34
35
|
- `xCallbackFunc` (Function, optional): Builds the feature object for each row. Returning `null` or `undefined` skips the row.
|
|
35
36
|
- `validateRows` (Function, optional): Extra row filter executed before the callbacks.
|
package/dist/xy-scale.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var XY_Scale;(()=>{"use strict";var e={d:(r,t)=>{for(var n in t)e.o(t,n)&&!e.o(r,n)&&Object.defineProperty(r,n,{enumerable:!0,get:t[n]})},o:(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},r={};e.r(r),e.d(r,{arrayShuffle:()=>t,
|
|
1
|
+
var XY_Scale;(()=>{"use strict";var e={d:(r,t)=>{for(var n in t)e.o(t,n)&&!e.o(r,n)&&Object.defineProperty(r,n,{enumerable:!0,get:t[n]})},o:(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},r={};e.r(r),e.d(r,{arrayShuffle:()=>t,arrayToTimesteps:()=>h,parseProductionX:()=>w,parseTrainingXY:()=>d,zscore:()=>f});const t=e=>{const r=[...e];for(let e=r.length-1;e>0;e--){const t=Math.floor(Math.random()*(e+1));[r[e],r[t]]=[r[t],r[e]]}return r},n=e=>null!=e&&Number.isFinite(e),o=e=>Number.isInteger(e)&&e>0,a=e=>null!==e&&"object"==typeof e&&!Array.isArray(e)&&Object.keys(e).length>0,i=(e,{min:r=-1/0,max:t=1/0},n)=>{if(!Array.isArray(e))throw new Error(`Invalid property. "${n}" expected an array.`);if(e.length<r)throw new Error(`Invalid property value. Array "${n}" expected at least ${r} items.`);if(e.length>t)throw new Error(`Invalid property value. Array "${n}" expected at max ${t} items.`);return!0},l=e=>{if(!a(e))throw new Error("The first item in arrObj is expeted to be a key par object.");const r={},t={};for(const[n,o]of Object.entries(e))"number"==typeof o?r[n]=o:t[n]=o;if(Object.keys(r).length>0&&s(r,"validateFirstRow"))throw new Error("Invalid numeric value at index 0.");if(Object.keys(t).length>0&&c(t,"validateFirstRow"))throw new Error("Invalid non-numeric value at index 0.");return!0},s=(e,r)=>{if(null==r)throw new Error('[hasInvalidNumbers] Missing required param "callerName".');if(!a(e))throw new Error(`[${r}:hasInvalidNumbers] Received an invalid "list" param — expected a non-empty key-pair object, got: ${JSON.stringify(e)}.`);for(const[t,o]of Object.entries(e))if(!n(o))return console.error(`[${r}:hasInvalidNumbers] property "${t}" only accept numbers. Invalid value is "${o}" and invalid type is "${typeof o}".`),!0;return!1},c=(e,r)=>{if(null==r)throw new Error('[hasNullOrUndefined] Missing required param "callerName".');if(!a(e))throw new Error(`[${r}:hasNullOrUndefined] Received an invalid "list" param — expected a non-empty key-pair object, got: ${JSON.stringify(e)}.`);for(const[t,n]of Object.entries(e))if(a(n)){for(const[e,o]of Object.entries(n))if(null==o)return console.error(`[${r}:hasNullOrUndefined] Null or undefined value detected for key "${t}.${e}".\n${String(o)}`),!0}else if(null==n)return console.error(`[${r}:hasNullOrUndefined] Null or undefined value detected for key "${t}".\n${String(n)}`),!0;return!1},u=e=>Array.from(e),y=(e,r)=>{const t=new Float64Array(r),n=new Float64Array(r),o=new Float64Array(r),a=new Uint32Array(r),i=Array.isArray(e?.mean)?e.mean:[],l=Array.isArray(e?.std)?e.std:[],s=Array.isArray(e?.scale)?e.scale:[],c=Array.isArray(e?.count)?e.count:[];for(let e=0;e<r;e++){const r=Number(i[e]),u=Number(l[e]),y=Number(s[e]),f=Number(c[e]);t[e]=Number.isFinite(r)?r:0,n[e]=Number.isFinite(u)&&u>0?u:0,a[e]=Number.isFinite(f)&&f>0?f:0,Number.isFinite(y)&&y>0?(o[e]=y,n[e]>0||(n[e]=1/y)):n[e]>0?o[e]=1/n[e]:o[e]=0}return{mean:t,std:n,scale:o,count:a}},f=(e,r=null)=>{if(!Array.isArray(e))throw new TypeError('[zscore] "arr" must be a 2D array.');if(0===e.length){const e=y(r??{},0);return{stats:{mean:u(e.mean),std:u(e.std),scale:u(e.scale),count:u(e.count)},data:[]}}const t=e[0];if(!Array.isArray(t))throw new TypeError('[zscore] "arr" must be a 2D array of rows.');const n=t.length,o=r?y(r,n):((e,r)=>{const t=e.length,n=new Float64Array(r),o=new Float64Array(r),a=new Uint32Array(r);for(let i=0;i<t;i++){const t=e[i];if(!Array.isArray(t))throw new TypeError(`[zscore] Invalid row at index=${i}. Expected an array.`);if(t.length!==r)throw new Error(`[zscore] Inconsistent row size at index=${i}. Expected ${r}, got ${t.length}.`);for(let e=0;e<r;e++){const r=Number(t[e]);if(!Number.isFinite(r))continue;a[e]++;const i=r-n[e];n[e]+=i/a[e],o[e]+=i*(r-n[e])}}const i=new Float64Array(r),l=new Float64Array(r);for(let e=0;e<r;e++){const r=a[e]>1?o[e]/a[e]:0,t=r>0?Math.sqrt(r):0;i[e]=t,l[e]=t>0?1/t:0}return{mean:n,std:i,scale:l,count:a}})(e,n),a=((e,r)=>{const t=e.length,n=r.mean.length,o=new Array(t);for(let a=0;a<t;a++){const t=e[a];if(!Array.isArray(t))throw new TypeError(`[zscore] Invalid row at index=${a}. Expected an array.`);if(t.length!==n)throw new Error(`[zscore] Inconsistent row size at index=${a}. Expected ${n}, got ${t.length}.`);const i=new Array(n);for(let e=0;e<n;e++){const n=Number(t[e]),o=r.scale[e];i[e]=Number.isFinite(n)&&o>0?(n-r.mean[e])*o:0}o[a]=i}return o})(e,o);return{stats:{mean:u(o.mean),std:u(o.std),scale:u(o.scale),count:u(o.count)},data:a}},d=({arrObj:e=[],trainSize:r=null,testSize:n=null,yCallbackFunc:a=e=>e,xCallbackFunc:c=e=>e,validateRows:u=()=>!0,shuffle:y=!1,state:d={},showSource:w=!1,scaling:h=null})=>{i(e,{min:2},"parseTrainingXY"),l(e[0]);const p=e.length;if((({arrObjSize:e,trainSize:r,testSize:t})=>{if(!o(r))throw new Error(`Invalid property: "trainSize" (${r}) must be a non-negative integer.`);if(!o(t))throw new Error(`Invalid property: "testSize" (${t}) must be a non-negative integer.`);if(!Number.isInteger(e)||e<0)throw new Error(`Invalid property: "arrObjSize" (${e}) must be a non-negative integer.`);if(e<r+t)throw new Error(`Invalid property: The sum of "trainSize" + "testSize" (${r+t}) must not be larger than "arrObj.length" (${e}).`)})({arrObjSize:p,trainSize:r,testSize:n}),![null,"zscore"].includes(h))throw new Error('Invalid "scaling" property. Accepting null or "zscore".');const g=r+n;let m=[],b=[],A=[],v=null,$=null;const E={};for(let r=0;r<e.length;r++)try{if(!u({objRow:e,index:r,state:d}))continue;const t=c({objRow:e,index:r,state:d}),n=a({objRow:e,index:r,state:d});if(null==t||null==n)continue;if(s(t,"parseTrainingXY"))throw new Error('Invalid numeric value returned from "xCallbackFunc".');if(null===v&&(v=Object.keys(t)),null===$){$=Object.keys(n);for(let e=0;e<$.length;e++)E[$[e]]={}}const o=v.length,i=$.length,l=new Array(o),y=new Array(i);for(let e=0;e<o;e++){const r=v[e];l[e]=t[r]}for(let e=0;e<i;e++){const r=$[e],t=n[r];y[e]=t;const o=Array.isArray(t)?JSON.stringify(t):String(t);E[r][o]=(E[r][o]??0)+1}m.push(l),b.push(y),w&&A.push(e[r])}catch(e){throw new Error(`[BUG] - Skipped row index=${r}: ${e.message}`)}if(y){const e=new Array(m.length);for(let r=0;r<m.length;r++)e[r]={x:m[r],y:b[r]},w&&(e[r].source=A[r]);const r=t(e);m=new Array(r.length),b=new Array(r.length),w&&(A=new Array(r.length));for(let e=0;e<r.length;e++)m[e]=r[e].x,b[e]=r[e].y,w&&(A[e]=r[e].source)}const N={keyNames:v??[]},S={keyNames:$??[],labelCounts:E},x=p-g;m.splice(0,x),b.splice(0,x);let j=m.slice(0,r),z=b.slice(0,r),O=null;if("zscore"===h){let e=f(j);O=e.stats,j=e.data,e=null}let I=m.slice(-n);"zscore"===h&&(I=f(I,O).data);let k,F,T=b.slice(-n);return m=null,b=null,w&&(A.splice(0,x),k=A.slice(0,r),F=A.slice(-n),A=null),{trainX:j,trainY:z,testX:I,testY:T,configX:N,configY:S,trainSource:k,testSource:F,stats:O}},w=({arrObj:e=[],xCallbackFunc:r=e=>e,yCallbackFunc:n=null,validateRows:o=()=>!0,shuffle:a=!1,state:c={},showSource:u=!1,scaling:y=null,stats:d})=>{let w=[],h=[],p=null;if(i(e,{min:1},"parseProductionX"),l(e[0]),![null,"zscore"].includes(y))throw new Error('Invalid "scaling" property. Accepting null or "zscore".');if(e.length,null!=n)throw new Error('The property "yCallbackFunc" must not be set in "parseProductionX".');for(let t=0;t<e.length;t++)try{if(!o({objRow:e,index:t,state:c}))continue;const n=r({objRow:e,index:t,state:c});if(null==n)continue;if(s(n,"parseProductionX"))throw new Error('Invalid numeric value returned from "xCallbackFunc".');null===p&&(p=Object.keys(n));const a=p.length,i=new Array(a);for(let e=0;e<a;e++){const r=p[e];i[e]=n[r]}w.push(i),u&&h.push(e[t])}catch(e){throw new Error(`[BUG] - Skipped row index=${t}: ${e.message}`)}if(a){const e=new Array(w.length);for(let r=0;r<w.length;r++)e[r]={x:w[r]},u&&(e[r].source=h[r]);const r=t(e);w=new Array(r.length),u&&(h=new Array(r.length));for(let e=0;e<r.length;e++)w[e]=r[e].x,u&&(h[e]=r[e].source)}const g={keyNames:p??[]};return"zscore"===y&&(w=f(w,d).data),{X:w,source:h,configX:g}},h=(e,r,t=1)=>{if(!Array.isArray(e))throw new Error("arr must be an array");if(!Number.isInteger(r)||r<=0)throw new Error("timeSteps must be a positive integer");if(!Number.isInteger(t)||t<=0)throw new Error("step must be a positive integer");if(r>e.length)return[];const n=[];for(let o=0;o<=e.length-r;o+=t)n.push(e.slice(o,o+r));return n};XY_Scale=r})();
|
package/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseTrainingXY, parseProductionX } from "./src/datasets.js"
|
|
2
2
|
import {arrayToTimesteps } from "./src/timeSteps.js"
|
|
3
3
|
import { arrayShuffle } from "./src/utilities.js"
|
|
4
|
+
import { zscore } from "./src/zscore.js"
|
|
4
5
|
|
|
5
|
-
export { parseTrainingXY, parseProductionX, arrayToTimesteps, arrayShuffle }
|
|
6
|
+
export { parseTrainingXY, parseProductionX, arrayToTimesteps, arrayShuffle, zscore }
|
package/package.json
CHANGED
package/src/datasets.js
CHANGED
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
import { arrayShuffle } from "./utilities.js";
|
|
2
|
-
import { validateFirstRow, validateArray, hasInvalidNumbers } from "./validators.js";
|
|
2
|
+
import { validateFirstRow, validateArray, hasInvalidNumbers, validateSizes } from "./validators.js";
|
|
3
|
+
import {zscore} from './zscore.js'
|
|
3
4
|
|
|
4
5
|
export const parseTrainingXY = ({
|
|
5
6
|
arrObj = [],
|
|
6
|
-
|
|
7
|
+
trainSize = null,
|
|
8
|
+
testSize = null,
|
|
7
9
|
yCallbackFunc = row => row,
|
|
8
10
|
xCallbackFunc = row => row,
|
|
9
11
|
validateRows = () => true,
|
|
10
12
|
shuffle = false,
|
|
11
13
|
state = {},
|
|
14
|
+
showSource = false,
|
|
15
|
+
scaling = null
|
|
12
16
|
}) => {
|
|
13
17
|
validateArray(arrObj, { min: 2 }, 'parseTrainingXY');
|
|
14
18
|
validateFirstRow(arrObj[0]);
|
|
15
19
|
|
|
20
|
+
const arrObjSize = arrObj.length;
|
|
21
|
+
|
|
22
|
+
validateSizes({arrObjSize, trainSize, testSize});
|
|
23
|
+
|
|
24
|
+
if(![null, 'zscore'].includes(scaling)) {
|
|
25
|
+
throw new Error(`Invalid "scaling" property. Accepting null or "zscore".`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const totalSize = trainSize + testSize;
|
|
16
29
|
let flatX = [];
|
|
17
30
|
let flatY = [];
|
|
18
31
|
let source = [];
|
|
19
32
|
|
|
20
|
-
|
|
21
33
|
let keyNamesX = null;
|
|
22
34
|
let keyNamesY = null;
|
|
23
35
|
|
|
@@ -75,7 +87,11 @@ export const parseTrainingXY = ({
|
|
|
75
87
|
|
|
76
88
|
flatX.push(rowX);
|
|
77
89
|
flatY.push(rowY);
|
|
78
|
-
|
|
90
|
+
|
|
91
|
+
if(showSource) {
|
|
92
|
+
source.push(arrObj[x])
|
|
93
|
+
}
|
|
94
|
+
|
|
79
95
|
|
|
80
96
|
} catch(err) {
|
|
81
97
|
throw new Error(`[BUG] - Skipped row index=${x}: ${err.message}`);
|
|
@@ -88,21 +104,31 @@ export const parseTrainingXY = ({
|
|
|
88
104
|
for (let i = 0; i < flatX.length; i++) {
|
|
89
105
|
merged[i] = {
|
|
90
106
|
x: flatX[i],
|
|
91
|
-
y: flatY[i]
|
|
92
|
-
source: source[i]
|
|
107
|
+
y: flatY[i]
|
|
93
108
|
};
|
|
109
|
+
|
|
110
|
+
if(showSource) {
|
|
111
|
+
merged[i].source = source[i]
|
|
112
|
+
}
|
|
94
113
|
}
|
|
95
114
|
|
|
96
115
|
const shuffled = arrayShuffle(merged);
|
|
97
116
|
|
|
98
117
|
flatX = new Array(shuffled.length);
|
|
99
118
|
flatY = new Array(shuffled.length);
|
|
100
|
-
|
|
119
|
+
|
|
120
|
+
if(showSource) {
|
|
121
|
+
source = new Array(shuffled.length)
|
|
122
|
+
}
|
|
123
|
+
|
|
101
124
|
|
|
102
125
|
for (let i = 0; i < shuffled.length; i++) {
|
|
103
126
|
flatX[i] = shuffled[i].x;
|
|
104
127
|
flatY[i] = shuffled[i].y;
|
|
105
|
-
|
|
128
|
+
|
|
129
|
+
if(showSource) {
|
|
130
|
+
source[i] = shuffled[i].source;
|
|
131
|
+
}
|
|
106
132
|
}
|
|
107
133
|
}
|
|
108
134
|
|
|
@@ -115,14 +141,44 @@ export const parseTrainingXY = ({
|
|
|
115
141
|
labelCounts,
|
|
116
142
|
};
|
|
117
143
|
|
|
118
|
-
const
|
|
144
|
+
const startSize = arrObjSize - totalSize
|
|
145
|
+
|
|
146
|
+
flatX.splice(0, startSize) //keeps the last items
|
|
147
|
+
flatY.splice(0, startSize) //keeps the last items
|
|
148
|
+
|
|
149
|
+
let trainX = flatX.slice(0, trainSize);
|
|
150
|
+
let trainY = flatY.slice(0, trainSize);
|
|
151
|
+
|
|
152
|
+
let stats = null
|
|
153
|
+
|
|
154
|
+
if(scaling === 'zscore') {
|
|
155
|
+
let trainNormalized = zscore(trainX)
|
|
156
|
+
stats = trainNormalized.stats
|
|
157
|
+
trainX = trainNormalized.data
|
|
158
|
+
trainNormalized = null
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let testX = flatX.slice(-testSize);
|
|
162
|
+
|
|
163
|
+
if(scaling === 'zscore') {
|
|
164
|
+
testX = zscore(testX, stats).data
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let testY = flatY.slice(-testSize);
|
|
119
168
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
let trainSource
|
|
125
|
-
let testSource
|
|
169
|
+
|
|
170
|
+
flatX = null
|
|
171
|
+
flatY = null
|
|
172
|
+
|
|
173
|
+
let trainSource
|
|
174
|
+
let testSource
|
|
175
|
+
|
|
176
|
+
if(showSource) {
|
|
177
|
+
source.splice(0, startSize) //keeps the last items
|
|
178
|
+
trainSource = source.slice(0, trainSize);
|
|
179
|
+
testSource = source.slice(-testSize);
|
|
180
|
+
source = null
|
|
181
|
+
}
|
|
126
182
|
|
|
127
183
|
return {
|
|
128
184
|
trainX,
|
|
@@ -132,8 +188,9 @@ export const parseTrainingXY = ({
|
|
|
132
188
|
configX,
|
|
133
189
|
configY,
|
|
134
190
|
trainSource,
|
|
135
|
-
testSource
|
|
136
|
-
|
|
191
|
+
testSource,
|
|
192
|
+
stats
|
|
193
|
+
}
|
|
137
194
|
};
|
|
138
195
|
export const parseProductionX = ({
|
|
139
196
|
arrObj = [],
|
|
@@ -142,6 +199,9 @@ export const parseProductionX = ({
|
|
|
142
199
|
validateRows = () => true,
|
|
143
200
|
shuffle = false,
|
|
144
201
|
state = {},
|
|
202
|
+
showSource = false,
|
|
203
|
+
scaling = null,
|
|
204
|
+
stats
|
|
145
205
|
}) => {
|
|
146
206
|
let flatX = [];
|
|
147
207
|
let source = [];
|
|
@@ -150,6 +210,12 @@ export const parseProductionX = ({
|
|
|
150
210
|
validateArray(arrObj, { min: 1 }, 'parseProductionX');
|
|
151
211
|
validateFirstRow(arrObj[0]);
|
|
152
212
|
|
|
213
|
+
if(![null, 'zscore'].includes(scaling)) {
|
|
214
|
+
throw new Error(`Invalid "scaling" property. Accepting null or "zscore".`)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const arrObjSize = arrObj.length
|
|
218
|
+
|
|
153
219
|
if (yCallbackFunc != null) {
|
|
154
220
|
throw new Error('The property "yCallbackFunc" must not be set in "parseProductionX".');
|
|
155
221
|
}
|
|
@@ -179,7 +245,11 @@ export const parseProductionX = ({
|
|
|
179
245
|
}
|
|
180
246
|
|
|
181
247
|
flatX.push(rowX);
|
|
182
|
-
|
|
248
|
+
|
|
249
|
+
if(showSource) {
|
|
250
|
+
source.push(arrObj[x])
|
|
251
|
+
}
|
|
252
|
+
|
|
183
253
|
|
|
184
254
|
} catch(err) {
|
|
185
255
|
throw new Error(`[BUG] - Skipped row index=${x}: ${err.message}`);
|
|
@@ -191,19 +261,29 @@ export const parseProductionX = ({
|
|
|
191
261
|
|
|
192
262
|
for (let i = 0; i < flatX.length; i++) {
|
|
193
263
|
merged[i] = {
|
|
194
|
-
x: flatX[i]
|
|
195
|
-
source: source[i]
|
|
264
|
+
x: flatX[i]
|
|
196
265
|
};
|
|
266
|
+
|
|
267
|
+
if(showSource) {
|
|
268
|
+
merged[i].source = source[i]
|
|
269
|
+
}
|
|
197
270
|
}
|
|
198
271
|
|
|
199
272
|
const shuffled = arrayShuffle(merged);
|
|
200
273
|
|
|
201
274
|
flatX = new Array(shuffled.length);
|
|
202
|
-
|
|
275
|
+
|
|
276
|
+
if(showSource) {
|
|
277
|
+
source = new Array(shuffled.length)
|
|
278
|
+
}
|
|
279
|
+
|
|
203
280
|
|
|
204
281
|
for (let i = 0; i < shuffled.length; i++) {
|
|
205
282
|
flatX[i] = shuffled[i].x;
|
|
206
|
-
|
|
283
|
+
|
|
284
|
+
if(showSource) {
|
|
285
|
+
source[i] = shuffled[i].source
|
|
286
|
+
}
|
|
207
287
|
}
|
|
208
288
|
}
|
|
209
289
|
|
|
@@ -211,6 +291,10 @@ export const parseProductionX = ({
|
|
|
211
291
|
keyNames: keyNamesX ?? [],
|
|
212
292
|
};
|
|
213
293
|
|
|
294
|
+
if(scaling === 'zscore') {
|
|
295
|
+
flatX = zscore(flatX, stats).data
|
|
296
|
+
}
|
|
297
|
+
|
|
214
298
|
return {
|
|
215
299
|
X: flatX,
|
|
216
300
|
source,
|
package/src/validators.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
export const isNumber = v => v != null && Number.isFinite(v)
|
|
3
3
|
|
|
4
|
+
export const isPositiveInteger = value => Number.isInteger(value) && value > 0
|
|
5
|
+
|
|
4
6
|
export const isKeyPairObject = param => {
|
|
5
7
|
return (
|
|
6
8
|
param !== null &&
|
|
@@ -132,4 +134,24 @@ export const arraysAreNotEqualSize = (list, callerName) => {
|
|
|
132
134
|
}
|
|
133
135
|
|
|
134
136
|
return false
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const validateSizes = ({arrObjSize, trainSize, testSize}) => {
|
|
140
|
+
|
|
141
|
+
if(!isPositiveInteger(trainSize)) {
|
|
142
|
+
throw new Error(`Invalid property: "trainSize" (${trainSize}) must be a non-negative integer.`)
|
|
143
|
+
}
|
|
144
|
+
if(!isPositiveInteger(testSize)) {
|
|
145
|
+
throw new Error(`Invalid property: "testSize" (${testSize}) must be a non-negative integer.`)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!Number.isInteger(arrObjSize) || arrObjSize < 0) {
|
|
149
|
+
throw new Error(`Invalid property: "arrObjSize" (${arrObjSize}) must be a non-negative integer.`)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if(arrObjSize < (trainSize + testSize)) {
|
|
153
|
+
throw new Error(`Invalid property: The sum of "trainSize" + "testSize" (${trainSize + testSize}) must not be larger than "arrObj.length" (${arrObjSize}).`)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return true
|
|
135
157
|
}
|
package/src/zscore.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
const toArray = typedOrArray => Array.from(typedOrArray);
|
|
2
|
+
|
|
3
|
+
const normalizeStats = (stats, cols) => {
|
|
4
|
+
const mean = new Float64Array(cols);
|
|
5
|
+
const std = new Float64Array(cols);
|
|
6
|
+
const scale = new Float64Array(cols);
|
|
7
|
+
const count = new Uint32Array(cols);
|
|
8
|
+
|
|
9
|
+
const meanIn = Array.isArray(stats?.mean) ? stats.mean : [];
|
|
10
|
+
const stdIn = Array.isArray(stats?.std) ? stats.std : [];
|
|
11
|
+
const scaleIn = Array.isArray(stats?.scale) ? stats.scale : [];
|
|
12
|
+
const countIn = Array.isArray(stats?.count) ? stats.count : [];
|
|
13
|
+
|
|
14
|
+
for (let j = 0; j < cols; j++) {
|
|
15
|
+
const m = Number(meanIn[j]);
|
|
16
|
+
const s = Number(stdIn[j]);
|
|
17
|
+
const sc = Number(scaleIn[j]);
|
|
18
|
+
const c = Number(countIn[j]);
|
|
19
|
+
|
|
20
|
+
mean[j] = Number.isFinite(m) ? m : 0;
|
|
21
|
+
std[j] = Number.isFinite(s) && s > 0 ? s : 0;
|
|
22
|
+
count[j] = Number.isFinite(c) && c > 0 ? c : 0;
|
|
23
|
+
|
|
24
|
+
if (Number.isFinite(sc) && sc > 0) {
|
|
25
|
+
scale[j] = sc;
|
|
26
|
+
if (!(std[j] > 0)) { std[j] = 1 / sc; }
|
|
27
|
+
} else if (std[j] > 0) {
|
|
28
|
+
scale[j] = 1 / std[j];
|
|
29
|
+
} else {
|
|
30
|
+
scale[j] = 0;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { mean, std, scale, count };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const fitStats = (arr, cols) => {
|
|
38
|
+
const rows = arr.length;
|
|
39
|
+
const mean = new Float64Array(cols);
|
|
40
|
+
const m2 = new Float64Array(cols);
|
|
41
|
+
const count = new Uint32Array(cols);
|
|
42
|
+
|
|
43
|
+
for (let i = 0; i < rows; i++) {
|
|
44
|
+
const row = arr[i];
|
|
45
|
+
|
|
46
|
+
if (!Array.isArray(row)) {
|
|
47
|
+
throw new TypeError(`[zscore] Invalid row at index=${i}. Expected an array.`);
|
|
48
|
+
}
|
|
49
|
+
if (row.length !== cols) {
|
|
50
|
+
throw new Error(`[zscore] Inconsistent row size at index=${i}. Expected ${cols}, got ${row.length}.`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (let j = 0; j < cols; j++) {
|
|
54
|
+
const x = Number(row[j]);
|
|
55
|
+
if (!Number.isFinite(x)) { continue; }
|
|
56
|
+
|
|
57
|
+
count[j]++;
|
|
58
|
+
|
|
59
|
+
const delta = x - mean[j];
|
|
60
|
+
mean[j] += delta / count[j];
|
|
61
|
+
m2[j] += delta * (x - mean[j]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const std = new Float64Array(cols);
|
|
66
|
+
const scale = new Float64Array(cols);
|
|
67
|
+
|
|
68
|
+
for (let j = 0; j < cols; j++) {
|
|
69
|
+
const variance = count[j] > 1 ? (m2[j] / count[j]) : 0;
|
|
70
|
+
const s = variance > 0 ? Math.sqrt(variance) : 0;
|
|
71
|
+
std[j] = s;
|
|
72
|
+
scale[j] = s > 0 ? (1 / s) : 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { mean, std, scale, count };
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const scaleFromStats = (arr, normalizedStats) => {
|
|
79
|
+
const rows = arr.length;
|
|
80
|
+
const cols = normalizedStats.mean.length;
|
|
81
|
+
const out = new Array(rows);
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < rows; i++) {
|
|
84
|
+
const row = arr[i];
|
|
85
|
+
|
|
86
|
+
if (!Array.isArray(row)) {
|
|
87
|
+
throw new TypeError(`[zscore] Invalid row at index=${i}. Expected an array.`);
|
|
88
|
+
}
|
|
89
|
+
if (row.length !== cols) {
|
|
90
|
+
throw new Error(`[zscore] Inconsistent row size at index=${i}. Expected ${cols}, got ${row.length}.`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const normalized = new Array(cols);
|
|
94
|
+
for (let j = 0; j < cols; j++) {
|
|
95
|
+
const x = Number(row[j]);
|
|
96
|
+
const sc = normalizedStats.scale[j];
|
|
97
|
+
|
|
98
|
+
normalized[j] = (Number.isFinite(x) && sc > 0)
|
|
99
|
+
? (x - normalizedStats.mean[j]) * sc
|
|
100
|
+
: 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
out[i] = normalized;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return out;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export const zscore = (arr, stats = null) => {
|
|
110
|
+
if (!Array.isArray(arr)) {
|
|
111
|
+
throw new TypeError('[zscore] "arr" must be a 2D array.');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (arr.length === 0) {
|
|
115
|
+
const normalizedStats = normalizeStats(stats ?? {}, 0);
|
|
116
|
+
return {
|
|
117
|
+
stats: {
|
|
118
|
+
mean: toArray(normalizedStats.mean),
|
|
119
|
+
std: toArray(normalizedStats.std),
|
|
120
|
+
scale: toArray(normalizedStats.scale),
|
|
121
|
+
count: toArray(normalizedStats.count)
|
|
122
|
+
},
|
|
123
|
+
data: []
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const firstRow = arr[0];
|
|
128
|
+
if (!Array.isArray(firstRow)) {
|
|
129
|
+
throw new TypeError('[zscore] "arr" must be a 2D array of rows.');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const cols = firstRow.length;
|
|
133
|
+
const normalizedStats = stats ? normalizeStats(stats, cols) : fitStats(arr, cols);
|
|
134
|
+
const data = scaleFromStats(arr, normalizedStats);
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
stats: {
|
|
138
|
+
mean: toArray(normalizedStats.mean),
|
|
139
|
+
std: toArray(normalizedStats.std),
|
|
140
|
+
scale: toArray(normalizedStats.scale),
|
|
141
|
+
count: toArray(normalizedStats.count)
|
|
142
|
+
},
|
|
143
|
+
data
|
|
144
|
+
};
|
|
145
|
+
};
|
package/test/test.js
CHANGED
|
@@ -31,6 +31,9 @@ const test = async () => {
|
|
|
31
31
|
|
|
32
32
|
const arrObj = indicators.getData()
|
|
33
33
|
|
|
34
|
+
const trainSize = Math.round(arrObj.length * 0.8)
|
|
35
|
+
const testSize = arrObj.length - trainSize
|
|
36
|
+
|
|
34
37
|
const {
|
|
35
38
|
trainX,
|
|
36
39
|
trainY,
|
|
@@ -39,8 +42,10 @@ const test = async () => {
|
|
|
39
42
|
configX,
|
|
40
43
|
configY
|
|
41
44
|
} = parseTrainingXY({
|
|
45
|
+
scaling: 'zscore',
|
|
42
46
|
arrObj,
|
|
43
|
-
|
|
47
|
+
trainSize,
|
|
48
|
+
testSize,
|
|
44
49
|
yCallbackFunc,
|
|
45
50
|
xCallbackFunc,
|
|
46
51
|
validateRows: ({objRow, index}) => {
|