xy-scale 1.2.1 → 1.2.2

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.
@@ -1 +1 @@
1
- var XY_Scale;(()=>{"use strict";var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{arrayToTimesteps:()=>c,descaleArrayObj:()=>l,parseProductionX:()=>s,parseTrainingXY:()=>o});const n=({arrObj:e,repeat:t={},minmaxRange:n=[0,1],groups:o={}})=>{const s=e.length;if(r(o),0===s)return{scaledOutput:[],scaledConfig:{},keyNames:[]};const[l,c]=n;if(l>=c)throw new Error("Invalid minmaxRange: rangeMin must be less than rangeMax");const i=Object.keys(e[0]),u=i.map((e=>t.hasOwnProperty(e)?Math.max(t[e],1):1)),f=u.reduce(((e,t)=>e+t),0),m=new Array(f);let h=0;for(let e=0;e<i.length;e++)for(let t=0;t<u[e];t++)m[h++]=i[e];const p={},g={},d={},b={},y={};for(const t of i){const n=e[0][t];p[t]=typeof n,"string"===p[t]&&(b[t]={});const r=a(t,o);r?y[r]||(y[r]={min:1/0,max:-1/0}):(g[t]=1/0,d[t]=-1/0)}for(const t of e)for(const e of i){let n=t[e];if("string"===p[e]){const r=b[e];r.hasOwnProperty(n)||(r[n]=Object.keys(r).length),n=r[n],t[e]=n}else"boolean"===p[e]&&(t[e]=Number(n));const r=a(e,o);r?(y[r].min=Math.min(y[r].min,n),y[r].max=Math.max(y[r].max,n)):(g[e]=Math.min(g[e],n),d[e]=Math.max(d[e],n))}const O=new Array(s);for(let t=0;t<s;t++){const n=e[t],r=new Array(f);let s=0;for(let e=0;e<i.length;e++){const t=i[e],f=n[t],m=a(t,o);let h,p;m?(h=y[m].min,p=y[m].max):(h=g[t],p=d[t]);const b=p!==h?l+(f-h)/(p-h)*(c-l):l,O=u[e];for(let e=0;e<O;e++)r[s++]=b}O[t]=r}return{scaledOutput:O,scaledConfig:{min:g,max:d,inputTypes:p,uniqueStringIndexes:b,rangeMin:l,rangeMax:c,groupMinMax:y},scaledKeyNames:m}},r=e=>{const t=new Set,n=[];for(const[r,a]of Object.entries(e))t.add(r),n.push(r),a.forEach((e=>{t.add(e),n.push(e)}));if(t.size!==n.length)throw new Error("Duplicate value found between properties in validateUniqueProperties function.")},a=(e,t)=>{for(const[n,r]of Object.entries(t))if(r.includes(e))return n;return null},o=({arrObj:e,trainingSplit:t=.8,repeat:r={},yCallbackFunc:a,xCallbackFunc:o,groups:s,shuffle:l=!1,minmaxRange:c,balancing:i=""})=>{let u=[],f=[];for(let t=0;t<e.length;t++){const n=o({objRow:e,index:t}),r=a({objRow:e,index:t});null!=n&&null!=r&&(u.push(n),f.push(r))}if(l){const{shuffledX:e,shuffledY:t}=((e,t)=>{if(e.length!==t.length)throw new Error("X and Y arrays must have the same length");const n=Array.from({length:e.length},((e,t)=>t));for(let e=n.length-1;e>0;e--){const t=Math.floor(Math.random()*(e+1));[n[e],n[t]]=[n[t],n[e]]}return{shuffledX:n.map((t=>e[t])),shuffledY:n.map((e=>t[e]))}})(u,f);u=e,f=t}let{scaledOutput:m,scaledConfig:h,scaledKeyNames:p}=n({arrObj:u,repeat:r,groups:s,minmaxRange:c}),{scaledOutput:g,scaledConfig:d,scaledKeyNames:b}=n({arrObj:f,repeat:r,groups:s,minmaxRange:c});if(i){let e;if("oversample"===i)e=((e,t)=>{const n={},r={};t.forEach(((a,o)=>{n[a]||(n[a]=0,r[a]=[]),n[a]++,r[a].push([e[o],t[o]])}));const a=Math.max(...Object.values(n)),o=[],s=[];return Object.keys(r).forEach((e=>{const t=r[e],n=t.length;for(let e=0;e<a;e++){const r=t[e%n];o.push(r[0]),s.push(r[1])}})),{X:o,Y:s}})(m,g),m=e.X,g=e.Y;else{if("undersample"!==i)throw Error('balancing argument only accepts "false", "oversample" and "undersample". Defaults to "false".');e=((e,t)=>{const n={},r={};t.forEach(((a,o)=>{n[a]||(n[a]=0,r[a]=[]),n[a]++,r[a].push([e[o],t[o]])}));const a=Math.min(...Object.values(n)),o=[],s=[];return Object.keys(r).forEach((e=>{const t=r[e];for(let e=0;e<a;e++){const n=t[e];o.push(n[0]),s.push(n[1])}})),{X:o,Y:s}})(m,g),m=e.X,g=e.Y}}const y=Math.floor(m.length*t);return{trainX:m.slice(0,y),trainY:g.slice(0,y),testX:m.slice(y),testY:g.slice(y),configX:h,keyNamesX:p,configY:d,keyNamesY:b}},s=({arrObj:e,repeat:t={},xCallbackFunc:r,groups:a,shuffle:o=!1,minmaxRange:s})=>{let l=[];for(let t=0;t<e.length;t++){const n=r({objRow:e,index:t});n&&l.push(n)}o&&(l=(e=>{const t=[...e];for(let e=t.length-1;e>0;e--){const n=Math.floor(Math.random()*(e+1));[t[e],t[n]]=[t[n],t[e]]}return t})(l));const{scaledOutput:c,scaledConfig:i,scaledKeyNames:u}=n({arrObj:l,repeat:t,groups:a,minmaxRange:s});return{X:c,configX:i,keyNamesX:u}},l=({scaled:e,config:t,keyNames:n})=>{const{min:r,max:a,std:o,mean:s,approach:l,inputTypes:c,uniqueStringIndexes:i}=t;return e.map((e=>{const t={};let u=0;for(const f of Object.keys(r)){const m=l[f],h=r[f],p=a[f],g=s[f],d=o[f],b=n.filter((e=>e===f)).length;let y=0;for(let t=0;t<b;t++)y+=e[u++];const O=y/b;let x;if("normalization"===m?x=O*(p-h)+h:"standardization"===m&&(x=O*d+g),"string"===c[f]){const e=Object.keys(i[f]).find((e=>i[f][e]===x));x=void 0!==e?e:x}t[f]=x}return t}))},c=(e,t)=>{if(0===t)return e;if(t<0)throw new Error("timeSteps must be greater than 0");const n=[];for(let r=0;r<=e.length-t;r++)n.push(e.slice(r,r+t));return n};XY_Scale=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,{arrayToTimesteps:()=>f,descaleArrayObj:()=>p,parseProductionX:()=>u,parseTrainingXY:()=>i});const t=({arrObj:e,repeat:r={},minmaxRange:t=[0,1],groups:i={},prevConfig:u=null})=>{const p=[...e],f=p.length,c=p[0];if(0===f)return{scaledOutput:[],scaledConfig:{}};let l={};const m=u&&o(u);if(m)s(u,{minmaxRange:t,repeat:r,groups:i,firstRow:c}),l={...u};else{const e=Object.keys(c),n=e.map((e=>r.hasOwnProperty(e)?Math.max(r[e],1):1)),a=n.reduce(((e,r)=>e+r),0);l={rangeMin:t[0],rangeMax:t[1],inputTypes:{},min:{},max:{},uniqueStrIdx:{},groupMinMax:{},repeat:r,groups:i,inputKeyNames:e,outputKeyNames:new Array(a),repeatedKeyNames:n};let s=0;for(let e=0;e<l.inputKeyNames.length;e++)for(let r=0;r<l.repeatedKeyNames[e];r++)l.outputKeyNames[s++]=l.inputKeyNames[e];o(l)}n(l.groups);for(const e of l.inputKeyNames){const r=typeof c[e];if(m){if(!l.inputTypes.hasOwnProperty(e))throw new Error(`Error: A new unknown inputType property "${e}" found.`);if(l.inputTypes[e]!==r)throw new Error(`Error: Current inputType of property "${e}" is not the same as in the prevConfig inputType.`);continue}l.inputTypes[e]=r,"string"===r&&(l.uniqueStrIdx[e]={});const t=a(e,l.groups);t?l.groupMinMax[t]={min:1/0,max:-1/0}:(l.min[e]=1/0,l.max[e]=-1/0)}for(const e of p)for(const r of l.inputKeyNames){let t=e[r];if("string"===l.inputTypes[r]){const n=l.uniqueStrIdx[r];n.hasOwnProperty(t)||(n[t]=Object.keys(n).length),t=n[t],e[r]=t}else"boolean"===l.inputTypes[r]&&(e[r]=Number(t));const n=a(r,l.groups);n?(l.groupMinMax[n].min=Math.min(l.groupMinMax[n].min,t),l.groupMinMax[n].max=Math.max(l.groupMinMax[n].max,t)):(l.min[r]=Math.min(l.min[r],t),l.max[r]=Math.max(l.max[r],t))}const g=new Array(f);for(let e=0;e<f;e++){const r=p[e],t=new Array(l.outputKeyNames.length);let n=0;for(let e=0;e<l.inputKeyNames.length;e++){const o=l.inputKeyNames[e],s=r[o],i=a(o,l.groups);let u,p;i?(u=l.groupMinMax[i].min,p=l.groupMinMax[i].max):(u=l.min[o],p=l.max[o]);const f=p!==u?l.rangeMin+(s-u)/(p-u)*(l.rangeMax-l.rangeMin):l.rangeMin,c=l.repeatedKeyNames[e];for(let e=0;e<c;e++)t[n++]=f}g[e]=t}return{scaledOutput:g,scaledConfig:l}},n=e=>{const r=new Set,t=[];for(const[n,a]of Object.entries(e))r.add(n),t.push(n),a.forEach((e=>{r.add(e),t.push(e)}));if(r.size!==t.length)throw new Error("Duplicate value found between properties in validateUniqueProperties function.")},a=(e,r)=>{for(const[t,n]of Object.entries(r))if(n.includes(e))return t;return null},o=e=>{if(!e)return!1;const r=["rangeMin","rangeMax","inputTypes","min","max","uniqueStrIdx","groupMinMax","repeat","groups","inputKeyNames","outputKeyNames","repeatedKeyNames"];for(const t of r)if(!e.hasOwnProperty(t))throw new Error(`Missing key "${t}" in config.`);const{rangeMin:t,rangeMax:n,inputTypes:a,min:o,max:s,uniqueStrIdx:i,groupMinMax:u,repeat:p,groups:f,inputKeyNames:c,outputKeyNames:l,repeatedKeyNames:m}=e;if("number"!=typeof t||"number"!=typeof n)throw new Error("rangeMin and rangeMax must be numbers.");if(t>=n)throw new Error("rangeMin must be less than rangeMax.");const g=e=>"object"==typeof e&&null!==e&&!Array.isArray(e);if(!g(a))throw new Error("inputTypes must be an object.");if(!g(o))throw new Error("min must be an object.");if(!g(s))throw new Error("max must be an object.");if(!g(i))throw new Error("uniqueStrIdx must be an object.");if(!g(u))throw new Error("groupMinMax must be an object.");if(!g(p))throw new Error("repeat must be an object.");if(!g(f))throw new Error("groups must be an object.");if(!Array.isArray(c))throw new Error("inputKeyNames must be an array.");if(!Array.isArray(l))throw new Error("outputKeyNames must be an array.");if(!Array.isArray(m))throw new Error("repeatedKeyNames must be an array.");return!0},s=(e,{minmaxRange:r,repeat:t,groups:n,firstRow:a})=>{if(e.rangeMin!==r[0]||e.rangeMax!==r[1])throw new Error('"prevConfig.minmaxRange" is not equal "minmaxRange".');if(JSON.stringify(e.inputKeyNames)!==JSON.stringify(Object.keys(a)))throw new Error('"prevConfig.inputKeyNames" structure does not match "Object.keys(firstRow)" structure. The order of keys is important.');if(JSON.stringify(e.repeat)!==JSON.stringify(t))throw new Error('"prevConfig.repeat" structure does not match "repeat" structure. The order of keys is important.');if(JSON.stringify(e.groups)!==JSON.stringify(n))throw new Error('"prevConfig.groups" structure does not match "groups" structure. The order of keys is important.');return!0},i=({arrObj:e,trainingSplit:r=.8,repeat:n={},yCallbackFunc:a,xCallbackFunc:o,validateRows:s=e=>e,groups:i,shuffle:u=!1,minmaxRange:p,balancing:f="",state:c={}})=>{let l=[],m=[];for(let r=0;r<e.length;r++){if(!s(e[r]))continue;const t=o({objRow:e,index:r,state:c}),n=a({objRow:e,index:r,state:c});null!=t&&null!=n&&(l.push(t),m.push(n))}if(u){const{shuffledX:e,shuffledY:r}=((e,r)=>{if(e.length!==r.length)throw new Error("X and Y arrays must have the same length");const t=Array.from({length:e.length},((e,r)=>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]]}return{shuffledX:t.map((r=>e[r])),shuffledY:t.map((e=>r[e]))}})(l,m);l=e,m=r}let{scaledOutput:g,scaledConfig:y}=t({arrObj:l,repeat:n,groups:i,minmaxRange:p}),{scaledOutput:h,scaledConfig:d}=t({arrObj:m,repeat:n,groups:i,minmaxRange:p});const w=Math.floor(g.length*r);let b=g.slice(0,w),x=h.slice(0,w),M=g.slice(w),O=h.slice(w);if(f){let e;if("oversample"===f)e=((e,r)=>{const t={},n={};r.forEach(((a,o)=>{t[a]||(t[a]=0,n[a]=[]),t[a]++,n[a].push([e[o],r[o]])}));const a=Math.max(...Object.values(t)),o=[],s=[];return Object.keys(n).forEach((e=>{const r=n[e],t=r.length;for(let e=0;e<a;e++){const n=r[e%t];o.push(n[0]),s.push(n[1])}})),{X:o,Y:s}})(b,x),b=e.X,x=e.Y;else{if("undersample"!==f)throw Error('balancing argument only accepts "false", "oversample" and "undersample". Defaults to "false".');e=((e,r)=>{const t={},n={};r.forEach(((a,o)=>{t[a]||(t[a]=0,n[a]=[]),t[a]++,n[a].push([e[o],r[o]])}));const a=Math.min(...Object.values(t)),o=[],s=[];return Object.keys(n).forEach((e=>{const r=n[e];for(let e=0;e<a;e++){const t=r[e];o.push(t[0]),s.push(t[1])}})),{X:o,Y:s}})(b,x),b=e.X,x=e.Y}}return{trainX:b,trainY:x,testX:M,testY:O,configX:y,configY:d}},u=({arrObj:e,repeat:r={},xCallbackFunc:n,validateRows:a=e=>e,groups:o,shuffle:s=!1,minmaxRange:i,state:u={}})=>{let p=[];for(let r=0;r<e.length;r++){if(!a(e[r]))continue;const t=n({objRow:e,index:r,state:u});t&&p.push(t)}s&&(p=(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})(p));const{scaledOutput:f,scaledConfig:c}=t({arrObj:p,repeat:r,groups:o,minmaxRange:i});return{X:f,configX:c,keyNamesX}},p=({scaled:e,config:r,keyNames:t})=>{const{min:n,max:a,std:o,mean:s,approach:i,inputTypes:u,uniqueStringIndexes:p}=r;return e.map((e=>{const r={};let f=0;for(const c of Object.keys(n)){const l=i[c],m=n[c],g=a[c],y=s[c],h=o[c],d=t.filter((e=>e===c)).length;let w=0;for(let r=0;r<d;r++)w+=e[f++];const b=w/d;let x;if("normalization"===l?x=b*(g-m)+m:"standardization"===l&&(x=b*h+y),"string"===u[c]){const e=Object.keys(p[c]).find((e=>p[c][e]===x));x=void 0!==e?e:x}r[c]=x}return r}))},f=(e,r)=>{if(0===r)return e;if(r<0)throw new Error("timeSteps must be greater than 0");const t=[];for(let n=0;n<=e.length-r;n++)t.push(e.slice(n,n+r));return t};XY_Scale=r})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xy-scale",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "main": "./index.js",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/datasets.js CHANGED
@@ -41,15 +41,12 @@ export const parseTrainingXY = ({
41
41
 
42
42
  let {
43
43
  scaledOutput: scaledX,
44
- scaledConfig: configX,
45
- scaledKeyNames: keyNamesX
46
-
44
+ scaledConfig: configX
47
45
  } = scaleArrayObj({arrObj: X, repeat, groups, minmaxRange})
48
46
 
49
47
  let {
50
48
  scaledOutput: scaledY,
51
49
  scaledConfig: configY,
52
- scaledKeyNames: keyNamesY
53
50
  } = scaleArrayObj({arrObj: Y, repeat, groups, minmaxRange})
54
51
 
55
52
 
@@ -93,11 +90,8 @@ export const parseTrainingXY = ({
93
90
  trainY,
94
91
  testX,
95
92
  testY,
96
-
97
93
  configX,
98
- keyNamesX,
99
94
  configY,
100
- keyNamesY
101
95
  };
102
96
  };
103
97
 
@@ -133,9 +127,7 @@ export const parseProductionX = ({
133
127
  // Scale X
134
128
  const {
135
129
  scaledOutput: scaledX,
136
- scaledConfig: configX,
137
- scaledKeyNames: keyNamesX
138
-
130
+ scaledConfig: configX
139
131
  } = scaleArrayObj({arrObj: X, repeat, groups, minmaxRange})
140
132
 
141
133
 
package/src/scale.js CHANGED
@@ -1,120 +1,155 @@
1
- export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], groups = {} }) => {
2
- const n = arrObj.length;
3
-
4
- validateUniqueProperties(groups);
1
+ export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], groups = {}, prevConfig = null }) => {
2
+
3
+ const arrObjClone = [...arrObj]
4
+ const n = arrObjClone.length;
5
+ const firstRow = arrObjClone[0]
5
6
 
6
7
  if (n === 0) {
7
8
  return {
8
9
  scaledOutput: [],
9
- scaledConfig: {},
10
- keyNames: []
10
+ scaledConfig: {}
11
11
  };
12
12
  }
13
13
 
14
- const [rangeMin, rangeMax] = minmaxRange;
14
+ let config = {}
15
15
 
16
- if (rangeMin >= rangeMax) {
17
- throw new Error('Invalid minmaxRange: rangeMin must be less than rangeMax');
16
+ const isValidPrevConfig = prevConfig && validateConfig(prevConfig)
17
+
18
+ if(isValidPrevConfig)
19
+ {
20
+ validateCurrPrevConfig(prevConfig, {minmaxRange, repeat, groups, firstRow})
21
+ config = {...prevConfig}
18
22
  }
23
+ else
24
+ {
25
+ const inputKeyNames = Object.keys(firstRow);
19
26
 
20
- const keyNames = Object.keys(arrObj[0]);
27
+ const repeatedKeyNames = inputKeyNames.map(key => {
28
+ return repeat.hasOwnProperty(key) ? Math.max(repeat[key], 1) : 1;
29
+ });
30
+
31
+ const countRepeatedKeyNames = repeatedKeyNames.reduce((sum, rep) => sum + rep, 0);
32
+
33
+ config = {
34
+ rangeMin: minmaxRange[0],
35
+ rangeMax: minmaxRange[1],
36
+ inputTypes: {},
37
+ min: {},
38
+ max: {},
39
+ uniqueStrIdx: {},
40
+ groupMinMax: {},
41
+ repeat,
42
+ groups,
43
+ inputKeyNames,
44
+ outputKeyNames: new Array(countRepeatedKeyNames),
45
+ repeatedKeyNames,
46
+ }
47
+
48
+ let keyNamesIdx = 0;
21
49
 
22
- const repeatedKeyNames = keyNames.map(key => {
23
- return repeat.hasOwnProperty(key) ? Math.max(repeat[key], 1) : 1;
24
- });
50
+ for (let i = 0; i < config.inputKeyNames.length; i++) {
51
+ for (let w = 0; w < config.repeatedKeyNames[i]; w++) {
52
+ config.outputKeyNames[keyNamesIdx++] = config.inputKeyNames[i];
53
+ }
54
+ }
25
55
 
26
- const totalColumns = repeatedKeyNames.reduce((sum, rep) => sum + rep, 0);
56
+ validateConfig(config)
27
57
 
28
- const outputKeyNames = new Array(totalColumns);
29
- let idx = 0;
30
- for (let i = 0; i < keyNames.length; i++) {
31
- for (let w = 0; w < repeatedKeyNames[i]; w++) {
32
- outputKeyNames[idx++] = keyNames[i];
33
- }
34
58
  }
35
59
 
36
- const inputTypes = {};
37
- const min = {};
38
- const max = {};
39
- const uniqueStringIndexes = {};
40
- const groupMinMax = {};
60
+ validateUniqueProperties(config.groups);
61
+
62
+ for (const key of config.inputKeyNames) {
41
63
 
42
- for (const key of keyNames) {
43
- const firstValue = arrObj[0][key];
44
- inputTypes[key] = typeof firstValue;
64
+ const firstType = typeof firstRow[key]
65
+
66
+ if(isValidPrevConfig)
67
+ {
68
+ if(!config.inputTypes.hasOwnProperty(key))
69
+ {
70
+ // If prevConfig is set, no new inputTypes can be introduced
71
+ throw new Error(`Error: A new unknown inputType property "${key}" found.`)
72
+ }
73
+ if(config.inputTypes[key] !== firstType)
74
+ {
75
+ //is prevConfig is set the types of "typeof firstRow[key]" and config.inputTypes[key] must be the same
76
+ throw new Error(`Error: Current inputType of property "${key}" is not the same as in the prevConfig inputType.`)
77
+ }
45
78
 
46
- if (inputTypes[key] === 'string') {
47
- uniqueStringIndexes[key] = {};
79
+ continue;
48
80
  }
49
81
 
82
+ config.inputTypes[key] = firstType;
83
+
84
+ if (firstType === 'string') {
85
+ config.uniqueStrIdx[key] = {};
86
+ }
50
87
 
88
+ const thisGroup = findGroup(key, config.groups);
51
89
 
52
- const thisGroup = findGroup(key, groups);
53
90
  if (thisGroup) {
54
- if (!groupMinMax[thisGroup]) {
55
- groupMinMax[thisGroup] = { min: Infinity, max: -Infinity };
56
- }
91
+ config.groupMinMax[thisGroup] = { min: Infinity, max: -Infinity };
57
92
  } else
58
93
  {
59
- min[key] = Infinity;
60
- max[key] = -Infinity;
94
+ config.min[key] = Infinity;
95
+ config.max[key] = -Infinity;
61
96
  }
62
97
  }
63
98
 
64
- for (const obj of arrObj) {
65
- for (const key of keyNames) {
99
+ for (const obj of arrObjClone) {
100
+ for (const key of config.inputKeyNames) {
66
101
  let value = obj[key];
67
102
 
68
- if (inputTypes[key] === 'string') {
69
- const uniqueIndexes = uniqueStringIndexes[key];
103
+ if (config.inputTypes[key] === 'string') {
104
+ const uniqueIndexes = config.uniqueStrIdx[key];
70
105
  if (!uniqueIndexes.hasOwnProperty(value)) {
71
106
  uniqueIndexes[value] = Object.keys(uniqueIndexes).length;
72
107
  }
73
108
  value = uniqueIndexes[value];
74
109
  obj[key] = value;
75
- } else if (inputTypes[key] === 'boolean') {
110
+ } else if (config.inputTypes[key] === 'boolean') {
76
111
  obj[key] = Number(value);
77
112
  }
78
113
 
79
- const thisGroup = findGroup(key, groups);
114
+ const thisGroup = findGroup(key, config.groups);
80
115
 
81
116
  if (thisGroup) {
82
- groupMinMax[thisGroup].min = Math.min(groupMinMax[thisGroup].min, value);
83
- groupMinMax[thisGroup].max = Math.max(groupMinMax[thisGroup].max, value);
117
+ config.groupMinMax[thisGroup].min = Math.min(config.groupMinMax[thisGroup].min, value);
118
+ config.groupMinMax[thisGroup].max = Math.max(config.groupMinMax[thisGroup].max, value);
84
119
  } else {
85
- min[key] = Math.min(min[key], value);
86
- max[key] = Math.max(max[key], value);
120
+ config.min[key] = Math.min(config.min[key], value);
121
+ config.max[key] = Math.max(config.max[key], value);
87
122
  }
88
123
  }
89
124
  }
90
125
 
91
126
  const scaledOutput = new Array(n);
92
127
  for (let i = 0; i < n; i++) {
93
- const obj = arrObj[i];
94
- const scaledRow = new Array(totalColumns);
128
+ const obj = arrObjClone[i];
129
+ const scaledRow = new Array(config.outputKeyNames.length);
95
130
  let idx = 0;
96
131
 
97
- for (let j = 0; j < keyNames.length; j++) {
98
- const key = keyNames[j];
132
+ for (let j = 0; j < config.inputKeyNames.length; j++) {
133
+ const key = config.inputKeyNames[j];
99
134
  const value = obj[key];
100
135
 
101
- const thisGroup = findGroup(key, groups);
136
+ const thisGroup = findGroup(key, config.groups);
102
137
  let minValue, maxValue;
103
138
 
104
139
  if (thisGroup) {
105
- minValue = groupMinMax[thisGroup].min;
106
- maxValue = groupMinMax[thisGroup].max;
140
+ minValue = config.groupMinMax[thisGroup].min;
141
+ maxValue = config.groupMinMax[thisGroup].max;
107
142
  } else {
108
- minValue = min[key];
109
- maxValue = max[key];
143
+ minValue = config.min[key];
144
+ maxValue = config.max[key];
110
145
  }
111
146
 
112
147
  const scaledValue =
113
148
  maxValue !== minValue
114
- ? rangeMin + ((value - minValue) / (maxValue - minValue)) * (rangeMax - rangeMin)
115
- : rangeMin;
149
+ ? config.rangeMin + ((value - minValue) / (maxValue - minValue)) * (config.rangeMax - config.rangeMin)
150
+ : config.rangeMin;
116
151
 
117
- const rep = repeatedKeyNames[j];
152
+ const rep = config.repeatedKeyNames[j];
118
153
 
119
154
  for (let w = 0; w < rep; w++) {
120
155
  scaledRow[idx++] = scaledValue;
@@ -124,15 +159,15 @@ export const scaleArrayObj = ({ arrObj, repeat = {}, minmaxRange = [0, 1], group
124
159
  scaledOutput[i] = scaledRow;
125
160
  }
126
161
 
127
- const scaledConfig = { min, max, inputTypes, uniqueStringIndexes, rangeMin, rangeMax, groupMinMax };
162
+
128
163
 
129
164
  return {
130
165
  scaledOutput,
131
- scaledConfig,
132
- scaledKeyNames: outputKeyNames
166
+ scaledConfig: config
133
167
  };
134
168
  };
135
169
 
170
+
136
171
  const validateUniqueProperties = obj => {
137
172
  const uniqueValues = new Set();
138
173
  const allValues = [];
@@ -160,3 +195,116 @@ const findGroup = (key, groups) => {
160
195
  }
161
196
  return null;
162
197
  };
198
+
199
+
200
+ const validateConfig = config => {
201
+
202
+ if(!config) return false
203
+
204
+ const requiredKeys = [
205
+ "rangeMin",
206
+ "rangeMax",
207
+ "inputTypes",
208
+ "min",
209
+ "max",
210
+ "uniqueStrIdx",
211
+ "groupMinMax",
212
+ "repeat",
213
+ "groups",
214
+ "inputKeyNames",
215
+ "outputKeyNames",
216
+ "repeatedKeyNames"
217
+ ];
218
+
219
+ // Check for missing keys
220
+ for (const key of requiredKeys) {
221
+ if (!config.hasOwnProperty(key)) {
222
+ throw new Error(`Missing key "${key}" in config.`);
223
+ }
224
+ }
225
+
226
+ const {
227
+ rangeMin,
228
+ rangeMax,
229
+ inputTypes,
230
+ min,
231
+ max,
232
+ uniqueStrIdx,
233
+ groupMinMax,
234
+ repeat,
235
+ groups,
236
+ inputKeyNames,
237
+ outputKeyNames,
238
+ repeatedKeyNames
239
+ } = config;
240
+
241
+ // Validate rangeMin and rangeMax are numbers and in proper order
242
+ if (typeof rangeMin !== 'number' || typeof rangeMax !== 'number') {
243
+ throw new Error("rangeMin and rangeMax must be numbers.");
244
+ }
245
+ if (rangeMin >= rangeMax) {
246
+ throw new Error("rangeMin must be less than rangeMax.");
247
+ }
248
+
249
+ // Helper to check if a value is a plain object (and not null or an array)
250
+ const isPlainObject = (obj) => typeof obj === 'object' && obj !== null && !Array.isArray(obj);
251
+
252
+ if (!isPlainObject(inputTypes)) {
253
+ throw new Error("inputTypes must be an object.");
254
+ }
255
+ if (!isPlainObject(min)) {
256
+ throw new Error("min must be an object.");
257
+ }
258
+ if (!isPlainObject(max)) {
259
+ throw new Error("max must be an object.");
260
+ }
261
+ if (!isPlainObject(uniqueStrIdx)) {
262
+ throw new Error("uniqueStrIdx must be an object.");
263
+ }
264
+ if (!isPlainObject(groupMinMax)) {
265
+ throw new Error("groupMinMax must be an object.");
266
+ }
267
+ if (!isPlainObject(repeat)) {
268
+ throw new Error("repeat must be an object.");
269
+ }
270
+ if (!isPlainObject(groups)) {
271
+ throw new Error("groups must be an object.");
272
+ }
273
+ if(!Array.isArray(inputKeyNames))
274
+ {
275
+ throw new Error("inputKeyNames must be an array.");
276
+ }
277
+ if(!Array.isArray(outputKeyNames))
278
+ {
279
+ throw new Error("outputKeyNames must be an array.");
280
+ }
281
+ if(!Array.isArray(repeatedKeyNames))
282
+ {
283
+ throw new Error("repeatedKeyNames must be an array.");
284
+ }
285
+
286
+ return true;
287
+ }
288
+
289
+ const validateCurrPrevConfig = (prevConfig, {minmaxRange, repeat, groups, firstRow}) => {
290
+
291
+ if(prevConfig.rangeMin !== minmaxRange[0] || prevConfig.rangeMax !== minmaxRange[1])
292
+ {
293
+ throw new Error(`"prevConfig.minmaxRange" is not equal "minmaxRange".`);
294
+ }
295
+
296
+ //it is important o keep the same key order
297
+ if (JSON.stringify(prevConfig.inputKeyNames) !== JSON.stringify(Object.keys(firstRow))) {
298
+ throw new Error(`"prevConfig.inputKeyNames" structure does not match "Object.keys(firstRow)" structure. The order of keys is important.`);
299
+ }
300
+
301
+ if (JSON.stringify(prevConfig.repeat) !== JSON.stringify(repeat)) {
302
+ throw new Error(`"prevConfig.repeat" structure does not match "repeat" structure. The order of keys is important.`);
303
+ }
304
+
305
+ if (JSON.stringify(prevConfig.groups) !== JSON.stringify(groups)) {
306
+ throw new Error(`"prevConfig.groups" structure does not match "groups" structure. The order of keys is important.`);
307
+ }
308
+
309
+ return true
310
+ }
package/test/test.js CHANGED
@@ -15,9 +15,10 @@ const test = async () => {
15
15
  //returning null or undefined will exclude current row X and Y from training
16
16
  if(typeof prev === 'undefined') return null
17
17
 
18
- const { open, high, low, close, volume } = curr
18
+ const { open, high, low, close, volume, date } = curr
19
19
 
20
20
  return {
21
+ date,
21
22
  open,
22
23
  high,
23
24
  low,
@@ -82,10 +83,10 @@ const test = async () => {
82
83
  const inputX = tf.tensor3d(timeSteppedTrainX, [timeSteppedTrainX.length, timeSteps, colsX])
83
84
  const targetY = tf.tensor2d(trimedTrainY, [trimedTrainY.length, colsY])
84
85
 
85
- console.log('trainX', trainX[trainX.length - 1])
86
- console.log('configX', keyNamesX)
87
- console.log('inputX', inputX)
88
- console.log('inputX', targetY)
86
+ //console.log('trainX', trainX[trainX.length - 1])
87
+ console.log('configX', configX)
88
+ //console.log('inputX', inputX)
89
+ //console.log('inputX', targetY)
89
90
  }
90
91
 
91
92
  test()