tranfi 0.0.1 → 0.1.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.
- package/README.md +395 -0
- package/app/assets/index-6quYZ5Ap.css +5 -0
- package/app/assets/index-pDFMluyz.js +160 -0
- package/app/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
- package/app/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
- package/app/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
- package/app/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
- package/app/index.html +13 -0
- package/binding.gyp +69 -0
- package/csrc/arena.c +91 -0
- package/csrc/batch.c +229 -0
- package/csrc/buffer.c +78 -0
- package/csrc/cJSON.c +3143 -0
- package/csrc/cJSON.h +300 -0
- package/csrc/codec_csv.c +1058 -0
- package/csrc/codec_jsonl.c +374 -0
- package/csrc/codec_table.c +218 -0
- package/csrc/codec_text.c +229 -0
- package/csrc/compiler.c +102 -0
- package/csrc/date_utils.h +94 -0
- package/csrc/dsl.c +1180 -0
- package/csrc/dsl.h +22 -0
- package/csrc/expr.c +1245 -0
- package/csrc/expr.h +56 -0
- package/csrc/internal.h +250 -0
- package/csrc/ir.c +119 -0
- package/csrc/ir.h +167 -0
- package/csrc/ir_schema.c +60 -0
- package/csrc/ir_serialize.c +104 -0
- package/csrc/ir_sql.c +1211 -0
- package/csrc/ir_validate.c +120 -0
- package/csrc/main.c +392 -0
- package/csrc/op_acf.c +133 -0
- package/csrc/op_anomaly.c +120 -0
- package/csrc/op_bin.c +109 -0
- package/csrc/op_cast.c +195 -0
- package/csrc/op_clip.c +88 -0
- package/csrc/op_date_trunc.c +181 -0
- package/csrc/op_datetime.c +212 -0
- package/csrc/op_derive.c +248 -0
- package/csrc/op_diff.c +134 -0
- package/csrc/op_ewma.c +103 -0
- package/csrc/op_explode.c +108 -0
- package/csrc/op_fill_down.c +163 -0
- package/csrc/op_fill_null.c +123 -0
- package/csrc/op_filter.c +132 -0
- package/csrc/op_frequency.c +193 -0
- package/csrc/op_grep.c +163 -0
- package/csrc/op_group_agg.c +285 -0
- package/csrc/op_hash.c +126 -0
- package/csrc/op_head.c +149 -0
- package/csrc/op_interpolate.c +239 -0
- package/csrc/op_join.c +384 -0
- package/csrc/op_label_encode.c +144 -0
- package/csrc/op_lead.c +190 -0
- package/csrc/op_normalize.c +226 -0
- package/csrc/op_onehot.c +185 -0
- package/csrc/op_pivot.c +370 -0
- package/csrc/op_registry.c +1148 -0
- package/csrc/op_rename.c +138 -0
- package/csrc/op_replace.c +202 -0
- package/csrc/op_sample.c +101 -0
- package/csrc/op_select.c +140 -0
- package/csrc/op_skip.c +152 -0
- package/csrc/op_sort.c +273 -0
- package/csrc/op_split.c +114 -0
- package/csrc/op_split_data.c +87 -0
- package/csrc/op_stack.c +315 -0
- package/csrc/op_stats.c +779 -0
- package/csrc/op_step.c +171 -0
- package/csrc/op_tail.c +96 -0
- package/csrc/op_top.c +150 -0
- package/csrc/op_trim.c +109 -0
- package/csrc/op_unique.c +300 -0
- package/csrc/op_unpivot.c +159 -0
- package/csrc/op_validate.c +71 -0
- package/csrc/op_window.c +150 -0
- package/csrc/pipeline.c +315 -0
- package/csrc/plan.c +206 -0
- package/csrc/recipes.c +102 -0
- package/csrc/recipes.h +27 -0
- package/csrc/report.c +463 -0
- package/csrc/report.h +22 -0
- package/csrc/tranfi.h +123 -0
- package/csrc/wasm_api.c +157 -0
- package/napi_api.c +326 -0
- package/package.json +46 -57
- package/src/cli.js +193 -0
- package/src/engines/duckdb.js +109 -0
- package/src/index.js +306 -0
- package/src/native.js +22 -0
- package/src/pipeline.js +286 -0
- package/src/server.js +277 -0
- package/src/wasm.js +19 -0
- package/wasm/index.js +244 -0
- package/wasm/package.json +1 -0
- package/wasm/tranfi_core.js +0 -0
- package/LICENSE +0 -21
- package/dist/bundle.js +0 -1
- package/index.html +0 -18
- package/logo.png +0 -0
- package/src/app.css +0 -160
- package/src/app.js +0 -203
- package/src/app.vue +0 -253
- package/src/bulma-input.vue +0 -110
- package/src/common-inputs.js +0 -28
- package/src/main.js +0 -18
- package/src/transforms.js +0 -166
- package/webpack.config.js +0 -108
package/csrc/op_skip.c
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* op_skip.c — Skip first N rows.
|
|
3
|
+
*
|
|
4
|
+
* Config: {"n": 5}
|
|
5
|
+
* Discards the first N rows, passes through the rest.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "internal.h"
|
|
9
|
+
#include "cJSON.h"
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
#include <string.h>
|
|
12
|
+
|
|
13
|
+
typedef struct {
|
|
14
|
+
size_t n;
|
|
15
|
+
size_t seen;
|
|
16
|
+
} skip_state;
|
|
17
|
+
|
|
18
|
+
static int skip_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
19
|
+
tf_side_channels *side) {
|
|
20
|
+
(void)side;
|
|
21
|
+
skip_state *st = self->state;
|
|
22
|
+
*out = NULL;
|
|
23
|
+
|
|
24
|
+
if (st->seen >= st->n) {
|
|
25
|
+
/* Already past skip region — pass through all rows */
|
|
26
|
+
tf_batch *ob = tf_batch_create(in->n_cols, in->n_rows);
|
|
27
|
+
if (!ob) return TF_ERROR;
|
|
28
|
+
for (size_t c = 0; c < in->n_cols; c++) {
|
|
29
|
+
tf_batch_set_schema(ob, c, in->col_names[c], in->col_types[c]);
|
|
30
|
+
}
|
|
31
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
32
|
+
tf_batch_ensure_capacity(ob, r + 1);
|
|
33
|
+
for (size_t c = 0; c < in->n_cols; c++) {
|
|
34
|
+
if (tf_batch_is_null(in, r, c)) {
|
|
35
|
+
tf_batch_set_null(ob, r, c);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
switch (in->col_types[c]) {
|
|
39
|
+
case TF_TYPE_BOOL:
|
|
40
|
+
tf_batch_set_bool(ob, r, c, tf_batch_get_bool(in, r, c));
|
|
41
|
+
break;
|
|
42
|
+
case TF_TYPE_INT64:
|
|
43
|
+
tf_batch_set_int64(ob, r, c, tf_batch_get_int64(in, r, c));
|
|
44
|
+
break;
|
|
45
|
+
case TF_TYPE_FLOAT64:
|
|
46
|
+
tf_batch_set_float64(ob, r, c, tf_batch_get_float64(in, r, c));
|
|
47
|
+
break;
|
|
48
|
+
case TF_TYPE_STRING:
|
|
49
|
+
tf_batch_set_string(ob, r, c, tf_batch_get_string(in, r, c));
|
|
50
|
+
break;
|
|
51
|
+
case TF_TYPE_DATE:
|
|
52
|
+
tf_batch_set_date(ob, r, c, tf_batch_get_date(in, r, c));
|
|
53
|
+
break;
|
|
54
|
+
case TF_TYPE_TIMESTAMP:
|
|
55
|
+
tf_batch_set_timestamp(ob, r, c, tf_batch_get_timestamp(in, r, c));
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
tf_batch_set_null(ob, r, c);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
ob->n_rows = r + 1;
|
|
63
|
+
}
|
|
64
|
+
*out = ob;
|
|
65
|
+
return TF_OK;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
size_t remaining_skip = st->n - st->seen;
|
|
69
|
+
|
|
70
|
+
if (remaining_skip >= in->n_rows) {
|
|
71
|
+
/* Skip entire batch */
|
|
72
|
+
st->seen += in->n_rows;
|
|
73
|
+
return TF_OK;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Partial skip — emit rows after the skip region */
|
|
77
|
+
size_t emit_start = remaining_skip;
|
|
78
|
+
size_t emit_count = in->n_rows - emit_start;
|
|
79
|
+
st->seen = st->n;
|
|
80
|
+
|
|
81
|
+
tf_batch *ob = tf_batch_create(in->n_cols, emit_count);
|
|
82
|
+
if (!ob) return TF_ERROR;
|
|
83
|
+
for (size_t c = 0; c < in->n_cols; c++) {
|
|
84
|
+
tf_batch_set_schema(ob, c, in->col_names[c], in->col_types[c]);
|
|
85
|
+
}
|
|
86
|
+
for (size_t i = 0; i < emit_count; i++) {
|
|
87
|
+
size_t r = emit_start + i;
|
|
88
|
+
tf_batch_ensure_capacity(ob, i + 1);
|
|
89
|
+
for (size_t c = 0; c < in->n_cols; c++) {
|
|
90
|
+
if (tf_batch_is_null(in, r, c)) {
|
|
91
|
+
tf_batch_set_null(ob, i, c);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
switch (in->col_types[c]) {
|
|
95
|
+
case TF_TYPE_BOOL:
|
|
96
|
+
tf_batch_set_bool(ob, i, c, tf_batch_get_bool(in, r, c));
|
|
97
|
+
break;
|
|
98
|
+
case TF_TYPE_INT64:
|
|
99
|
+
tf_batch_set_int64(ob, i, c, tf_batch_get_int64(in, r, c));
|
|
100
|
+
break;
|
|
101
|
+
case TF_TYPE_FLOAT64:
|
|
102
|
+
tf_batch_set_float64(ob, i, c, tf_batch_get_float64(in, r, c));
|
|
103
|
+
break;
|
|
104
|
+
case TF_TYPE_STRING:
|
|
105
|
+
tf_batch_set_string(ob, i, c, tf_batch_get_string(in, r, c));
|
|
106
|
+
break;
|
|
107
|
+
case TF_TYPE_DATE:
|
|
108
|
+
tf_batch_set_date(ob, i, c, tf_batch_get_date(in, r, c));
|
|
109
|
+
break;
|
|
110
|
+
case TF_TYPE_TIMESTAMP:
|
|
111
|
+
tf_batch_set_timestamp(ob, i, c, tf_batch_get_timestamp(in, r, c));
|
|
112
|
+
break;
|
|
113
|
+
default:
|
|
114
|
+
tf_batch_set_null(ob, i, c);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
ob->n_rows = i + 1;
|
|
119
|
+
}
|
|
120
|
+
*out = ob;
|
|
121
|
+
return TF_OK;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static int skip_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
125
|
+
(void)self; (void)side;
|
|
126
|
+
*out = NULL;
|
|
127
|
+
return TF_OK;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static void skip_destroy(tf_step *self) {
|
|
131
|
+
free(self->state);
|
|
132
|
+
free(self);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
tf_step *tf_skip_create(const cJSON *args) {
|
|
136
|
+
if (!args) return NULL;
|
|
137
|
+
cJSON *n_json = cJSON_GetObjectItemCaseSensitive(args, "n");
|
|
138
|
+
if (!cJSON_IsNumber(n_json) || n_json->valueint <= 0) return NULL;
|
|
139
|
+
|
|
140
|
+
skip_state *st = calloc(1, sizeof(skip_state));
|
|
141
|
+
if (!st) return NULL;
|
|
142
|
+
st->n = (size_t)n_json->valueint;
|
|
143
|
+
st->seen = 0;
|
|
144
|
+
|
|
145
|
+
tf_step *step = malloc(sizeof(tf_step));
|
|
146
|
+
if (!step) { free(st); return NULL; }
|
|
147
|
+
step->process = skip_process;
|
|
148
|
+
step->flush = skip_flush;
|
|
149
|
+
step->destroy = skip_destroy;
|
|
150
|
+
step->state = st;
|
|
151
|
+
return step;
|
|
152
|
+
}
|
package/csrc/op_sort.c
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* op_sort.c — Sort all rows by column(s). Requires buffering all data.
|
|
3
|
+
*
|
|
4
|
+
* Config: {"columns": [{"name": "age", "desc": false}, {"name": "name", "desc": true}]}
|
|
5
|
+
* Buffers all incoming batches, sorts on flush, emits as a single batch.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "internal.h"
|
|
9
|
+
#include "cJSON.h"
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
#include <string.h>
|
|
12
|
+
#include <stdio.h>
|
|
13
|
+
|
|
14
|
+
typedef struct {
|
|
15
|
+
char *name;
|
|
16
|
+
int desc; /* 1 = descending */
|
|
17
|
+
} sort_col;
|
|
18
|
+
|
|
19
|
+
typedef struct {
|
|
20
|
+
/* Accumulated data */
|
|
21
|
+
tf_batch *buf; /* growing buffer of all rows */
|
|
22
|
+
int has_schema;
|
|
23
|
+
|
|
24
|
+
/* Sort spec */
|
|
25
|
+
sort_col *cols;
|
|
26
|
+
size_t n_cols;
|
|
27
|
+
} sort_state;
|
|
28
|
+
|
|
29
|
+
/* Copy a row from src batch to dst batch */
|
|
30
|
+
static int copy_row(tf_batch *dst, size_t dst_row,
|
|
31
|
+
const tf_batch *src, size_t src_row) {
|
|
32
|
+
if (tf_batch_ensure_capacity(dst, dst_row + 1) != TF_OK) return TF_ERROR;
|
|
33
|
+
for (size_t c = 0; c < src->n_cols; c++) {
|
|
34
|
+
if (tf_batch_is_null(src, src_row, c)) {
|
|
35
|
+
tf_batch_set_null(dst, dst_row, c);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
switch (src->col_types[c]) {
|
|
39
|
+
case TF_TYPE_BOOL:
|
|
40
|
+
tf_batch_set_bool(dst, dst_row, c, tf_batch_get_bool(src, src_row, c));
|
|
41
|
+
break;
|
|
42
|
+
case TF_TYPE_INT64:
|
|
43
|
+
tf_batch_set_int64(dst, dst_row, c, tf_batch_get_int64(src, src_row, c));
|
|
44
|
+
break;
|
|
45
|
+
case TF_TYPE_FLOAT64:
|
|
46
|
+
tf_batch_set_float64(dst, dst_row, c, tf_batch_get_float64(src, src_row, c));
|
|
47
|
+
break;
|
|
48
|
+
case TF_TYPE_STRING:
|
|
49
|
+
tf_batch_set_string(dst, dst_row, c, tf_batch_get_string(src, src_row, c));
|
|
50
|
+
break;
|
|
51
|
+
case TF_TYPE_DATE:
|
|
52
|
+
tf_batch_set_date(dst, dst_row, c, tf_batch_get_date(src, src_row, c));
|
|
53
|
+
break;
|
|
54
|
+
case TF_TYPE_TIMESTAMP:
|
|
55
|
+
tf_batch_set_timestamp(dst, dst_row, c, tf_batch_get_timestamp(src, src_row, c));
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
tf_batch_set_null(dst, dst_row, c);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return TF_OK;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static int sort_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
66
|
+
tf_side_channels *side) {
|
|
67
|
+
(void)side;
|
|
68
|
+
sort_state *st = self->state;
|
|
69
|
+
*out = NULL;
|
|
70
|
+
|
|
71
|
+
/* Initialize buffer on first batch */
|
|
72
|
+
if (!st->has_schema) {
|
|
73
|
+
st->buf = tf_batch_create(in->n_cols, in->n_rows > 0 ? in->n_rows : 16);
|
|
74
|
+
if (!st->buf) return TF_ERROR;
|
|
75
|
+
for (size_t c = 0; c < in->n_cols; c++) {
|
|
76
|
+
tf_batch_set_schema(st->buf, c, in->col_names[c], in->col_types[c]);
|
|
77
|
+
}
|
|
78
|
+
st->has_schema = 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Append all rows */
|
|
82
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
83
|
+
size_t dst_row = st->buf->n_rows;
|
|
84
|
+
if (copy_row(st->buf, dst_row, in, r) != TF_OK) return TF_ERROR;
|
|
85
|
+
st->buf->n_rows = dst_row + 1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return TF_OK;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Comparator context for qsort_r / qsort */
|
|
92
|
+
typedef struct {
|
|
93
|
+
const tf_batch *batch;
|
|
94
|
+
int *col_indices;
|
|
95
|
+
int *col_desc;
|
|
96
|
+
size_t n_sort_cols;
|
|
97
|
+
} sort_ctx;
|
|
98
|
+
|
|
99
|
+
static sort_ctx *g_sort_ctx; /* global for qsort (no qsort_r on all platforms) */
|
|
100
|
+
|
|
101
|
+
static int compare_rows(const void *a, const void *b) {
|
|
102
|
+
size_t ra = *(const size_t *)a;
|
|
103
|
+
size_t rb = *(const size_t *)b;
|
|
104
|
+
const tf_batch *batch = g_sort_ctx->batch;
|
|
105
|
+
|
|
106
|
+
for (size_t k = 0; k < g_sort_ctx->n_sort_cols; k++) {
|
|
107
|
+
int ci = g_sort_ctx->col_indices[k];
|
|
108
|
+
if (ci < 0) continue;
|
|
109
|
+
|
|
110
|
+
int null_a = tf_batch_is_null(batch, ra, ci);
|
|
111
|
+
int null_b = tf_batch_is_null(batch, rb, ci);
|
|
112
|
+
|
|
113
|
+
/* Nulls sort last */
|
|
114
|
+
if (null_a && null_b) continue;
|
|
115
|
+
if (null_a) return 1;
|
|
116
|
+
if (null_b) return -1;
|
|
117
|
+
|
|
118
|
+
int cmp = 0;
|
|
119
|
+
switch (batch->col_types[ci]) {
|
|
120
|
+
case TF_TYPE_INT64: {
|
|
121
|
+
int64_t va = tf_batch_get_int64(batch, ra, ci);
|
|
122
|
+
int64_t vb = tf_batch_get_int64(batch, rb, ci);
|
|
123
|
+
cmp = (va > vb) - (va < vb);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case TF_TYPE_FLOAT64: {
|
|
127
|
+
double va = tf_batch_get_float64(batch, ra, ci);
|
|
128
|
+
double vb = tf_batch_get_float64(batch, rb, ci);
|
|
129
|
+
cmp = (va > vb) - (va < vb);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case TF_TYPE_STRING:
|
|
133
|
+
cmp = strcmp(tf_batch_get_string(batch, ra, ci),
|
|
134
|
+
tf_batch_get_string(batch, rb, ci));
|
|
135
|
+
break;
|
|
136
|
+
case TF_TYPE_BOOL: {
|
|
137
|
+
bool va = tf_batch_get_bool(batch, ra, ci);
|
|
138
|
+
bool vb = tf_batch_get_bool(batch, rb, ci);
|
|
139
|
+
cmp = (int)va - (int)vb;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case TF_TYPE_DATE: {
|
|
143
|
+
int32_t va = tf_batch_get_date(batch, ra, ci);
|
|
144
|
+
int32_t vb = tf_batch_get_date(batch, rb, ci);
|
|
145
|
+
cmp = (va > vb) - (va < vb);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case TF_TYPE_TIMESTAMP: {
|
|
149
|
+
int64_t va = tf_batch_get_timestamp(batch, ra, ci);
|
|
150
|
+
int64_t vb = tf_batch_get_timestamp(batch, rb, ci);
|
|
151
|
+
cmp = (va > vb) - (va < vb);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
default:
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (cmp != 0) {
|
|
159
|
+
return g_sort_ctx->col_desc[k] ? -cmp : cmp;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
static int sort_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
166
|
+
(void)side;
|
|
167
|
+
sort_state *st = self->state;
|
|
168
|
+
*out = NULL;
|
|
169
|
+
|
|
170
|
+
if (!st->buf || st->buf->n_rows == 0) return TF_OK;
|
|
171
|
+
|
|
172
|
+
size_t n = st->buf->n_rows;
|
|
173
|
+
|
|
174
|
+
/* Resolve sort column indices */
|
|
175
|
+
int *col_indices = malloc(st->n_cols * sizeof(int));
|
|
176
|
+
int *col_desc = malloc(st->n_cols * sizeof(int));
|
|
177
|
+
if (!col_indices || !col_desc) { free(col_indices); free(col_desc); return TF_ERROR; }
|
|
178
|
+
|
|
179
|
+
for (size_t k = 0; k < st->n_cols; k++) {
|
|
180
|
+
col_indices[k] = tf_batch_col_index(st->buf, st->cols[k].name);
|
|
181
|
+
col_desc[k] = st->cols[k].desc;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Build index array */
|
|
185
|
+
size_t *indices = malloc(n * sizeof(size_t));
|
|
186
|
+
if (!indices) { free(col_indices); free(col_desc); return TF_ERROR; }
|
|
187
|
+
for (size_t i = 0; i < n; i++) indices[i] = i;
|
|
188
|
+
|
|
189
|
+
/* Sort */
|
|
190
|
+
sort_ctx ctx = {
|
|
191
|
+
.batch = st->buf,
|
|
192
|
+
.col_indices = col_indices,
|
|
193
|
+
.col_desc = col_desc,
|
|
194
|
+
.n_sort_cols = st->n_cols,
|
|
195
|
+
};
|
|
196
|
+
g_sort_ctx = &ctx;
|
|
197
|
+
qsort(indices, n, sizeof(size_t), compare_rows);
|
|
198
|
+
g_sort_ctx = NULL;
|
|
199
|
+
|
|
200
|
+
/* Build output batch in sorted order */
|
|
201
|
+
tf_batch *ob = tf_batch_create(st->buf->n_cols, n);
|
|
202
|
+
if (!ob) { free(indices); free(col_indices); free(col_desc); return TF_ERROR; }
|
|
203
|
+
for (size_t c = 0; c < st->buf->n_cols; c++) {
|
|
204
|
+
tf_batch_set_schema(ob, c, st->buf->col_names[c], st->buf->col_types[c]);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
for (size_t i = 0; i < n; i++) {
|
|
208
|
+
if (copy_row(ob, i, st->buf, indices[i]) != TF_OK) {
|
|
209
|
+
free(indices); free(col_indices); free(col_desc);
|
|
210
|
+
tf_batch_free(ob);
|
|
211
|
+
return TF_ERROR;
|
|
212
|
+
}
|
|
213
|
+
ob->n_rows = i + 1;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
free(indices);
|
|
217
|
+
free(col_indices);
|
|
218
|
+
free(col_desc);
|
|
219
|
+
|
|
220
|
+
*out = ob;
|
|
221
|
+
return TF_OK;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
static void sort_destroy(tf_step *self) {
|
|
225
|
+
sort_state *st = self->state;
|
|
226
|
+
if (st) {
|
|
227
|
+
if (st->buf) tf_batch_free(st->buf);
|
|
228
|
+
for (size_t i = 0; i < st->n_cols; i++) free(st->cols[i].name);
|
|
229
|
+
free(st->cols);
|
|
230
|
+
free(st);
|
|
231
|
+
}
|
|
232
|
+
free(self);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
tf_step *tf_sort_create(const cJSON *args) {
|
|
236
|
+
if (!args) return NULL;
|
|
237
|
+
cJSON *columns = cJSON_GetObjectItemCaseSensitive(args, "columns");
|
|
238
|
+
if (!columns || !cJSON_IsArray(columns)) return NULL;
|
|
239
|
+
|
|
240
|
+
int n = cJSON_GetArraySize(columns);
|
|
241
|
+
if (n <= 0) return NULL;
|
|
242
|
+
|
|
243
|
+
sort_state *st = calloc(1, sizeof(sort_state));
|
|
244
|
+
if (!st) return NULL;
|
|
245
|
+
st->cols = calloc(n, sizeof(sort_col));
|
|
246
|
+
if (!st->cols) { free(st); return NULL; }
|
|
247
|
+
st->n_cols = n;
|
|
248
|
+
|
|
249
|
+
for (int i = 0; i < n; i++) {
|
|
250
|
+
cJSON *item = cJSON_GetArrayItem(columns, i);
|
|
251
|
+
cJSON *name_j = cJSON_GetObjectItemCaseSensitive(item, "name");
|
|
252
|
+
cJSON *desc_j = cJSON_GetObjectItemCaseSensitive(item, "desc");
|
|
253
|
+
if (!cJSON_IsString(name_j)) {
|
|
254
|
+
for (int j = 0; j < i; j++) free(st->cols[j].name);
|
|
255
|
+
free(st->cols); free(st);
|
|
256
|
+
return NULL;
|
|
257
|
+
}
|
|
258
|
+
st->cols[i].name = strdup(name_j->valuestring);
|
|
259
|
+
st->cols[i].desc = (desc_j && cJSON_IsBool(desc_j)) ? cJSON_IsTrue(desc_j) : 0;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
tf_step *step = malloc(sizeof(tf_step));
|
|
263
|
+
if (!step) {
|
|
264
|
+
for (int i = 0; i < n; i++) free(st->cols[i].name);
|
|
265
|
+
free(st->cols); free(st);
|
|
266
|
+
return NULL;
|
|
267
|
+
}
|
|
268
|
+
step->process = sort_process;
|
|
269
|
+
step->flush = sort_flush;
|
|
270
|
+
step->destroy = sort_destroy;
|
|
271
|
+
step->state = st;
|
|
272
|
+
return step;
|
|
273
|
+
}
|
package/csrc/op_split.c
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* op_split.c — Split column into multiple columns.
|
|
3
|
+
*
|
|
4
|
+
* Config: {"column": "name", "delimiter": " ", "names": ["first", "last"]}
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#include "internal.h"
|
|
8
|
+
#include "cJSON.h"
|
|
9
|
+
#include <stdlib.h>
|
|
10
|
+
#include <string.h>
|
|
11
|
+
|
|
12
|
+
typedef struct {
|
|
13
|
+
char *column;
|
|
14
|
+
char *delimiter;
|
|
15
|
+
char **names;
|
|
16
|
+
size_t n_names;
|
|
17
|
+
} split_state;
|
|
18
|
+
|
|
19
|
+
static int split_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
20
|
+
tf_side_channels *side) {
|
|
21
|
+
(void)side;
|
|
22
|
+
split_state *st = self->state;
|
|
23
|
+
*out = NULL;
|
|
24
|
+
|
|
25
|
+
size_t out_cols = in->n_cols + st->n_names;
|
|
26
|
+
tf_batch *ob = tf_batch_create(out_cols, in->n_rows);
|
|
27
|
+
if (!ob) return TF_ERROR;
|
|
28
|
+
|
|
29
|
+
/* Copy existing schema */
|
|
30
|
+
for (size_t c = 0; c < in->n_cols; c++)
|
|
31
|
+
tf_batch_set_schema(ob, c, in->col_names[c], in->col_types[c]);
|
|
32
|
+
/* Add new columns */
|
|
33
|
+
for (size_t k = 0; k < st->n_names; k++)
|
|
34
|
+
tf_batch_set_schema(ob, in->n_cols + k, st->names[k], TF_TYPE_STRING);
|
|
35
|
+
|
|
36
|
+
int ci = tf_batch_col_index(in, st->column);
|
|
37
|
+
size_t delim_len = strlen(st->delimiter);
|
|
38
|
+
|
|
39
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
40
|
+
tf_batch_copy_row(ob, r, in, r);
|
|
41
|
+
/* Initialize new columns as null */
|
|
42
|
+
for (size_t k = 0; k < st->n_names; k++)
|
|
43
|
+
tf_batch_set_null(ob, r, in->n_cols + k);
|
|
44
|
+
|
|
45
|
+
if (ci >= 0 && !tf_batch_is_null(in, r, ci) && in->col_types[ci] == TF_TYPE_STRING) {
|
|
46
|
+
const char *val = tf_batch_get_string(in, r, ci);
|
|
47
|
+
const char *p = val;
|
|
48
|
+
for (size_t k = 0; k < st->n_names && *p; k++) {
|
|
49
|
+
const char *found = strstr(p, st->delimiter);
|
|
50
|
+
size_t tok_len = found ? (size_t)(found - p) : strlen(p);
|
|
51
|
+
char *tok = malloc(tok_len + 1);
|
|
52
|
+
if (tok) {
|
|
53
|
+
memcpy(tok, p, tok_len);
|
|
54
|
+
tok[tok_len] = '\0';
|
|
55
|
+
tf_batch_set_string(ob, r, in->n_cols + k, tok);
|
|
56
|
+
free(tok);
|
|
57
|
+
}
|
|
58
|
+
if (found) p = found + delim_len;
|
|
59
|
+
else break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
ob->n_rows = r + 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
*out = ob;
|
|
66
|
+
return TF_OK;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static int split_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
70
|
+
(void)self; (void)side; *out = NULL; return TF_OK;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static void split_destroy(tf_step *self) {
|
|
74
|
+
split_state *st = self->state;
|
|
75
|
+
if (st) {
|
|
76
|
+
free(st->column); free(st->delimiter);
|
|
77
|
+
for (size_t i = 0; i < st->n_names; i++) free(st->names[i]);
|
|
78
|
+
free(st->names);
|
|
79
|
+
free(st);
|
|
80
|
+
}
|
|
81
|
+
free(self);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
tf_step *tf_split_create(const cJSON *args) {
|
|
85
|
+
if (!args) return NULL;
|
|
86
|
+
cJSON *col_j = cJSON_GetObjectItemCaseSensitive(args, "column");
|
|
87
|
+
cJSON *names_j = cJSON_GetObjectItemCaseSensitive(args, "names");
|
|
88
|
+
if (!cJSON_IsString(col_j) || !cJSON_IsArray(names_j)) return NULL;
|
|
89
|
+
|
|
90
|
+
int n = cJSON_GetArraySize(names_j);
|
|
91
|
+
if (n <= 0) return NULL;
|
|
92
|
+
|
|
93
|
+
split_state *st = calloc(1, sizeof(split_state));
|
|
94
|
+
if (!st) return NULL;
|
|
95
|
+
st->column = strdup(col_j->valuestring);
|
|
96
|
+
|
|
97
|
+
cJSON *delim_j = cJSON_GetObjectItemCaseSensitive(args, "delimiter");
|
|
98
|
+
st->delimiter = strdup(cJSON_IsString(delim_j) ? delim_j->valuestring : " ");
|
|
99
|
+
|
|
100
|
+
st->names = calloc(n, sizeof(char *));
|
|
101
|
+
st->n_names = n;
|
|
102
|
+
for (int i = 0; i < n; i++) {
|
|
103
|
+
cJSON *item = cJSON_GetArrayItem(names_j, i);
|
|
104
|
+
if (cJSON_IsString(item)) st->names[i] = strdup(item->valuestring);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
tf_step *step = malloc(sizeof(tf_step));
|
|
108
|
+
if (!step) { split_destroy(&(tf_step){.state = st}); return NULL; }
|
|
109
|
+
step->process = split_process;
|
|
110
|
+
step->flush = split_flush;
|
|
111
|
+
step->destroy = split_destroy;
|
|
112
|
+
step->state = st;
|
|
113
|
+
return step;
|
|
114
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* op_split_data.c — Train/test split with probabilistic assignment.
|
|
3
|
+
* Uses LCG PRNG for reproducible splitting.
|
|
4
|
+
*
|
|
5
|
+
* Config: {"ratio": 0.8, "result": "_split", "seed": 42}
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "internal.h"
|
|
9
|
+
#include "cJSON.h"
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
#include <string.h>
|
|
12
|
+
#include <stdio.h>
|
|
13
|
+
|
|
14
|
+
typedef struct {
|
|
15
|
+
char *result;
|
|
16
|
+
double ratio;
|
|
17
|
+
uint64_t seed;
|
|
18
|
+
uint64_t row_index;
|
|
19
|
+
} split_data_state;
|
|
20
|
+
|
|
21
|
+
/* Simple LCG for deterministic random */
|
|
22
|
+
static double lcg_random(uint64_t seed, uint64_t index) {
|
|
23
|
+
uint64_t x = seed ^ (index * 6364136223846793005ULL + 1442695040888963407ULL);
|
|
24
|
+
x = x * 6364136223846793005ULL + 1442695040888963407ULL;
|
|
25
|
+
x = x * 6364136223846793005ULL + 1442695040888963407ULL;
|
|
26
|
+
return (double)(x >> 33) / (double)(1ULL << 31);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static int split_data_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
30
|
+
tf_side_channels *side) {
|
|
31
|
+
(void)side;
|
|
32
|
+
split_data_state *st = self->state;
|
|
33
|
+
*out = NULL;
|
|
34
|
+
|
|
35
|
+
tf_batch *ob = tf_batch_create(in->n_cols + 1, in->n_rows);
|
|
36
|
+
if (!ob) return TF_ERROR;
|
|
37
|
+
for (size_t c = 0; c < in->n_cols; c++)
|
|
38
|
+
tf_batch_set_schema(ob, c, in->col_names[c], in->col_types[c]);
|
|
39
|
+
tf_batch_set_schema(ob, in->n_cols, st->result, TF_TYPE_STRING);
|
|
40
|
+
|
|
41
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
42
|
+
tf_batch_copy_row(ob, r, in, r);
|
|
43
|
+
|
|
44
|
+
double rval = lcg_random(st->seed, st->row_index);
|
|
45
|
+
const char *label = (rval < st->ratio) ? "train" : "test";
|
|
46
|
+
tf_batch_set_string(ob, r, in->n_cols, label);
|
|
47
|
+
ob->n_rows = r + 1;
|
|
48
|
+
st->row_index++;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
*out = ob;
|
|
52
|
+
return TF_OK;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static int split_data_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
56
|
+
(void)self; (void)side; *out = NULL; return TF_OK;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static void split_data_destroy(tf_step *self) {
|
|
60
|
+
split_data_state *st = self->state;
|
|
61
|
+
if (st) { free(st->result); free(st); }
|
|
62
|
+
free(self);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
tf_step *tf_split_data_create(const cJSON *args) {
|
|
66
|
+
if (!args) return NULL;
|
|
67
|
+
|
|
68
|
+
split_data_state *st = calloc(1, sizeof(split_data_state));
|
|
69
|
+
if (!st) return NULL;
|
|
70
|
+
|
|
71
|
+
cJSON *ratio_j = cJSON_GetObjectItemCaseSensitive(args, "ratio");
|
|
72
|
+
st->ratio = cJSON_IsNumber(ratio_j) ? ratio_j->valuedouble : 0.8;
|
|
73
|
+
|
|
74
|
+
cJSON *seed_j = cJSON_GetObjectItemCaseSensitive(args, "seed");
|
|
75
|
+
st->seed = cJSON_IsNumber(seed_j) ? (uint64_t)seed_j->valueint : 42;
|
|
76
|
+
|
|
77
|
+
cJSON *res_j = cJSON_GetObjectItemCaseSensitive(args, "result");
|
|
78
|
+
st->result = strdup(cJSON_IsString(res_j) ? res_j->valuestring : "_split");
|
|
79
|
+
|
|
80
|
+
tf_step *step = malloc(sizeof(tf_step));
|
|
81
|
+
if (!step) { free(st->result); free(st); return NULL; }
|
|
82
|
+
step->process = split_data_process;
|
|
83
|
+
step->flush = split_data_flush;
|
|
84
|
+
step->destroy = split_data_destroy;
|
|
85
|
+
step->state = st;
|
|
86
|
+
return step;
|
|
87
|
+
}
|