@nshiab/simple-data-analysis-core 2.0.3 → 2.0.5

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 (57) hide show
  1. package/esm/class/SimpleTable.d.ts +202 -8
  2. package/esm/class/SimpleTable.d.ts.map +1 -1
  3. package/esm/class/SimpleTable.js +211 -9
  4. package/esm/methods/addCharacterCount.d.ts +3 -0
  5. package/esm/methods/addCharacterCount.d.ts.map +1 -0
  6. package/esm/methods/addCharacterCount.js +15 -0
  7. package/esm/methods/addGeoNoise.d.ts +7 -0
  8. package/esm/methods/addGeoNoise.d.ts.map +1 -0
  9. package/esm/methods/addGeoNoise.js +135 -0
  10. package/esm/methods/addNoise.d.ts +5 -0
  11. package/esm/methods/addNoise.d.ts.map +1 -0
  12. package/esm/methods/addNoise.js +53 -0
  13. package/esm/methods/addWordCount.d.ts +3 -0
  14. package/esm/methods/addWordCount.d.ts.map +1 -0
  15. package/esm/methods/addWordCount.js +15 -0
  16. package/esm/methods/columnProportions.d.ts +1 -0
  17. package/esm/methods/columnProportions.d.ts.map +1 -1
  18. package/esm/methods/columnProportions.js +7 -2
  19. package/esm/methods/loadArray.d.ts +4 -2
  20. package/esm/methods/loadArray.d.ts.map +1 -1
  21. package/esm/methods/loadArray.js +113 -4
  22. package/esm/methods/loadYahooFinanceData.d.ts +10 -0
  23. package/esm/methods/loadYahooFinanceData.d.ts.map +1 -0
  24. package/esm/methods/loadYahooFinanceData.js +133 -0
  25. package/esm/methods/rowProportions.d.ts +1 -0
  26. package/esm/methods/rowProportions.d.ts.map +1 -1
  27. package/esm/methods/rowProportions.js +5 -1
  28. package/llm.md +291 -14
  29. package/llms.txt +1 -1
  30. package/package.json +1 -1
  31. package/script/class/SimpleTable.d.ts +202 -8
  32. package/script/class/SimpleTable.d.ts.map +1 -1
  33. package/script/class/SimpleTable.js +211 -9
  34. package/script/methods/addCharacterCount.d.ts +3 -0
  35. package/script/methods/addCharacterCount.d.ts.map +1 -0
  36. package/script/methods/addCharacterCount.js +21 -0
  37. package/script/methods/addGeoNoise.d.ts +7 -0
  38. package/script/methods/addGeoNoise.d.ts.map +1 -0
  39. package/script/methods/addGeoNoise.js +141 -0
  40. package/script/methods/addNoise.d.ts +5 -0
  41. package/script/methods/addNoise.d.ts.map +1 -0
  42. package/script/methods/addNoise.js +59 -0
  43. package/script/methods/addWordCount.d.ts +3 -0
  44. package/script/methods/addWordCount.d.ts.map +1 -0
  45. package/script/methods/addWordCount.js +21 -0
  46. package/script/methods/columnProportions.d.ts +1 -0
  47. package/script/methods/columnProportions.d.ts.map +1 -1
  48. package/script/methods/columnProportions.js +7 -2
  49. package/script/methods/loadArray.d.ts +4 -2
  50. package/script/methods/loadArray.d.ts.map +1 -1
  51. package/script/methods/loadArray.js +112 -3
  52. package/script/methods/loadYahooFinanceData.d.ts +10 -0
  53. package/script/methods/loadYahooFinanceData.d.ts.map +1 -0
  54. package/script/methods/loadYahooFinanceData.js +139 -0
  55. package/script/methods/rowProportions.d.ts +1 -0
  56. package/script/methods/rowProportions.d.ts.map +1 -1
  57. package/script/methods/rowProportions.js +5 -1
