@bicharts/shape-core 0.1.12 → 0.1.14
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/chunk-2LR73DNS.mjs +2 -0
- package/dist/chunk-3UOCZ2D6.mjs +2 -0
- package/dist/chunk-GY54KQLP.mjs +2 -0
- package/dist/chunk-NWDPA7DG.mjs +2 -0
- package/dist/chunk-QEH7WTLB.mjs +2 -0
- package/dist/chunk-WBEROMBG.mjs +2 -0
- package/dist/chunk-XWCQ7BJB.mjs +2 -0
- package/dist/formatDetector.mjs +1 -7
- package/dist/geoDetector.mjs +1 -14
- package/dist/geoPoint.mjs +1 -23
- package/dist/index.mjs +1 -250
- package/dist/indexedText.mjs +1 -23
- package/dist/models.mjs +0 -1
- package/dist/monthNames.mjs +1 -9
- package/dist/ordinalDetector.mjs +1 -11
- package/dist/util.mjs +1 -11
- package/package.json +2 -2
- package/dist/chunk-ICDJLWEM.mjs +0 -83
- package/dist/chunk-ICDJLWEM.mjs.map +0 -7
- package/dist/chunk-NGV56PGT.mjs +0 -787
- package/dist/chunk-NGV56PGT.mjs.map +0 -7
- package/dist/chunk-QTEE53OQ.mjs +0 -205
- package/dist/chunk-QTEE53OQ.mjs.map +0 -7
- package/dist/chunk-RZAZ7PZK.mjs +0 -31
- package/dist/chunk-RZAZ7PZK.mjs.map +0 -7
- package/dist/chunk-VHWC3THS.mjs +0 -572
- package/dist/chunk-VHWC3THS.mjs.map +0 -7
- package/dist/chunk-VLMGGCN4.mjs +0 -36
- package/dist/chunk-VLMGGCN4.mjs.map +0 -7
- package/dist/chunk-YJVU7G63.mjs +0 -559
- package/dist/chunk-YJVU7G63.mjs.map +0 -7
- package/dist/formatDetector.mjs.map +0 -7
- package/dist/geoDetector.mjs.map +0 -7
- package/dist/geoPoint.mjs.map +0 -7
- package/dist/index.mjs.map +0 -7
- package/dist/indexedText.mjs.map +0 -7
- package/dist/models.mjs.map +0 -7
- package/dist/monthNames.mjs.map +0 -7
- package/dist/ordinalDetector.mjs.map +0 -7
- package/dist/util.mjs.map +0 -7
package/dist/chunk-NGV56PGT.mjs
DELETED
|
@@ -1,787 +0,0 @@
|
|
|
1
|
-
/*! @bicharts/shape-core — Apache-2.0. Bundled reference data: GeoNames (https://www.geonames.org/) CC BY 4.0; US Census/TIGER (public domain). Full text: NOTICE in this package. */
|
|
2
|
-
import {
|
|
3
|
-
GET_RANDOM,
|
|
4
|
-
SIMPLE_STRING_HASH,
|
|
5
|
-
STR
|
|
6
|
-
} from "./chunk-VLMGGCN4.mjs";
|
|
7
|
-
import {
|
|
8
|
-
detectGeo
|
|
9
|
-
} from "./chunk-YJVU7G63.mjs";
|
|
10
|
-
import {
|
|
11
|
-
detectOrdinalDomain,
|
|
12
|
-
isOrdinalFriendlyName,
|
|
13
|
-
safeDistinctValuesToShip
|
|
14
|
-
} from "./chunk-VHWC3THS.mjs";
|
|
15
|
-
import {
|
|
16
|
-
detectFormatSignature
|
|
17
|
-
} from "./chunk-ICDJLWEM.mjs";
|
|
18
|
-
import {
|
|
19
|
-
monthLookupFor,
|
|
20
|
-
normalizeMonthKey
|
|
21
|
-
} from "./chunk-RZAZ7PZK.mjs";
|
|
22
|
-
|
|
23
|
-
// src/indexedText.ts
|
|
24
|
-
import Papa from "papaparse";
|
|
25
|
-
var DISCRETE_NUMERIC_MAX_DISTINCT = 12;
|
|
26
|
-
var CONTINUOUS_ABS_DISTINCT = 100;
|
|
27
|
-
var CONTINUOUS_DISTINCT_RATIO = 0.3;
|
|
28
|
-
var CONTIGUOUS_DENSITY = 0.9;
|
|
29
|
-
var ORDINAL_AXIS_MAX_DISTINCT = 120;
|
|
30
|
-
var ORDINAL_YEAR_MIN = 1900;
|
|
31
|
-
var ORDINAL_YEAR_MAX = 2100;
|
|
32
|
-
var ORDINAL_SMALL_MIN = 0;
|
|
33
|
-
var ORDINAL_SMALL_MAX = 20;
|
|
34
|
-
var IDENTIFIER_NAME_TOKENS = /* @__PURE__ */ new Set([
|
|
35
|
-
"id",
|
|
36
|
-
"ids",
|
|
37
|
-
"uuid",
|
|
38
|
-
"guid",
|
|
39
|
-
"key",
|
|
40
|
-
"zip",
|
|
41
|
-
"zipcode",
|
|
42
|
-
"postal",
|
|
43
|
-
"postalcode",
|
|
44
|
-
"ssn",
|
|
45
|
-
"code",
|
|
46
|
-
"codes",
|
|
47
|
-
"sku",
|
|
48
|
-
"isbn",
|
|
49
|
-
"msisdn",
|
|
50
|
-
"imei"
|
|
51
|
-
]);
|
|
52
|
-
function isIdentifierName(name) {
|
|
53
|
-
if (!name) return false;
|
|
54
|
-
const tokens = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-./]+/g, " ").toLowerCase().split(/\s+/).filter((t) => t.length > 0);
|
|
55
|
-
if (tokens.length === 0) return false;
|
|
56
|
-
return IDENTIFIER_NAME_TOKENS.has(tokens[tokens.length - 1]);
|
|
57
|
-
}
|
|
58
|
-
var TEMPORAL_YEAR_NAME = /\b(year|yr|fy|fiscal\s*year|calendar\s*year)\b/i;
|
|
59
|
-
var TEMPORAL_PERIOD_RE = /^(?:(?:19|20)\d{2}(?:[-/ ]?(?:q[1-4]|w(?:0?[1-9]|[1-4]\d|5[0-3])|(?:0?[1-9]|1[0-2])))?|q[1-4][-/ ](?:19|20)\d{2}|(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*[-/ ]?(?:19|20)?\d{2})$/i;
|
|
60
|
-
function isYyyymm(v) {
|
|
61
|
-
if (!Number.isInteger(v) || v < 190001 || v > 210012) return false;
|
|
62
|
-
const mm = v % 100;
|
|
63
|
-
return mm >= 1 && mm <= 12;
|
|
64
|
-
}
|
|
65
|
-
function isYyyymmdd(v) {
|
|
66
|
-
if (!Number.isInteger(v) || v < 19000101 || v > 21001231) return false;
|
|
67
|
-
const mm = Math.floor(v / 100) % 100, dd = v % 100;
|
|
68
|
-
return mm >= 1 && mm <= 12 && dd >= 1 && dd <= 31;
|
|
69
|
-
}
|
|
70
|
-
function looksLikeLocalizedMonthPeriod(s, monthMap) {
|
|
71
|
-
if (!monthMap) return false;
|
|
72
|
-
if (!/\b(?:19|20)\d{2}\b/.test(s)) return false;
|
|
73
|
-
const rest = s.replace(/\b(?:19|20)\d{2}\b/g, "").replace(/[-/.,]/g, " ").trim();
|
|
74
|
-
if (!rest) return false;
|
|
75
|
-
const key = normalizeMonthKey(rest);
|
|
76
|
-
return key.length > 0 && monthMap[key] !== void 0;
|
|
77
|
-
}
|
|
78
|
-
function classifyTemporal(args) {
|
|
79
|
-
const { dataType, name, isMeasure, distinctCount } = args;
|
|
80
|
-
if (dataType === "DateTime") return true;
|
|
81
|
-
if (isMeasure) return false;
|
|
82
|
-
if (dataType === "Integer" || dataType === "Decimal") {
|
|
83
|
-
const lo = args.minNum, hi = args.maxNum;
|
|
84
|
-
const bothInt = lo != null && hi != null && Number.isFinite(lo) && Number.isFinite(hi) && Number.isInteger(lo) && Number.isInteger(hi) && hi >= lo;
|
|
85
|
-
if (!bothInt) return false;
|
|
86
|
-
const inYearRange = lo >= 1900 && hi <= 2100;
|
|
87
|
-
if (inYearRange && distinctCount >= 2 && TEMPORAL_YEAR_NAME.test(name)) return true;
|
|
88
|
-
if (inYearRange && distinctCount >= 3 && distinctCount === hi - lo + 1) return true;
|
|
89
|
-
const endpointsYyyymm = isYyyymm(lo) && isYyyymm(hi);
|
|
90
|
-
const endpointsYyyymmdd = isYyyymmdd(lo) && isYyyymmdd(hi);
|
|
91
|
-
if (endpointsYyyymm || endpointsYyyymmdd) {
|
|
92
|
-
if (!args.sampleValues) return true;
|
|
93
|
-
let n = 0, ok = 0;
|
|
94
|
-
for (const v of args.sampleValues) {
|
|
95
|
-
if (v == null || v === "") continue;
|
|
96
|
-
n++;
|
|
97
|
-
const num = Number(String(v).trim());
|
|
98
|
-
if (Number.isFinite(num) && (endpointsYyyymm ? isYyyymm(num) : isYyyymmdd(num))) ok++;
|
|
99
|
-
if (n >= 60) break;
|
|
100
|
-
}
|
|
101
|
-
if (n >= 2 && ok === n) return true;
|
|
102
|
-
}
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
if (args.sampleValues) {
|
|
106
|
-
const monthMap = args.locale ? monthLookupFor(args.locale) : null;
|
|
107
|
-
let n = 0, hit = 0;
|
|
108
|
-
for (const v of args.sampleValues) {
|
|
109
|
-
if (v == null || v === "") continue;
|
|
110
|
-
n++;
|
|
111
|
-
const s = String(v).trim();
|
|
112
|
-
if (TEMPORAL_PERIOD_RE.test(s) || looksLikeLocalizedMonthPeriod(s, monthMap)) hit++;
|
|
113
|
-
if (n >= 60) break;
|
|
114
|
-
}
|
|
115
|
-
if (n >= 2 && hit / n >= 0.8) return true;
|
|
116
|
-
}
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
function hostAggHint(name) {
|
|
120
|
-
const n = (name || "").trim().toLowerCase();
|
|
121
|
-
if (n.startsWith("sum ") || n.startsWith("total ")) return "sum";
|
|
122
|
-
if (n.startsWith("average ") || n.startsWith("avg. ")) return "avg";
|
|
123
|
-
if (n.startsWith("count ") || n.startsWith("distinct count ") || n.startsWith("count (distinct) ")) return "count";
|
|
124
|
-
if (n.startsWith("min ") || n.startsWith("minimum ")) return "min";
|
|
125
|
-
if (n.startsWith("max ") || n.startsWith("maximum ")) return "max";
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
function classifyAdditivity(args) {
|
|
129
|
-
const { rows, measureColIdx, measureName, candidateDims, minNum, maxNum, discourageAgg } = args;
|
|
130
|
-
const SERIES_CARD_CAP = 100;
|
|
131
|
-
const TOL_FRAC = 0.01;
|
|
132
|
-
const SEP = "";
|
|
133
|
-
for (const c of candidateDims) {
|
|
134
|
-
if (c.distinct < 2 || c.distinct > SERIES_CARD_CAP) continue;
|
|
135
|
-
const otherDims = candidateDims.filter((d) => d.idx !== c.idx).map((d) => d.idx);
|
|
136
|
-
const groups = /* @__PURE__ */ new Map();
|
|
137
|
-
for (const row of rows) {
|
|
138
|
-
const mv = row[measureColIdx];
|
|
139
|
-
if (typeof mv !== "number" || !Number.isFinite(mv)) continue;
|
|
140
|
-
const key = otherDims.length ? otherDims.map((di) => String(row[di])).join(SEP) : "";
|
|
141
|
-
let g = groups.get(key);
|
|
142
|
-
if (!g) {
|
|
143
|
-
g = { sum: 0, cset: /* @__PURE__ */ new Set() };
|
|
144
|
-
groups.set(key, g);
|
|
145
|
-
}
|
|
146
|
-
g.sum += mv;
|
|
147
|
-
g.cset.add(String(row[c.idx]));
|
|
148
|
-
}
|
|
149
|
-
let n100 = 0, n1 = 0, tot = 0;
|
|
150
|
-
for (const g of groups.values()) {
|
|
151
|
-
if (g.cset.size < 2) continue;
|
|
152
|
-
tot++;
|
|
153
|
-
if (Math.abs(g.sum - 100) <= 100 * TOL_FRAC) n100++;
|
|
154
|
-
else if (Math.abs(g.sum - 1) <= 1 * TOL_FRAC) n1++;
|
|
155
|
-
}
|
|
156
|
-
if (tot >= 2) {
|
|
157
|
-
if (n100 / tot >= 0.9 || n1 / tot >= 0.9) {
|
|
158
|
-
return { additivity: "part_of_whole", partOfWholeDim: c.name };
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (discourageAgg) return { additivity: "intensive_rate" };
|
|
163
|
-
const agg = hostAggHint(measureName);
|
|
164
|
-
if (agg === "avg" || agg === "min" || agg === "max") return { additivity: "intensive_rate" };
|
|
165
|
-
if (agg === "sum" || agg === "count") return { additivity: "additive" };
|
|
166
|
-
if (minNum != null && maxNum != null && Number.isFinite(minNum) && Number.isFinite(maxNum) && minNum >= 0 && maxNum <= 1.0001 && maxNum > 0) {
|
|
167
|
-
return { additivity: "intensive_rate" };
|
|
168
|
-
}
|
|
169
|
-
return { additivity: "unknown" };
|
|
170
|
-
}
|
|
171
|
-
function classifyNumericValueNature(i) {
|
|
172
|
-
const { dataType, isMeasure, name, distinct, nonblank, prec, maxval, minval } = i;
|
|
173
|
-
const span = maxval - minval + 1;
|
|
174
|
-
const density = span > 0 ? distinct / span : 1;
|
|
175
|
-
const ratio = nonblank > 0 ? distinct / nonblank : 0;
|
|
176
|
-
const fractional = dataType === "Decimal" || prec > 0;
|
|
177
|
-
if (isIdentifierName(name)) return "Categorical";
|
|
178
|
-
if (density >= CONTIGUOUS_DENSITY) {
|
|
179
|
-
if (isOrdinalFriendlyName(name) && distinct <= ORDINAL_AXIS_MAX_DISTINCT) return "Ordinal";
|
|
180
|
-
if (minval >= ORDINAL_YEAR_MIN && maxval <= ORDINAL_YEAR_MAX) return "Ordinal";
|
|
181
|
-
if (minval >= ORDINAL_SMALL_MIN && maxval <= ORDINAL_SMALL_MAX) return "Ordinal";
|
|
182
|
-
}
|
|
183
|
-
if (isMeasure) return "Continuous";
|
|
184
|
-
if (distinct > DISCRETE_NUMERIC_MAX_DISTINCT && (fractional || distinct >= CONTINUOUS_ABS_DISTINCT || ratio >= CONTINUOUS_DISTINCT_RATIO)) {
|
|
185
|
-
return "Continuous";
|
|
186
|
-
}
|
|
187
|
-
return "Categorical";
|
|
188
|
-
}
|
|
189
|
-
var IndexedText = class {
|
|
190
|
-
_computedStatsForLevel = null;
|
|
191
|
-
_cols = [];
|
|
192
|
-
_rows = [];
|
|
193
|
-
_origIndices = [];
|
|
194
|
-
_rowHashes = /* @__PURE__ */ new Set();
|
|
195
|
-
STR(v) {
|
|
196
|
-
if (v === null || v === void 0) {
|
|
197
|
-
return "";
|
|
198
|
-
}
|
|
199
|
-
return v.toString();
|
|
200
|
-
}
|
|
201
|
-
getPrecision(value) {
|
|
202
|
-
if (!isFinite(value)) return 0;
|
|
203
|
-
const valueStr = value.toString();
|
|
204
|
-
const decimalPart = valueStr.split(".")[1];
|
|
205
|
-
return decimalPart ? decimalPart.length : 0;
|
|
206
|
-
}
|
|
207
|
-
getColumns() {
|
|
208
|
-
return this._cols;
|
|
209
|
-
}
|
|
210
|
-
hasTimeComponent(date) {
|
|
211
|
-
if (!(date instanceof Date) || isNaN(date.getTime())) {
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
return date.getHours() !== 0 || date.getMinutes() !== 0 || date.getSeconds() !== 0 || date.getMilliseconds() !== 0;
|
|
215
|
-
}
|
|
216
|
-
getColumnsWithStats(privacyLevel, locale) {
|
|
217
|
-
if (this._computedStatsForLevel == privacyLevel) {
|
|
218
|
-
return this._cols;
|
|
219
|
-
}
|
|
220
|
-
let cidx = 0;
|
|
221
|
-
const pl = parseInt(privacyLevel);
|
|
222
|
-
const colValueSets = [];
|
|
223
|
-
for (const col of this._cols) {
|
|
224
|
-
const vals = /* @__PURE__ */ new Map();
|
|
225
|
-
const arr = [];
|
|
226
|
-
let datalen = 0;
|
|
227
|
-
let nonblank = 0;
|
|
228
|
-
let prec = 0;
|
|
229
|
-
let minval = null;
|
|
230
|
-
let maxval = null;
|
|
231
|
-
let sumval = null;
|
|
232
|
-
let hastime = false;
|
|
233
|
-
let monoAsc = true;
|
|
234
|
-
let monoDesc = true;
|
|
235
|
-
let prevForOrder = null;
|
|
236
|
-
let sawPrev = false;
|
|
237
|
-
for (const row of this._rows) {
|
|
238
|
-
const val = row[cidx];
|
|
239
|
-
const strVal = this.STR(val);
|
|
240
|
-
if (strVal != "") {
|
|
241
|
-
datalen += strVal.length;
|
|
242
|
-
vals.set(strVal, (vals.get(strVal) || 0) + 1);
|
|
243
|
-
nonblank++;
|
|
244
|
-
if (col.dataType == "Integer" || col.dataType == "Decimal" || col.dataType == "DateTime") {
|
|
245
|
-
if (minval === null || val < minval) {
|
|
246
|
-
minval = val;
|
|
247
|
-
}
|
|
248
|
-
if (maxval === null || val > maxval) {
|
|
249
|
-
maxval = val;
|
|
250
|
-
}
|
|
251
|
-
if (col.dataType == "Integer" || col.dataType == "Decimal") {
|
|
252
|
-
if (sumval === null) {
|
|
253
|
-
sumval = val;
|
|
254
|
-
} else {
|
|
255
|
-
sumval += val;
|
|
256
|
-
}
|
|
257
|
-
if (col.dataType == "Decimal") {
|
|
258
|
-
const dp = this.getPrecision(val);
|
|
259
|
-
if (dp > prec) {
|
|
260
|
-
prec = dp;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
arr.push(val);
|
|
264
|
-
} else {
|
|
265
|
-
hastime = hastime || this.hasTimeComponent(val);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if (sawPrev) {
|
|
269
|
-
if (val < prevForOrder) monoAsc = false;
|
|
270
|
-
else if (val > prevForOrder) monoDesc = false;
|
|
271
|
-
}
|
|
272
|
-
prevForOrder = val;
|
|
273
|
-
sawPrev = true;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
if (nonblank >= 3) {
|
|
277
|
-
if (monoAsc && !monoDesc) col.sortedDirection = "asc";
|
|
278
|
-
else if (monoDesc && !monoAsc) col.sortedDirection = "desc";
|
|
279
|
-
else col.sortedDirection = "none";
|
|
280
|
-
}
|
|
281
|
-
if (nonblank > 0 && vals.size > 0 && !col.isMeasure) {
|
|
282
|
-
let topCount = 0;
|
|
283
|
-
let minCount = Number.MAX_SAFE_INTEGER;
|
|
284
|
-
for (const v of vals.values()) {
|
|
285
|
-
if (v > topCount) topCount = v;
|
|
286
|
-
if (v < minCount) minCount = v;
|
|
287
|
-
}
|
|
288
|
-
col.modalShare = topCount / nonblank;
|
|
289
|
-
col.minGroupCount = minCount === Number.MAX_SAFE_INTEGER ? null : minCount;
|
|
290
|
-
if (vals.size === 2) {
|
|
291
|
-
const ks = [...vals.keys()].map((k) => STR(k).trim().toLowerCase()).sort();
|
|
292
|
-
const boolPairs = [["false", "true"], ["no", "yes"], ["n", "y"], ["0", "1"], ["f", "t"]];
|
|
293
|
-
const valuesBoolean = boolPairs.some((p) => ks[0] === p[0] && ks[1] === p[1]);
|
|
294
|
-
const nameOutcome = /^(is|has)[_a-z0-9]|(?:default|churn|fraud|approv|convert|active|cancel|delinquen|flag|paid|win|pass|fail)/i.test(col.name);
|
|
295
|
-
if (valuesBoolean || nameOutcome) col.isBinaryFlag = true;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if (col.dataType == "DateTime") {
|
|
299
|
-
col.dateWithTime = hastime;
|
|
300
|
-
}
|
|
301
|
-
col.isTemporal = classifyTemporal({
|
|
302
|
-
dataType: col.dataType,
|
|
303
|
-
name: col.name,
|
|
304
|
-
isMeasure: col.isMeasure,
|
|
305
|
-
distinctCount: vals.size,
|
|
306
|
-
minNum: typeof minval === "number" ? minval : null,
|
|
307
|
-
maxNum: typeof maxval === "number" ? maxval : null,
|
|
308
|
-
sampleValues: vals.keys(),
|
|
309
|
-
locale
|
|
310
|
-
});
|
|
311
|
-
this.updateColumnStats10(pl, col, nonblank, vals, prec, maxval, minval);
|
|
312
|
-
this.updateColumnStats20(pl, arr, nonblank, sumval, col, datalen, hastime, minval, maxval, prec, vals, locale);
|
|
313
|
-
if (!col.isMeasure && (col.dataType === "Integer" || col.dataType === "Decimal")) {
|
|
314
|
-
const nature = classifyNumericValueNature({
|
|
315
|
-
dataType: col.dataType,
|
|
316
|
-
isMeasure: false,
|
|
317
|
-
name: col.name,
|
|
318
|
-
distinct: vals.size,
|
|
319
|
-
nonblank,
|
|
320
|
-
prec,
|
|
321
|
-
maxval: typeof maxval === "number" ? maxval : 0,
|
|
322
|
-
minval: typeof minval === "number" ? minval : 0
|
|
323
|
-
});
|
|
324
|
-
if (nature === "Continuous") col.isMeasure = true;
|
|
325
|
-
}
|
|
326
|
-
colValueSets[cidx] = col.isMeasure || vals.size > 2e3 ? null : new Set(vals.keys());
|
|
327
|
-
cidx++;
|
|
328
|
-
}
|
|
329
|
-
const dimCols = [];
|
|
330
|
-
this._cols.forEach((c, i) => {
|
|
331
|
-
if (!c.isMeasure) dimCols.push({ idx: i, distinct: c.distinctCount ?? 0, name: c.name });
|
|
332
|
-
});
|
|
333
|
-
this._cols.forEach((c, i) => {
|
|
334
|
-
if (!c.isMeasure) return;
|
|
335
|
-
const lo = typeof c.lowValue === "number" ? c.lowValue : null;
|
|
336
|
-
const hi = typeof c.highValue === "number" ? c.highValue : null;
|
|
337
|
-
const r = classifyAdditivity({
|
|
338
|
-
rows: this._rows,
|
|
339
|
-
measureColIdx: i,
|
|
340
|
-
measureName: c.name,
|
|
341
|
-
candidateDims: dimCols,
|
|
342
|
-
minNum: lo,
|
|
343
|
-
maxNum: hi,
|
|
344
|
-
discourageAgg: !!c.discourageAggregationAcrossGroups
|
|
345
|
-
});
|
|
346
|
-
c.additivity = r.additivity;
|
|
347
|
-
if (r.partOfWholeDim) c.partOfWholeDim = r.partOfWholeDim;
|
|
348
|
-
});
|
|
349
|
-
const DISCRIM_DIM_CAP = 50;
|
|
350
|
-
const discrimDims = dimCols.filter((d) => d.distinct >= 2 && d.distinct <= DISCRIM_DIM_CAP);
|
|
351
|
-
this._cols.forEach((c, mi) => {
|
|
352
|
-
if (!c.isMeasure) return;
|
|
353
|
-
const xs = [];
|
|
354
|
-
for (const row of this._rows) {
|
|
355
|
-
const v = row[mi];
|
|
356
|
-
if (v === null || v === void 0 || v === "") continue;
|
|
357
|
-
const n = typeof v === "number" ? v : parseFloat(v);
|
|
358
|
-
if (!isNaN(n)) xs.push(n);
|
|
359
|
-
}
|
|
360
|
-
if (xs.length === 0) return;
|
|
361
|
-
const sorted = [...xs].sort((a, b) => a - b);
|
|
362
|
-
const q = (p) => sorted[Math.min(sorted.length - 1, Math.max(0, Math.floor(p * (sorted.length - 1))))];
|
|
363
|
-
const median = q(0.5), p10 = q(0.1), p90 = q(0.9);
|
|
364
|
-
const denom = Math.abs(median) > 1e-9 ? Math.abs(median) : Math.abs(sorted[sorted.length - 1]) > 1e-9 ? Math.abs(sorted[sorted.length - 1]) : 1;
|
|
365
|
-
c.relativeDispersion = Math.round((p90 - p10) / denom * 1e3) / 1e3;
|
|
366
|
-
let grand = 0;
|
|
367
|
-
for (const x of xs) grand += x;
|
|
368
|
-
grand /= xs.length;
|
|
369
|
-
let ssTotal = 0;
|
|
370
|
-
for (const x of xs) ssTotal += (x - grand) * (x - grand);
|
|
371
|
-
if (ssTotal <= 1e-12) return;
|
|
372
|
-
const out = [];
|
|
373
|
-
for (const d of discrimDims) {
|
|
374
|
-
const groups = /* @__PURE__ */ new Map();
|
|
375
|
-
for (const row of this._rows) {
|
|
376
|
-
const mv = row[mi];
|
|
377
|
-
if (mv === null || mv === void 0 || mv === "") continue;
|
|
378
|
-
const n = typeof mv === "number" ? mv : parseFloat(mv);
|
|
379
|
-
if (isNaN(n)) continue;
|
|
380
|
-
const key = this.STR(row[d.idx]) + "";
|
|
381
|
-
const g = groups.get(key);
|
|
382
|
-
if (g) {
|
|
383
|
-
g.s += n;
|
|
384
|
-
g.n++;
|
|
385
|
-
} else groups.set(key, { s: n, n: 1 });
|
|
386
|
-
}
|
|
387
|
-
let ssBetween = 0;
|
|
388
|
-
for (const g of groups.values()) {
|
|
389
|
-
const gm = g.s / g.n;
|
|
390
|
-
ssBetween += g.n * (gm - grand) * (gm - grand);
|
|
391
|
-
}
|
|
392
|
-
out.push({ otherColumn: d.name, eta2: Math.round(ssBetween / ssTotal * 1e4) / 1e4 });
|
|
393
|
-
}
|
|
394
|
-
if (out.length > 0) c.groupDiscrimination = out;
|
|
395
|
-
});
|
|
396
|
-
const COLLINEAR_R = 0.97;
|
|
397
|
-
const MIN_CORR_ROWS = 5;
|
|
398
|
-
const measureIdx = this._cols.map((c, i) => ({ c, i })).filter((x) => x.c.isMeasure);
|
|
399
|
-
for (let a = 0; a < measureIdx.length; a++) {
|
|
400
|
-
for (let b = a + 1; b < measureIdx.length; b++) {
|
|
401
|
-
const ia = measureIdx[a].i, ib = measureIdx[b].i;
|
|
402
|
-
let n = 0, sx = 0, sy = 0, sxx = 0, syy = 0, sxy = 0;
|
|
403
|
-
for (const row of this._rows) {
|
|
404
|
-
const va = row[ia], vb = row[ib];
|
|
405
|
-
if (va === null || va === void 0 || va === "" || vb === null || vb === void 0 || vb === "") continue;
|
|
406
|
-
const x = typeof va === "number" ? va : parseFloat(va);
|
|
407
|
-
const y = typeof vb === "number" ? vb : parseFloat(vb);
|
|
408
|
-
if (isNaN(x) || isNaN(y)) continue;
|
|
409
|
-
n++;
|
|
410
|
-
sx += x;
|
|
411
|
-
sy += y;
|
|
412
|
-
sxx += x * x;
|
|
413
|
-
syy += y * y;
|
|
414
|
-
sxy += x * y;
|
|
415
|
-
}
|
|
416
|
-
if (n < MIN_CORR_ROWS) continue;
|
|
417
|
-
const vx = n * sxx - sx * sx, vy = n * syy - sy * sy;
|
|
418
|
-
if (vx <= 1e-12 || vy <= 1e-12) continue;
|
|
419
|
-
const r = (n * sxy - sx * sy) / Math.sqrt(vx * vy);
|
|
420
|
-
if (Math.abs(r) >= COLLINEAR_R) {
|
|
421
|
-
const ca = measureIdx[a].c, cb = measureIdx[b].c;
|
|
422
|
-
if (!ca.collinearWithMeasures) ca.collinearWithMeasures = [];
|
|
423
|
-
if (!cb.collinearWithMeasures) cb.collinearWithMeasures = [];
|
|
424
|
-
ca.collinearWithMeasures.push(cb.name);
|
|
425
|
-
cb.collinearWithMeasures.push(ca.name);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
for (let a = 0; a < this._cols.length; a++) {
|
|
430
|
-
const setA = colValueSets[a];
|
|
431
|
-
if (!setA || setA.size === 0) continue;
|
|
432
|
-
const ratios = [];
|
|
433
|
-
for (let b = 0; b < this._cols.length; b++) {
|
|
434
|
-
if (b === a) continue;
|
|
435
|
-
const setB = colValueSets[b];
|
|
436
|
-
if (!setB || setB.size === 0) continue;
|
|
437
|
-
let shared = 0;
|
|
438
|
-
for (const v of setA) if (setB.has(v)) shared++;
|
|
439
|
-
const pct = Math.round(shared / setA.size * 100);
|
|
440
|
-
if (pct > 0) ratios.push({ otherColumn: this._cols[b].name, percentShared: pct });
|
|
441
|
-
}
|
|
442
|
-
if (ratios.length > 0) this._cols[a].sharedValueRatioWithOthers = ratios;
|
|
443
|
-
}
|
|
444
|
-
const PAIR_CAP = 50;
|
|
445
|
-
const candIdx = [];
|
|
446
|
-
for (let i = 0; i < this._cols.length; i++) {
|
|
447
|
-
const s = colValueSets[i];
|
|
448
|
-
if (s && s.size >= 2 && s.size <= PAIR_CAP) candIdx.push(i);
|
|
449
|
-
}
|
|
450
|
-
if (candIdx.length >= 2) {
|
|
451
|
-
const pairCounts = /* @__PURE__ */ new Map();
|
|
452
|
-
const pk = (a, b) => a + ":" + b;
|
|
453
|
-
for (let x = 0; x < candIdx.length; x++)
|
|
454
|
-
for (let y = x + 1; y < candIdx.length; y++)
|
|
455
|
-
pairCounts.set(pk(candIdx[x], candIdx[y]), /* @__PURE__ */ new Map());
|
|
456
|
-
for (const row of this._rows) {
|
|
457
|
-
for (let x = 0; x < candIdx.length; x++) {
|
|
458
|
-
const va = this.STR(row[candIdx[x]]) + "";
|
|
459
|
-
for (let y = x + 1; y < candIdx.length; y++) {
|
|
460
|
-
const vb = String.fromCharCode(1) + this.STR(row[candIdx[y]]);
|
|
461
|
-
const cm = pairCounts.get(pk(candIdx[x], candIdx[y]));
|
|
462
|
-
const ck = va + "" + vb;
|
|
463
|
-
cm.set(ck, (cm.get(ck) ?? 0) + 1);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
const parentCand = /* @__PURE__ */ new Map();
|
|
468
|
-
for (let x = 0; x < candIdx.length; x++) {
|
|
469
|
-
for (let y = x + 1; y < candIdx.length; y++) {
|
|
470
|
-
const a = candIdx[x], b = candIdx[y];
|
|
471
|
-
const cardA = colValueSets[a].size, cardB = colValueSets[b].size;
|
|
472
|
-
const cellMap = pairCounts.get(pk(a, b));
|
|
473
|
-
const distinctPairs = cellMap.size;
|
|
474
|
-
let minCellCount = 0;
|
|
475
|
-
for (const cval of cellMap.values()) {
|
|
476
|
-
if (minCellCount === 0 || cval < minCellCount) minCellCount = cval;
|
|
477
|
-
}
|
|
478
|
-
const fillPct = Math.round(distinctPairs / (cardA * cardB) * 100);
|
|
479
|
-
const aDet = distinctPairs === cardA;
|
|
480
|
-
const bDet = distinctPairs === cardB;
|
|
481
|
-
(this._cols[a].categoricalPairStats ||= []).push(
|
|
482
|
-
{ otherColumn: this._cols[b].name, fillPct, determinesOther: aDet, minCellCount }
|
|
483
|
-
);
|
|
484
|
-
(this._cols[b].categoricalPairStats ||= []).push(
|
|
485
|
-
{ otherColumn: this._cols[a].name, fillPct, determinesOther: bDet, minCellCount }
|
|
486
|
-
);
|
|
487
|
-
if (aDet && cardA > cardB) {
|
|
488
|
-
const cur = parentCand.get(a);
|
|
489
|
-
if (!cur || cardB < cur.card)
|
|
490
|
-
parentCand.set(a, { name: this._cols[b].name, card: cardB, ratio: cardB / cardA });
|
|
491
|
-
}
|
|
492
|
-
if (bDet && cardB > cardA) {
|
|
493
|
-
const cur = parentCand.get(b);
|
|
494
|
-
if (!cur || cardA < cur.card)
|
|
495
|
-
parentCand.set(b, { name: this._cols[a].name, card: cardA, ratio: cardA / cardB });
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
for (const [i, p] of parentCand) {
|
|
500
|
-
this._cols[i].primaryParentColumn = p.name;
|
|
501
|
-
this._cols[i].nestingRatio = Math.round(p.ratio * 100) / 100;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
this._computedStatsForLevel = privacyLevel;
|
|
505
|
-
return this._cols;
|
|
506
|
-
}
|
|
507
|
-
updateColumnStats20(pl, arr, nonblank, sumval, col, datalen, hastime, minval, maxval, prec, vals, locale) {
|
|
508
|
-
if (pl >= 20) {
|
|
509
|
-
const sorted = [...arr].sort((a, b) => a - b);
|
|
510
|
-
const mid = Math.floor(sorted.length / 2);
|
|
511
|
-
if (nonblank > 0 && sumval != null) {
|
|
512
|
-
col.avgValue = col.dataType == "Integer" ? Math.round(sumval / nonblank) : sumval / nonblank;
|
|
513
|
-
} else {
|
|
514
|
-
col.avgValue = null;
|
|
515
|
-
}
|
|
516
|
-
if (nonblank > 0) {
|
|
517
|
-
col.avgLength = datalen / nonblank;
|
|
518
|
-
}
|
|
519
|
-
if (this._rows.length > 0) {
|
|
520
|
-
if (col.dataType != "DateTime" || hastime || !minval) {
|
|
521
|
-
col.lowValue = minval;
|
|
522
|
-
} else {
|
|
523
|
-
col.lowValue = new Date(minval).toISOString().slice(0, 10) + "T00:00:00.000Z";
|
|
524
|
-
}
|
|
525
|
-
if (col.dataType != "DateTime" || hastime || !maxval) {
|
|
526
|
-
col.highValue = maxval;
|
|
527
|
-
} else {
|
|
528
|
-
col.highValue = new Date(maxval).toISOString().slice(0, 10) + "T00:00:00.000Z";
|
|
529
|
-
}
|
|
530
|
-
col.medianValue = sorted.length === 0 ? null : sorted.length % 2 === 0 ? col.dataType == "Integer" ? Math.round((sorted[mid - 1] + sorted[mid]) / 2) : (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
531
|
-
col.numericPrecision = prec;
|
|
532
|
-
}
|
|
533
|
-
if (sorted.length > 0) {
|
|
534
|
-
const mean = col.avgValue ?? 0;
|
|
535
|
-
const n = sorted.length;
|
|
536
|
-
let m2 = 0, m3 = 0;
|
|
537
|
-
for (const v of sorted) {
|
|
538
|
-
const d = v - mean;
|
|
539
|
-
m2 += d * d;
|
|
540
|
-
m3 += d * d * d;
|
|
541
|
-
}
|
|
542
|
-
const variance = m2 / n;
|
|
543
|
-
col.skewness = variance === 0 ? 0 : m3 / n / Math.pow(Math.sqrt(variance), 3);
|
|
544
|
-
col.stdDev = Math.sqrt(variance);
|
|
545
|
-
}
|
|
546
|
-
if (sumval !== null) {
|
|
547
|
-
col.sum = sumval;
|
|
548
|
-
}
|
|
549
|
-
const topcatEntries = Array.from(vals.entries()).sort((a, b) => b[1] - a[1]);
|
|
550
|
-
col.topCategories = topcatEntries.slice(0, 10).map((e) => e[1]);
|
|
551
|
-
if (!col.isMeasure && topcatEntries.length > 0) {
|
|
552
|
-
if (col.dataType === "String") {
|
|
553
|
-
const allDistinct = topcatEntries.map((e) => e[0]);
|
|
554
|
-
col.formatSignature = detectFormatSignature(allDistinct);
|
|
555
|
-
const nonWordFmt = /^(UUID|EMAIL_LIKE|URL_LIKE|PHONE_LIKE|NUMERIC_ID|HEX_ID|OPAQUE_ID_ALPHANUMERIC|DATE_STR|BOOLEAN)$/;
|
|
556
|
-
if (col.formatSignature && !nonWordFmt.test(col.formatSignature) && (col.avgLength === void 0 || col.avgLength >= 3))
|
|
557
|
-
col.isFreeText = true;
|
|
558
|
-
} else {
|
|
559
|
-
col.topCategoryValues = topcatEntries.slice(0, 5).map((e) => e[0]);
|
|
560
|
-
}
|
|
561
|
-
if (pl >= 20) {
|
|
562
|
-
const allDistinctRaw = topcatEntries.map((e) => e[0]);
|
|
563
|
-
const detection = detectOrdinalDomain(allDistinctRaw, locale);
|
|
564
|
-
if (detection !== null) {
|
|
565
|
-
col.ordinalPattern = detection.pattern;
|
|
566
|
-
col.orderedDomain = detection.orderedDomain;
|
|
567
|
-
} else if (col.dataType === "String") {
|
|
568
|
-
const safe = safeDistinctValuesToShip(allDistinctRaw);
|
|
569
|
-
if (safe !== null) {
|
|
570
|
-
col.safeDistinctValues = safe;
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
const geo = detectGeo(topcatEntries.map((e) => e[0]), col.name, locale);
|
|
575
|
-
if (geo !== null) {
|
|
576
|
-
col.geoKind = geo.geoKind;
|
|
577
|
-
col.geoMatchPct = geo.geoMatchPct;
|
|
578
|
-
if (geo.geoAmbiguous) col.geoAmbiguous = true;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
if (sorted.length >= 8 && (col.dataType === "Integer" || col.dataType === "Decimal")) {
|
|
582
|
-
const pick = (p) => sorted[Math.floor((sorted.length - 1) * p)];
|
|
583
|
-
col.quantiles = {
|
|
584
|
-
p05: pick(0.05),
|
|
585
|
-
p25: pick(0.25),
|
|
586
|
-
p75: pick(0.75),
|
|
587
|
-
p95: pick(0.95)
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
updateColumnStats10(pl, col, nonblank, vals, prec, maxval, minval) {
|
|
593
|
-
if (pl >= 10) {
|
|
594
|
-
col.blankCount = this._rows.length - nonblank;
|
|
595
|
-
col.distinctCount = vals.size + (col.blankCount != 0 ? 1 : 0);
|
|
596
|
-
col.valueNature = "Categorical";
|
|
597
|
-
if (nonblank > 0) {
|
|
598
|
-
if (col.dataType === "Integer" || col.dataType === "Decimal") {
|
|
599
|
-
col.valueNature = classifyNumericValueNature({
|
|
600
|
-
dataType: col.dataType,
|
|
601
|
-
isMeasure: !!col.isMeasure,
|
|
602
|
-
name: col.name,
|
|
603
|
-
distinct: vals.size,
|
|
604
|
-
nonblank,
|
|
605
|
-
prec,
|
|
606
|
-
maxval,
|
|
607
|
-
minval
|
|
608
|
-
});
|
|
609
|
-
} else if (col.dataType === "DateTime") {
|
|
610
|
-
col.valueNature = col.dateWithTime ? "Continuous" : "Ordinal";
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
getRowCount() {
|
|
616
|
-
return this._rows.length;
|
|
617
|
-
}
|
|
618
|
-
// LEAF CARDINALITY (2026-07-13) — distinct count of the tuple of NON-MEASURE
|
|
619
|
-
// (grouping) columns: the finest grain at which the data would be counted. The
|
|
620
|
-
// server only has per-column marginals, never this JOINT distinctness — so the
|
|
621
|
-
// client measures it here and ships it, letting AggregationAnalyzer decide whether
|
|
622
|
-
// an implicit row COUNT is meaningful (rows repeat -> RowCount > leaf) or degenerate
|
|
623
|
-
// (each row a unique tuple -> leaf ~ RowCount, a count is all-1s). One pass over the
|
|
624
|
-
// accumulated rows; cached until reset().
|
|
625
|
-
getLeafCardinality() {
|
|
626
|
-
if (this._leafCardinality !== null) return this._leafCardinality;
|
|
627
|
-
const dimIdx = [];
|
|
628
|
-
this._cols.forEach((c, i) => {
|
|
629
|
-
if (!c.isMeasure) dimIdx.push(i);
|
|
630
|
-
});
|
|
631
|
-
if (dimIdx.length === 0) {
|
|
632
|
-
this._leafCardinality = this._rows.length;
|
|
633
|
-
return this._leafCardinality;
|
|
634
|
-
}
|
|
635
|
-
const seen = /* @__PURE__ */ new Set();
|
|
636
|
-
for (const row of this._rows) {
|
|
637
|
-
seen.add(JSON.stringify(dimIdx.map((di) => row[di])));
|
|
638
|
-
}
|
|
639
|
-
this._leafCardinality = seen.size;
|
|
640
|
-
return this._leafCardinality;
|
|
641
|
-
}
|
|
642
|
-
_leafCardinality = null;
|
|
643
|
-
obfuscateString(input) {
|
|
644
|
-
const lowers = "abcdefghijklmnopqrstuvwxyz";
|
|
645
|
-
const getRandomLower = () => lowers[Math.floor(GET_RANDOM() * lowers.length)];
|
|
646
|
-
const getRandomUpper = () => getRandomLower().toUpperCase();
|
|
647
|
-
const getRandomDigit = () => Math.floor(GET_RANDOM() * 10).toString();
|
|
648
|
-
const substituteSameClass = (origChar) => {
|
|
649
|
-
if (/[A-Z]/.test(origChar)) return getRandomUpper();
|
|
650
|
-
if (/[a-z]/.test(origChar)) return getRandomLower();
|
|
651
|
-
if (/[0-9]/.test(origChar)) return getRandomDigit();
|
|
652
|
-
return origChar;
|
|
653
|
-
};
|
|
654
|
-
const isEmail = input.includes("@") && input.includes(".");
|
|
655
|
-
const emailParts = isEmail ? input.split("@") : [input];
|
|
656
|
-
const domain = isEmail ? emailParts[1] : "";
|
|
657
|
-
const preserveSuffix = domain.endsWith(".com");
|
|
658
|
-
const suffix = preserveSuffix ? ".com" : "";
|
|
659
|
-
const domainName = preserveSuffix ? domain.slice(0, -4) : domain;
|
|
660
|
-
const obfuscatePart = (str) => {
|
|
661
|
-
let result = "";
|
|
662
|
-
for (const char of str) {
|
|
663
|
-
result += substituteSameClass(char);
|
|
664
|
-
if (/[a-zA-Z0-9]/.test(char) && GET_RANDOM() < 0.2) {
|
|
665
|
-
result += substituteSameClass(char);
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
if (result.length > 6) {
|
|
669
|
-
const numToRemove = Math.floor(GET_RANDOM() * Math.min(3, result.length - 6));
|
|
670
|
-
for (let i = 0; i < numToRemove; i++) {
|
|
671
|
-
let attempts = 0;
|
|
672
|
-
while (attempts < 5) {
|
|
673
|
-
const removeIndex = Math.floor(GET_RANDOM() * result.length);
|
|
674
|
-
if (/[a-zA-Z0-9]/.test(result[removeIndex])) {
|
|
675
|
-
result = result.slice(0, removeIndex) + result.slice(removeIndex + 1);
|
|
676
|
-
break;
|
|
677
|
-
}
|
|
678
|
-
attempts++;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
return result;
|
|
683
|
-
};
|
|
684
|
-
if (isEmail) {
|
|
685
|
-
const localPart = obfuscatePart(emailParts[0]);
|
|
686
|
-
const obfuscatedDomain = obfuscatePart(domainName);
|
|
687
|
-
return `${localPart}@${obfuscatedDomain}${suffix}`;
|
|
688
|
-
} else {
|
|
689
|
-
return obfuscatePart(input);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
toObjectArray() {
|
|
693
|
-
return this._rows.map((row) => {
|
|
694
|
-
const obj = {};
|
|
695
|
-
this._cols.forEach((col, index) => {
|
|
696
|
-
obj[col.name] = row[index];
|
|
697
|
-
});
|
|
698
|
-
return obj;
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
async getCSVAsync(forSample) {
|
|
702
|
-
const rows = [];
|
|
703
|
-
let cnt = forSample ? 25 : 1e7;
|
|
704
|
-
let len = forSample ? 4e3 : 1e7;
|
|
705
|
-
const strMap = /* @__PURE__ */ new Map();
|
|
706
|
-
for (const r of this._rows) {
|
|
707
|
-
const obj = {};
|
|
708
|
-
for (let c = 0; c < this._cols.length; c++) {
|
|
709
|
-
const col = this._cols[c];
|
|
710
|
-
let v = r[c];
|
|
711
|
-
if (this.STR(v) != "") {
|
|
712
|
-
if (forSample && col.dataType == "String") {
|
|
713
|
-
if (strMap.has(v)) {
|
|
714
|
-
v = strMap.get(v);
|
|
715
|
-
} else {
|
|
716
|
-
const nv = this.obfuscateString(v);
|
|
717
|
-
strMap.set(v, nv);
|
|
718
|
-
v = nv;
|
|
719
|
-
}
|
|
720
|
-
} else {
|
|
721
|
-
if (col.dataType == "DateTime" && !col.dateWithTime) {
|
|
722
|
-
v = new Date(v).toISOString().slice(0, 10) + "T00:00:00.000Z";
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
len -= this.STR(v).length;
|
|
726
|
-
} else {
|
|
727
|
-
v = null;
|
|
728
|
-
}
|
|
729
|
-
obj[c] = v;
|
|
730
|
-
--len;
|
|
731
|
-
}
|
|
732
|
-
rows.push(obj);
|
|
733
|
-
--cnt;
|
|
734
|
-
if (cnt <= 0 || len <= 0) {
|
|
735
|
-
break;
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
const csv = await Papa.unparse(rows, { header: false });
|
|
739
|
-
return csv;
|
|
740
|
-
}
|
|
741
|
-
// Column-name header line matching getCSVAsync's column ORDER (this._cols, by index).
|
|
742
|
-
// getCSVAsync emits a HEADERLESS body; legacy cached code reads CSV_STRING with a bare
|
|
743
|
-
// pd.read_csv (no names), so the host prepends this line at injection time — otherwise
|
|
744
|
-
// pandas uses row 0 as the header and every column reference KeyErrors (v1->v2 upgrade
|
|
745
|
-
// running cached v1 Python code). Built via Papa so escaping matches the data rows.
|
|
746
|
-
getCSVHeaderLine() {
|
|
747
|
-
return Papa.unparse([this._cols.map((c) => this.STR(c.name))]);
|
|
748
|
-
}
|
|
749
|
-
addRow(colvals, originalIdx) {
|
|
750
|
-
let str = "";
|
|
751
|
-
for (const v of colvals) {
|
|
752
|
-
str += this.STR(v) + "~";
|
|
753
|
-
}
|
|
754
|
-
const hash = SIMPLE_STRING_HASH(str);
|
|
755
|
-
if (this._rowHashes.has(hash)) {
|
|
756
|
-
return false;
|
|
757
|
-
}
|
|
758
|
-
this._rows.push(colvals);
|
|
759
|
-
this._origIndices.push(originalIdx === void 0 || originalIdx === null ? -1 : originalIdx);
|
|
760
|
-
this._rowHashes.add(hash);
|
|
761
|
-
return true;
|
|
762
|
-
}
|
|
763
|
-
getOriginalRowIndex(i) {
|
|
764
|
-
if (i < 0 || i >= this._origIndices.length) return -1;
|
|
765
|
-
return this._origIndices[i];
|
|
766
|
-
}
|
|
767
|
-
reset() {
|
|
768
|
-
this._cols = [];
|
|
769
|
-
this._rows = [];
|
|
770
|
-
this._origIndices = [];
|
|
771
|
-
this._rowHashes = /* @__PURE__ */ new Set();
|
|
772
|
-
this._leafCardinality = null;
|
|
773
|
-
}
|
|
774
|
-
setColumns(cols) {
|
|
775
|
-
this._cols = cols;
|
|
776
|
-
}
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
export {
|
|
780
|
-
isIdentifierName,
|
|
781
|
-
classifyTemporal,
|
|
782
|
-
hostAggHint,
|
|
783
|
-
classifyAdditivity,
|
|
784
|
-
classifyNumericValueNature,
|
|
785
|
-
IndexedText
|
|
786
|
-
};
|
|
787
|
-
//# sourceMappingURL=chunk-NGV56PGT.mjs.map
|