@datagrok/eda 1.1.9 → 1.1.11
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/.eslintignore +1 -0
- package/.eslintrc.json +45 -0
- package/CHANGELOG.md +21 -13
- package/README.md +2 -0
- package/dist/100.js +2 -2
- package/dist/42.js +2 -0
- package/dist/729.js +1 -1
- package/dist/935.js +3 -0
- package/dist/package-test.js +2 -2
- package/dist/package.js +2 -2
- package/package.json +7 -3
- package/src/data-generators.ts +13 -13
- package/src/eda-tools.ts +42 -42
- package/src/eda-ui.ts +65 -58
- package/src/missing-values-imputation/knn-imputer.ts +468 -0
- package/src/missing-values-imputation/ui-constants.ts +64 -0
- package/src/missing-values-imputation/ui.ts +246 -0
- package/src/package-test.ts +2 -2
- package/src/package.ts +61 -60
- package/src/stat-tools.ts +72 -61
- package/src/svm.ts +144 -151
- package/src/utils.ts +13 -17
- package/src/workers/tsne-worker.ts +6 -6
- package/src/workers/umap-worker.ts +3 -3
- package/dist/943.js +0 -3
- /package/dist/{943.js.LICENSE.txt → 935.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
// Tools for missing values imputation using the k-nearest neighbors method
|
|
2
|
+
|
|
3
|
+
import * as grok from 'datagrok-api/grok';
|
|
4
|
+
import * as ui from 'datagrok-api/ui';
|
|
5
|
+
import * as DG from 'datagrok-api/dg';
|
|
6
|
+
|
|
7
|
+
import {ERROR_MSG, COPY_SUFFIX} from './ui-constants';
|
|
8
|
+
|
|
9
|
+
/** Column types supported by the missing values imputer */
|
|
10
|
+
export const SUPPORTED_COLUMN_TYPES = [
|
|
11
|
+
DG.COLUMN_TYPE.INT,
|
|
12
|
+
DG.COLUMN_TYPE.FLOAT,
|
|
13
|
+
DG.COLUMN_TYPE.STRING,
|
|
14
|
+
DG.COLUMN_TYPE.DATE_TIME,
|
|
15
|
+
DG.COLUMN_TYPE.QNUM,
|
|
16
|
+
] as string[];
|
|
17
|
+
|
|
18
|
+
/** Return null value with respect to the column type */
|
|
19
|
+
export function getNullValue(col: DG.Column): number {
|
|
20
|
+
switch (col.type) {
|
|
21
|
+
case DG.COLUMN_TYPE.INT:
|
|
22
|
+
return DG.INT_NULL;
|
|
23
|
+
|
|
24
|
+
case DG.COLUMN_TYPE.FLOAT:
|
|
25
|
+
return DG.FLOAT_NULL;
|
|
26
|
+
|
|
27
|
+
case DG.COLUMN_TYPE.QNUM:
|
|
28
|
+
return DG.FLOAT_NULL;
|
|
29
|
+
|
|
30
|
+
case DG.COLUMN_TYPE.DATE_TIME:
|
|
31
|
+
return DG.FLOAT_NULL;
|
|
32
|
+
|
|
33
|
+
case DG.COLUMN_TYPE.STRING:
|
|
34
|
+
return col.max;
|
|
35
|
+
|
|
36
|
+
default:
|
|
37
|
+
throw new Error(ERROR_MSG.UNSUPPORTED_COLUMN_TYPE);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Metric types (between column elements) */
|
|
42
|
+
export enum METRIC_TYPE {
|
|
43
|
+
ONE_HOT = 'One-hot',
|
|
44
|
+
DIFFERENCE = 'Difference',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Distance types (over several columns). */
|
|
48
|
+
export enum DISTANCE_TYPE {
|
|
49
|
+
EUCLIDEAN = 'Euclidean',
|
|
50
|
+
MANHATTAN = 'Manhattan',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Metric specification. */
|
|
54
|
+
export type MetricInfo = {
|
|
55
|
+
weight: number,
|
|
56
|
+
type: METRIC_TYPE,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Default values */
|
|
60
|
+
export enum DEFAULT {
|
|
61
|
+
WEIGHT = 1,
|
|
62
|
+
NEIGHBORS = 4,
|
|
63
|
+
IN_PLACE = 1,
|
|
64
|
+
SELECTED = 1,
|
|
65
|
+
KEEP_EMPTY = 0,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** Min number of neighbors for KNN */
|
|
69
|
+
export const MIN_NEIGHBORS = 1;
|
|
70
|
+
|
|
71
|
+
/** Dataframe item: index - number of row, dist - distance to the target element */
|
|
72
|
+
type Item = {
|
|
73
|
+
index: number,
|
|
74
|
+
dist: number,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** Impute missing values using the KNN method and returns an array of items for which an imputation fails */
|
|
78
|
+
export function impute(df: DG.DataFrame, targetColNames: string[], featuresMetrics: Map<string, MetricInfo>,
|
|
79
|
+
missingValsIndices: Map<string, number[]>, distance: DISTANCE_TYPE, neighbors: number, inPlace: boolean): Map<string, number[]>
|
|
80
|
+
{
|
|
81
|
+
// 1. Check inputs completness
|
|
82
|
+
|
|
83
|
+
if (neighbors < MIN_NEIGHBORS)
|
|
84
|
+
throw new Error(ERROR_MSG.INCORRECT_NEIGHBORS);
|
|
85
|
+
|
|
86
|
+
if (df.rowCount < 2)
|
|
87
|
+
throw new Error(ERROR_MSG.KNN_NOT_ENOUGH_OF_ROWS);
|
|
88
|
+
|
|
89
|
+
if (targetColNames.length === 0)
|
|
90
|
+
throw new Error(ERROR_MSG.KNN_NO_TARGET_COLUMNS);
|
|
91
|
+
|
|
92
|
+
if (featuresMetrics.size === 0)
|
|
93
|
+
throw new Error(ERROR_MSG.KNN_NO_FEATURE_COLUMNS);
|
|
94
|
+
|
|
95
|
+
if (featuresMetrics.size === 1)
|
|
96
|
+
targetColNames.forEach((name) => {
|
|
97
|
+
if (featuresMetrics.has(name))
|
|
98
|
+
throw new Error(`${ERROR_MSG.KNN_NO_FEATURE_COLUMNS} can be used for the column '${name}'`);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
targetColNames.forEach((name) => {
|
|
102
|
+
if (!missingValsIndices.has(name))
|
|
103
|
+
throw new Error(`${ERROR_MSG.KNN_FAILS}: ${ERROR_MSG.WRONG_PREDICTIONS}`);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const columns = df.columns;
|
|
107
|
+
|
|
108
|
+
// 2. Imputation
|
|
109
|
+
|
|
110
|
+
targetColNames.forEach((name) => {
|
|
111
|
+
if (!SUPPORTED_COLUMN_TYPES.includes(columns.byName(name).type))
|
|
112
|
+
throw new Error(ERROR_MSG.UNSUPPORTED_COLUMN_TYPE);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
featuresMetrics.forEach((val, name) => {
|
|
116
|
+
if (!SUPPORTED_COLUMN_TYPES.includes(df.getCol(name).type))
|
|
117
|
+
throw new Error(ERROR_MSG.UNSUPPORTED_COLUMN_TYPE);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/** Failed to impute items */
|
|
121
|
+
const failedToImpute = new Map<string, number[]>();
|
|
122
|
+
|
|
123
|
+
// 2. Missing values imputation in each target column
|
|
124
|
+
targetColNames.forEach((name) => {
|
|
125
|
+
const col = columns.byName(name);
|
|
126
|
+
const nullValue = getNullValue(col);
|
|
127
|
+
const len = col.length;
|
|
128
|
+
const source = col.getRawData();
|
|
129
|
+
const frequencies = new Uint16Array(col.categories.length);
|
|
130
|
+
|
|
131
|
+
const featureSource = [] as Array<Int32Array | Uint32Array | Float32Array | Float64Array>;
|
|
132
|
+
const featureNullVal = [] as number[];
|
|
133
|
+
const metricFunc = [] as ((a: number, b: number) => number)[];
|
|
134
|
+
|
|
135
|
+
const failedToImputeIndices = [] as number[];
|
|
136
|
+
|
|
137
|
+
// create features tools
|
|
138
|
+
featuresMetrics.forEach((metricInfo, name) => {
|
|
139
|
+
if (name !== col.name) {
|
|
140
|
+
const feature = columns.byName(name);
|
|
141
|
+
featureSource.push(feature.getRawData());
|
|
142
|
+
featureNullVal.push(getNullValue(feature));
|
|
143
|
+
|
|
144
|
+
switch (metricInfo.type) {
|
|
145
|
+
case METRIC_TYPE.DIFFERENCE:
|
|
146
|
+
metricFunc.push((a: number, b: number) => metricInfo.weight * Math.abs(a - b));
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case METRIC_TYPE.ONE_HOT:
|
|
150
|
+
metricFunc.push((a: number, b: number) => metricInfo.weight * ((a === b) ? 0 : 1));
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
default:
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}});
|
|
157
|
+
|
|
158
|
+
const featuresCount = featureSource.length;
|
|
159
|
+
const properIndices = new Uint32Array(featureSource.length);
|
|
160
|
+
const bufferVector = new Float32Array(featureSource.length);
|
|
161
|
+
let properIndicesCount = 0;
|
|
162
|
+
|
|
163
|
+
// closest items
|
|
164
|
+
const nearestItems = new Array<Item>(neighbors);
|
|
165
|
+
let nearestItemsCount = 0;
|
|
166
|
+
|
|
167
|
+
// auxiliry variables
|
|
168
|
+
let maxInd = 0;
|
|
169
|
+
let maxDist = 0;
|
|
170
|
+
let sum = 0;
|
|
171
|
+
let fillValue = 0;
|
|
172
|
+
|
|
173
|
+
/** Obtain proper indices for KNN: features with missing vals are skipped */
|
|
174
|
+
const getProperIndeces = (idx: number) => {
|
|
175
|
+
properIndicesCount = 0;
|
|
176
|
+
|
|
177
|
+
for (let i = 0; i < featuresCount; ++i)
|
|
178
|
+
if (featureSource[i][idx] !== featureNullVal[i]) {
|
|
179
|
+
properIndices[properIndicesCount] = i;
|
|
180
|
+
++properIndicesCount;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/** Compute buffer vector */
|
|
185
|
+
const computeBufferVector = (idx: number, cur: number) => {
|
|
186
|
+
properIndices.forEach((properIndex, k) => {
|
|
187
|
+
bufferVector[k] = metricFunc[properIndex](featureSource[properIndex][idx], featureSource[properIndex][cur]);
|
|
188
|
+
})
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/** Euclidean distance function */
|
|
192
|
+
const euclideanDistFunc = () => {
|
|
193
|
+
let sum = 0;
|
|
194
|
+
|
|
195
|
+
for (let i = 0; i < properIndicesCount; ++i)
|
|
196
|
+
sum +=bufferVector[i] * bufferVector[i];
|
|
197
|
+
|
|
198
|
+
return Math.sqrt(sum);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
/** Manhattan distance function */
|
|
202
|
+
const manhattanDistFunc = () => {
|
|
203
|
+
let sum = 0;
|
|
204
|
+
|
|
205
|
+
for (let i = 0; i < properIndicesCount; ++i)
|
|
206
|
+
sum += Math.abs(bufferVector[i]);
|
|
207
|
+
|
|
208
|
+
return Math.sqrt(sum);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/** Return norm of the buffer vector (distance between i-th & j-th elements) */
|
|
212
|
+
const dist = (distance === DISTANCE_TYPE.EUCLIDEAN) ? euclideanDistFunc : manhattanDistFunc;
|
|
213
|
+
|
|
214
|
+
/** Check if the current item (i.e. table row) can be used */
|
|
215
|
+
const canItemBeUsed = (cur: number) => {
|
|
216
|
+
if (source[cur] === nullValue)
|
|
217
|
+
return false;
|
|
218
|
+
|
|
219
|
+
for (let i = 0; i < properIndicesCount; ++i)
|
|
220
|
+
if (featureSource[properIndices[i]][cur] === featureNullVal[properIndices[i]])
|
|
221
|
+
return false;
|
|
222
|
+
|
|
223
|
+
return true;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/** Return the most frequent of the nearest items (for categorial data) */
|
|
227
|
+
const mostFrequentOfTheNearestItems = () => {
|
|
228
|
+
frequencies.forEach((v, i,arr) => arr[i] = 0);
|
|
229
|
+
let i = 0;
|
|
230
|
+
|
|
231
|
+
for (i = 0; i < nearestItemsCount; ++i)
|
|
232
|
+
++frequencies[source[nearestItems[i].index]];
|
|
233
|
+
|
|
234
|
+
let maxFreq = frequencies[0];
|
|
235
|
+
let maxFreqIdx = 0;
|
|
236
|
+
|
|
237
|
+
frequencies.forEach((v, i) => {
|
|
238
|
+
if (v > maxFreq) {
|
|
239
|
+
maxFreq = v;
|
|
240
|
+
maxFreqIdx = i;
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
return maxFreqIdx;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/** Get imputation value */
|
|
248
|
+
const getFillValue = (idx: number) => {
|
|
249
|
+
getProperIndeces(idx);
|
|
250
|
+
|
|
251
|
+
// check available features
|
|
252
|
+
if (properIndicesCount === 0)
|
|
253
|
+
throw new Error(`${ERROR_MSG.KNN_IMPOSSIBLE_IMPUTATION}: the column "${col.name}", row ${idx + 1}`);
|
|
254
|
+
|
|
255
|
+
nearestItemsCount = 0;
|
|
256
|
+
|
|
257
|
+
// search for the closest items
|
|
258
|
+
for (let cur = 0; cur < len; ++cur)
|
|
259
|
+
if (canItemBeUsed(cur) && (cur !== idx)) {
|
|
260
|
+
// 1) compute distance between cur-th and idx-th items
|
|
261
|
+
computeBufferVector(idx, cur);
|
|
262
|
+
const curDist = dist();
|
|
263
|
+
|
|
264
|
+
// 2) insert the current item
|
|
265
|
+
if (nearestItemsCount < neighbors) {
|
|
266
|
+
nearestItems[nearestItemsCount] = {index: cur, dist: curDist};
|
|
267
|
+
++nearestItemsCount;
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
// 2.1) find the farest
|
|
271
|
+
maxInd = 0;
|
|
272
|
+
maxDist = nearestItems[0].dist;
|
|
273
|
+
|
|
274
|
+
for(let i = 1; i < nearestItemsCount; ++i)
|
|
275
|
+
if (maxDist < nearestItems[i].dist) {
|
|
276
|
+
maxDist = nearestItems[i].dist;
|
|
277
|
+
maxInd = i;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// 2.2) replace
|
|
281
|
+
if (curDist < maxDist)
|
|
282
|
+
nearestItems[maxInd] = {index: cur, dist: curDist};
|
|
283
|
+
} // else
|
|
284
|
+
} // for cur
|
|
285
|
+
|
|
286
|
+
// check found nearest items
|
|
287
|
+
if (nearestItemsCount === 0)
|
|
288
|
+
throw new Error(`${ERROR_MSG.KNN_IMPOSSIBLE_IMPUTATION}: the column "${col.name}", row ${idx + 1}`);
|
|
289
|
+
|
|
290
|
+
if (col.type === DG.COLUMN_TYPE.STRING)
|
|
291
|
+
return mostFrequentOfTheNearestItems();
|
|
292
|
+
|
|
293
|
+
// compute fill value
|
|
294
|
+
sum = 0;
|
|
295
|
+
for (let i = 0; i < nearestItemsCount; ++i)
|
|
296
|
+
sum += source[nearestItems[i].index];
|
|
297
|
+
|
|
298
|
+
fillValue = sum / nearestItemsCount;
|
|
299
|
+
|
|
300
|
+
if (col.type === DG.COLUMN_TYPE.INT)
|
|
301
|
+
return Math.round(fillValue);
|
|
302
|
+
|
|
303
|
+
return fillValue;
|
|
304
|
+
}; // getFillValue
|
|
305
|
+
|
|
306
|
+
if (inPlace) {
|
|
307
|
+
// use indices found previousely
|
|
308
|
+
for (const i of missingValsIndices.get(name)!)
|
|
309
|
+
try {
|
|
310
|
+
source[i] = getFillValue(i);
|
|
311
|
+
} catch (err) {
|
|
312
|
+
failedToImputeIndices.push(i);
|
|
313
|
+
|
|
314
|
+
if (!(err instanceof Error))
|
|
315
|
+
grok.shell.error(ERROR_MSG.CORE_ISSUE);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (failedToImputeIndices.length > 0)
|
|
319
|
+
failedToImpute.set(name, failedToImputeIndices);
|
|
320
|
+
|
|
321
|
+
// to reset view
|
|
322
|
+
col.set(0, col.get(0));
|
|
323
|
+
} // if
|
|
324
|
+
else {
|
|
325
|
+
//@ts-ignore
|
|
326
|
+
const copy = col.clone();
|
|
327
|
+
|
|
328
|
+
let i = 1;
|
|
329
|
+
let copyName = `${name}(${COPY_SUFFIX})`;
|
|
330
|
+
|
|
331
|
+
// find an appropriate name
|
|
332
|
+
while (df.columns.contains(copyName)) {
|
|
333
|
+
copyName = `${name}(${COPY_SUFFIX} ${i})`;
|
|
334
|
+
++i;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
copy.name = copyName;
|
|
338
|
+
|
|
339
|
+
const copySource = copy.getRawData();
|
|
340
|
+
|
|
341
|
+
// use indices found previousely
|
|
342
|
+
for (const i of missingValsIndices.get(name)!)
|
|
343
|
+
try {
|
|
344
|
+
copySource[i] = getFillValue(i);
|
|
345
|
+
} catch (err) {
|
|
346
|
+
failedToImputeIndices.push(i);
|
|
347
|
+
|
|
348
|
+
if (!(err instanceof Error))
|
|
349
|
+
grok.shell.error(ERROR_MSG.CORE_ISSUE);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (failedToImputeIndices.length > 0)
|
|
353
|
+
failedToImpute.set(copyName, failedToImputeIndices);
|
|
354
|
+
|
|
355
|
+
copy.set(0, copy.get(0));
|
|
356
|
+
|
|
357
|
+
df.columns.add(copy);
|
|
358
|
+
} // else
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
return failedToImpute;
|
|
362
|
+
} // impute
|
|
363
|
+
|
|
364
|
+
/** Return indices of missing values for each column */
|
|
365
|
+
export function getMissingValsIndices(columns: DG.Column[]): Map<string, number[]> {
|
|
366
|
+
const misValsInds = new Map<string, number[]>();
|
|
367
|
+
|
|
368
|
+
for (const col of columns) {
|
|
369
|
+
if (!SUPPORTED_COLUMN_TYPES.includes(col.type))
|
|
370
|
+
throw new Error(ERROR_MSG.UNSUPPORTED_COLUMN_TYPE);
|
|
371
|
+
|
|
372
|
+
if (col.stats.missingValueCount === 0)
|
|
373
|
+
continue;
|
|
374
|
+
|
|
375
|
+
const indices = [] as number[];
|
|
376
|
+
const nullValue = getNullValue(col);
|
|
377
|
+
|
|
378
|
+
col.getRawData().forEach((val, idx) => {
|
|
379
|
+
if (val === nullValue)
|
|
380
|
+
indices.push(idx);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
misValsInds.set(col.name, indices);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return misValsInds;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/** Predict existence of missing values imputation fails */
|
|
390
|
+
export function areThereFails(targetColNames: string[], featureColNames: string[], misValsInds: Map<string, number[]>): boolean {
|
|
391
|
+
// check feature columns
|
|
392
|
+
for (const name of featureColNames)
|
|
393
|
+
if (!misValsInds.has(name))
|
|
394
|
+
return false;
|
|
395
|
+
|
|
396
|
+
// check target columns
|
|
397
|
+
for (const target of targetColNames) {
|
|
398
|
+
const indices = misValsInds.get(target);
|
|
399
|
+
|
|
400
|
+
if (indices === undefined)
|
|
401
|
+
throw new Error(ERROR_MSG.FAILS_TO_PREDICT_IMPUTATION_FAILS);
|
|
402
|
+
|
|
403
|
+
for (const idx of indices) {
|
|
404
|
+
let failToImpute = true;
|
|
405
|
+
|
|
406
|
+
for (const feature of featureColNames) {
|
|
407
|
+
const featureInds = misValsInds.get(feature);
|
|
408
|
+
|
|
409
|
+
if (featureInds === undefined)
|
|
410
|
+
throw new Error(ERROR_MSG.FAILS_TO_PREDICT_IMPUTATION_FAILS);
|
|
411
|
+
|
|
412
|
+
if (!featureInds.includes(idx)) {
|
|
413
|
+
failToImpute = false;
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (failToImpute)
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return false;
|
|
424
|
+
} // predictFails
|
|
425
|
+
|
|
426
|
+
/** Returns first non-null value */
|
|
427
|
+
function getFirstNonNull<T>(col: DG.Column<T>): T {
|
|
428
|
+
const nullValue = getNullValue(col);
|
|
429
|
+
const raw = col.getRawData();
|
|
430
|
+
const len = raw.length;
|
|
431
|
+
|
|
432
|
+
for (let i = 0; i < len; ++i)
|
|
433
|
+
if (raw[i] !== nullValue)
|
|
434
|
+
return col.get(i)!;
|
|
435
|
+
|
|
436
|
+
throw new Error(ERROR_MSG.EMPTY_COLUMN);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** Return default fill value with respect to the column type */
|
|
440
|
+
function getDefaultFillValue<T>(col: DG.Column<T>): T {
|
|
441
|
+
switch (col.type) {
|
|
442
|
+
case DG.COLUMN_TYPE.STRING:
|
|
443
|
+
case DG.COLUMN_TYPE.DATE_TIME:
|
|
444
|
+
return getFirstNonNull(col); // TODO: replace by most frequent
|
|
445
|
+
|
|
446
|
+
case DG.COLUMN_TYPE.INT:
|
|
447
|
+
case DG.COLUMN_TYPE.FLOAT:
|
|
448
|
+
case DG.COLUMN_TYPE.QNUM:
|
|
449
|
+
return col.stats.avg as T;
|
|
450
|
+
|
|
451
|
+
default:
|
|
452
|
+
throw new Error(ERROR_MSG.UNSUPPORTED_COLUMN_TYPE);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Perform missing values imputation using the simple approach */
|
|
457
|
+
export function imputeFailed(df: DG.DataFrame, failedToImpute: Map<string, number[]>): void {
|
|
458
|
+
failedToImpute.forEach((indices, colName) => {
|
|
459
|
+
const col = df.col(colName);
|
|
460
|
+
if (col !== null) {
|
|
461
|
+
if (!SUPPORTED_COLUMN_TYPES.includes(col.type))
|
|
462
|
+
throw new Error(ERROR_MSG.UNSUPPORTED_COLUMN_TYPE);
|
|
463
|
+
|
|
464
|
+
const fillVal = getDefaultFillValue(col);
|
|
465
|
+
indices.forEach((idx) => col.set(idx, fillVal));
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** Error & info messages */
|
|
2
|
+
export enum ERROR_MSG {
|
|
3
|
+
NO_DATAFRAME = 'No dataframe is opened',
|
|
4
|
+
NO_MISSING_VALUES = 'No missing values',
|
|
5
|
+
ONE_AVAILABLE_FEATURE = 'Not enough of feature columns to apply imputation using the KNN method',
|
|
6
|
+
ONE_FEATURE_SELECTED = 'Imputation cannot be applied to',
|
|
7
|
+
UNSUPPORTED_COLUMN_TYPE = 'Unsupported column type',
|
|
8
|
+
UNSUPPORTED_IMPUTATION_STRATEGY = 'Unsupported imputation strategy',
|
|
9
|
+
KNN_CANNOT_BE_APPLIED = 'KNN imputer cannot be applied: no columns to be used as features',
|
|
10
|
+
KNN_NO_TARGET_COLUMNS = 'KNN imputer cannot be applied: no columns with missing values',
|
|
11
|
+
KNN_NO_FEATURE_COLUMNS = 'KNN imputer cannot be applied: no feature columns',
|
|
12
|
+
KNN_NOT_ENOUGH_OF_ROWS = 'KNN imputer cannot be applied: not enough of rows',
|
|
13
|
+
KNN_IMPOSSIBLE_IMPUTATION = 'Imputation is impossible, no features can be used',
|
|
14
|
+
INCORRECT_NEIGHBORS = 'Incorrect number of neighbors',
|
|
15
|
+
KNN_FAILS = 'KNN IMPUTATION FAILS',
|
|
16
|
+
CORE_ISSUE = 'Core issue',
|
|
17
|
+
FAILED_TO_IMPUTE = 'Failed to impute',
|
|
18
|
+
UNSUPPORTED_FILL_VALUE_TYPE = 'Unsupported fill value type',
|
|
19
|
+
EMPTY_COLUMN = 'Column contains just null values',
|
|
20
|
+
FAILS_TO_PREDICT_IMPUTATION_FAILS = 'Failed to predict imputation fails',
|
|
21
|
+
WRONG_PREDICTIONS = 'wrong evaluation of KNN imputation fails',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** Suffix used for column copy */
|
|
25
|
+
export const COPY_SUFFIX = 'copy';
|
|
26
|
+
|
|
27
|
+
/** UI titles */
|
|
28
|
+
export enum TITLE {
|
|
29
|
+
KNN_IMPUTER = 'Impute',
|
|
30
|
+
TABLE = 'Table',
|
|
31
|
+
IN_PLACE = 'In-place',
|
|
32
|
+
COLUMNS = 'Impute',
|
|
33
|
+
FEATURES = 'Using',
|
|
34
|
+
CANCEL = 'CANCEL',
|
|
35
|
+
RUN = 'RUN',
|
|
36
|
+
OK = 'OK',
|
|
37
|
+
NEIGHBORS = 'Neighbors',
|
|
38
|
+
DISTANCE = 'Distance',
|
|
39
|
+
FILL = 'Fill',
|
|
40
|
+
MARK = 'Mark',
|
|
41
|
+
SIMPLE_IMPUTER = 'Simple impute',
|
|
42
|
+
SETTINGS = 'Settings',
|
|
43
|
+
KEEP_EMPTY = 'Keep empty',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Help links */
|
|
47
|
+
export const KNN_IMPUTER = '/help/transform/missing-values-imputation';
|
|
48
|
+
|
|
49
|
+
/** Tooltips */
|
|
50
|
+
export enum HINT {
|
|
51
|
+
TARGET = 'Columns with missing values that must be filled',
|
|
52
|
+
FEATURES = "Columns with features to be used for determining the 'nearest' elements in the KNN method",
|
|
53
|
+
IN_PLACE = 'Defines whether to use in-place imputation or add a new column without missing values',
|
|
54
|
+
METRIC = 'Type of metric between the feature values',
|
|
55
|
+
WEIGHT = 'Weight',
|
|
56
|
+
NEIGHBORS = 'Neighbors count used in the KNN method',
|
|
57
|
+
DISTANCE = 'Type of distance between elements with the specified features',
|
|
58
|
+
METRIC_SETTINGS = 'Show additional options',
|
|
59
|
+
FILL_FAILED_ITEMS = 'Impute missing values using a simple approach: mean, median or most frequent',
|
|
60
|
+
MARK_FAILED_ITEMS = 'Mark missing values cells with a color',
|
|
61
|
+
FILL_VALUE = 'Fill value',
|
|
62
|
+
IMPUTATION_SETTINGS = 'Simple imputation settings',
|
|
63
|
+
KEEP_EMPTY = 'Defines whether to keep empty missing values failed to be imputed OR fill them using simple imputation',
|
|
64
|
+
};
|