juxscript 1.1.239 → 1.1.243

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 (297) hide show
  1. package/index.js +7 -137
  2. package/lib/components/dataframe.ts +0 -569
  3. package/lib/components/tag.ts +68 -0
  4. package/lib/styles/shadcn.css +20 -7
  5. package/lib/utils/idgen.ts +6 -0
  6. package/package.json +5 -6
  7. package/index.d.ts +0 -239
  8. package/index.d.ts.map +0 -1
  9. package/lib/components/alert.d.ts +0 -36
  10. package/lib/components/alert.d.ts.map +0 -1
  11. package/lib/components/alert.js +0 -172
  12. package/lib/components/alert.ts +0 -219
  13. package/lib/components/app.d.ts +0 -89
  14. package/lib/components/app.d.ts.map +0 -1
  15. package/lib/components/app.js +0 -175
  16. package/lib/components/app.ts +0 -247
  17. package/lib/components/badge.d.ts +0 -26
  18. package/lib/components/badge.d.ts.map +0 -1
  19. package/lib/components/badge.js +0 -91
  20. package/lib/components/badge.ts +0 -118
  21. package/lib/components/base/Animations.d.ts +0 -36
  22. package/lib/components/base/Animations.d.ts.map +0 -1
  23. package/lib/components/base/Animations.js +0 -70
  24. package/lib/components/base/Animations.ts +0 -112
  25. package/lib/components/base/BaseComponent.d.ts +0 -294
  26. package/lib/components/base/BaseComponent.d.ts.map +0 -1
  27. package/lib/components/base/BaseComponent.js +0 -735
  28. package/lib/components/base/BaseComponent.ts +0 -884
  29. package/lib/components/base/FormInput.d.ts +0 -77
  30. package/lib/components/base/FormInput.d.ts.map +0 -1
  31. package/lib/components/base/FormInput.js +0 -171
  32. package/lib/components/base/FormInput.ts +0 -237
  33. package/lib/components/blueprint.d.ts +0 -40
  34. package/lib/components/blueprint.d.ts.map +0 -1
  35. package/lib/components/blueprint.js +0 -327
  36. package/lib/components/button.d.ts +0 -70
  37. package/lib/components/button.d.ts.map +0 -1
  38. package/lib/components/button.js +0 -177
  39. package/lib/components/button.ts +0 -237
  40. package/lib/components/card.d.ts +0 -35
  41. package/lib/components/card.d.ts.map +0 -1
  42. package/lib/components/card.js +0 -130
  43. package/lib/components/card.ts +0 -177
  44. package/lib/components/chart.d.ts +0 -49
  45. package/lib/components/chart.d.ts.map +0 -1
  46. package/lib/components/chart.js +0 -205
  47. package/lib/components/chart.ts +0 -254
  48. package/lib/components/checkbox.d.ts +0 -33
  49. package/lib/components/checkbox.d.ts.map +0 -1
  50. package/lib/components/checkbox.js +0 -202
  51. package/lib/components/checkbox.ts +0 -260
  52. package/lib/components/code.d.ts +0 -52
  53. package/lib/components/code.d.ts.map +0 -1
  54. package/lib/components/code.js +0 -201
  55. package/lib/components/code.ts +0 -260
  56. package/lib/components/container.d.ts +0 -60
  57. package/lib/components/container.d.ts.map +0 -1
  58. package/lib/components/container.js +0 -195
  59. package/lib/components/container.ts +0 -259
  60. package/lib/components/data.d.ts +0 -36
  61. package/lib/components/data.d.ts.map +0 -1
  62. package/lib/components/data.js +0 -110
  63. package/lib/components/data.ts +0 -135
  64. package/lib/components/dataframe/DataFrameSource.d.ts +0 -118
  65. package/lib/components/dataframe/DataFrameSource.d.ts.map +0 -1
  66. package/lib/components/dataframe/DataFrameSource.js +0 -421
  67. package/lib/components/dataframe/DataFrameSource.ts +0 -532
  68. package/lib/components/dataframe/ImportSettingsModal.d.ts +0 -60
  69. package/lib/components/dataframe/ImportSettingsModal.d.ts.map +0 -1
  70. package/lib/components/dataframe/ImportSettingsModal.js +0 -442
  71. package/lib/components/dataframe/ImportSettingsModal.ts +0 -531
  72. package/lib/components/dataframe.d.ts +0 -110
  73. package/lib/components/dataframe.d.ts.map +0 -1
  74. package/lib/components/dataframe.js +0 -470
  75. package/lib/components/datepicker.d.ts +0 -40
  76. package/lib/components/datepicker.d.ts.map +0 -1
  77. package/lib/components/datepicker.js +0 -193
  78. package/lib/components/datepicker.ts +0 -251
  79. package/lib/components/dialog.d.ts +0 -39
  80. package/lib/components/dialog.d.ts.map +0 -1
  81. package/lib/components/dialog.js +0 -131
  82. package/lib/components/dialog.ts +0 -178
  83. package/lib/components/divider.d.ts +0 -31
  84. package/lib/components/divider.d.ts.map +0 -1
  85. package/lib/components/divider.js +0 -72
  86. package/lib/components/divider.ts +0 -104
  87. package/lib/components/dropdown-menu.d.ts +0 -42
  88. package/lib/components/dropdown-menu.d.ts.map +0 -1
  89. package/lib/components/dropdown-menu.js +0 -177
  90. package/lib/components/dropdown-menu.ts +0 -214
  91. package/lib/components/dropdown.d.ts +0 -41
  92. package/lib/components/dropdown.d.ts.map +0 -1
  93. package/lib/components/dropdown.js +0 -136
  94. package/lib/components/dropdown.ts +0 -188
  95. package/lib/components/element.d.ts +0 -51
  96. package/lib/components/element.d.ts.map +0 -1
  97. package/lib/components/element.js +0 -209
  98. package/lib/components/element.ts +0 -271
  99. package/lib/components/event-chain.d.ts +0 -9
  100. package/lib/components/event-chain.d.ts.map +0 -1
  101. package/lib/components/event-chain.js +0 -33
  102. package/lib/components/fileupload.d.ts +0 -98
  103. package/lib/components/fileupload.d.ts.map +0 -1
  104. package/lib/components/fileupload.js +0 -351
  105. package/lib/components/fileupload.ts +0 -449
  106. package/lib/components/grid.d.ts +0 -88
  107. package/lib/components/grid.d.ts.map +0 -1
  108. package/lib/components/grid.js +0 -208
  109. package/lib/components/grid.ts +0 -295
  110. package/lib/components/heading.d.ts +0 -25
  111. package/lib/components/heading.d.ts.map +0 -1
  112. package/lib/components/heading.js +0 -83
  113. package/lib/components/heading.ts +0 -113
  114. package/lib/components/helpers.d.ts +0 -9
  115. package/lib/components/helpers.d.ts.map +0 -1
  116. package/lib/components/helpers.js +0 -30
  117. package/lib/components/helpers.ts +0 -41
  118. package/lib/components/hero.d.ts +0 -60
  119. package/lib/components/hero.d.ts.map +0 -1
  120. package/lib/components/hero.js +0 -239
  121. package/lib/components/hero.ts +0 -302
  122. package/lib/components/history/StateHistory.d.ts +0 -91
  123. package/lib/components/history/StateHistory.d.ts.map +0 -1
  124. package/lib/components/history/StateHistory.js +0 -154
  125. package/lib/components/history/StateHistory.ts +0 -200
  126. package/lib/components/icon.d.ts +0 -36
  127. package/lib/components/icon.d.ts.map +0 -1
  128. package/lib/components/icon.js +0 -135
  129. package/lib/components/icon.ts +0 -182
  130. package/lib/components/icons.d.ts +0 -25
  131. package/lib/components/icons.d.ts.map +0 -1
  132. package/lib/components/icons.js +0 -440
  133. package/lib/components/icons.ts +0 -464
  134. package/lib/components/image.d.ts +0 -42
  135. package/lib/components/image.d.ts.map +0 -1
  136. package/lib/components/image.js +0 -204
  137. package/lib/components/image.ts +0 -260
  138. package/lib/components/include.d.ts +0 -86
  139. package/lib/components/include.d.ts.map +0 -1
  140. package/lib/components/include.js +0 -238
  141. package/lib/components/include.ts +0 -281
  142. package/lib/components/input.d.ts +0 -85
  143. package/lib/components/input.d.ts.map +0 -1
  144. package/lib/components/input.js +0 -362
  145. package/lib/components/input.ts +0 -473
  146. package/lib/components/layer.d.ts +0 -72
  147. package/lib/components/layer.d.ts.map +0 -1
  148. package/lib/components/layer.js +0 -219
  149. package/lib/components/layer.ts +0 -304
  150. package/lib/components/link.d.ts +0 -41
  151. package/lib/components/link.d.ts.map +0 -1
  152. package/lib/components/link.js +0 -216
  153. package/lib/components/link.ts +0 -268
  154. package/lib/components/list.d.ts +0 -83
  155. package/lib/components/list.d.ts.map +0 -1
  156. package/lib/components/list.js +0 -314
  157. package/lib/components/list.ts +0 -423
  158. package/lib/components/loading.d.ts +0 -25
  159. package/lib/components/loading.d.ts.map +0 -1
  160. package/lib/components/loading.js +0 -76
  161. package/lib/components/loading.ts +0 -104
  162. package/lib/components/menu.d.ts +0 -38
  163. package/lib/components/menu.d.ts.map +0 -1
  164. package/lib/components/menu.js +0 -205
  165. package/lib/components/menu.ts +0 -279
  166. package/lib/components/modal.d.ts +0 -97
  167. package/lib/components/modal.d.ts.map +0 -1
  168. package/lib/components/modal.js +0 -463
  169. package/lib/components/modal.ts +0 -576
  170. package/lib/components/nav.d.ts +0 -46
  171. package/lib/components/nav.d.ts.map +0 -1
  172. package/lib/components/nav.js +0 -193
  173. package/lib/components/nav.ts +0 -261
  174. package/lib/components/paragraph.d.ts +0 -30
  175. package/lib/components/paragraph.d.ts.map +0 -1
  176. package/lib/components/paragraph.js +0 -93
  177. package/lib/components/paragraph.ts +0 -123
  178. package/lib/components/pen.d.ts +0 -125
  179. package/lib/components/pen.d.ts.map +0 -1
  180. package/lib/components/pen.js +0 -443
  181. package/lib/components/pen.ts +0 -567
  182. package/lib/components/progress.d.ts +0 -40
  183. package/lib/components/progress.d.ts.map +0 -1
  184. package/lib/components/progress.js +0 -116
  185. package/lib/components/progress.ts +0 -163
  186. package/lib/components/radio.d.ts +0 -43
  187. package/lib/components/radio.d.ts.map +0 -1
  188. package/lib/components/radio.js +0 -226
  189. package/lib/components/radio.ts +0 -303
  190. package/lib/components/registry.d.ts +0 -34
  191. package/lib/components/registry.d.ts.map +0 -1
  192. package/lib/components/registry.js +0 -163
  193. package/lib/components/registry.ts +0 -193
  194. package/lib/components/req.d.ts +0 -155
  195. package/lib/components/req.d.ts.map +0 -1
  196. package/lib/components/req.js +0 -253
  197. package/lib/components/req.ts +0 -303
  198. package/lib/components/script.d.ts +0 -14
  199. package/lib/components/script.d.ts.map +0 -1
  200. package/lib/components/script.js +0 -33
  201. package/lib/components/script.ts +0 -41
  202. package/lib/components/select.d.ts +0 -42
  203. package/lib/components/select.d.ts.map +0 -1
  204. package/lib/components/select.js +0 -209
  205. package/lib/components/select.ts +0 -281
  206. package/lib/components/sidebar.d.ts +0 -59
  207. package/lib/components/sidebar.d.ts.map +0 -1
  208. package/lib/components/sidebar.js +0 -298
  209. package/lib/components/sidebar.ts +0 -395
  210. package/lib/components/stack/BaseStack.d.ts +0 -65
  211. package/lib/components/stack/BaseStack.d.ts.map +0 -1
  212. package/lib/components/stack/BaseStack.js +0 -274
  213. package/lib/components/stack/BaseStack.ts +0 -328
  214. package/lib/components/stack/HStack.d.ts +0 -18
  215. package/lib/components/stack/HStack.d.ts.map +0 -1
  216. package/lib/components/stack/HStack.js +0 -22
  217. package/lib/components/stack/HStack.ts +0 -25
  218. package/lib/components/stack/VStack.d.ts +0 -19
  219. package/lib/components/stack/VStack.d.ts.map +0 -1
  220. package/lib/components/stack/VStack.js +0 -23
  221. package/lib/components/stack/VStack.ts +0 -26
  222. package/lib/components/stack/ZStack.d.ts +0 -18
  223. package/lib/components/stack/ZStack.d.ts.map +0 -1
  224. package/lib/components/stack/ZStack.js +0 -22
  225. package/lib/components/stack/ZStack.ts +0 -25
  226. package/lib/components/style.d.ts +0 -14
  227. package/lib/components/style.d.ts.map +0 -1
  228. package/lib/components/style.js +0 -33
  229. package/lib/components/style.ts +0 -41
  230. package/lib/components/switch.d.ts +0 -34
  231. package/lib/components/switch.d.ts.map +0 -1
  232. package/lib/components/switch.js +0 -209
  233. package/lib/components/switch.ts +0 -272
  234. package/lib/components/table.d.ts +0 -137
  235. package/lib/components/table.d.ts.map +0 -1
  236. package/lib/components/table.js +0 -1019
  237. package/lib/components/table.ts +0 -1225
  238. package/lib/components/tabs.d.ts +0 -53
  239. package/lib/components/tabs.d.ts.map +0 -1
  240. package/lib/components/tabs.js +0 -275
  241. package/lib/components/tabs.ts +0 -349
  242. package/lib/components/theme-toggle.d.ts +0 -45
  243. package/lib/components/theme-toggle.d.ts.map +0 -1
  244. package/lib/components/theme-toggle.js +0 -218
  245. package/lib/components/theme-toggle.ts +0 -297
  246. package/lib/components/tooltip.d.ts +0 -31
  247. package/lib/components/tooltip.d.ts.map +0 -1
  248. package/lib/components/tooltip.js +0 -112
  249. package/lib/components/tooltip.ts +0 -148
  250. package/lib/components/watcher.d.ts +0 -195
  251. package/lib/components/watcher.d.ts.map +0 -1
  252. package/lib/components/watcher.js +0 -241
  253. package/lib/components/watcher.ts +0 -261
  254. package/lib/components/write.d.ts +0 -107
  255. package/lib/components/write.d.ts.map +0 -1
  256. package/lib/components/write.js +0 -222
  257. package/lib/components/write.ts +0 -272
  258. package/lib/data/DataPipeline.d.ts +0 -113
  259. package/lib/data/DataPipeline.d.ts.map +0 -1
  260. package/lib/data/DataPipeline.js +0 -359
  261. package/lib/data/DataPipeline.ts +0 -452
  262. package/lib/facades/dataframe.jux +0 -0
  263. package/lib/globals.d.ts +0 -21
  264. package/lib/reactivity/state.d.ts +0 -36
  265. package/lib/reactivity/state.d.ts.map +0 -1
  266. package/lib/reactivity/state.js +0 -67
  267. package/lib/reactivity/state.ts +0 -78
  268. package/lib/storage/DataFrame.d.ts +0 -284
  269. package/lib/storage/DataFrame.d.ts.map +0 -1
  270. package/lib/storage/DataFrame.js +0 -1022
  271. package/lib/storage/DataFrame.ts +0 -1195
  272. package/lib/storage/DataFrameSource.d.ts +0 -158
  273. package/lib/storage/DataFrameSource.d.ts.map +0 -1
  274. package/lib/storage/DataFrameSource.js +0 -409
  275. package/lib/storage/DataFrameSource.ts +0 -556
  276. package/lib/storage/FileStorage.d.ts +0 -53
  277. package/lib/storage/FileStorage.d.ts.map +0 -1
  278. package/lib/storage/FileStorage.js +0 -80
  279. package/lib/storage/FileStorage.ts +0 -95
  280. package/lib/storage/IndexedDBDriver.d.ts +0 -75
  281. package/lib/storage/IndexedDBDriver.d.ts.map +0 -1
  282. package/lib/storage/IndexedDBDriver.js +0 -177
  283. package/lib/storage/IndexedDBDriver.ts +0 -226
  284. package/lib/storage/TabularDriver.d.ts +0 -118
  285. package/lib/storage/TabularDriver.d.ts.map +0 -1
  286. package/lib/storage/TabularDriver.js +0 -731
  287. package/lib/storage/TabularDriver.ts +0 -874
  288. package/lib/utils/codeparser.d.ts +0 -29
  289. package/lib/utils/codeparser.d.ts.map +0 -1
  290. package/lib/utils/codeparser.js +0 -409
  291. package/lib/utils/fetch.d.ts +0 -176
  292. package/lib/utils/fetch.d.ts.map +0 -1
  293. package/lib/utils/fetch.js +0 -427
  294. package/lib/utils/formatId.d.ts +0 -16
  295. package/lib/utils/formatId.d.ts.map +0 -1
  296. package/lib/utils/formatId.js +0 -27
  297. package/lib/utils/path-resolver.js +0 -23
