node-pandas 1.0.5 → 2.0.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 (39) hide show
  1. package/.kiro/agents/git-committer-agent.md +208 -0
  2. package/.kiro/agents/npm-publisher-agent.md +501 -0
  3. package/.kiro/publish-status-2.0.0.md +134 -0
  4. package/.kiro/published-versions.md +11 -0
  5. package/.kiro/specs/pandas-like-enhancements/.config.kiro +1 -0
  6. package/.kiro/specs/pandas-like-enhancements/design.md +377 -0
  7. package/.kiro/specs/pandas-like-enhancements/requirements.md +257 -0
  8. package/.kiro/specs/pandas-like-enhancements/tasks.md +477 -0
  9. package/CHANGELOG.md +42 -0
  10. package/README.md +243 -0
  11. package/TESTING_SETUP.md +183 -0
  12. package/jest.config.js +25 -0
  13. package/package.json +11 -3
  14. package/src/bases/CsvBase.js +4 -13
  15. package/src/dataframe/dataframe.js +595 -66
  16. package/src/features/GroupBy.js +561 -0
  17. package/src/features/dateRange.js +106 -0
  18. package/src/index.js +6 -1
  19. package/src/series/series.js +688 -46
  20. package/src/utils/errors.js +314 -0
  21. package/src/utils/logger.js +259 -0
  22. package/src/utils/typeDetection.js +339 -0
  23. package/src/utils/utils.js +5 -1
  24. package/src/utils/validation.js +450 -0
  25. package/tests/README.md +151 -0
  26. package/tests/integration/.gitkeep +0 -0
  27. package/tests/integration/README.md +3 -0
  28. package/tests/property/.gitkeep +0 -0
  29. package/tests/property/README.md +3 -0
  30. package/tests/setup.js +16 -0
  31. package/tests/test.js +2 -1
  32. package/tests/unit/.gitkeep +0 -0
  33. package/tests/unit/README.md +3 -0
  34. package/tests/unit/dataframe.test.js +1141 -0
  35. package/tests/unit/example.test.js +23 -0
  36. package/tests/unit/series.test.js +441 -0
  37. package/tests/unit/tocsv.test.js +838 -0
  38. package/tests/utils/testAssertions.js +143 -0
  39. package/tests/utils/testDataGenerator.js +123 -0
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Example test to verify Jest setup is working
3
+ */
4
+
5
+ describe('Testing Infrastructure Setup', () => {
6
+ test('Jest is configured correctly', () => {
7
+ expect(true).toBe(true);
8
+ });
9
+
10
+ test('Test utilities can be imported', () => {
11
+ const { generateNumericArray } = require('../utils/testDataGenerator');
12
+ const arr = generateNumericArray(5);
13
+ expect(arr).toHaveLength(5);
14
+ expect(arr[0]).toBe(1);
15
+ });
16
+
17
+ test('Assertion utilities can be imported', () => {
18
+ const { assertArrayEqual } = require('../utils/testAssertions');
19
+ expect(() => {
20
+ assertArrayEqual([1, 2, 3], [1, 2, 3]);
21
+ }).not.toThrow();
22
+ });
23
+ });
@@ -0,0 +1,441 @@
1
+ /**
2
+ * Unit tests for Series class
3
+ * Tests Series creation, iteration, statistical methods, and transformations
4
+ *
5
+ * Validates: Requirements 13.1, 13.3
6
+ */
7
+
8
+ const Series = require('../../src/series/series');
9
+ const { DataFrameError, OperationError, TypeError: TypeErrorClass } = require('../../src/utils/errors');
10
+
11
+ describe('Series Class', () => {
12
+ describe('Creation and Basic Properties', () => {
13
+ test('should create a Series from an array', () => {
14
+ const data = [1, 2, 3, 4, 5];
15
+ const series = new Series(data);
16
+ expect(series.length).toBe(5);
17
+ expect(series[0]).toBe(1);
18
+ expect(series[4]).toBe(5);
19
+ });
20
+
21
+ test('should create a Series with custom index', () => {
22
+ const data = [10, 20, 30];
23
+ const series = new Series(data, { index: ['a', 'b', 'c'] });
24
+ expect(series.index).toEqual(['a', 'b', 'c']);
25
+ expect(series.get('a')).toBe(10);
26
+ expect(series.get('b')).toBe(20);
27
+ });
28
+
29
+ test('should create a Series with a name', () => {
30
+ const series = new Series([1, 2, 3], { name: 'test_series' });
31
+ expect(series.name).toBe('test_series');
32
+ });
33
+
34
+ test('should infer numeric type for numeric data', () => {
35
+ const series = new Series([1, 2, 3, 4, 5]);
36
+ expect(series.dtype).toBe('numeric');
37
+ });
38
+
39
+ test('should infer string type for string data', () => {
40
+ const series = new Series(['a', 'b', 'c']);
41
+ expect(series.dtype).toBe('string');
42
+ });
43
+
44
+ test('should infer mixed type for mixed data', () => {
45
+ const series = new Series([1, 'a', true]);
46
+ expect(series.dtype).toBe('mixed');
47
+ });
48
+
49
+ test('should throw error if data is not an array', () => {
50
+ expect(() => new Series('not an array')).toThrow();
51
+ expect(() => new Series(123)).toThrow();
52
+ expect(() => new Series({ a: 1 })).toThrow();
53
+ });
54
+
55
+ test('should handle empty Series', () => {
56
+ const series = new Series([]);
57
+ expect(series.length).toBe(0);
58
+ expect(series.dtype).toBe('null');
59
+ });
60
+ });
61
+
62
+ describe('Index and Value Access', () => {
63
+ test('should get value by index label', () => {
64
+ const series = new Series([10, 20, 30], { index: ['x', 'y', 'z'] });
65
+ expect(series.get('x')).toBe(10);
66
+ expect(series.get('y')).toBe(20);
67
+ expect(series.get('z')).toBe(30);
68
+ });
69
+
70
+ test('should set value by index label', () => {
71
+ const series = new Series([10, 20, 30], { index: ['a', 'b', 'c'] });
72
+ series.set('b', 25);
73
+ expect(series.get('b')).toBe(25);
74
+ });
75
+
76
+ test('should throw error when getting non-existent label', () => {
77
+ const series = new Series([1, 2, 3], { index: ['a', 'b', 'c'] });
78
+ expect(() => series.get('d')).toThrow(DataFrameError);
79
+ });
80
+
81
+ test('should throw error when setting non-existent label', () => {
82
+ const series = new Series([1, 2, 3], { index: ['a', 'b', 'c'] });
83
+ expect(() => series.set('d', 99)).toThrow(DataFrameError);
84
+ });
85
+
86
+ test('should support numeric index access', () => {
87
+ const series = new Series([10, 20, 30]);
88
+ expect(series[0]).toBe(10);
89
+ expect(series[1]).toBe(20);
90
+ expect(series[2]).toBe(30);
91
+ });
92
+ });
93
+
94
+ describe('Iteration', () => {
95
+ test('should support for...of iteration', () => {
96
+ const series = new Series([1, 2, 3]);
97
+ const values = [];
98
+ for (const value of series) {
99
+ values.push(value);
100
+ }
101
+ expect(values).toEqual([1, 2, 3]);
102
+ });
103
+
104
+ test('should support forEach iteration', () => {
105
+ const series = new Series([1, 2, 3]);
106
+ const values = [];
107
+ series.forEach(value => values.push(value));
108
+ expect(values).toEqual([1, 2, 3]);
109
+ });
110
+
111
+ test('should support map iteration', () => {
112
+ const series = new Series([1, 2, 3]);
113
+ const doubled = Array.from(series).map(x => x * 2);
114
+ expect(doubled).toEqual([2, 4, 6]);
115
+ });
116
+ });
117
+
118
+ describe('Transformation Methods', () => {
119
+ test('should map values to new Series', () => {
120
+ const series = new Series([1, 2, 3]);
121
+ const doubled = series.map(x => x * 2);
122
+ expect(doubled.length).toBe(3);
123
+ expect(doubled[0]).toBe(2);
124
+ expect(doubled[1]).toBe(4);
125
+ expect(doubled[2]).toBe(6);
126
+ });
127
+
128
+ test('should preserve index in map operation', () => {
129
+ const series = new Series([1, 2, 3], { index: ['a', 'b', 'c'] });
130
+ const doubled = series.map(x => x * 2);
131
+ expect(doubled.index).toEqual(['a', 'b', 'c']);
132
+ });
133
+
134
+ test('should preserve name in map operation', () => {
135
+ const series = new Series([1, 2, 3], { name: 'original' });
136
+ const doubled = series.map(x => x * 2);
137
+ expect(doubled.name).toBe('original');
138
+ });
139
+
140
+ test('should apply function with index parameter', () => {
141
+ const series = new Series([10, 20, 30]);
142
+ const result = series.map((value, idx) => value + idx);
143
+ expect(result[0]).toBe(10); // 10 + 0
144
+ expect(result[1]).toBe(21); // 20 + 1
145
+ expect(result[2]).toBe(32); // 30 + 2
146
+ });
147
+
148
+ test('should throw error if map function is not a function', () => {
149
+ const series = new Series([1, 2, 3]);
150
+ expect(() => series.map('not a function')).toThrow();
151
+ });
152
+
153
+ test('should throw error if map function throws', () => {
154
+ const series = new Series([1, 2, 3]);
155
+ expect(() => series.map(() => {
156
+ throw new Error('test error');
157
+ })).toThrow(OperationError);
158
+ });
159
+
160
+ test('should apply function with apply method', () => {
161
+ const series = new Series([1, 2, 3]);
162
+ const result = series.apply(x => x + 10);
163
+ expect(result[0]).toBe(11);
164
+ expect(result[1]).toBe(12);
165
+ expect(result[2]).toBe(13);
166
+ });
167
+
168
+ test('should replace single value', () => {
169
+ const series = new Series([1, 2, 3, 2, 1]);
170
+ const replaced = series.replace(2, 99);
171
+ expect(replaced[0]).toBe(1);
172
+ expect(replaced[1]).toBe(99);
173
+ expect(replaced[2]).toBe(3);
174
+ expect(replaced[3]).toBe(99);
175
+ expect(replaced[4]).toBe(1);
176
+ });
177
+
178
+ test('should replace with function condition', () => {
179
+ const series = new Series([1, 2, 3, 4, 5]);
180
+ const replaced = series.replace(x => x > 3, 0);
181
+ expect(replaced[0]).toBe(1);
182
+ expect(replaced[1]).toBe(2);
183
+ expect(replaced[2]).toBe(3);
184
+ expect(replaced[3]).toBe(0);
185
+ expect(replaced[4]).toBe(0);
186
+ });
187
+
188
+ test('should preserve index in replace operation', () => {
189
+ const series = new Series([1, 2, 3], { index: ['a', 'b', 'c'] });
190
+ const replaced = series.replace(2, 99);
191
+ expect(replaced.index).toEqual(['a', 'b', 'c']);
192
+ });
193
+ });
194
+
195
+ describe('Statistical Methods - Sum and Count', () => {
196
+ test('should compute sum of numeric Series', () => {
197
+ const series = new Series([1, 2, 3, 4, 5]);
198
+ expect(series.sum()).toBe(15);
199
+ });
200
+
201
+ test('should exclude null values from sum', () => {
202
+ const series = new Series([1, null, 3, undefined, 5]);
203
+ expect(series.sum()).toBe(9);
204
+ });
205
+
206
+ test('should throw error for non-numeric Series sum', () => {
207
+ const series = new Series(['a', 'b', 'c']);
208
+ expect(() => series.sum()).toThrow(TypeErrorClass);
209
+ });
210
+
211
+ test('should count non-null values', () => {
212
+ const series = new Series([1, 2, null, 4, undefined, 6]);
213
+ expect(series.count()).toBe(4);
214
+ });
215
+
216
+ test('should count all values in Series without nulls', () => {
217
+ const series = new Series([1, 2, 3, 4, 5]);
218
+ expect(series.count()).toBe(5);
219
+ });
220
+
221
+ test('should return 0 count for empty Series', () => {
222
+ const series = new Series([]);
223
+ expect(series.count()).toBe(0);
224
+ });
225
+ });
226
+
227
+ describe('Statistical Methods - Mean and Median', () => {
228
+ test('should compute mean of numeric Series', () => {
229
+ const series = new Series([1, 2, 3, 4, 5]);
230
+ expect(series.mean()).toBe(3);
231
+ });
232
+
233
+ test('should exclude null values from mean', () => {
234
+ const series = new Series([10, 20, null, 30]);
235
+ expect(series.mean()).toBe(20);
236
+ });
237
+
238
+ test('should throw error for non-numeric Series mean', () => {
239
+ const series = new Series(['a', 'b', 'c']);
240
+ expect(() => series.mean()).toThrow(TypeErrorClass);
241
+ });
242
+
243
+ test('should compute median of odd-length numeric Series', () => {
244
+ const series = new Series([1, 2, 3, 4, 5]);
245
+ expect(series.median()).toBe(3);
246
+ });
247
+
248
+ test('should compute median of even-length numeric Series', () => {
249
+ const series = new Series([1, 2, 3, 4]);
250
+ expect(series.median()).toBe(2.5);
251
+ });
252
+
253
+ test('should exclude null values from median', () => {
254
+ const series = new Series([1, null, 2, 3, null, 4, 5]);
255
+ expect(series.median()).toBe(3);
256
+ });
257
+
258
+ test('should throw error for non-numeric Series median', () => {
259
+ const series = new Series(['a', 'b', 'c']);
260
+ expect(() => series.median()).toThrow(TypeErrorClass);
261
+ });
262
+ });
263
+
264
+ describe('Statistical Methods - Mode', () => {
265
+ test('should compute mode of numeric Series', () => {
266
+ const series = new Series([1, 2, 2, 3, 3, 3]);
267
+ expect(series.mode()).toBe(3);
268
+ });
269
+
270
+ test('should compute mode of string Series', () => {
271
+ const series = new Series(['a', 'b', 'a', 'c', 'a']);
272
+ expect(series.mode()).toBe('a');
273
+ });
274
+
275
+ test('should return first mode if multiple modes exist', () => {
276
+ const series = new Series([1, 1, 2, 2, 3]);
277
+ const mode = series.mode();
278
+ expect([1, 2]).toContain(mode);
279
+ });
280
+
281
+ test('should exclude null values from mode', () => {
282
+ const series = new Series([1, null, 1, 2, 2, 2]);
283
+ expect(series.mode()).toBe(2);
284
+ });
285
+
286
+ test('should throw error for empty Series', () => {
287
+ const series = new Series([]);
288
+ expect(() => series.mode()).toThrow(DataFrameError);
289
+ });
290
+
291
+ test('should throw error for all-null Series', () => {
292
+ const series = new Series([null, null, undefined]);
293
+ expect(() => series.mode()).toThrow(DataFrameError);
294
+ });
295
+ });
296
+
297
+ describe('Statistical Methods - Min and Max', () => {
298
+ test('should compute min of numeric Series', () => {
299
+ const series = new Series([5, 2, 8, 1, 9]);
300
+ expect(series.min()).toBe(1);
301
+ });
302
+
303
+ test('should compute max of numeric Series', () => {
304
+ const series = new Series([5, 2, 8, 1, 9]);
305
+ expect(series.max()).toBe(9);
306
+ });
307
+
308
+ test('should compute min of string Series', () => {
309
+ const series = new Series(['zebra', 'apple', 'mango']);
310
+ expect(series.min()).toBe('apple');
311
+ });
312
+
313
+ test('should compute max of string Series', () => {
314
+ const series = new Series(['zebra', 'apple', 'mango']);
315
+ expect(series.max()).toBe('zebra');
316
+ });
317
+
318
+ test('should exclude null values from min', () => {
319
+ const series = new Series([5, null, 2, undefined, 8]);
320
+ expect(series.min()).toBe(2);
321
+ });
322
+
323
+ test('should exclude null values from max', () => {
324
+ const series = new Series([5, null, 2, undefined, 8]);
325
+ expect(series.max()).toBe(8);
326
+ });
327
+
328
+ test('should throw error for empty Series min', () => {
329
+ const series = new Series([]);
330
+ expect(() => series.min()).toThrow(DataFrameError);
331
+ });
332
+
333
+ test('should throw error for all-null Series min', () => {
334
+ const series = new Series([null, undefined]);
335
+ expect(() => series.min()).toThrow(DataFrameError);
336
+ });
337
+
338
+ test('should throw error for empty Series max', () => {
339
+ const series = new Series([]);
340
+ expect(() => series.max()).toThrow(DataFrameError);
341
+ });
342
+
343
+ test('should throw error for all-null Series max', () => {
344
+ const series = new Series([null, undefined]);
345
+ expect(() => series.max()).toThrow(DataFrameError);
346
+ });
347
+ });
348
+
349
+ describe('Statistical Methods - Std and Var', () => {
350
+ test('should compute standard deviation', () => {
351
+ const series = new Series([1, 2, 3, 4, 5]);
352
+ const std = series.std();
353
+ expect(std).toBeCloseTo(1.5811, 4);
354
+ });
355
+
356
+ test('should compute variance', () => {
357
+ const series = new Series([1, 2, 3, 4, 5]);
358
+ const variance = series.var();
359
+ expect(variance).toBeCloseTo(2.5, 4);
360
+ });
361
+
362
+ test('should exclude null values from std', () => {
363
+ const series = new Series([1, null, 2, 3, null, 4, 5]);
364
+ const std = series.std();
365
+ expect(std).toBeCloseTo(1.5811, 4);
366
+ });
367
+
368
+ test('should exclude null values from var', () => {
369
+ const series = new Series([1, null, 2, 3, null, 4, 5]);
370
+ const variance = series.var();
371
+ expect(variance).toBeCloseTo(2.5, 4);
372
+ });
373
+
374
+ test('should throw error for std with fewer than 2 numeric values', () => {
375
+ const series = new Series([1]);
376
+ expect(() => series.std()).toThrow(TypeErrorClass);
377
+ });
378
+
379
+ test('should throw error for var with fewer than 2 numeric values', () => {
380
+ const series = new Series([1]);
381
+ expect(() => series.var()).toThrow(TypeErrorClass);
382
+ });
383
+
384
+ test('should throw error for std of non-numeric Series', () => {
385
+ const series = new Series(['a', 'b', 'c']);
386
+ expect(() => series.std()).toThrow(TypeErrorClass);
387
+ });
388
+
389
+ test('should throw error for var of non-numeric Series', () => {
390
+ const series = new Series(['a', 'b', 'c']);
391
+ expect(() => series.var()).toThrow(TypeErrorClass);
392
+ });
393
+ });
394
+
395
+ describe('Edge Cases', () => {
396
+ test('should handle Series with mixed numeric and string values', () => {
397
+ const series = new Series([1, 'a', 2, 'b', 3]);
398
+ expect(series.dtype).toBe('mixed');
399
+ expect(series.count()).toBe(5);
400
+ });
401
+
402
+ test('should handle Series with numeric strings', () => {
403
+ const series = new Series(['1', '2', '3']);
404
+ // Numeric strings are detected as numeric type
405
+ expect(series.dtype).toBe('numeric');
406
+ });
407
+
408
+ test('should handle Series with boolean values', () => {
409
+ const series = new Series([true, false, true]);
410
+ expect(series.dtype).toBe('boolean');
411
+ });
412
+
413
+ test('should handle Series with all null values', () => {
414
+ const series = new Series([null, null, undefined]);
415
+ expect(series.count()).toBe(0);
416
+ expect(series.dtype).toBe('null');
417
+ });
418
+
419
+ test('should handle transformation that returns null', () => {
420
+ const series = new Series([1, 2, 3]);
421
+ const result = series.map(() => null);
422
+ expect(result[0]).toBeNull();
423
+ expect(result[1]).toBeNull();
424
+ expect(result[2]).toBeNull();
425
+ });
426
+
427
+ test('should handle large Series', () => {
428
+ const data = Array.from({ length: 10000 }, (_, i) => i);
429
+ const series = new Series(data);
430
+ expect(series.length).toBe(10000);
431
+ expect(series.sum()).toBe((10000 * 9999) / 2);
432
+ });
433
+ });
434
+
435
+ describe('Show Property', () => {
436
+ test('should have show property that displays data', () => {
437
+ const series = new Series([1, 2, 3]);
438
+ expect(() => series.show).not.toThrow();
439
+ });
440
+ });
441
+ });