@rokkit/data 1.0.0-next.149 → 1.0.0-next.151
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/benchmark/fixtures.d.ts +1 -5
- package/dist/src/dataset.d.ts +1 -1
- package/dist/src/dataset.svelte.d.ts +1 -1
- package/dist/src/infer.d.ts +1 -14
- package/dist/src/pipeline.d.ts +1 -20
- package/dist/src/rollup.d.ts +1 -8
- package/package.json +1 -1
- package/src/infer.js +14 -11
- package/src/pipeline.js +40 -34
- package/src/rollup.js +14 -14
|
@@ -25,13 +25,9 @@ export function generateJoinData(n: any): {
|
|
|
25
25
|
year: number;
|
|
26
26
|
}[];
|
|
27
27
|
};
|
|
28
|
-
/**
|
|
29
|
-
* Pre-aggregated data for alignBy benchmarks:
|
|
30
|
-
* one row per (region, product, year) — some combos intentionally missing.
|
|
31
|
-
*/
|
|
32
28
|
export function generateAlignData(n: any): {
|
|
29
|
+
revenue: number;
|
|
33
30
|
region: string;
|
|
34
31
|
product: string;
|
|
35
32
|
year: number;
|
|
36
|
-
revenue: number;
|
|
37
33
|
}[];
|
package/dist/src/dataset.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export class ReactiveDataSet {
|
|
|
14
14
|
* Reactive computed result of filter + transforms + sort.
|
|
15
15
|
* Bind to this in templates instead of calling `.select()`.
|
|
16
16
|
*/
|
|
17
|
-
get rows(): any
|
|
17
|
+
get rows(): any;
|
|
18
18
|
/**
|
|
19
19
|
* Reactive derived column definitions: name, type, scale (discrete|continuous),
|
|
20
20
|
* sortable, filterable, fields, formatter — merged with any `withColumns` overrides.
|
package/dist/src/infer.d.ts
CHANGED
|
@@ -78,20 +78,7 @@ export function deriveActions(columns: Array<import("./types").ColumnMetadata>,
|
|
|
78
78
|
* @returns {Array<import('./types').ColumnMetadata>} - The derived column metadata.
|
|
79
79
|
*/
|
|
80
80
|
export function deriveMetadata(dataArray: any, options?: Object): Array<import("./types").ColumnMetadata>;
|
|
81
|
-
|
|
82
|
-
* Infers whether a column's values are discrete (categorical) or continuous (quantitative).
|
|
83
|
-
*
|
|
84
|
-
* Rules:
|
|
85
|
-
* - string, boolean → always discrete
|
|
86
|
-
* - date, number (float) → always continuous
|
|
87
|
-
* - integer → discrete when ≤20 unique values OR unique ratio < 5 %,
|
|
88
|
-
* continuous otherwise (e.g. large ID column)
|
|
89
|
-
* - anything else → discrete (safe default for unknown types)
|
|
90
|
-
*
|
|
91
|
-
* @param {any[]} values - All values for the column (may include nulls).
|
|
92
|
-
* @returns {'discrete'|'continuous'}
|
|
93
|
-
*/
|
|
94
|
-
export function inferScale(values: any[]): "discrete" | "continuous";
|
|
81
|
+
export function inferScale(values: any): any;
|
|
95
82
|
/**
|
|
96
83
|
* Adds a `scale` property to each column definition.
|
|
97
84
|
*
|
package/dist/src/pipeline.d.ts
CHANGED
|
@@ -5,23 +5,4 @@
|
|
|
5
5
|
* @returns {any[]}
|
|
6
6
|
*/
|
|
7
7
|
export function sortDataByFields(data: any[], ...fields: (string | Object | any[])[]): any[];
|
|
8
|
-
|
|
9
|
-
* Execute a list of pipeline ops in a single fused pass over `data`.
|
|
10
|
-
*
|
|
11
|
-
* Ops:
|
|
12
|
-
* { type: 'filter', fn } — keep rows where fn(row) is truthy
|
|
13
|
-
* { type: 'map', fn } — transform row via fn(row)
|
|
14
|
-
* { type: 'sort', fields } — sort the result by fields
|
|
15
|
-
*
|
|
16
|
-
* Consecutive filter/map ops are merged into one loop.
|
|
17
|
-
* Sort ops are applied after the linear pass.
|
|
18
|
-
*
|
|
19
|
-
* @param {any[]} data
|
|
20
|
-
* @param {Array<{type:string, fn?:Function, fields?:string[]}>} ops
|
|
21
|
-
* @returns {any[]}
|
|
22
|
-
*/
|
|
23
|
-
export function executeFused(data: any[], ops: Array<{
|
|
24
|
-
type: string;
|
|
25
|
-
fn?: Function;
|
|
26
|
-
fields?: string[];
|
|
27
|
-
}>): any[];
|
|
8
|
+
export function executeFused(data: any, ops: any): any;
|
package/dist/src/rollup.d.ts
CHANGED
|
@@ -30,14 +30,7 @@ export function groupDataByKeys(data: any[], groupByKeys: any[], summaries: any[
|
|
|
30
30
|
* @returns {Array} Aligned data array.
|
|
31
31
|
*/
|
|
32
32
|
export function fillAlignedData(groupedData: Object, config: Object, fillRowsFunc: any): any[];
|
|
33
|
-
|
|
34
|
-
* Creates a generator that produces missing rows in a dataset based on specified columns.
|
|
35
|
-
*
|
|
36
|
-
* @param {Array<Object>} data - The array of objects representing the dataset.
|
|
37
|
-
* @param {Object} config - The configuration object.
|
|
38
|
-
* @returns {Function} A generator function that when called, produces the missing rows.
|
|
39
|
-
*/
|
|
40
|
-
export function getAlignGenerator(data: Array<Object>, config: Object): Function;
|
|
33
|
+
export function getAlignGenerator(data: any, config: any): (children: any) => any[];
|
|
41
34
|
/**
|
|
42
35
|
* Performs aggregation operations on grouped data.
|
|
43
36
|
*
|
package/package.json
CHANGED
package/src/infer.js
CHANGED
|
@@ -289,21 +289,24 @@ export function deriveMetadata(dataArray, options = {}) {
|
|
|
289
289
|
* @param {any[]} values - All values for the column (may include nulls).
|
|
290
290
|
* @returns {'discrete'|'continuous'}
|
|
291
291
|
*/
|
|
292
|
+
function inferIntegerScale(nonNull) {
|
|
293
|
+
const unique = new Set(nonNull).size
|
|
294
|
+
return unique <= 20 || unique / nonNull.length < 0.05 ? 'discrete' : 'continuous'
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const SCALE_BY_TYPE = {
|
|
298
|
+
string: () => 'discrete',
|
|
299
|
+
boolean: () => 'discrete',
|
|
300
|
+
date: () => 'continuous',
|
|
301
|
+
number: () => 'continuous',
|
|
302
|
+
integer: (nonNull) => inferIntegerScale(nonNull)
|
|
303
|
+
}
|
|
304
|
+
|
|
292
305
|
export function inferScale(values) {
|
|
293
306
|
const nonNull = values.filter((v) => v !== null && v !== undefined)
|
|
294
307
|
if (nonNull.length === 0) return 'discrete'
|
|
295
|
-
|
|
296
308
|
const type = typeOf(nonNull[0])
|
|
297
|
-
|
|
298
|
-
if (type === 'string' || type === 'boolean') return 'discrete'
|
|
299
|
-
if (type === 'date' || type === 'number') return 'continuous'
|
|
300
|
-
|
|
301
|
-
if (type === 'integer') {
|
|
302
|
-
const unique = new Set(nonNull).size
|
|
303
|
-
return unique <= 20 || unique / nonNull.length < 0.05 ? 'discrete' : 'continuous'
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return 'discrete'
|
|
309
|
+
return (SCALE_BY_TYPE[type] ?? (() => 'discrete'))(nonNull)
|
|
307
310
|
}
|
|
308
311
|
|
|
309
312
|
/**
|
package/src/pipeline.js
CHANGED
|
@@ -41,6 +41,39 @@ export function sortDataByFields(data, ...fields) {
|
|
|
41
41
|
* @param {Array<{type:string, fn?:Function, fields?:string[]}>} ops
|
|
42
42
|
* @returns {any[]}
|
|
43
43
|
*/
|
|
44
|
+
function applyOpsToRow(row, linearOps) {
|
|
45
|
+
let cur = row
|
|
46
|
+
for (const op of linearOps) {
|
|
47
|
+
if (op.type === 'filter') {
|
|
48
|
+
if (!op.fn(cur)) return null
|
|
49
|
+
} else {
|
|
50
|
+
cur = op.fn(cur)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return cur
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function applyLinearOps(data, linearOps) {
|
|
57
|
+
const out = []
|
|
58
|
+
for (const row of data) {
|
|
59
|
+
const result = applyOpsToRow(row, linearOps)
|
|
60
|
+
if (result !== null) out.push(result)
|
|
61
|
+
}
|
|
62
|
+
return out
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function collectLinearOps(ops, i) {
|
|
66
|
+
const linearOps = []
|
|
67
|
+
while (i < ops.length && ops[i].type !== 'sort') linearOps.push(ops[i++])
|
|
68
|
+
return { linearOps, i }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function collectSortFields(ops, i) {
|
|
72
|
+
const sortFields = []
|
|
73
|
+
while (i < ops.length && ops[i].type === 'sort') sortFields.push(...ops[i++].fields)
|
|
74
|
+
return { sortFields, i }
|
|
75
|
+
}
|
|
76
|
+
|
|
44
77
|
export function executeFused(data, ops) {
|
|
45
78
|
if (ops.length === 0) return data
|
|
46
79
|
|
|
@@ -48,42 +81,15 @@ export function executeFused(data, ops) {
|
|
|
48
81
|
let i = 0
|
|
49
82
|
|
|
50
83
|
while (i < ops.length) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
linearOps.push(ops[i])
|
|
55
|
-
i++
|
|
56
|
-
}
|
|
84
|
+
const linear = collectLinearOps(ops, i)
|
|
85
|
+
i = linear.i
|
|
86
|
+
if (linear.linearOps.length > 0) result = applyLinearOps(result, linear.linearOps)
|
|
57
87
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
let cur = row
|
|
62
|
-
let keep = true
|
|
63
|
-
for (const op of linearOps) {
|
|
64
|
-
if (op.type === 'filter') {
|
|
65
|
-
if (!op.fn(cur)) {
|
|
66
|
-
keep = false
|
|
67
|
-
break
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
cur = op.fn(cur)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (keep) out.push(cur)
|
|
74
|
-
}
|
|
75
|
-
result = out
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Collect a run of sort ops and apply them as a single sort
|
|
79
|
-
const sortFields = []
|
|
80
|
-
while (i < ops.length && ops[i].type === 'sort') {
|
|
81
|
-
sortFields.push(...ops[i].fields)
|
|
82
|
-
i++
|
|
83
|
-
}
|
|
84
|
-
if (sortFields.length > 0) {
|
|
88
|
+
const sorted = collectSortFields(ops, i)
|
|
89
|
+
i = sorted.i
|
|
90
|
+
if (sorted.sortFields.length > 0) {
|
|
85
91
|
result = [...result]
|
|
86
|
-
sortDataByFields(result, ...sortFields)
|
|
92
|
+
sortDataByFields(result, ...sorted.sortFields)
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
95
|
|
package/src/rollup.js
CHANGED
|
@@ -77,17 +77,17 @@ export function fillAlignedData(groupedData, config, fillRowsFunc) {
|
|
|
77
77
|
* @param {Object} config - The configuration object.
|
|
78
78
|
* @returns {Function} A generator function that when called, produces the missing rows.
|
|
79
79
|
*/
|
|
80
|
-
|
|
80
|
+
function buildAlignTemplate(config) {
|
|
81
81
|
const { align_by, group_by, actual_flag } = config
|
|
82
|
-
|
|
83
|
-
// Build template: omit align_by and group_by keys, add actual_flag=0
|
|
84
82
|
const omitKeys = new Set([...align_by, ...group_by])
|
|
85
83
|
const template = { [actual_flag]: 0 }
|
|
86
84
|
for (const [k, v] of Object.entries(config.template)) {
|
|
87
85
|
if (!omitKeys.has(k)) template[k] = v
|
|
88
86
|
}
|
|
87
|
+
return template
|
|
88
|
+
}
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
function collectAllCombos(data, align_by) {
|
|
91
91
|
const seen = new Map()
|
|
92
92
|
for (const row of data) {
|
|
93
93
|
const key = align_by.map((k) => row[k]).join('\x00')
|
|
@@ -97,19 +97,19 @@ export function getAlignGenerator(data, config) {
|
|
|
97
97
|
seen.set(key, combo)
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
|
|
100
|
+
return [...seen.values()]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getAlignGenerator(data, config) {
|
|
104
|
+
const { align_by } = config
|
|
105
|
+
const template = buildAlignTemplate(config)
|
|
106
|
+
const allCombos = collectAllCombos(data, align_by)
|
|
101
107
|
|
|
102
|
-
// Return a function that, given a group's children, yields the missing combos
|
|
103
108
|
return (children) => {
|
|
104
109
|
const childKeys = new Set(children.map((c) => align_by.map((k) => c[k]).join('\x00')))
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (!childKeys.has(key)) {
|
|
109
|
-
missing.push({ ...combo, ...template })
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return missing
|
|
110
|
+
return allCombos
|
|
111
|
+
.filter((combo) => !childKeys.has(align_by.map((k) => combo[k]).join('\x00')))
|
|
112
|
+
.map((combo) => ({ ...combo, ...template }))
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|