@@ -1,1022 +0,0 @@
1
- /**
2
- * DataFrame - A pandas-like data manipulation class for JavaScript
3
- *
4
- * Core capabilities:
5
- * - Column operations (select, drop, rename, reorder)
6
- * - Row operations (filter, head, tail, sample, slice)
7
- * - Data transformation (withColumn, apply, map)
8
- * - Aggregation (groupBy, agg, pivot)
9
- * - Joins (merge, concat, union)
10
- * - Type inference and conversion
11
- * - Sorting and indexing
12
- * - Statistics and describe
13
- * - Missing data handling
14
- */
15
- export class DataFrame {
16
- constructor(data, options = {}) {
17
- this._inferTypes = options.inferTypes ?? true;
18
- this._schema = new Map();
19
- if (data instanceof DataFrame) {
20
- // Clone from another DataFrame
21
- this._columns = [...data._columns];
22
- this._data = new Map();
23
- data._columns.forEach(col => {
24
- this._data.set(col, [...data._data.get(col)]);
25
- });
26
- this._height = data._height;
27
- this._schema = new Map(data._schema);
28
- }
29
- else if (Array.isArray(data)) {
30
- // Row-oriented: [{col1: val1, col2: val2}, ...]
31
- this._initFromRows(data, options.columns);
32
- }
33
- else {
34
- // Column-oriented: {col1: [val1, val2], col2: [val1, val2]}
35
- this._initFromColumns(data, options.columns);
36
- }
37
- if (this._inferTypes) {
38
- this._inferSchema();
39
- }
40
- }
41
- _initFromRows(rows, columnOrder) {
42
- if (rows.length === 0) {
43
- this._columns = columnOrder || [];
44
- this._data = new Map();
45
- this._height = 0;
46
- return;
47
- }
48
- // Get all unique columns across all rows
49
- const colSet = new Set();
50
- rows.forEach(row => Object.keys(row).forEach(k => colSet.add(k)));
51
- this._columns = columnOrder || Array.from(colSet);
52
- this._data = new Map();
53
- this._height = rows.length;
54
- this._columns.forEach(col => {
55
- this._data.set(col, rows.map(row => row[col] ?? null));
56
- });
57
- }
58
- _initFromColumns(cols, columnOrder) {
59
- this._columns = columnOrder || Object.keys(cols);
60
- this._data = new Map();
61
- const lengths = this._columns.map(c => cols[c]?.length ?? 0);
62
- this._height = Math.max(0, ...lengths);
63
- this._columns.forEach(col => {
64
- const values = cols[col] || [];
65
- // Pad shorter columns with null
66
- const padded = [...values];
67
- while (padded.length < this._height) {
68
- padded.push(null);
69
- }
70
- this._data.set(col, padded);
71
- });
72
- }
73
- _inferSchema() {
74
- this._columns.forEach(col => {
75
- const values = this._data.get(col);
76
- const schema = this._inferColumnSchema(col, values);
77
- this._schema.set(col, schema);
78
- });
79
- }
80
- _inferColumnSchema(name, values) {
81
- let hasNull = false;
82
- let hasString = false;
83
- let hasNumber = false;
84
- let hasBoolean = false;
85
- let hasDate = false;
86
- let min = undefined;
87
- let max = undefined;
88
- const uniqueValues = new Set();
89
- values.forEach(v => {
90
- if (v === null || v === undefined || v === '') {
91
- hasNull = true;
92
- return;
93
- }
94
- uniqueValues.add(v);
95
- if (typeof v === 'boolean') {
96
- hasBoolean = true;
97
- }
98
- else if (typeof v === 'number' && !isNaN(v)) {
99
- hasNumber = true;
100
- if (min === undefined || v < min)
101
- min = v;
102
- if (max === undefined || v > max)
103
- max = v;
104
- }
105
- else if (v instanceof Date) {
106
- hasDate = true;
107
- }
108
- else if (typeof v === 'string') {
109
- // Try to parse as number
110
- const num = Number(v);
111
- if (!isNaN(num) && v.trim() !== '') {
112
- hasNumber = true;
113
- if (min === undefined || num < min)
114
- min = num;
115
- if (max === undefined || num > max)
116
- max = num;
117
- }
118
- else if (v === 'true' || v === 'false') {
119
- hasBoolean = true;
120
- }
121
- else {
122
- hasString = true;
123
- }
124
- }
125
- });
126
- let dtype = 'null';
127
- const typeCount = [hasString, hasNumber, hasBoolean, hasDate].filter(Boolean).length;
128
- if (typeCount === 0) {
129
- dtype = 'null';
130
- }
131
- else if (typeCount > 1) {
132
- dtype = 'mixed';
133
- }
134
- else if (hasNumber) {
135
- dtype = 'number';
136
- }
137
- else if (hasBoolean) {
138
- dtype = 'boolean';
139
- }
140
- else if (hasDate) {
141
- dtype = 'date';
142
- }
143
- else {
144
- dtype = 'string';
145
- }
146
- return {
147
- name,
148
- dtype,
149
- nullable: hasNull,
150
- unique: uniqueValues.size,
151
- min,
152
- max
153
- };
154
- }
155
- /* ═══════════════════════════════════════════════════
156
- * ACCESSORS
157
- * ═══════════════════════════════════════════════════ */
158
- get columns() {
159
- return [...this._columns];
160
- }
161
- get height() {
162
- return this._height;
163
- }
164
- get width() {
165
- return this._columns.length;
166
- }
167
- get shape() {
168
- return [this._height, this._columns.length];
169
- }
170
- get dtypes() {
171
- const result = {};
172
- this._schema.forEach((schema, col) => {
173
- result[col] = schema.dtype;
174
- });
175
- return result;
176
- }
177
- get schema() {
178
- return this._columns.map(col => this._schema.get(col));
179
- }
180
- get isEmpty() {
181
- return this._height === 0;
182
- }
183
- /* ═══════════════════════════════════════════════════
184
- * DATA ACCESS
185
- * ═══════════════════════════════════════════════════ */
186
- /**
187
- * Get a column as an array
188
- */
189
- col(name) {
190
- const data = this._data.get(name);
191
- if (!data)
192
- throw new Error(`Column '${name}' not found`);
193
- return [...data];
194
- }
195
- /**
196
- * Get column (alias for col)
197
- */
198
- getColumn(name) {
199
- return this.col(name);
200
- }
201
- /**
202
- * Get a single row by index
203
- */
204
- row(index) {
205
- if (index < 0 || index >= this._height) {
206
- throw new Error(`Row index ${index} out of bounds`);
207
- }
208
- const result = {};
209
- this._columns.forEach(col => {
210
- result[col] = this._data.get(col)[index];
211
- });
212
- return result;
213
- }
214
- /**
215
- * Get value at specific row and column
216
- */
217
- at(row, col) {
218
- const data = this._data.get(col);
219
- if (!data)
220
- throw new Error(`Column '${col}' not found`);
221
- if (row < 0 || row >= this._height)
222
- throw new Error(`Row index ${row} out of bounds`);
223
- return data[row];
224
- }
225
- /**
226
- * Set value at specific row and column (mutates!)
227
- */
228
- setAt(row, col, value) {
229
- const data = this._data.get(col);
230
- if (!data)
231
- throw new Error(`Column '${col}' not found`);
232
- if (row < 0 || row >= this._height)
233
- throw new Error(`Row index ${row} out of bounds`);
234
- data[row] = value;
235
- return this;
236
- }
237
- /**
238
- * Convert to array of row objects
239
- */
240
- toRows() {
241
- const rows = [];
242
- for (let i = 0; i < this._height; i++) {
243
- rows.push(this.row(i));
244
- }
245
- return rows;
246
- }
247
- /**
248
- * Convert to column-oriented object
249
- */
250
- toColumns() {
251
- const result = {};
252
- this._columns.forEach(col => {
253
- result[col] = [...this._data.get(col)];
254
- });
255
- return result;
256
- }
257
- /**
258
- * Get distinct/unique values in a column
259
- */
260
- distinct(column) {
261
- const data = this._data.get(column);
262
- if (!data)
263
- throw new Error(`Column '${column}' not found`);
264
- return [...new Set(data)];
265
- }
266
- /**
267
- * Alias for distinct
268
- */
269
- unique(column) {
270
- return this.distinct(column);
271
- }
272
- /**
273
- * Count occurrences of each value in a column
274
- */
275
- valueCounts(column) {
276
- const data = this._data.get(column);
277
- if (!data)
278
- throw new Error(`Column '${column}' not found`);
279
- const counts = {};
280
- data.forEach(v => {
281
- const key = v === null ? '__null__' : String(v);
282
- counts[key] = (counts[key] || 0) + 1;
283
- });
284
- return counts;
285
- }
286
- /* ═══════════════════════════════════════════════════
287
- * COLUMN OPERATIONS
288
- * ═══════════════════════════════════════════════════ */
289
- /**
290
- * Select specific columns
291
- */
292
- select(...cols) {
293
- const missing = cols.filter(c => !this._data.has(c));
294
- if (missing.length > 0) {
295
- throw new Error(`Columns not found: ${missing.join(', ')}`);
296
- }
297
- const newData = {};
298
- cols.forEach(col => {
299
- newData[col] = [...this._data.get(col)];
300
- });
301
- return new DataFrame(newData, { columns: cols, inferTypes: this._inferTypes });
302
- }
303
- /**
304
- * Drop columns
305
- */
306
- drop(...cols) {
307
- const keepCols = this._columns.filter(c => !cols.includes(c));
308
- return this.select(...keepCols);
309
- }
310
- /**
311
- * Rename columns
312
- */
313
- rename(mapping) {
314
- const newData = {};
315
- const newCols = [];
316
- this._columns.forEach(col => {
317
- const newName = mapping[col] || col;
318
- newCols.push(newName);
319
- newData[newName] = [...this._data.get(col)];
320
- });
321
- return new DataFrame(newData, { columns: newCols, inferTypes: this._inferTypes });
322
- }
323
- /**
324
- * Reorder columns
325
- */
326
- reorder(...cols) {
327
- // Include any columns not specified at the end
328
- const remaining = this._columns.filter(c => !cols.includes(c));
329
- return this.select(...cols, ...remaining);
330
- }
331
- /**
332
- * Add or replace a column with computed values
333
- */
334
- withColumn(name, fn) {
335
- const newValues = this.toRows().map((row, i) => fn(row, i));
336
- const newData = {};
337
- this._columns.forEach(col => {
338
- newData[col] = [...this._data.get(col)];
339
- });
340
- newData[name] = newValues;
341
- const newCols = this._columns.includes(name)
342
- ? this._columns
343
- : [...this._columns, name];
344
- return new DataFrame(newData, { columns: newCols, inferTypes: this._inferTypes });
345
- }
346
- /**
347
- * Add multiple computed columns at once
348
- */
349
- withColumns(columns) {
350
- let result = this;
351
- Object.entries(columns).forEach(([name, fn]) => {
352
- result = result.withColumn(name, fn);
353
- });
354
- return result;
355
- }
356
- /**
357
- * Cast column to a specific type
358
- */
359
- asType(column, dtype) {
360
- return this.withColumn(column, (row) => {
361
- const val = row[column];
362
- if (val === null || val === undefined)
363
- return null;
364
- switch (dtype) {
365
- case 'string':
366
- return String(val);
367
- case 'number':
368
- const num = Number(val);
369
- return isNaN(num) ? null : num;
370
- case 'boolean':
371
- if (typeof val === 'boolean')
372
- return val;
373
- if (val === 'true' || val === '1' || val === 1)
374
- return true;
375
- if (val === 'false' || val === '0' || val === 0)
376
- return false;
377
- return null;
378
- case 'date':
379
- const date = new Date(val);
380
- return isNaN(date.getTime()) ? null : date;
381
- default:
382
- return val;
383
- }
384
- });
385
- }
386
- /* ═══════════════════════════════════════════════════
387
- * ROW OPERATIONS
388
- * ═══════════════════════════════════════════════════ */
389
- /**
390
- * Filter rows by predicate
391
- */
392
- filter(predicate) {
393
- const rows = this.toRows();
394
- const filtered = rows.filter((row, i) => predicate(row, i));
395
- return new DataFrame(filtered, { columns: this._columns, inferTypes: this._inferTypes });
396
- }
397
- /**
398
- * Filter with SQL-like where clause
399
- */
400
- where(col, op, value) {
401
- return this.filter(row => {
402
- const v = row[col];
403
- switch (op) {
404
- case '==': return v === value;
405
- case '!=': return v !== value;
406
- case '>': return v > value;
407
- case '<': return v < value;
408
- case '>=': return v >= value;
409
- case '<=': return v <= value;
410
- case 'in': return Array.isArray(value) && value.includes(v);
411
- case 'not in': return Array.isArray(value) && !value.includes(v);
412
- case 'contains': return String(v).includes(String(value));
413
- case 'startsWith': return String(v).startsWith(String(value));
414
- case 'endsWith': return String(v).endsWith(String(value));
415
- case 'isNull': return v === null || v === undefined || v === '';
416
- case 'notNull': return v !== null && v !== undefined && v !== '';
417
- default: return true;
418
- }
419
- });
420
- }
421
- /**
422
- * Get first N rows
423
- */
424
- head(n = 5) {
425
- return this.slice(0, n);
426
- }
427
- /**
428
- * Get last N rows
429
- */
430
- tail(n = 5) {
431
- return this.slice(Math.max(0, this._height - n));
432
- }
433
- /**
434
- * Slice rows by index range
435
- */
436
- slice(start, end) {
437
- const rows = this.toRows().slice(start, end);
438
- return new DataFrame(rows, { columns: this._columns, inferTypes: this._inferTypes });
439
- }
440
- /**
441
- * Get random sample of rows
442
- */
443
- sample(n, seed) {
444
- const rows = this.toRows();
445
- const shuffled = [...rows];
446
- // Simple shuffle (use seed if provided for reproducibility)
447
- for (let i = shuffled.length - 1; i > 0; i--) {
448
- const j = Math.floor(Math.random() * (i + 1));
449
- [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
450
- }
451
- return new DataFrame(shuffled.slice(0, n), { columns: this._columns, inferTypes: this._inferTypes });
452
- }
453
- /**
454
- * Drop duplicate rows
455
- */
456
- dropDuplicates(columns) {
457
- const cols = columns || this._columns;
458
- const seen = new Set();
459
- const rows = this.toRows().filter(row => {
460
- const key = cols.map(c => JSON.stringify(row[c])).join('|');
461
- if (seen.has(key))
462
- return false;
463
- seen.add(key);
464
- return true;
465
- });
466
- return new DataFrame(rows, { columns: this._columns, inferTypes: this._inferTypes });
467
- }
468
- /**
469
- * Drop rows with null values
470
- */
471
- dropna(columns) {
472
- const cols = columns || this._columns;
473
- return this.filter(row => {
474
- return cols.every(c => row[c] !== null && row[c] !== undefined && row[c] !== '');
475
- });
476
- }
477
- /**
478
- * Fill null values
479
- */
480
- fillna(value, columns) {
481
- const cols = columns || this._columns;
482
- let result = this;
483
- cols.forEach(col => {
484
- result = result.withColumn(col, row => {
485
- const v = row[col];
486
- return (v === null || v === undefined || v === '') ? value : v;
487
- });
488
- });
489
- return result;
490
- }
491
- /* ═══════════════════════════════════════════════════
492
- * SORTING
493
- * ═══════════════════════════════════════════════════ */
494
- /**
495
- * Sort by column(s)
496
- */
497
- sort(column, descending = false) {
498
- const cols = Array.isArray(column) ? column : [column];
499
- const descs = Array.isArray(descending) ? descending : cols.map(() => descending);
500
- const rows = this.toRows();
501
- rows.sort((a, b) => {
502
- for (let i = 0; i < cols.length; i++) {
503
- const col = cols[i];
504
- const desc = descs[i] || false;
505
- const aVal = a[col];
506
- const bVal = b[col];
507
- if (aVal === bVal)
508
- continue;
509
- if (aVal === null || aVal === undefined)
510
- return 1;
511
- if (bVal === null || bVal === undefined)
512
- return -1;
513
- const cmp = aVal < bVal ? -1 : 1;
514
- return desc ? -cmp : cmp;
515
- }
516
- return 0;
517
- });
518
- return new DataFrame(rows, { columns: this._columns, inferTypes: this._inferTypes });
519
- }
520
- /**
521
- * Sort descending (shorthand)
522
- */
523
- sortDesc(column) {
524
- return this.sort(column, true);
525
- }
526
- /* ═══════════════════════════════════════════════════
527
- * AGGREGATION
528
- * ═══════════════════════════════════════════════════ */
529
- /**
530
- * Group by column(s) and aggregate
531
- */
532
- groupBy(columns) {
533
- return new GroupedDataFrame(this, Array.isArray(columns) ? columns : [columns]);
534
- }
535
- /**
536
- * Aggregate entire DataFrame
537
- */
538
- agg(aggregations) {
539
- const result = {};
540
- Object.entries(aggregations).forEach(([col, aggFn]) => {
541
- const values = this._data.get(col);
542
- if (!values)
543
- throw new Error(`Column '${col}' not found`);
544
- const nonNull = values.filter(v => v !== null && v !== undefined);
545
- if (typeof aggFn === 'function') {
546
- result[col] = aggFn(nonNull);
547
- }
548
- else {
549
- switch (aggFn) {
550
- case 'sum':
551
- result[col] = nonNull.reduce((a, b) => a + Number(b), 0);
552
- break;
553
- case 'mean':
554
- result[col] = nonNull.length > 0
555
- ? nonNull.reduce((a, b) => a + Number(b), 0) / nonNull.length
556
- : null;
557
- break;
558
- case 'min':
559
- result[col] = nonNull.length > 0 ? Math.min(...nonNull.map(Number)) : null;
560
- break;
561
- case 'max':
562
- result[col] = nonNull.length > 0 ? Math.max(...nonNull.map(Number)) : null;
563
- break;
564
- case 'count':
565
- result[col] = nonNull.length;
566
- break;
567
- case 'first':
568
- result[col] = nonNull[0] ?? null;
569
- break;
570
- case 'last':
571
- result[col] = nonNull[nonNull.length - 1] ?? null;
572
- break;
573
- }
574
- }
575
- });
576
- return new DataFrame([result], { inferTypes: this._inferTypes });
577
- }
578
- /**
579
- * Count rows
580
- */
581
- count() {
582
- return this._height;
583
- }
584
- /**
585
- * Sum of numeric column
586
- */
587
- sum(column) {
588
- const values = this._data.get(column);
589
- if (!values)
590
- throw new Error(`Column '${column}' not found`);
591
- return values.reduce((a, b) => a + (Number(b) || 0), 0);
592
- }
593
- /**
594
- * Mean of numeric column
595
- */
596
- mean(column) {
597
- const values = this._data.get(column);
598
- if (!values)
599
- throw new Error(`Column '${column}' not found`);
600
- const nonNull = values.filter(v => v !== null && v !== undefined);
601
- if (nonNull.length === 0)
602
- return 0;
603
- return nonNull.reduce((a, b) => a + Number(b), 0) / nonNull.length;
604
- }
605
- /**
606
- * Min of column
607
- */
608
- min(column) {
609
- const values = this._data.get(column);
610
- if (!values)
611
- throw new Error(`Column '${column}' not found`);
612
- const nonNull = values.filter(v => v !== null && v !== undefined);
613
- if (nonNull.length === 0)
614
- return null;
615
- return Math.min(...nonNull.map(Number));
616
- }
617
- /**
618
- * Max of column
619
- */
620
- max(column) {
621
- const values = this._data.get(column);
622
- if (!values)
623
- throw new Error(`Column '${column}' not found`);
624
- const nonNull = values.filter(v => v !== null && v !== undefined);
625
- if (nonNull.length === 0)
626
- return null;
627
- return Math.max(...nonNull.map(Number));
628
- }
629
- /* ═══════════════════════════════════════════════════
630
- * JOINS & MERGING
631
- * ═══════════════════════════════════════════════════ */
632
- /**
633
- * Merge with another DataFrame (SQL-like join)
634
- */
635
- merge(other, options = {}) {
636
- const { on, leftOn = on, rightOn = on, how = 'inner', suffixes = ['_x', '_y'] } = options;
637
- if (!leftOn || !rightOn) {
638
- throw new Error('Must specify join columns with "on" or "leftOn"/"rightOn"');
639
- }
640
- const leftKeys = Array.isArray(leftOn) ? leftOn : [leftOn];
641
- const rightKeys = Array.isArray(rightOn) ? rightOn : [rightOn];
642
- // Build index on right DataFrame
643
- const rightIndex = new Map();
644
- other.toRows().forEach((row, i) => {
645
- const key = rightKeys.map(k => JSON.stringify(row[k])).join('|');
646
- if (!rightIndex.has(key))
647
- rightIndex.set(key, []);
648
- rightIndex.get(key).push(i);
649
- });
650
- const leftRows = this.toRows();
651
- const rightRows = other.toRows();
652
- const result = [];
653
- // Determine output columns
654
- const leftCols = this._columns;
655
- const rightCols = other._columns.filter(c => !rightKeys.includes(c));
656
- // Handle column name conflicts
657
- const colMapping = {};
658
- rightCols.forEach(c => {
659
- if (leftCols.includes(c)) {
660
- colMapping[c] = c + suffixes[1];
661
- }
662
- else {
663
- colMapping[c] = c;
664
- }
665
- });
666
- const matchedRight = new Set();
667
- // Process left rows
668
- leftRows.forEach(leftRow => {
669
- const key = leftKeys.map(k => JSON.stringify(leftRow[k])).join('|');
670
- const matches = rightIndex.get(key) || [];
671
- if (matches.length > 0) {
672
- matches.forEach(rightIdx => {
673
- matchedRight.add(rightIdx);
674
- const rightRow = rightRows[rightIdx];
675
- const merged = { ...leftRow };
676
- rightCols.forEach(c => {
677
- merged[colMapping[c]] = rightRow[c];
678
- });
679
- result.push(merged);
680
- });
681
- }
682
- else if (how === 'left' || how === 'outer') {
683
- const merged = { ...leftRow };
684
- rightCols.forEach(c => {
685
- merged[colMapping[c]] = null;
686
- });
687
- result.push(merged);
688
- }
689
- });
690
- // For right/outer joins, add unmatched right rows
691
- if (how === 'right' || how === 'outer') {
692
- rightRows.forEach((rightRow, i) => {
693
- if (!matchedRight.has(i)) {
694
- const merged = {};
695
- leftCols.forEach(c => {
696
- merged[c] = null;
697
- });
698
- // Copy join keys from right
699
- rightKeys.forEach((rk, idx) => {
700
- merged[leftKeys[idx]] = rightRow[rk];
701
- });
702
- rightCols.forEach(c => {
703
- merged[colMapping[c]] = rightRow[c];
704
- });
705
- result.push(merged);
706
- }
707
- });
708
- }
709
- const outputCols = [
710
- ...leftCols,
711
- ...rightCols.map(c => colMapping[c])
712
- ];
713
- return new DataFrame(result, { columns: outputCols, inferTypes: this._inferTypes });
714
- }
715
- /**
716
- * Concatenate DataFrames vertically (union)
717
- */
718
- concat(other, ignoreIndex = true) {
719
- const others = Array.isArray(other) ? other : [other];
720
- const allDfs = [this, ...others];
721
- // Collect all unique columns
722
- const allCols = new Set();
723
- allDfs.forEach(df => df._columns.forEach(c => allCols.add(c)));
724
- const columns = Array.from(allCols);
725
- // Combine all rows
726
- const rows = [];
727
- allDfs.forEach(df => {
728
- df.toRows().forEach(row => {
729
- const newRow = {};
730
- columns.forEach(c => {
731
- newRow[c] = row[c] ?? null;
732
- });
733
- rows.push(newRow);
734
- });
735
- });
736
- return new DataFrame(rows, { columns, inferTypes: this._inferTypes });
737
- }
738
- /**
739
- * Union (alias for concat)
740
- */
741
- union(other) {
742
- return this.concat(other);
743
- }
744
- /* ═══════════════════════════════════════════════════
745
- * TRANSFORMATION
746
- * ═══════════════════════════════════════════════════ */
747
- /**
748
- * Apply function to each row
749
- */
750
- apply(fn) {
751
- const rows = this.toRows().map((row, i) => fn(row, i));
752
- return new DataFrame(rows, { inferTypes: this._inferTypes });
753
- }
754
- /**
755
- * Map function over a column
756
- */
757
- map(column, fn) {
758
- return this.withColumn(column, (row, i) => fn(row[column], i));
759
- }
760
- /**
761
- * Pivot table
762
- */
763
- pivot(index, columns, values, aggFunc = 'first') {
764
- const pivotValues = this.distinct(columns);
765
- const indexValues = this.distinct(index);
766
- const result = [];
767
- indexValues.forEach(idxVal => {
768
- const row = { [index]: idxVal };
769
- pivotValues.forEach(pivotVal => {
770
- const filtered = this
771
- .where(index, '==', idxVal)
772
- .where(columns, '==', pivotVal);
773
- const vals = filtered.col(values).filter(v => v !== null && v !== undefined);
774
- let aggValue = null;
775
- if (vals.length > 0) {
776
- switch (aggFunc) {
777
- case 'sum':
778
- aggValue = vals.reduce((a, b) => a + Number(b), 0);
779
- break;
780
- case 'mean':
781
- aggValue = vals.reduce((a, b) => a + Number(b), 0) / vals.length;
782
- break;
783
- case 'count':
784
- aggValue = vals.length;
785
- break;
786
- case 'first':
787
- aggValue = vals[0];
788
- break;
789
- }
790
- }
791
- row[String(pivotVal)] = aggValue;
792
- });
793
- result.push(row);
794
- });
795
- return new DataFrame(result, { inferTypes: this._inferTypes });
796
- }
797
- /**
798
- * Melt (unpivot) wide format to long format
799
- */
800
- melt(idVars, valueVars, varName = 'variable', valueName = 'value') {
801
- const valueCols = valueVars || this._columns.filter(c => !idVars.includes(c));
802
- const result = [];
803
- this.toRows().forEach(row => {
804
- valueCols.forEach(col => {
805
- const newRow = {};
806
- idVars.forEach(id => {
807
- newRow[id] = row[id];
808
- });
809
- newRow[varName] = col;
810
- newRow[valueName] = row[col];
811
- result.push(newRow);
812
- });
813
- });
814
- return new DataFrame(result, { inferTypes: this._inferTypes });
815
- }
816
- /* ═══════════════════════════════════════════════════
817
- * STATISTICS
818
- * ═══════════════════════════════════════════════════ */
819
- /**
820
- * Describe numeric columns
821
- */
822
- describe() {
823
- const stats = {};
824
- this._columns.forEach(col => {
825
- const values = this._data.get(col);
826
- const numeric = values.filter(v => typeof v === 'number' && !isNaN(v));
827
- if (numeric.length === 0) {
828
- stats[col] = {
829
- count: values.filter(v => v !== null && v !== undefined).length,
830
- unique: new Set(values).size,
831
- dtype: this._schema.get(col)?.dtype || 'unknown'
832
- };
833
- }
834
- else {
835
- const sorted = [...numeric].sort((a, b) => a - b);
836
- const sum = numeric.reduce((a, b) => a + b, 0);
837
- const mean = sum / numeric.length;
838
- const variance = numeric.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / numeric.length;
839
- stats[col] = {
840
- count: numeric.length,
841
- mean: mean,
842
- std: Math.sqrt(variance),
843
- min: sorted[0],
844
- '25%': sorted[Math.floor(sorted.length * 0.25)],
845
- '50%': sorted[Math.floor(sorted.length * 0.5)],
846
- '75%': sorted[Math.floor(sorted.length * 0.75)],
847
- max: sorted[sorted.length - 1]
848
- };
849
- }
850
- });
851
- return stats;
852
- }
853
- /**
854
- * Get info about DataFrame
855
- */
856
- info() {
857
- const nullCounts = {};
858
- let memoryEstimate = 0;
859
- this._columns.forEach(col => {
860
- const values = this._data.get(col);
861
- nullCounts[col] = values.filter(v => v === null || v === undefined || v === '').length;
862
- memoryEstimate += values.reduce((acc, v) => acc + (typeof v === 'string' ? v.length * 2 : 8), 0);
863
- });
864
- return {
865
- rows: this._height,
866
- columns: this._columns.length,
867
- dtypes: this.dtypes,
868
- nullCounts,
869
- memoryEstimate
870
- };
871
- }
872
- /* ═══════════════════════════════════════════════════
873
- * EXPORT
874
- * ═══════════════════════════════════════════════════ */
875
- /**
876
- * Convert to CSV string
877
- */
878
- toCSV(delimiter = ',') {
879
- const escape = (val) => {
880
- if (val === null || val === undefined)
881
- return '';
882
- const str = String(val);
883
- if (str.includes(delimiter) || str.includes('"') || str.includes('\n')) {
884
- return `"${str.replace(/"/g, '""')}"`;
885
- }
886
- return str;
887
- };
888
- const header = this._columns.map(escape).join(delimiter);
889
- const rows = this.toRows().map(row => this._columns.map(col => escape(row[col])).join(delimiter));
890
- return [header, ...rows].join('\n');
891
- }
892
- /**
893
- * Convert to JSON string
894
- */
895
- toJSON(orient = 'records') {
896
- switch (orient) {
897
- case 'records':
898
- return JSON.stringify(this.toRows());
899
- case 'columns':
900
- return JSON.stringify(this.toColumns());
901
- case 'split':
902
- return JSON.stringify({
903
- columns: this._columns,
904
- data: this.toRows().map(row => this._columns.map(c => row[c]))
905
- });
906
- default:
907
- return JSON.stringify(this.toRows());
908
- }
909
- }
910
- /**
911
- * Clone the DataFrame
912
- */
913
- clone() {
914
- return new DataFrame(this);
915
- }
916
- /**
917
- * Print preview (for debugging)
918
- */
919
- print(n = 10) {
920
- console.log(`DataFrame: ${this._height} rows × ${this._columns.length} columns`);
921
- console.log('Columns:', this._columns.join(', '));
922
- console.table(this.head(n).toRows());
923
- }
924
- }
925
- /**
926
- * GroupedDataFrame for group-by operations
927
- */
928
- export class GroupedDataFrame {
929
- constructor(df, groupCols) {
930
- this._df = df;
931
- this._groupCols = groupCols;
932
- this._groups = new Map();
933
- // Build groups
934
- df.toRows().forEach((row, i) => {
935
- const key = groupCols.map(c => JSON.stringify(row[c])).join('|');
936
- if (!this._groups.has(key))
937
- this._groups.set(key, []);
938
- this._groups.get(key).push(i);
939
- });
940
- }
941
- /**
942
- * Aggregate groups
943
- */
944
- agg(aggregations) {
945
- const result = [];
946
- const allRows = this._df.toRows();
947
- this._groups.forEach((indices, key) => {
948
- const groupRows = indices.map(i => allRows[i]);
949
- const row = {};
950
- // Add group columns
951
- this._groupCols.forEach(c => {
952
- row[c] = groupRows[0][c];
953
- });
954
- // Add aggregated values
955
- Object.entries(aggregations).forEach(([col, aggFn]) => {
956
- const values = groupRows.map(r => r[col]).filter(v => v !== null && v !== undefined);
957
- if (typeof aggFn === 'function') {
958
- row[col] = aggFn(values);
959
- }
960
- else {
961
- switch (aggFn) {
962
- case 'sum':
963
- row[col] = values.reduce((a, b) => a + Number(b), 0);
964
- break;
965
- case 'mean':
966
- row[col] = values.length > 0
967
- ? values.reduce((a, b) => a + Number(b), 0) / values.length
968
- : null;
969
- break;
970
- case 'min':
971
- row[col] = values.length > 0 ? Math.min(...values.map(Number)) : null;
972
- break;
973
- case 'max':
974
- row[col] = values.length > 0 ? Math.max(...values.map(Number)) : null;
975
- break;
976
- case 'count':
977
- row[col] = values.length;
978
- break;
979
- case 'first':
980
- row[col] = values[0] ?? null;
981
- break;
982
- case 'last':
983
- row[col] = values[values.length - 1] ?? null;
984
- break;
985
- }
986
- }
987
- });
988
- result.push(row);
989
- });
990
- return new DataFrame(result);
991
- }
992
- /**
993
- * Count per group
994
- */
995
- count() {
996
- return this.agg({ _count: 'count' }).rename({ _count: 'count' });
997
- }
998
- /**
999
- * Sum per group
1000
- */
1001
- sum(column) {
1002
- return this.agg({ [column]: 'sum' });
1003
- }
1004
- /**
1005
- * Mean per group
1006
- */
1007
- mean(column) {
1008
- return this.agg({ [column]: 'mean' });
1009
- }
1010
- /**
1011
- * Get number of groups
1012
- */
1013
- get ngroups() {
1014
- return this._groups.size;
1015
- }
1016
- /**
1017
- * Get group keys
1018
- */
1019
- get groups() {
1020
- return Array.from(this._groups.keys());
1021
- }
1022
- }