@nshiab/simple-data-analysis-core 0.0.20 → 0.0.22

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 (53) hide show
  1. package/esm/class/SimpleTable.d.ts +76 -21
  2. package/esm/class/SimpleTable.d.ts.map +1 -1
  3. package/esm/class/SimpleTable.js +103 -98
  4. package/esm/helpers/findGeoColumn.js +1 -1
  5. package/esm/helpers/getProjection.d.ts.map +1 -1
  6. package/esm/helpers/getProjection.js +3 -0
  7. package/esm/helpers/hasGeometryColumn.d.ts +20 -0
  8. package/esm/helpers/hasGeometryColumn.d.ts.map +1 -0
  9. package/esm/helpers/hasGeometryColumn.js +21 -0
  10. package/esm/helpers/writeGeoData.d.ts +9 -0
  11. package/esm/helpers/writeGeoData.d.ts.map +1 -0
  12. package/esm/helpers/writeGeoData.js +110 -0
  13. package/esm/methods/fuzzyClean.d.ts +2 -2
  14. package/esm/methods/fuzzyClean.d.ts.map +1 -1
  15. package/esm/methods/fuzzyClean.js +25 -6
  16. package/esm/methods/fuzzyJoin.d.ts +2 -2
  17. package/esm/methods/fuzzyJoin.d.ts.map +1 -1
  18. package/esm/methods/fuzzyJoin.js +3 -3
  19. package/esm/methods/fuzzyJoinQuery.d.ts +1 -1
  20. package/esm/methods/fuzzyJoinQuery.d.ts.map +1 -1
  21. package/esm/methods/fuzzyJoinQuery.js +13 -3
  22. package/esm/methods/loadSample.d.ts +9 -0
  23. package/esm/methods/loadSample.d.ts.map +1 -0
  24. package/esm/methods/loadSample.js +44 -0
  25. package/esm/methods/writeGeoDataQuery.d.ts.map +1 -1
  26. package/esm/methods/writeGeoDataQuery.js +4 -1
  27. package/package.json +1 -1
  28. package/script/class/SimpleTable.d.ts +76 -21
  29. package/script/class/SimpleTable.d.ts.map +1 -1
  30. package/script/class/SimpleTable.js +103 -98
  31. package/script/helpers/findGeoColumn.js +1 -1
  32. package/script/helpers/getProjection.d.ts.map +1 -1
  33. package/script/helpers/getProjection.js +3 -0
  34. package/script/helpers/hasGeometryColumn.d.ts +20 -0
  35. package/script/helpers/hasGeometryColumn.d.ts.map +1 -0
  36. package/script/helpers/hasGeometryColumn.js +24 -0
  37. package/script/helpers/writeGeoData.d.ts +9 -0
  38. package/script/helpers/writeGeoData.d.ts.map +1 -0
  39. package/script/helpers/writeGeoData.js +116 -0
  40. package/script/methods/fuzzyClean.d.ts +2 -2
  41. package/script/methods/fuzzyClean.d.ts.map +1 -1
  42. package/script/methods/fuzzyClean.js +25 -6
  43. package/script/methods/fuzzyJoin.d.ts +2 -2
  44. package/script/methods/fuzzyJoin.d.ts.map +1 -1
  45. package/script/methods/fuzzyJoin.js +3 -3
  46. package/script/methods/fuzzyJoinQuery.d.ts +1 -1
  47. package/script/methods/fuzzyJoinQuery.d.ts.map +1 -1
  48. package/script/methods/fuzzyJoinQuery.js +13 -3
  49. package/script/methods/loadSample.d.ts +9 -0
  50. package/script/methods/loadSample.d.ts.map +1 -0
  51. package/script/methods/loadSample.js +47 -0
  52. package/script/methods/writeGeoDataQuery.d.ts.map +1 -1
  53. package/script/methods/writeGeoDataQuery.js +4 -1
@@ -6,10 +6,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.default = fuzzyClean;
7
7
  const mergeOptions_js_1 = __importDefault(require("../helpers/mergeOptions.js"));
