xy-scale 1.4.40 → 1.4.42
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/xy-scale.min.js +1 -1
- package/package.json +1 -1
- package/src/datasets.js +91 -397
- package/src/utilities.js +0 -1
- package/src/validators.js +102 -16
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:()=>
|
|
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,arrayShuffleXY:()=>n,arrayToTimesteps:()=>b,parseProductionX:()=>g,parseTrainingXY:()=>w});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,r)=>{if(!Array.isArray(e)||!Array.isArray(r))throw new TypeError("Both X and Y must be arrays");if(e.length!==r.length)throw new Error("X and Y must have the same length");const t=[...e],n=[...r];for(let e=t.length-1;e>0;e--){const r=Math.floor(Math.random()*(e+1));[t[e],t[r]]=[t[r],t[e]],[n[e],n[r]]=[n[r],n[e]]}return{X:t,Y:n}},o=e=>"string"==typeof e||"boolean"==typeof e||("number"==typeof e?Number.isFinite(e):!!Array.isArray(e)&&e.every(o)),a=({random:e,seed:r}={})=>"function"==typeof e?e:Number.isInteger(r)?(e=>{let r=e>>>0;return()=>{r+=1831565813;let e=Math.imul(r^r>>>15,1|r);return e^=e+Math.imul(e^e>>>7,61|e),((e^e>>>14)>>>0)/4294967296}})(r):Math.random,l=(e,r=Math.random)=>{for(let t=e.length-1;t>0;t--){const n=Math.floor(r()*(t+1));[e[t],e[n]]=[e[n],e[t]]}return e},i=(e,r,t=Math.random)=>{if(r>e.length)throw new Error("Cannot sample more items than available without replacement.");const n=[...e];return l(n,t),n.slice(0,r)},s=(e,r,t=Math.random)=>{if(0===e.length)throw new Error("Cannot sample from an empty array.");const n=[];for(let o=0;o<r;o++){const r=Math.floor(t()*e.length);n.push(e[r])}return n},u=(e,r)=>r?structuredClone(e):e,c=(e,r,{cloneX:t=!1}={})=>{((e,r)=>{if(!Array.isArray(e)||!Array.isArray(r))throw new Error("X and Y must be arrays.");if(e.length!==r.length)throw new Error("X and Y must have the same length.");if(0===e.length)throw new Error("X and Y cannot be empty.")})(e,r);const n=new Map;return r.forEach(((r,a)=>{const l=(e=>{if(!o(e))throw new Error("Invalid Y label. Allowed types: finite numbers, strings, booleans, or nested arrays of those.");return JSON.stringify(e)})(r);n.has(l)||n.set(l,[]),n.get(l).push({x:u(e[a],t),y:r})})),n},f=e=>null!=e&&Number.isFinite(e),h=e=>null!==e&&"object"==typeof e&&!Array.isArray(e)&&Object.keys(e).length>0,d=(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},y=e=>{if(!h(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&&m(r,"validateFirstRow"))throw new Error("Invalid numeric value at index 0.");if(Object.keys(t).length>0&&p(t,"validateFirstRow"))throw new Error("Invalid non-numeric value at index 0.");return!0},m=(e,r)=>{if(null==r)throw new Error('[hasInvalidNumbers] Missing required param "callerName".');if(!h(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,n]of Object.entries(e))if(!f(n))return console.error(`[${r}:hasInvalidNumbers] property "${t}" only accept numbers. Invalid value is "${n}" and invalid type is "${typeof n}".`),!0;return!1},p=(e,r)=>{if(null==r)throw new Error('[hasNullOrUndefined] Missing required param "callerName".');if(!h(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(h(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},w=({arrObj:e=[],trainingSplit:r=.8,yCallbackFunc:n=e=>e,xCallbackFunc:o=e=>e,validateRows:f=()=>!0,shuffle:h=!1,balancing:p="",state:w={}})=>{d(e,{min:2},"parseTrainingXY"),y(e[0]);let g=[],b=[],v=null,x=null;const X={};for(let r=0;r<e.length;r++)try{if(!f({objRow:e,index:r,state:w}))continue;const t=o({objRow:e,index:r,state:w}),a=n({objRow:e,index:r,state:w});if(null==t||null==a)continue;if(m(t,"parseTrainingXY"))throw new Error('Invalid numeric value returned from "xCallbackFunc".');if(null===v&&(v=Object.keys(t)),null===x){x=Object.keys(a);for(let e=0;e<x.length;e++)X[x[e]]={}}const l=v.length,i=x.length,s=new Array(l),u=new Array(i);for(let e=0;e<l;e++){const r=v[e];s[e]=t[r]}for(let e=0;e<i;e++){const r=x[e],t=a[r];u[e]=t;const n=Array.isArray(t)?JSON.stringify(t):String(t);X[r][n]=(X[r][n]??0)+1}g.push(s),b.push(u)}catch(e){throw new Error(`[BUG] - Skipped row index=${r}: ${e.message}`)}if(h){const e=new Array(g.length);for(let r=0;r<g.length;r++)e[r]={x:g[r],y:b[r]};const r=t(e);g=new Array(r.length),b=new Array(r.length);for(let e=0;e<r.length;e++)g[e]=r[e].x,b[e]=r[e].y}const E={keyNames:v??[]},A={keyNames:x??[],labelCounts:X},j=Math.floor(g.length*r);let O=g.slice(0,j),$=b.slice(0,j),k=g.slice(j),N=b.slice(j);if(p){let e;if("oversample"===p)e=((e,r,t={})=>{const{random:n,seed:o,shuffleResult:i=!0,cloneX:f=!1}=t,h=a({random:n,seed:o}),d=c(e,r,{cloneX:f}),y=[...d.values()].map((e=>e.length)),m=Math.max(...y),p=[];for(const e of d.values()){const r=[...e],t=m-r.length,n=t>0?s(e,t,h).map((e=>({x:u(e.x,f),y:e.y}))):[];p.push(...r,...n)}return i&&l(p,h),{X:p.map((({x:e})=>e)),Y:p.map((({y:e})=>e))}})(O,$),O=e.X,$=e.Y;else{if("undersample"!==p)throw Error('balancing argument only accepts "", "oversample" and "undersample". Defaults to "".');e=((e,r,t={})=>{const{random:n,seed:o,shuffleResult:s=!0,cloneX:f=!1}=t,h=a({random:n,seed:o}),d=c(e,r,{cloneX:f}),y=[...d.values()].map((e=>e.length)),m=Math.min(...y),p=[];for(const e of d.values()){const r=i(e,m,h).map((e=>({x:u(e.x,f),y:e.y})));p.push(...r)}return s&&l(p,h),{X:p.map((({x:e})=>e)),Y:p.map((({y:e})=>e))}})(O,$),O=e.X,$=e.Y}}return{trainX:O,trainY:$,testX:k,testY:N,configX:E,configY:A}},g=({arrObj:e=[],xCallbackFunc:r=e=>e,yCallbackFunc:n=null,validateRows:o=()=>!0,shuffle:a=!1,state:l={}})=>{let i=[],s=null;if(d(e,{min:1},"parseProductionX"),y(e[0]),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:l}))continue;const n=r({objRow:e,index:t,state:l});if(null==n)continue;if(m(n,"parseProductionX"))throw new Error('Invalid numeric value returned from "xCallbackFunc".');null===s&&(s=Object.keys(n));const a=s.length,u=new Array(a);for(let e=0;e<a;e++){const r=s[e];u[e]=n[r]}i.push(u)}catch(e){throw new Error(`[BUG] - Skipped row index=${t}: ${e.message}`)}return a&&(i=t(i)),{X:i,configX:{keyNames:s??[]}}},b=(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/package.json
CHANGED
package/src/datasets.js
CHANGED
|
@@ -1,382 +1,115 @@
|
|
|
1
|
-
import { arrayShuffle
|
|
1
|
+
import { arrayShuffle } from "./utilities.js";
|
|
2
2
|
import { oversampleXY, undersampleXY } from "./balancing.js";
|
|
3
|
-
import { validateFirstRow, validateArray } from "./validators.js";
|
|
3
|
+
import { validateFirstRow, validateArray, hasInvalidNumbers } from "./validators.js";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const getComparableKeys = row =>
|
|
17
|
-
Object.keys(row).filter(key => key !== 'tempIdx');
|
|
18
|
-
|
|
19
|
-
const buildPath = ({ parentPath = '', key, isArrayParent = false }) => {
|
|
20
|
-
const nextPart = isArrayParent ? `[${key}]` : (parentPath ? `.${key}` : `${key}`);
|
|
21
|
-
return `${parentPath}${nextPart}`;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const createStrictSchemaError = ({
|
|
25
|
-
rowLabel,
|
|
26
|
-
sourceIndex,
|
|
27
|
-
path = '',
|
|
28
|
-
detail,
|
|
29
|
-
strictFlagName,
|
|
30
|
-
}) => {
|
|
31
|
-
const pathText = path ? ` path "${path}"` : '';
|
|
32
|
-
return new Error(
|
|
33
|
-
`${rowLabel} schema error at index "${sourceIndex}"${pathText}. ${detail} Set "${strictFlagName}" to false to disable this validation.`
|
|
34
|
-
);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const assertSameTopLevelSchema = ({
|
|
38
|
-
referenceRow,
|
|
39
|
-
currentRow,
|
|
40
|
-
rowLabel,
|
|
41
|
-
currentIndex,
|
|
42
|
-
strictFlagName,
|
|
5
|
+
export const parseTrainingXY = ({
|
|
6
|
+
arrObj = [],
|
|
7
|
+
trainingSplit = 0.8,
|
|
8
|
+
yCallbackFunc = row => row,
|
|
9
|
+
xCallbackFunc = row => row,
|
|
10
|
+
validateRows = () => true,
|
|
11
|
+
shuffle = false,
|
|
12
|
+
balancing = '',
|
|
13
|
+
state = {},
|
|
43
14
|
}) => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (referenceKind !== currentKind) {
|
|
48
|
-
throw createStrictSchemaError({
|
|
49
|
-
rowLabel,
|
|
50
|
-
sourceIndex: currentIndex,
|
|
51
|
-
detail: `Expected row type "${referenceKind}" based on the first parsed ${rowLabel} row, but got "${currentKind}".`,
|
|
52
|
-
strictFlagName,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (referenceKind !== 'array' && referenceKind !== 'object') {
|
|
57
|
-
throw createStrictSchemaError({
|
|
58
|
-
rowLabel,
|
|
59
|
-
sourceIndex: currentIndex,
|
|
60
|
-
detail: `Parsed ${rowLabel} rows must be arrays or plain objects.`,
|
|
61
|
-
strictFlagName,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const referenceKeys = getComparableKeys(referenceRow);
|
|
66
|
-
const currentKeys = getComparableKeys(currentRow);
|
|
67
|
-
|
|
68
|
-
const referenceKeySet = new Set(referenceKeys);
|
|
69
|
-
const currentKeySet = new Set(currentKeys);
|
|
70
|
-
const isArrayRow = Array.isArray(referenceRow);
|
|
71
|
-
|
|
72
|
-
for (let i = 0; i < referenceKeys.length; i++) {
|
|
73
|
-
const key = referenceKeys[i];
|
|
74
|
-
|
|
75
|
-
if (!currentKeySet.has(key)) {
|
|
76
|
-
throw createStrictSchemaError({
|
|
77
|
-
rowLabel,
|
|
78
|
-
sourceIndex: currentIndex,
|
|
79
|
-
path: buildPath({ key, isArrayParent: isArrayRow }),
|
|
80
|
-
detail: `Missing required ${isArrayRow ? 'index' : 'property'} "${key}" found in the first parsed ${rowLabel} row.`,
|
|
81
|
-
strictFlagName,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (let i = 0; i < currentKeys.length; i++) {
|
|
87
|
-
const key = currentKeys[i];
|
|
88
|
-
|
|
89
|
-
if (!referenceKeySet.has(key)) {
|
|
90
|
-
throw createStrictSchemaError({
|
|
91
|
-
rowLabel,
|
|
92
|
-
sourceIndex: currentIndex,
|
|
93
|
-
path: buildPath({ key, isArrayParent: isArrayRow }),
|
|
94
|
-
detail: `Unexpected ${isArrayRow ? 'index' : 'property'} "${key}" not present in the first parsed ${rowLabel} row.`,
|
|
95
|
-
strictFlagName,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return referenceKeys;
|
|
101
|
-
};
|
|
15
|
+
validateArray(arrObj, { min: 2 }, 'parseTrainingXY');
|
|
16
|
+
validateFirstRow(arrObj[0]);
|
|
102
17
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return typeof value;
|
|
106
|
-
};
|
|
18
|
+
let flatX = [];
|
|
19
|
+
let flatY = [];
|
|
107
20
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
currentValue,
|
|
111
|
-
sourceIndex,
|
|
112
|
-
path,
|
|
113
|
-
strictFlagName,
|
|
114
|
-
}) => {
|
|
115
|
-
const referenceKind = getYValueKind(referenceValue);
|
|
116
|
-
const currentKind = getYValueKind(currentValue);
|
|
117
|
-
|
|
118
|
-
if (referenceKind !== currentKind) {
|
|
119
|
-
throw createStrictSchemaError({
|
|
120
|
-
rowLabel: 'Y',
|
|
121
|
-
sourceIndex,
|
|
122
|
-
path,
|
|
123
|
-
detail: `Expected type "${referenceKind}" based on the first parsed Y row, but got "${currentKind}".`,
|
|
124
|
-
strictFlagName,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
21
|
+
let keyNamesX = null;
|
|
22
|
+
let keyNamesY = null;
|
|
127
23
|
|
|
128
|
-
|
|
129
|
-
if (referenceValue.length !== currentValue.length) {
|
|
130
|
-
throw createStrictSchemaError({
|
|
131
|
-
rowLabel: 'Y',
|
|
132
|
-
sourceIndex,
|
|
133
|
-
path,
|
|
134
|
-
detail: `Expected array length "${referenceValue.length}" based on the first parsed Y row, but got "${currentValue.length}".`,
|
|
135
|
-
strictFlagName,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
24
|
+
const labelCounts = {};
|
|
138
25
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
currentValue: currentValue[i],
|
|
143
|
-
sourceIndex,
|
|
144
|
-
path: buildPath({ parentPath: path, key: i, isArrayParent: true }),
|
|
145
|
-
strictFlagName,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
26
|
+
for (let x = 0; x < arrObj.length; x++) {
|
|
27
|
+
try {
|
|
28
|
+
if (!validateRows({ objRow: arrObj, index: x, state })) continue;
|
|
148
29
|
|
|
149
|
-
|
|
150
|
-
|
|
30
|
+
const parsedX = xCallbackFunc({ objRow: arrObj, index: x, state });
|
|
31
|
+
const parsedY = yCallbackFunc({ objRow: arrObj, index: x, state });
|
|
151
32
|
|
|
152
|
-
|
|
153
|
-
referenceKind !== 'number' &&
|
|
154
|
-
referenceKind !== 'boolean' &&
|
|
155
|
-
referenceKind !== 'string'
|
|
156
|
-
) {
|
|
157
|
-
throw createStrictSchemaError({
|
|
158
|
-
rowLabel: 'Y',
|
|
159
|
-
sourceIndex,
|
|
160
|
-
path,
|
|
161
|
-
detail: `Unsupported Y value type "${referenceKind}". Y values must be numbers, booleans, strings, or nested arrays of those types.`,
|
|
162
|
-
strictFlagName,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
};
|
|
33
|
+
if (parsedX == null || parsedY == null) continue;
|
|
166
34
|
|
|
167
|
-
|
|
168
|
-
|
|
35
|
+
if (hasInvalidNumbers(parsedX, 'parseTrainingXY')) {
|
|
36
|
+
throw new Error(`Invalid numeric value returned from "xCallbackFunc".`);
|
|
37
|
+
}
|
|
169
38
|
|
|
170
|
-
|
|
39
|
+
if (keyNamesX === null) {
|
|
40
|
+
keyNamesX = Object.keys(parsedX);
|
|
41
|
+
}
|
|
171
42
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
referenceRow,
|
|
175
|
-
currentRow,
|
|
176
|
-
rowLabel: 'X',
|
|
177
|
-
currentIndex,
|
|
178
|
-
strictFlagName,
|
|
179
|
-
});
|
|
180
|
-
};
|
|
43
|
+
if (keyNamesY === null) {
|
|
44
|
+
keyNamesY = Object.keys(parsedY);
|
|
181
45
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
46
|
+
for (let i = 0; i < keyNamesY.length; i++) {
|
|
47
|
+
labelCounts[keyNamesY[i]] = {};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
185
50
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
};
|
|
51
|
+
const xLen = keyNamesX.length;
|
|
52
|
+
const yLen = keyNamesY.length;
|
|
190
53
|
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const referenceRow = rows[0];
|
|
195
|
-
|
|
196
|
-
const compareRow = (currentRow, currentIndex) => {
|
|
197
|
-
const referenceKeys = assertSameTopLevelSchema({
|
|
198
|
-
referenceRow,
|
|
199
|
-
currentRow,
|
|
200
|
-
rowLabel: 'Y',
|
|
201
|
-
currentIndex,
|
|
202
|
-
strictFlagName,
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
const isArrayRow = Array.isArray(referenceRow);
|
|
206
|
-
|
|
207
|
-
for (let i = 0; i < referenceKeys.length; i++) {
|
|
208
|
-
const key = referenceKeys[i];
|
|
209
|
-
const path = buildPath({ key, isArrayParent: isArrayRow });
|
|
210
|
-
|
|
211
|
-
assertSameYValueSchema({
|
|
212
|
-
referenceValue: referenceRow[key],
|
|
213
|
-
currentValue: currentRow[key],
|
|
214
|
-
sourceIndex: currentIndex,
|
|
215
|
-
path,
|
|
216
|
-
strictFlagName,
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
};
|
|
54
|
+
const rowX = new Array(xLen);
|
|
55
|
+
const rowY = new Array(yLen);
|
|
220
56
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
57
|
+
for (let i = 0; i < xLen; i++) {
|
|
58
|
+
const key = keyNamesX[i];
|
|
59
|
+
rowX[i] = parsedX[key];
|
|
60
|
+
}
|
|
224
61
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
};
|
|
62
|
+
for (let i = 0; i < yLen; i++) {
|
|
63
|
+
const key = keyNamesY[i];
|
|
64
|
+
const value = parsedY[key];
|
|
229
65
|
|
|
230
|
-
|
|
231
|
-
arrObj = [], // array of objects
|
|
232
|
-
trainingSplit = 0.8, // numeric float between 0.01 and 0.99
|
|
233
|
-
yCallbackFunc = row => row, // accepted callback functions
|
|
234
|
-
xCallbackFunc = row => row, // accepted callback functions
|
|
235
|
-
validateRows = () => true, // accepted callback functions
|
|
236
|
-
shuffle = false, // only booleans
|
|
237
|
-
balancing = '', // accepted '', 'oversample' or 'undersample'
|
|
238
|
-
strictXSchema = true,
|
|
239
|
-
strictYSchema = true,
|
|
240
|
-
state = {}, // accepted object or classes
|
|
241
|
-
}) => {
|
|
242
|
-
let X = [];
|
|
243
|
-
let Y = [];
|
|
244
|
-
const sourceIndexes = [];
|
|
66
|
+
rowY[i] = value;
|
|
245
67
|
|
|
246
|
-
|
|
247
|
-
|
|
68
|
+
const labelKey = Array.isArray(value)
|
|
69
|
+
? JSON.stringify(value)
|
|
70
|
+
: String(value);
|
|
248
71
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
if (!validateRows({ objRow: arrObj, index: x, state })) continue;
|
|
72
|
+
labelCounts[key][labelKey] = (labelCounts[key][labelKey] ?? 0) + 1;
|
|
73
|
+
}
|
|
253
74
|
|
|
254
|
-
|
|
255
|
-
|
|
75
|
+
flatX.push(rowX);
|
|
76
|
+
flatY.push(rowY);
|
|
256
77
|
|
|
257
|
-
if (
|
|
258
|
-
typeof parsedX !== 'undefined' &&
|
|
259
|
-
parsedX !== null &&
|
|
260
|
-
typeof parsedY !== 'undefined' &&
|
|
261
|
-
parsedY !== null
|
|
262
|
-
) {
|
|
263
|
-
X.push(parsedX);
|
|
264
|
-
Y.push(parsedY);
|
|
265
|
-
sourceIndexes.push(x);
|
|
266
|
-
}
|
|
267
78
|
} catch(err) {
|
|
268
|
-
|
|
269
|
-
continue
|
|
79
|
+
throw new Error(`[BUG] - Skipped row index=${x}: ${err.message}`);
|
|
270
80
|
}
|
|
271
81
|
}
|
|
272
82
|
|
|
273
|
-
if (strictXSchema) {
|
|
274
|
-
validateStrictXRows({
|
|
275
|
-
rows: X,
|
|
276
|
-
sourceIndexes,
|
|
277
|
-
strictFlagName: 'strictXSchema',
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (strictYSchema) {
|
|
282
|
-
validateStrictYRows({
|
|
283
|
-
rows: Y,
|
|
284
|
-
sourceIndexes,
|
|
285
|
-
strictFlagName: 'strictYSchema',
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
|
|
289
83
|
if (shuffle) {
|
|
290
|
-
const merged = new Array(
|
|
84
|
+
const merged = new Array(flatX.length);
|
|
291
85
|
|
|
292
|
-
for (let i = 0; i <
|
|
86
|
+
for (let i = 0; i < flatX.length; i++) {
|
|
293
87
|
merged[i] = {
|
|
294
|
-
x:
|
|
295
|
-
y:
|
|
296
|
-
sourceIndex: sourceIndexes[i],
|
|
88
|
+
x: flatX[i],
|
|
89
|
+
y: flatY[i]
|
|
297
90
|
};
|
|
298
91
|
}
|
|
299
92
|
|
|
300
93
|
const shuffled = arrayShuffle(merged);
|
|
301
94
|
|
|
302
|
-
|
|
303
|
-
|
|
95
|
+
flatX = new Array(shuffled.length);
|
|
96
|
+
flatY = new Array(shuffled.length);
|
|
304
97
|
|
|
305
98
|
for (let i = 0; i < shuffled.length; i++) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
sourceIndexes[i] = shuffled[i].sourceIndex;
|
|
99
|
+
flatX[i] = shuffled[i].x;
|
|
100
|
+
flatY[i] = shuffled[i].y;
|
|
309
101
|
}
|
|
310
102
|
}
|
|
311
103
|
|
|
312
|
-
const xLen = X.length;
|
|
313
|
-
const yLen = Y.length;
|
|
314
|
-
|
|
315
|
-
const xKeys = xLen ? getComparableKeys(X[0]) : [];
|
|
316
|
-
const yKeys = yLen ? getComparableKeys(Y[0]) : [];
|
|
317
|
-
|
|
318
|
-
const flatX = new Array(xLen);
|
|
319
|
-
const flatY = new Array(yLen);
|
|
320
|
-
|
|
321
104
|
const configX = {
|
|
322
|
-
keyNames:
|
|
105
|
+
keyNames: keyNamesX ?? [],
|
|
323
106
|
};
|
|
324
107
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
for (let idx = 0; idx < xLen; idx++) {
|
|
328
|
-
const rowObj = X[idx];
|
|
329
|
-
const sourceIndex = sourceIndexes[idx];
|
|
330
|
-
const flatRow = new Array(xKeys.length);
|
|
331
|
-
|
|
332
|
-
for (let j = 0; j < xKeys.length; j++) {
|
|
333
|
-
const key = xKeys[j];
|
|
334
|
-
const value = rowObj[key];
|
|
335
|
-
|
|
336
|
-
if (isBadNumber(value)) {
|
|
337
|
-
throw new Error(
|
|
338
|
-
`Invalid property value (${value}) returned from "xCallbackFunc" on index "${sourceIndex}" property "${key}".`
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
flatRow[j] = value;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
flatX[idx] = flatRow;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const toLabelKey = value => Array.isArray(value)
|
|
349
|
-
? JSON.stringify(value)
|
|
350
|
-
: String(value);
|
|
351
|
-
|
|
352
|
-
const initLabelCounts = keyNames =>
|
|
353
|
-
Object.fromEntries(
|
|
354
|
-
keyNames.map(keyName => [keyName, ({})])
|
|
355
|
-
);
|
|
356
|
-
|
|
357
108
|
const configY = {
|
|
358
|
-
keyNames:
|
|
359
|
-
labelCounts
|
|
109
|
+
keyNames: keyNamesY ?? [],
|
|
110
|
+
labelCounts,
|
|
360
111
|
};
|
|
361
112
|
|
|
362
|
-
for (let idx = 0; idx < yLen; idx++) {
|
|
363
|
-
const rowObj = Y[idx];
|
|
364
|
-
const flatRow = new Array(yKeys.length);
|
|
365
|
-
|
|
366
|
-
for (let j = 0; j < yKeys.length; j++) {
|
|
367
|
-
const keyName = yKeys[j];
|
|
368
|
-
const value = rowObj[keyName];
|
|
369
|
-
|
|
370
|
-
flatRow[j] = value;
|
|
371
|
-
|
|
372
|
-
const labelKey = toLabelKey(value);
|
|
373
|
-
configY.labelCounts[keyName][labelKey] =
|
|
374
|
-
(configY.labelCounts[keyName][labelKey] ?? 0) + 1;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
flatY[idx] = flatRow;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
113
|
const splitIndex = Math.floor(flatX.length * trainingSplit);
|
|
381
114
|
|
|
382
115
|
let trainX = flatX.slice(0, splitIndex);
|
|
@@ -409,101 +142,62 @@ export const parseTrainingXY = ({
|
|
|
409
142
|
configY,
|
|
410
143
|
};
|
|
411
144
|
};
|
|
412
|
-
|
|
413
145
|
export const parseProductionX = ({
|
|
414
146
|
arrObj = [],
|
|
415
147
|
xCallbackFunc = row => row,
|
|
416
148
|
yCallbackFunc = null,
|
|
417
149
|
validateRows = () => true,
|
|
418
150
|
shuffle = false,
|
|
419
|
-
strictXSchema = true,
|
|
420
151
|
state = {},
|
|
421
152
|
}) => {
|
|
422
|
-
let
|
|
423
|
-
let
|
|
153
|
+
let flatX = [];
|
|
154
|
+
let keyNamesX = null;
|
|
424
155
|
|
|
425
156
|
validateArray(arrObj, { min: 1 }, 'parseProductionX');
|
|
426
157
|
validateFirstRow(arrObj[0]);
|
|
427
158
|
|
|
428
|
-
if(yCallbackFunc != null) {
|
|
429
|
-
throw new Error('The property "yCallbackFunc" must not be set in "parseProductionX".')
|
|
159
|
+
if (yCallbackFunc != null) {
|
|
160
|
+
throw new Error('The property "yCallbackFunc" must not be set in "parseProductionX".');
|
|
430
161
|
}
|
|
431
162
|
|
|
432
163
|
for (let x = 0; x < arrObj.length; x++) {
|
|
433
|
-
|
|
434
164
|
try {
|
|
435
165
|
if (!validateRows({ objRow: arrObj, index: x, state })) continue;
|
|
436
166
|
|
|
437
167
|
const parsedX = xCallbackFunc({ objRow: arrObj, index: x, state });
|
|
438
168
|
|
|
439
|
-
if (
|
|
440
|
-
X.push(parsedX);
|
|
441
|
-
sourceIndexes.push(x);
|
|
442
|
-
}
|
|
443
|
-
} catch(err) {
|
|
444
|
-
console.error(`[BUG] - Skipped row index=${x}: ${err.message}`)
|
|
445
|
-
continue
|
|
446
|
-
}
|
|
447
|
-
}
|
|
169
|
+
if (parsedX == null) continue;
|
|
448
170
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
sourceIndexes,
|
|
453
|
-
strictFlagName: 'strictXSchema',
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (shuffle) {
|
|
458
|
-
const merged = new Array(X.length);
|
|
171
|
+
if (hasInvalidNumbers(parsedX, 'parseProductionX')) {
|
|
172
|
+
throw new Error(`Invalid numeric value returned from "xCallbackFunc".`);
|
|
173
|
+
}
|
|
459
174
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
sourceIndex: sourceIndexes[i],
|
|
464
|
-
};
|
|
465
|
-
}
|
|
175
|
+
if (keyNamesX === null) {
|
|
176
|
+
keyNamesX = Object.keys(parsedX);
|
|
177
|
+
}
|
|
466
178
|
|
|
467
|
-
|
|
179
|
+
const xLen = keyNamesX.length;
|
|
180
|
+
const rowX = new Array(xLen);
|
|
468
181
|
|
|
469
|
-
|
|
470
|
-
|
|
182
|
+
for (let i = 0; i < xLen; i++) {
|
|
183
|
+
const key = keyNamesX[i];
|
|
184
|
+
rowX[i] = parsedX[key];
|
|
185
|
+
}
|
|
471
186
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
187
|
+
flatX.push(rowX);
|
|
188
|
+
} catch(err) {
|
|
189
|
+
throw new Error(`[BUG] - Skipped row index=${x}: ${err.message}`);
|
|
475
190
|
}
|
|
476
191
|
}
|
|
477
192
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
193
|
+
if (shuffle) {
|
|
194
|
+
flatX = arrayShuffle(flatX);
|
|
195
|
+
}
|
|
481
196
|
|
|
482
197
|
const configX = {
|
|
483
|
-
keyNames:
|
|
198
|
+
keyNames: keyNamesX ?? [],
|
|
484
199
|
};
|
|
485
200
|
|
|
486
|
-
for (let idx = 0; idx < xLen; idx++) {
|
|
487
|
-
const rowObj = X[idx];
|
|
488
|
-
const sourceIndex = sourceIndexes[idx];
|
|
489
|
-
const flatRow = new Array(xKeys.length);
|
|
490
|
-
|
|
491
|
-
for (let j = 0; j < xKeys.length; j++) {
|
|
492
|
-
const key = xKeys[j];
|
|
493
|
-
const value = rowObj[key];
|
|
494
|
-
|
|
495
|
-
if (isBadNumber(value)) {
|
|
496
|
-
throw new Error(
|
|
497
|
-
`Invalid property value (${value}) returned from "xCallbackFunc" on index "${sourceIndex}" property "${key}".`
|
|
498
|
-
);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
flatRow[j] = value;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
flatX[idx] = flatRow;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
201
|
return {
|
|
508
202
|
X: flatX,
|
|
509
203
|
configX,
|
package/src/utilities.js
CHANGED
package/src/validators.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
export const validateExcludes = (row, excludes) => {
|
|
2
1
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if(!Array.isArray(excludes))
|
|
6
|
-
{
|
|
7
|
-
throw new Error(`Property "excludes" must be an array.`)
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
for(const k of excludes)
|
|
11
|
-
{
|
|
12
|
-
if(!keyNames.includes(k)) throw new Error(`An item in "excludes" property was not found in "arrObj".\n\nexcludes: ${JSON.stringify(excludes)}\n\narrObj: ${JSON.stringify(keyNames)}`)
|
|
13
|
-
}
|
|
2
|
+
export const isNumber = v => v != null && Number.isFinite(v)
|
|
14
3
|
|
|
4
|
+
export const isKeyPairObject = param => {
|
|
5
|
+
return (
|
|
6
|
+
param !== null &&
|
|
7
|
+
typeof param === "object" &&
|
|
8
|
+
!Array.isArray(param) &&
|
|
9
|
+
Object.keys(param).length > 0
|
|
10
|
+
);
|
|
15
11
|
}
|
|
16
12
|
|
|
17
13
|
export const validateArray = (arr, {min = -Infinity, max = Infinity}, paramName) => {
|
|
@@ -35,15 +31,105 @@ export const validateArray = (arr, {min = -Infinity, max = Infinity}, paramName)
|
|
|
35
31
|
|
|
36
32
|
export const validateFirstRow = row => {
|
|
37
33
|
|
|
34
|
+
if(!isKeyPairObject(row)) {
|
|
35
|
+
throw new Error(`The first item in arrObj is expeted to be a key par object.`)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const numeric = {}
|
|
39
|
+
const nonNumeric = {}
|
|
40
|
+
|
|
38
41
|
for(const [k, v] of Object.entries(row))
|
|
39
42
|
{
|
|
40
|
-
if (typeof v === 'number'
|
|
41
|
-
|
|
43
|
+
if (typeof v === 'number') {
|
|
44
|
+
numeric[k] = v
|
|
42
45
|
}
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
else {
|
|
47
|
+
nonNumeric[k] = v
|
|
45
48
|
}
|
|
46
49
|
}
|
|
50
|
+
|
|
51
|
+
if (Object.keys(numeric).length > 0 && hasInvalidNumbers(numeric, 'validateFirstRow')) {
|
|
52
|
+
throw new Error(`Invalid numeric value at index 0.`)
|
|
53
|
+
}
|
|
54
|
+
if (Object.keys(nonNumeric).length > 0 && hasNullOrUndefined(nonNumeric, 'validateFirstRow')) {
|
|
55
|
+
throw new Error(`Invalid non-numeric value at index 0.`)
|
|
56
|
+
}
|
|
47
57
|
|
|
48
58
|
return true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const hasInvalidNumbers = (list, callerName) => {
|
|
62
|
+
if (callerName == null) {
|
|
63
|
+
throw new Error('[hasInvalidNumbers] Missing required param "callerName".')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!isKeyPairObject(list)) {
|
|
67
|
+
throw new Error(`[${callerName}:hasInvalidNumbers] Received an invalid "list" param — expected a non-empty key-pair object, got: ${JSON.stringify(list)}.`)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const [k, v] of Object.entries(list)) {
|
|
71
|
+
if(!isNumber(v)) {
|
|
72
|
+
console.error(`[${callerName}:hasInvalidNumbers] property "${k}" only accept numbers. Invalid value is "${v}" and invalid type is "${typeof v}".`)
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const hasNullOrUndefined = (list, callerName) => {
|
|
81
|
+
if (callerName == null) {
|
|
82
|
+
throw new Error('[hasNullOrUndefined] Missing required param "callerName".')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!isKeyPairObject(list)) {
|
|
86
|
+
throw new Error(`[${callerName}:hasNullOrUndefined] Received an invalid "list" param — expected a non-empty key-pair object, got: ${JSON.stringify(list)}.`)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const [k, v] of Object.entries(list)) {
|
|
90
|
+
|
|
91
|
+
if(isKeyPairObject(v)) {
|
|
92
|
+
for(const [k2, v2] of Object.entries(v)) {
|
|
93
|
+
if(v2 == null) {
|
|
94
|
+
console.error(`[${callerName}:hasNullOrUndefined] Null or undefined value detected for key "${k}.${k2}".\n${String(v2)}`)
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (v == null) {
|
|
100
|
+
console.error(`[${callerName}:hasNullOrUndefined] Null or undefined value detected for key "${k}".\n${String(v)}`)
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const arraysAreNotEqualSize = (list, callerName) => {
|
|
109
|
+
if (callerName == null) {
|
|
110
|
+
throw new Error('[arraysArentTheSameSize] Missing required param "callerName".')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!isKeyPairObject(list)) {
|
|
114
|
+
throw new Error(`[${callerName}:arraysArentTheSameSize] Received an invalid "list" param — expected a non-empty key-pair object, got: ${JSON.stringify(list)}.`)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let preArr = null
|
|
118
|
+
|
|
119
|
+
for (const [k, v] of Object.entries(list)) {
|
|
120
|
+
if(!Array.isArray(v)) {
|
|
121
|
+
console.error(`[${callerName}:arraysArentTheSameSize] Invalid array detected in key "${k}".\n${String(v)}`)
|
|
122
|
+
return true
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if(preArr === null) {
|
|
126
|
+
preArr = v.length
|
|
127
|
+
} else {
|
|
128
|
+
if(preArr !== v.length) {
|
|
129
|
+
return true
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return false
|
|
49
135
|
}
|