tranfi 0.0.2 → 0.2.0

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.
Files changed (151) hide show
  1. package/LICENSE +177 -21
  2. package/NOTICE +8 -0
  3. package/README.md +627 -0
  4. package/app/assets/index-6quYZ5Ap.css +5 -0
  5. package/app/assets/index-BIAIKnrp.js +160 -0
  6. package/app/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
  7. package/app/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
  8. package/app/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
  9. package/app/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
  10. package/app/index.html +13 -0
  11. package/binding.gyp +121 -0
  12. package/csrc/arena.c +93 -0
  13. package/csrc/batch.c +976 -0
  14. package/csrc/buffer.c +154 -0
  15. package/csrc/cJSON.c +3386 -0
  16. package/csrc/cJSON.h +316 -0
  17. package/csrc/codec_csv.c +1951 -0
  18. package/csrc/codec_jsonl.c +1086 -0
  19. package/csrc/codec_table.c +248 -0
  20. package/csrc/codec_text.c +447 -0
  21. package/csrc/compiler.c +130 -0
  22. package/csrc/config.h +21 -0
  23. package/csrc/date_utils.h +94 -0
  24. package/csrc/dsl.c +5417 -0
  25. package/csrc/dsl.h +22 -0
  26. package/csrc/expr.c +1553 -0
  27. package/csrc/expr.h +58 -0
  28. package/csrc/internal.h +539 -0
  29. package/csrc/ir.c +166 -0
  30. package/csrc/ir.h +208 -0
  31. package/csrc/ir_schema.c +75 -0
  32. package/csrc/ir_serialize.c +166 -0
  33. package/csrc/ir_sql.c +1822 -0
  34. package/csrc/ir_validate.c +576 -0
  35. package/csrc/json_path.c +210 -0
  36. package/csrc/main.c +1241 -0
  37. package/csrc/memory_estimate.c +477 -0
  38. package/csrc/op_acf.c +283 -0
  39. package/csrc/op_across.c +477 -0
  40. package/csrc/op_anomaly.c +255 -0
  41. package/csrc/op_assert.c +761 -0
  42. package/csrc/op_bin.c +248 -0
  43. package/csrc/op_cast.c +523 -0
  44. package/csrc/op_clip.c +99 -0
  45. package/csrc/op_date_trunc.c +355 -0
  46. package/csrc/op_datetime.c +394 -0
  47. package/csrc/op_derive.c +216 -0
  48. package/csrc/op_diff.c +250 -0
  49. package/csrc/op_ewma.c +222 -0
  50. package/csrc/op_explode.c +206 -0
  51. package/csrc/op_fill_down.c +235 -0
  52. package/csrc/op_fill_null.c +268 -0
  53. package/csrc/op_filter.c +181 -0
  54. package/csrc/op_frequency.c +721 -0
  55. package/csrc/op_grep.c +181 -0
  56. package/csrc/op_group_agg.c +1956 -0
  57. package/csrc/op_hash.c +159 -0
  58. package/csrc/op_head.c +84 -0
  59. package/csrc/op_interpolate.c +445 -0
  60. package/csrc/op_join.c +2902 -0
  61. package/csrc/op_json_extract.c +227 -0
  62. package/csrc/op_json_filter.c +384 -0
  63. package/csrc/op_json_flatten.c +293 -0
  64. package/csrc/op_json_schema.c +503 -0
  65. package/csrc/op_label_encode.c +419 -0
  66. package/csrc/op_lag.c +181 -0
  67. package/csrc/op_lead.c +242 -0
  68. package/csrc/op_normalize.c +510 -0
  69. package/csrc/op_onehot.c +457 -0
  70. package/csrc/op_pivot.c +1754 -0
  71. package/csrc/op_quarantine.c +189 -0
  72. package/csrc/op_registry.c +3044 -0
  73. package/csrc/op_rename.c +129 -0
  74. package/csrc/op_replace.c +354 -0
  75. package/csrc/op_rleid.c +297 -0
  76. package/csrc/op_rowid.c +559 -0
  77. package/csrc/op_sample.c +158 -0
  78. package/csrc/op_schema.c +1341 -0
  79. package/csrc/op_schema_infer.c +252 -0
  80. package/csrc/op_select.c +340 -0
  81. package/csrc/op_set.c +3449 -0
  82. package/csrc/op_skip.c +95 -0
  83. package/csrc/op_sort.c +819 -0
  84. package/csrc/op_source_name.c +120 -0
  85. package/csrc/op_split.c +151 -0
  86. package/csrc/op_split_data.c +119 -0
  87. package/csrc/op_stack.c +271 -0
  88. package/csrc/op_stats.c +875 -0
  89. package/csrc/op_step.c +333 -0
  90. package/csrc/op_tail.c +105 -0
  91. package/csrc/op_tee.c +338 -0
  92. package/csrc/op_top.c +357 -0
  93. package/csrc/op_trim.c +138 -0
  94. package/csrc/op_unique.c +1343 -0
  95. package/csrc/op_unpivot.c +193 -0
  96. package/csrc/op_validate.c +648 -0
  97. package/csrc/op_window.c +591 -0
  98. package/csrc/path_policy.c +85 -0
  99. package/csrc/pipeline.c +1088 -0
  100. package/csrc/recipes.c +104 -0
  101. package/csrc/recipes.h +27 -0
  102. package/csrc/report.c +506 -0
  103. package/csrc/report.h +22 -0
  104. package/csrc/selector.c +1097 -0
  105. package/csrc/size_utils.c +348 -0
  106. package/csrc/spill.c +317 -0
  107. package/csrc/spill.h +21 -0
  108. package/csrc/tranfi.h +291 -0
  109. package/csrc/transform.h +209 -0
  110. package/csrc/transform_api.c +2237 -0
  111. package/csrc/transform_categorical.c +923 -0
  112. package/csrc/transform_internal.h +472 -0
  113. package/csrc/transform_json.c +3812 -0
  114. package/csrc/transform_numeric.c +1966 -0
  115. package/csrc/transform_sha256.c +154 -0
  116. package/csrc/transform_wasm.h +162 -0
  117. package/csrc/transform_wasm_api.c +1373 -0
  118. package/csrc/wasm_api.c +218 -0
  119. package/napi_api.c +534 -0
  120. package/napi_transform.c +1648 -0
  121. package/napi_transform.h +8 -0
  122. package/package.json +64 -59
  123. package/scripts/install-native.js +76 -0
  124. package/scripts/prepack.js +64 -0
  125. package/scripts/sync-csrc.js +23 -0
  126. package/src/cli.js +190 -0
  127. package/src/engines/duckdb.js +142 -0
  128. package/src/index.js +925 -0
  129. package/src/memory_policy.js +411 -0
  130. package/src/native.js +18 -0
  131. package/src/pipeline.js +709 -0
  132. package/src/recipe_json.js +80 -0
  133. package/src/server.js +279 -0
  134. package/src/transform.js +403 -0
  135. package/src/transform_error.js +10 -0
  136. package/src/wasm.js +21 -0
  137. package/wasm/index.js +732 -0
  138. package/wasm/package.json +1 -0
  139. package/wasm/tranfi_core.js +0 -0
  140. package/wasm/transform.js +1156 -0
  141. package/wasm/worker.js +786 -0
  142. package/dist/bundle.js +0 -1
  143. package/index.html +0 -18
  144. package/src/app.css +0 -169
  145. package/src/app.js +0 -203
  146. package/src/app.vue +0 -250
  147. package/src/bulma-input.vue +0 -110
  148. package/src/common-inputs.js +0 -28
  149. package/src/main.js +0 -20
  150. package/src/transforms.js +0 -166
  151. package/webpack.config.js +0 -108