8
8
  const queryDB_js_1 = __importDefault(require("../helpers/queryDB.js"));
9
- async function fuzzyClean(table, column, newColumn, options = {}) {
9
+ async function fuzzyClean(table, column, newColumn, threshold, options = {}) {
10
10
  const method = options.method ?? "ratio";
11
- const threshold = options.threshold ?? 80;
12
11
  const keep = options.keep ?? "mostCommon";
12
+ let onClause = `rapidfuzz_${method}(a.value, b.value) >= ${threshold}`;
13
+ if (method === "ratio") {
14
+ const maxDiffMultiplier = (200 - 2 * threshold) / (200 - threshold);
15
+ onClause +=
16
+ ` AND ABS(LENGTH(a.value) - LENGTH(b.value)) <= ${maxDiffMultiplier} * GREATEST(LENGTH(a.value), LENGTH(b.value))`;
17
+ }
18
+ if (options.preFilterPrefixLen !== undefined) {
19
+ onClause +=
20
+ ` AND SUBSTR(a.value, 1, ${options.preFilterPrefixLen}) = SUBSTR(b.value, 1, ${options.preFilterPrefixLen})`;
21
+ }
13
22
  // Single round trip: compute fuzzy pairs and embed counts for both sides.
14
23
  // Only values that appear in at least one pair above the threshold can be
15
24
  // normalized — singletons need no processing at all.
@@ -28,16 +37,26 @@ async function fuzzyClean(table, column, newColumn, options = {}) {
28
37
  rapidfuzz_${method}(a.value, b.value) AS score
29
38
  FROM uniques a
30
39
  JOIN uniques b
31
- ON rapidfuzz_${method}(a.value, b.value) >= ${threshold}
40
+ ON ${onClause}
32
41
  AND a.value < b.value`, (0, mergeOptions_js_1.default)(table, {
33
42
  table: table.name,
34
43
  method: "fuzzyClean()",
35
- parameters: { column, newColumn, options },
44
+ parameters: { column, newColumn, threshold, options },
36
45
  returnDataFrom: "query",
37
46
  }));
38
47
  const pairs = pairsData ?? [];
39
- if (pairs.length === 0)
40
- return; // Nothing to normalize.
48
+ if (pairs.length === 0) {
49
+ if (newColumn !== column) {
50
+ await (0, queryDB_js_1.default)(table, `ALTER TABLE "${table.name}" ADD "${newColumn}" VARCHAR;
51
+ UPDATE "${table.name}"
52
+ SET "${newColumn}" = "${column}";`, (0, mergeOptions_js_1.default)(table, {
53
+ table: table.name,
54
+ method: "fuzzyClean()",
55
+ parameters: { column, newColumn, threshold, options },
56
+ }));
57
+ }
58
+ return;
59
+ }
41
60
  // Build count map from the pairs — no separate query needed.
42
61
  const countMap = new Map();
43
62
  for (const { left_value, left_cnt, right_value, right_cnt } of pairs) {
@@ -1,8 +1,8 @@
1
1
  import type SimpleTable from "../class/SimpleTable.js";
2
- export default function fuzzyJoin(leftTable: SimpleTable, rightTable: SimpleTable, leftColumn: string, rightColumn: string, options?: {
2
+ export default function fuzzyJoin(leftTable: SimpleTable, rightTable: SimpleTable, leftColumn: string, rightColumn: string, threshold: number, options?: {
3
3
  method?: "ratio" | "partial_ratio" | "token_sort_ratio" | "token_set_ratio";
4
- threshold?: number;
5
4
  similarityColumn?: string;
6
5
  outputTable?: string | boolean;
6
+ preFilterPrefixLen?: number;
7
7
  }): Promise<SimpleTable>;
8
8
  //# sourceMappingURL=fuzzyJoin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fuzzyJoin.d.ts","sourceRoot":"","sources":["../../src/methods/fuzzyJoin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,wBAA8B,SAAS,CACrC,SAAS,EAAE,WAAW,EACtB,UAAU,EAAE,WAAW,EACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IACP,MAAM,CAAC,EACH,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,iBAAiB,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B,wBAuFP"}
1
+ {"version":3,"file":"fuzzyJoin.d.ts","sourceRoot":"","sources":["../../src/methods/fuzzyJoin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,wBAA8B,SAAS,CACrC,SAAS,EAAE,WAAW,EACtB,UAAU,EAAE,WAAW,EACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,MAAM,CAAC,EACH,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,iBAAiB,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACxB,wBAwFP"}
@@ -8,7 +8,7 @@ const getIdenticalColumns_js_1 = __importDefault(require("../helpers/getIdentica
8
8
  const mergeOptions_js_1 = __importDefault(require("../helpers/mergeOptions.js"));
9
9
  const queryDB_js_1 = __importDefault(require("../helpers/queryDB.js"));
10
10
  const fuzzyJoinQuery_js_1 = __importDefault(require("./fuzzyJoinQuery.js"));
11
- async function fuzzyJoin(leftTable, rightTable, leftColumn, rightColumn, options = {}) {
11
+ async function fuzzyJoin(leftTable, rightTable, leftColumn, rightColumn, threshold, options = {}) {
12
12
  if (leftColumn === rightColumn) {
13
13
  throw new Error(`The leftColumn and rightColumn have the same name "${leftColumn}". Rename one of them before doing the fuzzy join.`);
14
14
  }
@@ -27,13 +27,12 @@ async function fuzzyJoin(leftTable, rightTable, leftColumn, rightColumn, options
27
27
  }
28
28
  }
29
29
  const method = options.method ?? "ratio";
30
- const threshold = options.threshold ?? 80;
31
30
  const similarityColumn = options.similarityColumn;
32
31
  const outputTableName = typeof options.outputTable === "string"
33
32
  ? options.outputTable
34
33
  : leftTable.name;
35
34
  const sql = `INSTALL rapidfuzz FROM community; LOAD rapidfuzz;\n` +
36
- (0, fuzzyJoinQuery_js_1.default)(leftTable.name, leftColumn, rightTable.name, rightColumn, method, threshold, outputTableName, similarityColumn);
35
+ (0, fuzzyJoinQuery_js_1.default)(leftTable.name, leftColumn, rightTable.name, rightColumn, method, threshold, outputTableName, similarityColumn, options.preFilterPrefixLen);
37
36
  await (0, queryDB_js_1.default)(leftTable, sql, (0, mergeOptions_js_1.default)(leftTable, {
38
37
  table: outputTableName,
39
38
  method: "fuzzyJoin()",
@@ -41,6 +40,7 @@ async function fuzzyJoin(leftTable, rightTable, leftColumn, rightColumn, options
41
40
  leftColumn,
42
41
  rightColumn,
43
42
  rightTable: rightTable.name,
43
+ threshold,
44
44
  options,
45
45
  },
46
46
  }));
@@ -1,2 +1,2 @@
1
- export default function fuzzyJoinQuery(leftTable: string, leftColumn: string, rightTable: string, rightColumn: string, method: "ratio" | "partial_ratio" | "token_sort_ratio" | "token_set_ratio", threshold: number, outputTable: string, similarityColumn: string | undefined): string;
1
+ export default function fuzzyJoinQuery(leftTable: string, leftColumn: string, rightTable: string, rightColumn: string, method: "ratio" | "partial_ratio" | "token_sort_ratio" | "token_set_ratio", threshold: number, outputTable: string, similarityColumn: string | undefined, preFilterPrefixLen?: number): string;
2
2
  //# sourceMappingURL=fuzzyJoinQuery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fuzzyJoinQuery.d.ts","sourceRoot":"","sources":["../../src/methods/fuzzyJoinQuery.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,MAAM,EACF,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,iBAAiB,EACrB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,GAAG,SAAS,UAsBrC"}
1
+ {"version":3,"file":"fuzzyJoinQuery.d.ts","sourceRoot":"","sources":["../../src/methods/fuzzyJoinQuery.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,MAAM,EACF,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,iBAAiB,EACrB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,kBAAkB,CAAC,EAAE,MAAM,UAkC5B"}
@@ -1,14 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = fuzzyJoinQuery;
4
- function fuzzyJoinQuery(leftTable, leftColumn, rightTable, rightColumn, method, threshold, outputTable, similarityColumn) {
4
+ function fuzzyJoinQuery(leftTable, leftColumn, rightTable, rightColumn, method, threshold, outputTable, similarityColumn, preFilterPrefixLen) {
5
5
  const fn = `ROUND(rapidfuzz_${method}("${leftTable}"."${leftColumn}", "${rightTable}"."${rightColumn}"), 2)`;
6
+ let onClause = `${fn} >= ${threshold}`;
7
+ if (method === "ratio") {
8
+ const maxDiffMultiplier = (200 - 2 * threshold) / (200 - threshold);
9
+ onClause +=
10
+ ` AND ABS(LENGTH("${leftTable}"."${leftColumn}") - LENGTH("${rightTable}"."${rightColumn}")) <= ${maxDiffMultiplier} * GREATEST(LENGTH("${leftTable}"."${leftColumn}"), LENGTH("${rightTable}"."${rightColumn}"))`;
11
+ }
12
+ if (preFilterPrefixLen !== undefined) {
13
+ onClause +=
14
+ ` AND SUBSTR("${leftTable}"."${leftColumn}", 1, ${preFilterPrefixLen}) = SUBSTR("${rightTable}"."${rightColumn}", 1, ${preFilterPrefixLen})`;
15
+ }
6
16
  if (similarityColumn) {
7
17
  return `CREATE OR REPLACE TABLE "${outputTable}" AS
8
18
  SELECT * EXCLUDE ("_sda_score"), "_sda_score" AS "${similarityColumn}"
9
19
  FROM (
10
20
  SELECT "${leftTable}".*, "${rightTable}".*, ${fn} AS "_sda_score"
11
- FROM "${leftTable}" LEFT JOIN "${rightTable}" ON ${fn} >= ${threshold}
21
+ FROM "${leftTable}" LEFT JOIN "${rightTable}" ON ${onClause}
12
22
  ) _sda
13
23
  ORDER BY "${leftColumn}", "_sda_score" DESC;\n`;
14
24
  }
@@ -16,7 +26,7 @@ ORDER BY "${leftColumn}", "_sda_score" DESC;\n`;
16
26
  SELECT *
17
27
  FROM (
18
28
  SELECT "${leftTable}".*, "${rightTable}".*
19
- FROM "${leftTable}" LEFT JOIN "${rightTable}" ON ${fn} >= ${threshold}
29
+ FROM "${leftTable}" LEFT JOIN "${rightTable}" ON ${onClause}
20
30
  ) _sda
21
31
  ORDER BY "${leftColumn}", "${rightColumn}";\n`;
22
32
  }
@@ -0,0 +1,9 @@
1
+ import type SimpleTable from "../class/SimpleTable.js";
2
+ /**
3
+ * Fetches sample data from the simple-data-analysis-core GitHub repository.
4
+ *
5
+ * @param table - The SimpleTable instance to load the data into.
6
+ * @param sample - The name of the sample to load.
7
+ */
8
+ export default function loadSample(table: SimpleTable, sample: "fires" | "recipes" | "temperatures" | "temperaturesCities" | "canada" | "firesGeo"): Promise<SimpleTable>;
9
+ //# sourceMappingURL=loadSample.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadSample.d.ts","sourceRoot":"","sources":["../../src/methods/loadSample.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD;;;;;GAKG;AACH,wBAA8B,UAAU,CACtC,KAAK,EAAE,WAAW,EAClB,MAAM,EACF,OAAO,GACP,SAAS,GACT,cAAc,GACd,oBAAoB,GACpB,QAAQ,GACR,UAAU,GACb,OAAO,CAAC,WAAW,CAAC,CA6CtB"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = loadSample;
4
+ /**
5
+ * Fetches sample data from the simple-data-analysis-core GitHub repository.
6
+ *
7
+ * @param table - The SimpleTable instance to load the data into.
8
+ * @param sample - The name of the sample to load.
9
+ */
10
+ async function loadSample(table, sample) {
11
+ const samples = {
12
+ fires: {
13
+ url: "https://raw.githubusercontent.com/nshiab/simple-data-analysis-core/refs/heads/main/test/geodata/files/firesCanada2023.csv",
14
+ geo: false,
15
+ },
16
+ recipes: {
17
+ url: "https://github.com/nshiab/simple-data-analysis-core/raw/refs/heads/main/test/data/files/recipes.parquet",
18
+ geo: false,
19
+ },
20
+ temperatures: {
21
+ url: "https://raw.githubusercontent.com/nshiab/simple-data-analysis-core/refs/heads/main/test/data/files/dailyTemperatures.csv",
22
+ geo: false,
23
+ },
24
+ temperaturesCities: {
25
+ url: "https://raw.githubusercontent.com/nshiab/simple-data-analysis-core/refs/heads/main/test/data/files/cities.csv",
26
+ geo: false,
27
+ },
28
+ canada: {
29
+ url: "https://raw.githubusercontent.com/nshiab/simple-data-analysis-core/refs/heads/main/test/geodata/files/CanadianProvincesAndTerritories.json",
30
+ geo: true,
31
+ },
32
+ firesGeo: {
33
+ url: "https://raw.githubusercontent.com/nshiab/simple-data-analysis-core/refs/heads/main/test/geodata/files/firesCanada2023.geojson",
34
+ geo: true,
35
+ },
36
+ };
37
+ const sampleToLoad = samples[sample];
38
+ if (!sampleToLoad) {
39
+ throw new Error(`Unknown sample: ${sample}`);
40
+ }
41
+ if (sampleToLoad.geo) {
42
+ return await table.loadGeoData(sampleToLoad.url);
43
+ }
44
+ else {
45
+ return await table.loadData(sampleToLoad.url);
46
+ }
47
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"writeGeoDataQuery.d.ts","sourceRoot":"","sources":["../../src/methods/writeGeoDataQuery.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,UAqBrC"}
1
+ {"version":3,"file":"writeGeoDataQuery.d.ts","sourceRoot":"","sources":["../../src/methods/writeGeoDataQuery.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,UAyBrC"}
@@ -12,10 +12,13 @@ function writeGeoDataQuery(table, file, fileExtension, options = {}) {
12
12
  layerOptions.push(`COORDINATE_PRECISION=${options.precision}`);
13
13
  }
14
14
  layerOptions.push(`RFC7946=YES`);
15
- return `COPY "${table}" to '${(0, cleanPath_js_1.default)(file)}' WITH (FORMAT GDAL, DRIVER 'GeoJSON'${layerOptions.length > 0
15
+ return `INSTALL spatial; LOAD spatial; COPY "${table}" to '${(0, cleanPath_js_1.default)(file)}' WITH (FORMAT GDAL, DRIVER 'GeoJSON'${layerOptions.length > 0
16
16
  ? `, LAYER_CREATION_OPTIONS ('WRITE_NAME=NO', ${layerOptions.map((d) => `'${d}'`).join(", ")})`
17
17
  : ""})`;
18
18
  }
19
+ else if (fileExtension === "shp") {
20
+ return `INSTALL spatial; LOAD spatial; COPY "${table}" TO '${(0, cleanPath_js_1.default)(file)}' WITH (FORMAT GDAL, DRIVER 'ESRI Shapefile', LAYER_CREATION_OPTIONS 'ENCODING=UTF-8')`;
21
+ }
19
22
  else {
20
23
  throw new Error(`Unknown extension ${fileExtension}`);
21
24
  }