@@ -22,6 +22,8 @@ const removeMissing_js_1 = __importDefault(require("../methods/removeMissing.js"
22
22
  const getColumns_js_1 = __importDefault(require("../methods/getColumns.js"));
23
23
  const getRowCount_js_1 = __importDefault(require("../methods/getRowCount.js"));
24
24
  const getCharacterCount_js_1 = __importDefault(require("../methods/getCharacterCount.js"));
25
+ const addCharacterCount_js_1 = __importDefault(require("../methods/addCharacterCount.js"));
26
+ const addWordCount_js_1 = __importDefault(require("../methods/addWordCount.js"));
25
27
  const getTypes_js_1 = __importDefault(require("../methods/getTypes.js"));
26
28
  const getHash_js_1 = __importDefault(require("../methods/getHash.js"));
27
29
  const getValues_js_1 = __importDefault(require("../methods/getValues.js"));
@@ -123,6 +125,7 @@ const length_js_1 = __importDefault(require("../methods/length.js"));
123
125
  const area_js_1 = __importDefault(require("../methods/area.js"));
124
126
  const reproject_js_1 = __importDefault(require("../methods/reproject.js"));
125
127
  const reducePrecision_js_1 = __importDefault(require("../methods/reducePrecision.js"));
128
+ const addGeoNoise_js_1 = __importDefault(require("../methods/addGeoNoise.js"));
126
129
  const flipCoordinates_js_1 = __importDefault(require("../methods/flipCoordinates.js"));
127
130
  const addGeoType_js_1 = __importDefault(require("../methods/addGeoType.js"));
128
131
  const addGeoClosedStatus_js_1 = __importDefault(require("../methods/addGeoClosedStatus.js"));
@@ -137,6 +140,7 @@ const getSchema_js_1 = __importDefault(require("../methods/getSchema.js"));
137
140
  const outliersIQR_js_1 = __importDefault(require("../methods/outliersIQR.js"));
138
141
  const bins_js_1 = __importDefault(require("../methods/bins.js"));
139
142
  const round_js_1 = __importDefault(require("../methods/round.js"));
143
+ const addNoise_js_1 = __importDefault(require("../methods/addNoise.js"));
140
144
  const rowToText_js_1 = __importDefault(require("../methods/rowToText.js"));
141
145
  const replaceNulls_js_1 = __importDefault(require("../methods/replaceNulls.js"));
142
146
  const pad_js_1 = __importDefault(require("../methods/pad.js"));
@@ -158,6 +162,7 @@ const insertRows_js_1 = __importDefault(require("../methods/insertRows.js"));
158
162
  const loadGeoData_js_1 = __importDefault(require("../methods/loadGeoData.js"));
159
163
  const loadOpenStreetMap_js_1 = __importDefault(require("../methods/loadOpenStreetMap.js"));
160
164
  const loadStatCanData_js_1 = __importDefault(require("../methods/loadStatCanData.js"));
165
+ const loadYahooFinanceData_js_1 = __importDefault(require("../methods/loadYahooFinanceData.js"));
161
166
  const setTypes_js_1 = __importDefault(require("../methods/setTypes.js"));
162
167
  const flushAllTables_js_1 = __importDefault(require("../helpers/flushAllTables.js"));
163
168
  const queueOp_js_1 = __importDefault(require("../helpers/queueOp.js"));
@@ -341,7 +346,7 @@ class SimpleTable extends Simple_js_1.default {
341
346
  return this;
342
347
  }
343
348
  /**
344
- * Loads an array of JavaScript objects into the table. This method queues the load; it runs when an async observer method (like `getData()` or `log()`) is awaited, or when `run()` is called.
349
+ * Loads an array of JavaScript objects into the table. Types can also be specified for individual columns instead of inferred from their values. This method queues the load; it runs when an async observer method (like `getData()` or `log()`) is awaited, or when `run()` is called.
345
350
  *
346
351
  * JavaScript `Date` values are inferred as DuckDB `TIMESTAMP` values. Their
347
352
  * instant is preserved, but JavaScript `Date` does not retain the timezone or
@@ -349,6 +354,8 @@ class SimpleTable extends Simple_js_1.default {
349
354
  * use `convert()` to parse them as temporal values.
350
355
  *
351
356
  * @param rows - An array of objects, where each object represents a row and its properties represent columns.
357
+ * @param options - Options for loading the array, captured when called.
358
+ * @param options.columnTypes - Types for specific columns; omitted columns are inferred. Values must be compatible with the selected type without losing information.
352
359
  * @returns The table, so methods can be chained.
353
360
  * @category Importing Data
354
361
  *
@@ -364,6 +371,15 @@ class SimpleTable extends Simple_js_1.default {
364
371
  *
365
372
  * @example
366
373
  * ```ts
374
+ * // Specify a numeric type for an all-null column
375
+ * await table.loadArray(
376
+ * [{ name: "A", value: null }, { name: "B", value: null }],
377
+ * { columnTypes: { value: "DOUBLE" } },
378
+ * ).log();
379
+ * ```
380
+ *
381
+ * @example
382
+ * ```ts
367
383
  * // The offset determines the instant; the loaded TIMESTAMP is returned as
368
384
  * // the equivalent UTC JavaScript Date.
369
385
  * await table.loadArray([{
@@ -371,8 +387,8 @@ class SimpleTable extends Simple_js_1.default {
371
387
  * }]).log();
372
388
  * ```
373
389
  */
374
- loadArray(rows) {
375
- (0, loadArray_js_1.default)(this, rows);
390
+ loadArray(rows, options = {}) {
391
+ (0, loadArray_js_1.default)(this, rows, options);
376
392
  return this;
377
393
  }
378
394
  /**
@@ -503,6 +519,70 @@ class SimpleTable extends Simple_js_1.default {
503
519
  (0, loadStatCanData_js_1.default)(this, pid, options);
504
520
  return this;
505
521
  }
522
+ /**
523
+ * Downloads historical market data from Yahoo Finance and loads it into this
524
+ * table as `datetime`, `open`, `high`, `low`, `close`, `adjustedClose`, and
525
+ * `volume` columns. `datetime` contains Yahoo's timestamps as JavaScript
526
+ * `Date` values. Unavailable values are preserved as `null`.
527
+ *
528
+ * All range boundaries are evaluated in UTC. Use ISO date-time strings ending
529
+ * in `Z`. Yahoo trading sessions may occur on a different UTC date from their
530
+ * exchange-local date, so choose the range appropriate for the market and
531
+ * interval. Avoid numeric constructors such as `new Date(2025, 2, 15)`, which
532
+ * use the runtime's local timezone.
533
+ *
534
+ * This method uses an undocumented Yahoo Finance endpoint and is not
535
+ * affiliated with or endorsed by Yahoo. It is provided for educational,
536
+ * research, and journalistic purposes. Before using it, review Yahoo's terms
537
+ * and any applicable data-provider restrictions.
538
+ *
539
+ * The method queues the download and load; they run when an async observer
540
+ * method (like `getData()` or `log()`) is awaited, or when `run()` is called.
541
+ *
542
+ * @param symbol - The stock or index symbol, such as `"AAPL"` or `"^GSPTSE"`.
543
+ * @param startDate - The inclusive UTC start of the requested range.
544
+ * @param endDate - The inclusive UTC end of the requested range. The UTC day,
545
+ * hour, or minute containing this instant is included, according to
546
+ * `interval`.
547
+ * @param interval - The interval between observations: daily, hourly, or every
548
+ * minute.
549
+ * @returns The table, so methods can be chained.
550
+ * @throws {RangeError} If either date is invalid or `endDate` is before `startDate`.
551
+ * @throws {Error} If Yahoo rejects the request or returns no data.
552
+ * @see https://legal.yahoo.com/us/en/yahoo/terms/otos/index.html
553
+ * @see https://help.yahoo.com/kb/finance/SLN2310.html
554
+ * @category Importing Data
555
+ *
556
+ * @example
557
+ * ```ts
558
+ * // Request daily observations using explicit UTC boundaries.
559
+ * await table
560
+ * .loadYahooFinanceData(
561
+ * "^GSPTSE",
562
+ * new Date("2025-03-01T00:00:00Z"),
563
+ * new Date("2025-03-15T00:00:00Z"),
564
+ * "1d",
565
+ * )
566
+ * .log();
567
+ * ```
568
+ *
569
+ * @example
570
+ * ```ts
571
+ * // Include the UTC hours from 13:00 through 16:00.
572
+ * await table
573
+ * .loadYahooFinanceData(
574
+ * "AAPL",
575
+ * new Date("2025-03-14T13:00:00Z"),
576
+ * new Date("2025-03-14T16:00:00Z"),
577
+ * "1h",
578
+ * )
579
+ * .log();
580
+ * ```
581
+ */
582
+ loadYahooFinanceData(symbol, startDate, endDate, interval) {
583
+ (0, loadYahooFinanceData_js_1.default)(this, symbol, startDate, endDate, interval);
584
+ return this;
585
+ }
506
586
  /**
507
587
  * Loads geospatial data from an external file or URL into the table.
508
588
  *
@@ -2695,6 +2775,37 @@ class SimpleTable extends Simple_js_1.default {
2695
2775
  (0, round_js_1.default)(this, columns, options);
2696
2776
  return this;
2697
2777
  }
2778
+ /**
2779
+ * Adds independently generated uniform random noise to numeric values in one or more columns.
2780
+ * Each changed value receives an offset between `-max` and `max`. Selected integer and decimal columns become `DOUBLE` columns so fractional noise is retained. This method adds random jitter; it does not provide anonymization or differential privacy guarantees.
2781
+ *
2782
+ * This method queues the operation; it runs when an async observer method (like `getData()` or `log()`) is awaited, or when `run()` is called.
2783
+ *
2784
+ * @param columns - The numeric column name or array of numeric column names to which noise will be added. When multiple columns are provided, each value receives an independent random offset.
2785
+ * @param max - The maximum absolute offset, expressed in each column's units. Must be a finite number greater than or equal to `0`.
2786
+ * @param options - An optional object with configuration options:
2787
+ * @param options.onlyDuplicates - If `true`, adds noise only to values that occur more than once in their column. Each selected column is evaluated independently, and every occurrence of a duplicated value is changed. Defaults to `false`.
2788
+ * @returns The table, so methods can be chained.
2789
+ * @category Updating Data
2790
+ *
2791
+ * @example
2792
+ * ```ts
2793
+ * // Add an offset between -0.01 and 0.01 to every value
2794
+ * await table.addNoise("measurement", 0.01).log();
2795
+ * ```
2796
+ *
2797
+ * @example
2798
+ * ```ts
2799
+ * // Add noise independently to duplicated x values and duplicated y values
2800
+ * await table.addNoise(["x", "y"], 0.1, {
2801
+ * onlyDuplicates: true,
2802
+ * }).log();
2803
+ * ```
2804
+ */
2805
+ addNoise(columns, max, options = {}) {
2806
+ (0, addNoise_js_1.default)(this, columns, max, options);
2807
+ return this;
2808
+ }
2698
2809
  /**
2699
2810
  * Updates values in a specified column using a SQL expression.
2700
2811
  *
@@ -2878,11 +2989,18 @@ class SimpleTable extends Simple_js_1.default {
2878
2989
  * | 2022 | 354 | 278 | 56 | 0.51 | 0.4 | 0.08 |
2879
2990
  * | 2023 | 856 | 321 | 221 | 0.61 | 0.23 | 0.16 |
2880
2991
  *
2992
+ * @example
2993
+ * ```ts
2994
+ * // Compute percentages that sum to 100 on each row before rounding
2995
+ * await table.rowProportions(["Men", "Women", "NonBinary"], { base: 100, decimals: 1 }).log();
2996
+ * ```
2997
+ *
2881
2998
  * This method queues the operation; it runs when an async observer method (like `getData()` or `log()`) is awaited, or when `run()` is called.
2882
2999
  *
2883
3000
  * @param columns - An array of column names for which proportions will be computed on each row.
2884
3001
  * @param options - An optional object with configuration options:
2885
3002
  * @param options.suffix - A string suffix to append to the names of the new columns storing the computed proportions. Defaults to `"Perc"`.
3003
+ * @param options.base - A finite positive value that the proportions in each row sum to before rounding. Defaults to `1`.
2886
3004
  * @param options.decimals - The number of decimal places to round the computed proportions. Defaults to `undefined` (no rounding).
2887
3005
  * @returns The table, so methods can be chained.
2888
3006
  * @category Analyzing Data
@@ -2947,6 +3065,7 @@ class SimpleTable extends Simple_js_1.default {
2947
3065
  * @param newColumn - The name of the new column where the proportions will be stored.
2948
3066
  * @param options - An optional object with configuration options:
2949
3067
  * @param options.by - The column name or an array of column names to partition by. Proportions are calculated independently within each group.
3068
+ * @param options.base - A finite positive value that the proportions in the column or each group sum to before rounding. Defaults to `1`.
2950
3069
  * @param options.decimals - The number of decimal places to round the computed proportions. Defaults to `undefined` (no rounding).
2951
3070
  * @returns The table, so methods can be chained.
2952
3071
  * @category Analyzing Data
@@ -2965,6 +3084,12 @@ class SimpleTable extends Simple_js_1.default {
2965
3084
  *
2966
3085
  * @example
2967
3086
  * ```ts
3087
+ * // Compute percentages that sum to 100 before rounding
3088
+ * await table.columnProportions("sales", "sales_percentage", { base: 100, decimals: 1 }).log();
3089
+ * ```
3090
+ *
3091
+ * @example
3092
+ * ```ts
2968
3093
  * // Compute proportions for 'sales' by 'region' and 'product_type'
2969
3094
  * await table.columnProportions("sales", "sales_proportion", { by: ["region", "product_type"] }).log();
2970
3095
  * ```
@@ -3475,17 +3600,15 @@ class SimpleTable extends Simple_js_1.default {
3475
3600
  *
3476
3601
  * @example
3477
3602
  * ```ts
3478
- * // Count words correctly across multilingual article text.
3479
- * const segmenter = new Intl.Segmenter(undefined, { granularity: "word" });
3603
+ * // Extract hostnames with JavaScript's URL parser.
3480
3604
  * const table = await sdb
3481
3605
  * .newTable()
3482
- * .loadData("articles.csv")
3606
+ * .loadData("websites.csv")
3483
3607
  * .updateWithJS((rows) => {
3484
3608
  * return rows.map((row) => ({
3485
3609
  * ...row,
3486
- * wordCount: typeof row.text === "string"
3487
- * ? [...segmenter.segment(row.text)].filter((part) => part.isWordLike)
3488
- * .length
3610
+ * hostname: typeof row.url === "string"
3611
+ * ? new URL(row.url).hostname
3489
3612
  * : null,
3490
3613
  * }));
3491
3614
  * })
@@ -3634,8 +3757,52 @@ class SimpleTable extends Simple_js_1.default {
3634
3757
  const result = (await (0, getColumns_js_1.default)(this)).length;
3635
3758
  return result;
3636
3759
  }
3760
+ /**
3761
+ * Adds a new column containing the number of characters in each string in the specified column.
3762
+ * Counts are based on Unicode code points, not grapheme clusters. A user-perceived character composed of multiple code points, such as some emoji or decomposed accented letters, counts as multiple characters.
3763
+ * `NULL` input values produce `NULL` counts.
3764
+ *
3765
+ * This method queues the operation; it runs when an async observer method (like `getData()` or `log()`) is awaited, or when `run()` is called.
3766
+ *
3767
+ * @param column - The name of the column containing the strings to count.
3768
+ * @param newColumn - The name of the new column where the character counts will be stored.
3769
+ * @returns The table, so methods can be chained.
3770
+ * @category Text Processing
3771
+ *
3772
+ * @example
3773
+ * ```ts
3774
+ * // Add a character count for each value in the 'name' column
3775
+ * await table.addCharacterCount("name", "nameCharacterCount").log();
3776
+ * ```
3777
+ */
3778
+ addCharacterCount(column, newColumn) {
3779
+ (0, addCharacterCount_js_1.default)(this, column, newColumn);
3780
+ return this;
3781
+ }
3782
+ /**
3783
+ * Adds a new column containing the word count for each string in the specified column.
3784
+ * A word is any contiguous sequence of non-whitespace characters. Spaces, tabs, and line breaks separate words. Punctuation is not removed, so a standalone punctuation sequence counts as a word. Text without whitespace counts as one word, regardless of language. Empty or whitespace-only strings produce `0`, and `NULL` input values produce `NULL` counts.
3785
+ *
3786
+ * This method queues the operation; it runs when an async observer method (like `getData()` or `log()`) is awaited, or when `run()` is called.
3787
+ *
3788
+ * @param column - The name of the column containing the strings to count.
3789
+ * @param newColumn - The name of the new column where the word counts will be stored.
3790
+ * @returns The table, so methods can be chained.
3791
+ * @category Text Processing
3792
+ *
3793
+ * @example
3794
+ * ```ts
3795
+ * // Add a word count for each value in the 'article' column
3796
+ * await table.addWordCount("article", "wordCount").log();
3797
+ * ```
3798
+ */
3799
+ addWordCount(column, newColumn) {
3800
+ (0, addWordCount_js_1.default)(this, column, newColumn);
3801
+ return this;
3802
+ }
3637
3803
  /**
3638
3804
  * Returns the total number of characters in a column storing strings.
3805
+ * Counts are based on Unicode code points, not grapheme clusters. A user-perceived character composed of multiple code points, such as some emoji or decomposed accented letters, counts as multiple characters.
3639
3806
  *
3640
3807
  * @param column - The name of the string column to count characters from.
3641
3808
  * @returns A promise that resolves to the total number of characters across all rows in the specified column.
@@ -4465,6 +4632,41 @@ class SimpleTable extends Simple_js_1.default {
4465
4632
  (0, flipCoordinates_js_1.default)(this, column);
4466
4633
  return this;
4467
4634
  }
4635
+ /**
4636
+ * Moves EPSG:4326 point geometries to random locations within a maximum great-circle distance of their original positions.
4637
+ *
4638
+ * Points are sampled uniformly within the requested distance using the spherical direct geodesic (destination-point) formula and the same spherical Earth model as DuckDB's `ST_Distance_Sphere()`. This accounts for longitude scale changing with latitude. This method adds random jitter; it does not provide anonymization or differential privacy guarantees.
4639
+ *
4640
+ * This method supports only `POINT` geometries in `EPSG:4326`. Null and empty geometries are preserved. It queues the operation; the operation runs when an async observer method (like `getData()` or `log()`) is awaited, or when `run()` is called.
4641
+ *
4642
+ * @param maxDistance - The maximum great-circle displacement in the selected unit. Must be a finite number greater than or equal to `0` and no greater than half Earth's circumference.
4643
+ * @param options - An optional object with configuration options:
4644
+ * @param options.column - The name of the EPSG:4326 point geometry column. If omitted, the method will automatically attempt to find a geometry column.
4645
+ * @param options.unit - The unit of `maxDistance`: `"m"` for metres or `"km"` for kilometres. Defaults to `"m"`.
4646
+ * @param options.onlyDuplicates - If `true`, moves only points whose complete original geometry occurs more than once in the selected geometry column. Every point in a duplicated group is moved. Defaults to `false`.
4647
+ * @returns The table, so methods can be chained.
4648
+ * @category Geospatial
4649
+ *
4650
+ * @example
4651
+ * ```ts
4652
+ * // Move every point by no more than 500 metres
4653
+ * await table.addGeoNoise(500).log();
4654
+ * ```
4655
+ *
4656
+ * @example
4657
+ * ```ts
4658
+ * // Separate duplicated points by up to 0.1 kilometres
4659
+ * await table.addGeoNoise(0.1, {
4660
+ * column: "geom",
4661
+ * unit: "km",
4662
+ * onlyDuplicates: true,
4663
+ * }).log();
4664
+ * ```
4665
+ */
4666
+ addGeoNoise(maxDistance, options = {}) {
4667
+ (0, addGeoNoise_js_1.default)(this, maxDistance, options);
4668
+ return this;
4669
+ }
4468
4670
  /**
4469
4671
  * Reduces the precision of geometries in a specified column to a given number of decimal places.
4470
4672
  *
@@ -0,0 +1,3 @@
1
+ import type SimpleTable from "../class/SimpleTable.js";
2
+ export default function addCharacterCount(simpleTable: SimpleTable, column: string, newColumn: string): void;
3
+ //# sourceMappingURL=addCharacterCount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addCharacterCount.d.ts","sourceRoot":"","sources":["../../src/methods/addCharacterCount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAKvD,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACvC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,QAclB"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = addCharacterCount;
7
+ const assertNewColumns_js_1 = __importDefault(require("../helpers/assertNewColumns.js"));
8
+ const queueOp_js_1 = __importDefault(require("../helpers/queueOp.js"));
9
+ const quoteIdentifier_js_1 = __importDefault(require("../helpers/quoteIdentifier.js"));
10
+ function addCharacterCount(simpleTable, column, newColumn) {
11
+ (0, queueOp_js_1.default)(simpleTable, {
12
+ kind: "fusable",
13
+ method: "addCharacterCount()",
14
+ parameters: { column, newColumn },
15
+ needsSchema: true,
16
+ buildSelect: (input, types) => {
17
+ (0, assertNewColumns_js_1.default)(types, [newColumn], "addCharacterCount()");
18
+ return `SELECT *, LENGTH(${(0, quoteIdentifier_js_1.default)(column)}) AS ${(0, quoteIdentifier_js_1.default)(newColumn)} FROM ${input}`;
19
+ },
20
+ });
21
+ }
@@ -0,0 +1,7 @@
1
+ import type SimpleTable from "../class/SimpleTable.js";
2
+ export default function addGeoNoise(simpleTable: SimpleTable, maxDistance: number, options?: {
3
+ column?: string;
4
+ unit?: "m" | "km";
5
+ onlyDuplicates?: boolean;
6
+ }): void;
7
+ //# sourceMappingURL=addGeoNoise.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addGeoNoise.d.ts","sourceRoot":"","sources":["../../src/methods/addGeoNoise.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAKvD,MAAM,CAAC,OAAO,UAAU,WAAW,CACjC,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;CACrB,QA2CP"}
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = addGeoNoise;
7
+ const findGeoColumnFromSchema_js_1 = __importDefault(require("../helpers/findGeoColumnFromSchema.js"));
8
+ const queueOp_js_1 = __importDefault(require("../helpers/queueOp.js"));
9
+ const quoteIdentifier_js_1 = __importDefault(require("../helpers/quoteIdentifier.js"));
10
+ // This is the mean Earth radius used by DuckDB's ST_Distance_Sphere().
11
+ const EARTH_RADIUS_METERS = 6_371_000;
12
+ function addGeoNoise(simpleTable, maxDistance, options = {}) {
13
+ // This validation doesn't need the database, so it stays at call time.
14
+ if (!Number.isFinite(maxDistance) || maxDistance < 0) {
15
+ throw new Error("addGeoNoise() maxDistance must be a finite number greater than or equal to 0.");
16
+ }
17
+ if (options.unit !== undefined && options.unit !== "m" && options.unit !== "km") {
18
+ throw new Error('addGeoNoise() options.unit must be "m" or "km".');
19
+ }
20
+ options = structuredClone(options);
21
+ const maxDistanceMeters = options.unit === "km"
22
+ ? maxDistance * 1_000
23
+ : maxDistance;
24
+ if (maxDistanceMeters > Math.PI * EARTH_RADIUS_METERS) {
25
+ throw new Error("addGeoNoise() maxDistance cannot exceed half the Earth's circumference.");
26
+ }
27
+ (0, queueOp_js_1.default)(simpleTable, {
28
+ kind: "fusable",
29
+ method: "addGeoNoise()",
30
+ parameters: { maxDistance, options },
31
+ needsSchema: true,
32
+ needsSpatial: true,
33
+ preservesSchema: true,
34
+ buildSelect: (input, schema) => {
35
+ const column = options.column ??
36
+ (0, findGeoColumnFromSchema_js_1.default)(schema, "addGeoNoise()", simpleTable.name);
37
+ return addGeoNoiseSelect(input, schema, column, maxDistanceMeters, options.onlyDuplicates === true);
38
+ },
39
+ });
40
+ }
41
+ function addGeoNoiseSelect(input, schema, column, maxDistanceMeters, onlyDuplicates) {
42
+ const type = schema[column];
43
+ if (type === undefined) {
44
+ throw new Error(`addGeoNoise() could not find column ${(0, quoteIdentifier_js_1.default)(column)}.`);
45
+ }
46
+ if (type !== "GEOMETRY('EPSG:4326')") {
47
+ throw new Error(`addGeoNoise() requires an EPSG:4326 geometry column, but ${(0, quoteIdentifier_js_1.default)(column)} has type ${type}. Reproject it to EPSG:4326 first.`);
48
+ }
49
+ const internalNames = makeInternalNames(schema, input);
50
+ const quotedColumn = (0, quoteIdentifier_js_1.default)(column);
51
+ const pointError = `error('addGeoNoise() requires POINT geometries in column ${column.replaceAll("'", "''")}.')`;
52
+ const coordinateGuard = (coordinate) => `CASE
53
+ WHEN ${quotedColumn} IS NULL OR ST_IsEmpty(${quotedColumn}) THEN NULL
54
+ WHEN CAST(ST_GeometryType(${quotedColumn}) AS VARCHAR) <> 'POINT'
55
+ THEN ${pointError}
56
+ ELSE radians(ST_${coordinate}(${quotedColumn}))
57
+ END`;
58
+ const duplicateCount = onlyDuplicates
59
+ ? `COUNT(*) OVER (PARTITION BY ${quotedColumn})`
60
+ : "2";
61
+ const maxAngularDistance = maxDistanceMeters / EARTH_RADIUS_METERS;
62
+ const latitudeExpression = `asin(
63
+ sin(${(0, quoteIdentifier_js_1.default)(internalNames.latitude)}) * cos(${(0, quoteIdentifier_js_1.default)(internalNames.angularDistance)}) +
64
+ cos(${(0, quoteIdentifier_js_1.default)(internalNames.latitude)}) * sin(${(0, quoteIdentifier_js_1.default)(internalNames.angularDistance)}) * cos(${(0, quoteIdentifier_js_1.default)(internalNames.bearing)})
65
+ )`;
66
+ const longitudeExpression = `${(0, quoteIdentifier_js_1.default)(internalNames.longitude)} + atan2(
67
+ sin(${(0, quoteIdentifier_js_1.default)(internalNames.bearing)}) * sin(${(0, quoteIdentifier_js_1.default)(internalNames.angularDistance)}) * cos(${(0, quoteIdentifier_js_1.default)(internalNames.latitude)}),
68
+ cos(${(0, quoteIdentifier_js_1.default)(internalNames.angularDistance)}) -
69
+ sin(${(0, quoteIdentifier_js_1.default)(internalNames.latitude)}) * sin(${(0, quoteIdentifier_js_1.default)(internalNames.destinationLatitude)})
70
+ )`;
71
+ const replacement = `CASE
72
+ WHEN ${quotedColumn} IS NULL OR ST_IsEmpty(${quotedColumn}) THEN ${quotedColumn}
73
+ WHEN ${(0, quoteIdentifier_js_1.default)(internalNames.duplicateCount)} <= 1 THEN ${quotedColumn}
74
+ ELSE ST_Point(
75
+ degrees(atan2(
76
+ sin(${(0, quoteIdentifier_js_1.default)(internalNames.destinationLongitude)}),
77
+ cos(${(0, quoteIdentifier_js_1.default)(internalNames.destinationLongitude)})
78
+ )),
79
+ degrees(${(0, quoteIdentifier_js_1.default)(internalNames.destinationLatitude)})
80
+ )::GEOMETRY('EPSG:4326')
81
+ END AS ${quotedColumn}`;
82
+ const outputColumns = Object.keys(schema).map((outputColumn) => outputColumn === column ? replacement : (0, quoteIdentifier_js_1.default)(outputColumn)).join(", ");
83
+ return `WITH ${(0, quoteIdentifier_js_1.default)(internalNames.randomValues)} AS MATERIALIZED (
84
+ SELECT *,
85
+ random() AS ${(0, quoteIdentifier_js_1.default)(internalNames.distanceRandom)},
86
+ random() AS ${(0, quoteIdentifier_js_1.default)(internalNames.bearingRandom)},
87
+ ${duplicateCount} AS ${(0, quoteIdentifier_js_1.default)(internalNames.duplicateCount)}
88
+ FROM ${input}
89
+ ),
90
+ ${(0, quoteIdentifier_js_1.default)(internalNames.start)} AS (
91
+ SELECT *,
92
+ ${coordinateGuard("Y")} AS ${(0, quoteIdentifier_js_1.default)(internalNames.latitude)},
93
+ ${coordinateGuard("X")} AS ${(0, quoteIdentifier_js_1.default)(internalNames.longitude)},
94
+ 2 * pi() * ${(0, quoteIdentifier_js_1.default)(internalNames.bearingRandom)} AS ${(0, quoteIdentifier_js_1.default)(internalNames.bearing)},
95
+ 2 * asin(
96
+ sqrt(${(0, quoteIdentifier_js_1.default)(internalNames.distanceRandom)}) *
97
+ sin(${maxAngularDistance} / 2)
98
+ ) AS ${(0, quoteIdentifier_js_1.default)(internalNames.angularDistance)}
99
+ FROM ${(0, quoteIdentifier_js_1.default)(internalNames.randomValues)}
100
+ ),
101
+ ${(0, quoteIdentifier_js_1.default)(internalNames.destinationLatitudeCte)} AS (
102
+ SELECT *, ${latitudeExpression} AS ${(0, quoteIdentifier_js_1.default)(internalNames.destinationLatitude)}
103
+ FROM ${(0, quoteIdentifier_js_1.default)(internalNames.start)}
104
+ ),
105
+ ${(0, quoteIdentifier_js_1.default)(internalNames.destination)} AS (
106
+ SELECT *, ${longitudeExpression} AS ${(0, quoteIdentifier_js_1.default)(internalNames.destinationLongitude)}
107
+ FROM ${(0, quoteIdentifier_js_1.default)(internalNames.destinationLatitudeCte)}
108
+ )
109
+ SELECT ${outputColumns}
110
+ FROM ${(0, quoteIdentifier_js_1.default)(internalNames.destination)}`;
111
+ }
112
+ function makeInternalNames(schema, input) {
113
+ const used = new Set(Object.keys(schema));
114
+ if (input.startsWith('"') && input.endsWith('"')) {
115
+ used.add(input.slice(1, -1).replaceAll('""', '"'));
116
+ }
117
+ const next = (base) => {
118
+ let name = base;
119
+ let suffix = 2;
120
+ while (used.has(name)) {
121
+ name = `${base}_${suffix++}`;
122
+ }
123
+ used.add(name);
124
+ return name;
125
+ };
126
+ return {
127
+ randomValues: next("__sda_add_geo_noise_random_values"),
128
+ start: next("__sda_add_geo_noise_start"),
129
+ destinationLatitudeCte: next("__sda_add_geo_noise_latitude_result"),
130
+ destination: next("__sda_add_geo_noise_result"),
131
+ distanceRandom: next("__sda_add_geo_noise_distance_random"),
132
+ bearingRandom: next("__sda_add_geo_noise_bearing_random"),
133
+ duplicateCount: next("__sda_add_geo_noise_duplicate_count"),
134
+ latitude: next("__sda_add_geo_noise_latitude"),
135
+ longitude: next("__sda_add_geo_noise_longitude"),
136
+ bearing: next("__sda_add_geo_noise_bearing"),
137
+ angularDistance: next("__sda_add_geo_noise_angular_distance"),
138
+ destinationLatitude: next("__sda_add_geo_noise_destination_latitude"),
139
+ destinationLongitude: next("__sda_add_geo_noise_destination_longitude"),
140
+ };
141
+ }
@@ -0,0 +1,5 @@
1
+ import type SimpleTable from "../class/SimpleTable.js";
2
+ export default function addNoise(simpleTable: SimpleTable, columns: string | string[], max: number, options?: {
3
+ onlyDuplicates?: boolean;
4
+ }): void;
5
+ //# sourceMappingURL=addNoise.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addNoise.d.ts","sourceRoot":"","sources":["../../src/methods/addNoise.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAC9B,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,EAC1B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,QAwC3C"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = addNoise;
7
+ const assertColumnsExist_js_1 = __importDefault(require("../helpers/assertColumnsExist.js"));
8
+ const keepNumericalColumns_js_1 = __importDefault(require("../helpers/keepNumericalColumns.js"));
9
+ const queueOp_js_1 = __importDefault(require("../helpers/queueOp.js"));
10
+ const quoteIdentifier_js_1 = __importDefault(require("../helpers/quoteIdentifier.js"));
11
+ const stringToArray_js_1 = __importDefault(require("../helpers/stringToArray.js"));
12
+ function addNoise(simpleTable, columns, max, options = {}) {
13
+ // This validation doesn't need the database, so it stays at call time.
14
+ if (!Number.isFinite(max) || max < 0) {
15
+ throw new Error("addNoise() max must be a finite number greater than or equal to 0.");
16
+ }
17
+ const cols = (0, stringToArray_js_1.default)(columns);
18
+ if (cols.length === 0) {
19
+ throw new Error("addNoise() requires at least one column.");
20
+ }
21
+ const duplicateColumns = cols.filter((column, index) => cols.indexOf(column) !== index);
22
+ if (duplicateColumns.length > 0) {
23
+ const duplicates = [...new Set(duplicateColumns)];
24
+ throw new Error(`addNoise() received duplicate column${duplicates.length === 1 ? "" : "s"} ${duplicates.map(quoteIdentifier_js_1.default).join(", ")}. Include each column only once.`);
25
+ }
26
+ options = structuredClone(options);
27
+ (0, queueOp_js_1.default)(simpleTable, {
28
+ kind: "fusable",
29
+ method: "addNoise()",
30
+ parameters: { columns: cols, max, options },
31
+ needsSchema: true,
32
+ outputSchema: (schema) => ({
33
+ ...schema,
34
+ ...Object.fromEntries(cols.map((column) => [column, "DOUBLE"])),
35
+ }),
36
+ buildSelect: (input, schema) => addNoiseSelect(input, schema, cols, max, options.onlyDuplicates === true),
37
+ });
38
+ }
39
+ function addNoiseSelect(input, schema, columns, max, onlyDuplicates) {
40
+ (0, assertColumnsExist_js_1.default)(schema, columns, "addNoise()");
41
+ const numericalColumns = new Set((0, keepNumericalColumns_js_1.default)(schema));
42
+ const nonNumericalColumns = columns.filter((column) => !numericalColumns.has(column));
43
+ if (nonNumericalColumns.length > 0) {
44
+ throw new Error(`addNoise() requires numeric columns, but ${nonNumericalColumns.map((column) => `${(0, quoteIdentifier_js_1.default)(column)} has type ${schema[column]}`).join(", ")}. Convert ${nonNumericalColumns.length === 1 ? "it" : "them"} to a numeric type first.`);
45
+ }
46
+ const replacements = columns.map((column) => {
47
+ const quotedColumn = (0, quoteIdentifier_js_1.default)(column);
48
+ const duplicateCondition = onlyDuplicates
49
+ ? `COUNT(*) OVER (PARTITION BY ${quotedColumn}) > 1`
50
+ : "TRUE";
51
+ return `CASE
52
+ WHEN ${quotedColumn} IS NULL THEN NULL
53
+ WHEN ${duplicateCondition}
54
+ THEN CAST(${quotedColumn} AS DOUBLE) + (random() * 2 - 1) * ${max}
55
+ ELSE CAST(${quotedColumn} AS DOUBLE)
56
+ END AS ${quotedColumn}`;
57
+ }).join(", ");
58
+ return `SELECT * REPLACE (${replacements}) FROM ${input}`;
59
+ }
@@ -0,0 +1,3 @@
1
+ import type SimpleTable from "../class/SimpleTable.js";
2
+ export default function addWordCount(simpleTable: SimpleTable, column: string, newColumn: string): void;
3
+ //# sourceMappingURL=addWordCount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addWordCount.d.ts","sourceRoot":"","sources":["../../src/methods/addWordCount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAKvD,MAAM,CAAC,OAAO,UAAU,YAAY,CAClC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,QAclB"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = addWordCount;
7
+ const assertNewColumns_js_1 = __importDefault(require("../helpers/assertNewColumns.js"));
8
+ const queueOp_js_1 = __importDefault(require("../helpers/queueOp.js"));
9
+ const quoteIdentifier_js_1 = __importDefault(require("../helpers/quoteIdentifier.js"));
10
+ function addWordCount(simpleTable, column, newColumn) {
11
+ (0, queueOp_js_1.default)(simpleTable, {
12
+ kind: "fusable",
13
+ method: "addWordCount()",
14
+ parameters: { column, newColumn },
15
+ needsSchema: true,
16
+ buildSelect: (input, types) => {
17
+ (0, assertNewColumns_js_1.default)(types, [newColumn], "addWordCount()");
18
+ return `SELECT *, ARRAY_LENGTH(REGEXP_EXTRACT_ALL(${(0, quoteIdentifier_js_1.default)(column)}, '[^[:space:]]+')) AS ${(0, quoteIdentifier_js_1.default)(newColumn)} FROM ${input}`;
19
+ },
20
+ });
21
+ }
@@ -1,6 +1,7 @@
1
1
  import type SimpleTable from "../class/SimpleTable.js";
2
2
  export default function columnProportions(simpleTable: SimpleTable, column: string, newColumn: string, options?: {
3
3
  by?: string | string[];
4
+ base?: number;
4
5
  decimals?: number;
5
6
  }): void;
6
7
  //# sourceMappingURL=columnProportions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"columnProportions.d.ts","sourceRoot":"","sources":["../../src/methods/columnProportions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACvC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACd,QA4BP"}
1
+ {"version":3,"file":"columnProportions.d.ts","sourceRoot":"","sources":["../../src/methods/columnProportions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACvC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACd,QAqCP"}