@@ -0,0 +1,255 @@
1
+ /*
2
+ * op_anomaly.c — Streaming anomaly detection via z-score.
3
+ * Uses Welford's online algorithm for mean/variance.
4
+ *
5
+ * Config: {"column": "price", "threshold": 3.0, "result": "price_anomaly"}
6
+ */
7
+
8
+ #include "internal.h"
9
+ #include "cJSON.h"
10
+ #include <stdlib.h>
11
+ #include <string.h>
12
+ #include <stdio.h>
13
+ #include <math.h>
14
+ #include <stdint.h>
15
+
16
+ typedef enum {
17
+ ANOMALY_MISSING_ERROR,
18
+ ANOMALY_MISSING_NULL,
19
+ ANOMALY_MISSING_IGNORE,
20
+ } anomaly_missing_policy;
21
+
22
+ typedef enum {
23
+ ANOMALY_TYPE_FAIL,
24
+ ANOMALY_TYPE_NULL,
25
+ } anomaly_type_policy;
26
+
27
+ typedef struct {
28
+ char *column;
29
+ char *result;
30
+ double threshold;
31
+ /* Welford's online algorithm */
32
+ size_t count;
33
+ double mean;
34
+ double m2;
35
+ anomaly_missing_policy missing;
36
+ anomaly_type_policy on_type_error;
37
+ } anomaly_state;
38
+
39
+ static int anomaly_is_numeric_type(tf_type type) {
40
+ return type == TF_TYPE_INT64 || type == TF_TYPE_FLOAT64;
41
+ }
42
+
43
+ static double anomaly_get_numeric(const tf_batch *b, size_t r, int ci) {
44
+ if (b->col_types[ci] == TF_TYPE_INT64) return (double)tf_batch_get_int64(b, r, ci);
45
+ return tf_batch_get_float64(b, r, ci);
46
+ }
47
+
48
+ static void anomaly_set_col_error(const char *column, const char *suffix) {
49
+ char msg[512];
50
+ snprintf(msg, sizeof(msg), "anomaly: column '%s' %s", column ? column : "", suffix);
51
+ tf_set_last_error(msg);
52
+ }
53
+
54
+ static int anomaly_parse_missing_policy(const cJSON *args, anomaly_missing_policy *out) {
55
+ *out = ANOMALY_MISSING_ERROR;
56
+ const cJSON *j = cJSON_GetObjectItemCaseSensitive(args, "missing");
57
+ if (!j) return TF_OK;
58
+ if (!cJSON_IsString(j)) {
59
+ tf_set_last_error("anomaly: missing must be error, null, or ignore");
60
+ return TF_ERROR;
61
+ }
62
+ if (strcmp(j->valuestring, "error") == 0) *out = ANOMALY_MISSING_ERROR;
63
+ else if (strcmp(j->valuestring, "null") == 0) *out = ANOMALY_MISSING_NULL;
64
+ else if (strcmp(j->valuestring, "ignore") == 0) *out = ANOMALY_MISSING_IGNORE;
65
+ else {
66
+ tf_set_last_error("anomaly: missing must be error, null, or ignore");
67
+ return TF_ERROR;
68
+ }
69
+ return TF_OK;
70
+ }
71
+
72
+ static int anomaly_parse_type_policy(const cJSON *args, anomaly_type_policy *out) {
73
+ *out = ANOMALY_TYPE_FAIL;
74
+ const cJSON *j = cJSON_GetObjectItemCaseSensitive(args, "on_type_error");
75
+ if (!j) return TF_OK;
76
+ if (!cJSON_IsString(j)) {
77
+ tf_set_last_error("anomaly: on_type_error must be fail or null");
78
+ return TF_ERROR;
79
+ }
80
+ if (strcmp(j->valuestring, "fail") == 0) *out = ANOMALY_TYPE_FAIL;
81
+ else if (strcmp(j->valuestring, "null") == 0) *out = ANOMALY_TYPE_NULL;
82
+ else {
83
+ tf_set_last_error("anomaly: on_type_error must be fail or null");
84
+ return TF_ERROR;
85
+ }
86
+ return TF_OK;
87
+ }
88
+
89
+ static int anomaly_passthrough(tf_batch *in, tf_batch **out) {
90
+ tf_batch *ob = tf_batch_create(in->n_cols, in->n_rows);
91
+ if (!ob) return TF_ERROR;
92
+ if (tf_batch_clone_schema(ob, in) != TF_OK) {
93
+ tf_batch_free(ob);
94
+ return TF_ERROR;
95
+ }
96
+ for (size_t r = 0; r < in->n_rows; r++) {
97
+ if (tf_batch_copy_row(ob, r, in, r) != TF_OK) {
98
+ tf_batch_free(ob);
99
+ return TF_ERROR;
100
+ }
101
+ if (tf_batch_expose_row(ob, r) != TF_OK) {
102
+ tf_batch_free(ob);
103
+ return TF_ERROR;
104
+ }
105
+ }
106
+ *out = ob;
107
+ return TF_OK;
108
+ }
109
+
110
+ static int anomaly_process(tf_step *self, tf_batch *in, tf_batch **out,
111
+ tf_side_channels *side) {
112
+ (void)side;
113
+ anomaly_state *st = self->state;
114
+ *out = NULL;
115
+
116
+ int ci = tf_batch_col_index(in, st->column);
117
+ int force_null = 0;
118
+ if (ci < 0) {
119
+ if (st->missing == ANOMALY_MISSING_ERROR) {
120
+ anomaly_set_col_error(st->column, "not found");
121
+ return TF_ERROR;
122
+ }
123
+ if (st->missing == ANOMALY_MISSING_IGNORE) return anomaly_passthrough(in, out);
124
+ force_null = 1;
125
+ } else if (!anomaly_is_numeric_type(in->col_types[ci])) {
126
+ if (st->on_type_error == ANOMALY_TYPE_FAIL) {
127
+ anomaly_set_col_error(st->column, "must be numeric");
128
+ return TF_ERROR;
129
+ }
130
+ force_null = 1;
131
+ }
132
+
133
+ const char *extra_names[1] = {st->result};
134
+ tf_type extra_types[1] = {TF_TYPE_INT64};
135
+ tf_batch *ob = tf_batch_create(in->n_cols + 1, in->n_rows);
136
+ if (!ob) return TF_ERROR;
137
+ if (tf_batch_clone_with_extra_cols(ob, in, extra_names, extra_types, 1) != TF_OK) {
138
+ tf_batch_free(ob);
139
+ return TF_ERROR;
140
+ }
141
+
142
+ for (size_t r = 0; r < in->n_rows; r++) {
143
+ if (tf_batch_copy_row(ob, r, in, r) != TF_OK) {
144
+ tf_batch_free(ob);
145
+ return TF_ERROR;
146
+ }
147
+
148
+ if (force_null) {
149
+ if (tf_batch_set_null(ob, r, in->n_cols) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
150
+ if (tf_batch_expose_row(ob, r) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
151
+ continue;
152
+ }
153
+
154
+ if (tf_batch_is_null(in, r, ci)) {
155
+ if (tf_batch_set_int64(ob, r, in->n_cols, 0) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
156
+ if (tf_batch_expose_row(ob, r) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
157
+ continue;
158
+ }
159
+
160
+ double val = anomaly_get_numeric(in, r, ci);
161
+
162
+ if (st->count == SIZE_MAX) {
163
+ tf_batch_free(ob);
164
+ return TF_ERROR;
165
+ }
166
+ size_t next_count = st->count + 1;
167
+ double next_mean = st->mean;
168
+ double next_m2 = st->m2;
169
+ double delta = val - next_mean;
170
+ next_mean += delta / (double)next_count;
171
+ double delta2 = val - next_mean;
172
+ next_m2 += delta * delta2;
173
+
174
+ /* Compute z-score (need at least 2 data points for variance) */
175
+ int is_anomaly = 0;
176
+ if (next_count >= 2) {
177
+ double var = next_m2 / (double)(next_count - 1);
178
+ double std = sqrt(var);
179
+ if (std > 0) {
180
+ double z = fabs((val - next_mean) / std);
181
+ is_anomaly = z > st->threshold ? 1 : 0;
182
+ }
183
+ }
184
+
185
+ if (tf_batch_set_int64(ob, r, in->n_cols, is_anomaly) != TF_OK) {
186
+ tf_batch_free(ob);
187
+ return TF_ERROR;
188
+ }
189
+ if (tf_batch_expose_row(ob, r) != TF_OK) {
190
+ tf_batch_free(ob);
191
+ return TF_ERROR;
192
+ }
193
+ st->count = next_count;
194
+ st->mean = next_mean;
195
+ st->m2 = next_m2;
196
+ }
197
+
198
+ *out = ob;
199
+ return TF_OK;
200
+ }
201
+
202
+ static int anomaly_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
203
+ (void)self; (void)side; *out = NULL; return TF_OK;
204
+ }
205
+
206
+ static void anomaly_destroy(tf_step *self) {
207
+ anomaly_state *st = self->state;
208
+ if (st) { free(st->column); free(st->result); free(st); }
209
+ free(self);
210
+ }
211
+
212
+ tf_step *tf_anomaly_create(const cJSON *args) {
213
+ if (!args) return NULL;
214
+ cJSON *col_j = cJSON_GetObjectItemCaseSensitive(args, "column");
215
+ if (!cJSON_IsString(col_j) || !col_j->valuestring[0]) {
216
+ tf_set_last_error("anomaly: column is required");
217
+ return NULL;
218
+ }
219
+
220
+ anomaly_state *st = tf_callocarray_checked(1, sizeof(anomaly_state));
221
+ if (!st) return NULL;
222
+ st->column = tf_strdup_checked(col_j->valuestring);
223
+ if (!st->column) { free(st); return NULL; }
224
+
225
+ cJSON *thresh_j = cJSON_GetObjectItemCaseSensitive(args, "threshold");
226
+ st->threshold = cJSON_IsNumber(thresh_j) ? thresh_j->valuedouble : 3.0;
227
+ if (!isfinite(st->threshold) || st->threshold < 0.0) {
228
+ tf_set_last_error("anomaly: threshold must be a non-negative finite number");
229
+ free(st->column);
230
+ free(st);
231
+ return NULL;
232
+ }
233
+ if (anomaly_parse_missing_policy(args, &st->missing) != TF_OK ||
234
+ anomaly_parse_type_policy(args, &st->on_type_error) != TF_OK) {
235
+ free(st->column);
236
+ free(st);
237
+ return NULL;
238
+ }
239
+
240
+ cJSON *res_j = cJSON_GetObjectItemCaseSensitive(args, "result");
241
+ if (cJSON_IsString(res_j)) {
242
+ st->result = tf_strdup_checked(res_j->valuestring);
243
+ } else {
244
+ st->result = tf_string_append_suffix_checked(st->column, "_anomaly");
245
+ }
246
+ if (!st->result) { free(st->column); free(st); return NULL; }
247
+
248
+ tf_step *step = tf_callocarray_checked(1, sizeof(tf_step));
249
+ if (!step) { free(st->column); free(st->result); free(st); return NULL; }
250
+ step->process = anomaly_process;
251
+ step->flush = anomaly_flush;
252
+ step->destroy = anomaly_destroy;
253
+ step->state = st;
254
+ return step;
255
+ }