splink 5.0.0.dev3__tar.gz → 5.0.0.dev4__tar.gz
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.
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/PKG-INFO +5 -5
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/README.md +3 -3
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/pyproject.toml +8 -6
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/__init__.py +1 -1
- splink-5.0.0.dev4/splink/blocking_analysis.py +11 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/accuracy.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/blocking.py +45 -8
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/blocking_analysis.py +269 -263
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/cache_dict_with_logging.py +2 -2
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/charts.py +31 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/cluster_studio.py +19 -15
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/clustering.py +2 -2
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/column_expression.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/comparison_level.py +2 -3
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/comparison_level_library.py +2 -2
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/completeness.py +2 -2
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/connected_components.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/database_api.py +63 -19
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/datasets/splink_datasets.py +11 -10
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/dialects.py +65 -76
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/duckdb/database_api.py +5 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/duckdb/dataframe.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/edge_metrics.py +16 -5
- splink-5.0.0.dev4/splink/internals/em_sampling.py +236 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/em_training_session.py +55 -3
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/estimate_u.py +13 -18
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/blocking_rule_generated_comparisons.json +35 -28
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/match_weights_interactive_history.json +9 -3
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/precision_recall.json +4 -2
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/roc.json +4 -2
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/labelling_tool/slt.js +2 -2
- splink-5.0.0.dev4/splink/internals/files/spark_jars/scala-udf-similarity-0.2.1_spark4.x.jar +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/labelling_tool.py +26 -9
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/linker.py +23 -190
- splink-5.0.0.dev4/splink/internals/linker_components/blocking_analysis.py +164 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/linker_components/evaluation.py +3 -3
- splink-5.0.0.dev4/splink/internals/linker_components/inference.py +1550 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/linker_components/misc.py +3 -25
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/linker_components/table_management.py +65 -87
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/linker_components/training.py +36 -9
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/linker_components/visualisations.py +7 -7
- splink-5.0.0.dev4/splink/internals/logging_messages.py +6 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/lower_id_on_lhs.py +6 -11
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/match_weights_histogram.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/misc.py +16 -5
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/one_to_one_clustering.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/parse_sql.py +4 -4
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/pipeline.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/postgres/dataframe.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/profile_data.py +5 -5
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/settings.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/settings_validation/settings_column_cleaner.py +3 -3
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/settings_validation/settings_validation_log_strings.py +4 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/similarity_analysis.py +10 -10
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/spark/database_api.py +5 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/spark/dataframe.py +5 -5
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/spark/jar_location.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/splink_dataframe.py +38 -10
- splink-5.0.0.dev4/splink/internals/splink_logging.py +56 -0
- splink-5.0.0.dev4/splink/internals/splinkdataframe_utils.py +99 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/sqlite/dataframe.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/term_frequencies.py +4 -5
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/testing.py +2 -2
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/unlinkables.py +1 -1
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/vertically_concatenate.py +3 -3
- splink-5.0.0.dev4/splink/logging.py +9 -0
- splink-5.0.0.dev3/splink/blocking_analysis.py +0 -13
- splink-5.0.0.dev3/splink/internals/cost_of_blocking_rules.py +0 -116
- splink-5.0.0.dev3/splink/internals/files/settings_jsonschema.json +0 -317
- splink-5.0.0.dev3/splink/internals/find_brs_with_comparison_counts_below_threshold.py +0 -278
- splink-5.0.0.dev3/splink/internals/linker_components/inference.py +0 -971
- splink-5.0.0.dev3/splink/internals/logging_messages.py +0 -10
- splink-5.0.0.dev3/splink/internals/optimise_cost_of_brs.py +0 -219
- splink-5.0.0.dev3/splink/internals/splinkdataframe_utils.py +0 -53
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/.gitignore +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/LICENSE +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/backends/duckdb.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/backends/postgres.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/backends/spark.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/backends/sqlite.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/blocking_rule_library.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/clustering.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/comparison_level_library.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/comparison_library.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/datasets.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/exploratory.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/block_from_labels.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/blocking_rule_creator.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/blocking_rule_creator_utils.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/blocking_rule_library.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/chunking.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/comparison.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/comparison_creator.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/comparison_level_composition.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/comparison_level_creator.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/comparison_level_sql.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/comparison_library.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/comparison_vector_distribution.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/comparison_vector_values.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/constants.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/databricks/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/databricks/enable_splink.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/datasets/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/datasets/metadata.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/datasets/utils.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/duckdb/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/duckdb/database_api_with_profiling.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/duckdb/duckdb_helpers/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/duckdb/duckdb_helpers/duckdb_helpers.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/exceptions.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/expectation_maximisation.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/DEPENDENCY_LICENSES.txt +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/accuracy_chart.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/comparator_score_chart.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/comparator_score_threshold_chart.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/completeness.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/m_u_parameters_interactive_history.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/match_weight_histogram.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/match_weights_waterfall.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/parameter_estimate_comparisons.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/phonetic_match_chart.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/probability_two_random_records_match_iteration.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/profile_data.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/tf_adjustment_chart.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/threshold_selection_tool.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/chart_defs/unlinkables_chart_def.json +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/external_js/d3@7.8.5 +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/external_js/stdlib.js@5.8.3 +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/external_js/vega-embed@6.20.2 +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/external_js/vega-lite@5.2.0 +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/external_js/vega@5.31.0 +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/labelling_tool/template.j2 +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/spark_jars/scala-udf-similarity-0.1.2_spark3.x.jar +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/spark_jars/scala-udf-similarity-0.2.0_spark4.x.jar +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/splink_cluster_studio/cluster_template.j2 +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/splink_cluster_studio/custom.css +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/splink_comparison_viewer/custom.css +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/splink_comparison_viewer/template.j2 +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/splink_vis_utils/splink_vis_utils.js +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/files/templates/single_chart_template.html +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/find_matches_to_new_records.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/graph_metrics.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/input_column.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/linker_components/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/linker_components/clustering.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/m_from_labels.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/m_training.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/m_u_records_to_parameters.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/postgres/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/postgres/database_api.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/predict.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/realtime.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/settings_creator.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/settings_validation/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/settings_validation/log_invalid_columns.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/settings_validation/valid_types.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/spark/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/spark/database_api_with_profiling.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/spark/spark_helpers/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/spark/spark_helpers/custom_spark_dialect.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/spark/spark_helpers/version.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/splink_comparison_viewer.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/sql_transform.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/sqlite/__init__.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/sqlite/database_api.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/unique_id_concat.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/internals/waterfall_chart.py +0 -0
- {splink-5.0.0.dev3 → splink-5.0.0.dev4}/splink/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: splink
|
|
3
|
-
Version: 5.0.0.
|
|
3
|
+
Version: 5.0.0.dev4
|
|
4
4
|
Summary: Fast probabilistic data linkage at scale
|
|
5
5
|
Project-URL: Homepage, https://github.com/moj-analytical-services/splink
|
|
6
6
|
Project-URL: Repository, https://github.com/moj-analytical-services/splink
|
|
@@ -8,7 +8,7 @@ Author: Sam Lindsay, Theodore Manassis, Tom Hepworth, Andy Bond, Ross Kennedy
|
|
|
8
8
|
Author-email: Robin Linacre <robinlinacre@hotmail.com>
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
License-File: LICENSE
|
|
11
|
-
Requires-Python: <4.0.0,>=3.
|
|
11
|
+
Requires-Python: <4.0.0,>=3.10.0
|
|
12
12
|
Requires-Dist: altair>=5.0.1
|
|
13
13
|
Requires-Dist: duckdb>=0.9.2
|
|
14
14
|
Requires-Dist: igraph>=0.11.2
|
|
@@ -60,15 +60,15 @@ Splink's linkage algorithm is based on Fellegi-Sunter's model of record linkage,
|
|
|
60
60
|
|
|
61
61
|
Consider the following records that lack a unique person identifier:
|
|
62
62
|
|
|
63
|
-

|
|
64
64
|
|
|
65
65
|
Splink predicts which rows link together:
|
|
66
66
|
|
|
67
|
-

|
|
68
68
|
|
|
69
69
|
and clusters these links to produce an estimated person ID:
|
|
70
70
|
|
|
71
|
-

|
|
72
72
|
|
|
73
73
|
## What data does Splink work best with?
|
|
74
74
|
|
|
@@ -31,15 +31,15 @@ Splink's linkage algorithm is based on Fellegi-Sunter's model of record linkage,
|
|
|
31
31
|
|
|
32
32
|
Consider the following records that lack a unique person identifier:
|
|
33
33
|
|
|
34
|
-

|
|
35
35
|
|
|
36
36
|
Splink predicts which rows link together:
|
|
37
37
|
|
|
38
|
-

|
|
39
39
|
|
|
40
40
|
and clusters these links to produce an estimated person ID:
|
|
41
41
|
|
|
42
|
-

|
|
43
43
|
|
|
44
44
|
## What data does Splink work best with?
|
|
45
45
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "splink"
|
|
3
|
-
version = "5.0.0.
|
|
3
|
+
version = "5.0.0.dev4"
|
|
4
4
|
description = "Fast probabilistic data linkage at scale"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Robin Linacre", email = "robinlinacre@hotmail.com" },
|
|
@@ -10,7 +10,7 @@ authors = [
|
|
|
10
10
|
{ name = "Andy Bond" },
|
|
11
11
|
{ name = "Ross Kennedy" },
|
|
12
12
|
]
|
|
13
|
-
requires-python = ">=3.
|
|
13
|
+
requires-python = ">=3.10.0,<4.0.0"
|
|
14
14
|
readme = "README.md"
|
|
15
15
|
license = "MIT"
|
|
16
16
|
dependencies = [
|
|
@@ -44,6 +44,7 @@ demos = [
|
|
|
44
44
|
"nbmake>=1.5.5",
|
|
45
45
|
"pseudopeople>=1.2.6",
|
|
46
46
|
"pytest-xdist>=3.6.1",
|
|
47
|
+
{ "include-group" = "pandas" },
|
|
47
48
|
]
|
|
48
49
|
# dev deliberately doesn't include spark, so we can opt in to different spark versions
|
|
49
50
|
dev = [
|
|
@@ -60,6 +61,7 @@ docs = [
|
|
|
60
61
|
# keep jinja2 relaxed so it doesn't constrain us in rest of dev
|
|
61
62
|
"Jinja2>=3.1.6",
|
|
62
63
|
"mkdocs>=1.6.1",
|
|
64
|
+
"mkdocs-llmstxt>=0.5.0",
|
|
63
65
|
# TODO: maybe we can use this to remove generating dataset docs as separate step
|
|
64
66
|
#"mkdocs-gen-files==0.5.0",
|
|
65
67
|
"mkdocs-material>=9.6.22",
|
|
@@ -72,7 +74,7 @@ docs = [
|
|
|
72
74
|
"neoteroi-mkdocs>=1.1.2",
|
|
73
75
|
"pymdown-extensions>=10.15",
|
|
74
76
|
]
|
|
75
|
-
linting = ["ruff>=0.
|
|
77
|
+
linting = ["ruff>=0.15.18"]
|
|
76
78
|
pandas = ["pandas>=1.3.5", "numpy>=1.19.3"]
|
|
77
79
|
postgres = [
|
|
78
80
|
"psycopg2-binary>=2.9.0",
|
|
@@ -86,22 +88,22 @@ testing-core = [
|
|
|
86
88
|
"pyarrow>=15.0.0",
|
|
87
89
|
"networkx>=2.5.1",
|
|
88
90
|
"pytest-cov>=5.0.0",
|
|
91
|
+
"pytest-xdist>=3.0.0",
|
|
89
92
|
{ "include-group" = "postgres" },
|
|
90
93
|
{ "include-group" = "sqlite" },
|
|
91
|
-
# TODO: temporary measure until we can depanda tests
|
|
92
|
-
{ "include-group" = "pandas" },
|
|
93
94
|
]
|
|
94
95
|
testing = [
|
|
95
96
|
{ "include-group" = "testing-core" },
|
|
96
97
|
{ "include-group" = "spark-4" },
|
|
97
98
|
]
|
|
98
|
-
typechecking = ["mypy
|
|
99
|
+
typechecking = ["mypy>=2.1.0"]
|
|
99
100
|
# we will migrate to ty, but won't happen all at once, so run in parallel
|
|
100
101
|
typechecking-dev = [
|
|
101
102
|
"ty>=0.0.21",
|
|
102
103
|
]
|
|
103
104
|
|
|
104
105
|
[tool.uv]
|
|
106
|
+
exclude-newer = "7 days"
|
|
105
107
|
default-groups = [
|
|
106
108
|
"dev",
|
|
107
109
|
"spark-4"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .internals.blocking_analysis import (
|
|
2
|
+
chart_comparisons_from_blocking_rules,
|
|
3
|
+
count_comparisons_from_blocking_rules,
|
|
4
|
+
n_largest_blocks,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"count_comparisons_from_blocking_rules",
|
|
9
|
+
"chart_comparisons_from_blocking_rules",
|
|
10
|
+
"n_largest_blocks",
|
|
11
|
+
]
|
|
@@ -359,7 +359,7 @@ def truth_space_table_from_labels_column(
|
|
|
359
359
|
|
|
360
360
|
pipeline.enqueue_sql(sql, "__splink__cartesian_product")
|
|
361
361
|
cartesian_count = linker._db_api.sql_pipeline_to_splink_dataframe(pipeline)
|
|
362
|
-
row_count_df = cartesian_count.
|
|
362
|
+
row_count_df = cartesian_count.as_record_list()
|
|
363
363
|
cartesian_count.drop_table_from_database_and_remove_from_cache()
|
|
364
364
|
|
|
365
365
|
total_labels = calculate_cartesian(row_count_df, link_type)
|
|
@@ -5,14 +5,17 @@ import time
|
|
|
5
5
|
from typing import TYPE_CHECKING, Any, List, Literal, Optional, TypedDict
|
|
6
6
|
|
|
7
7
|
from sqlglot import parse_one
|
|
8
|
-
from sqlglot.expressions import Column,
|
|
8
|
+
from sqlglot.expressions import Column, Identifier, Join
|
|
9
9
|
from sqlglot.optimizer.eliminate_joins import join_condition
|
|
10
10
|
from sqlglot.optimizer.optimizer import optimize
|
|
11
11
|
from sqlglot.optimizer.simplify import flatten
|
|
12
12
|
|
|
13
|
-
from splink.internals.chunking import
|
|
13
|
+
from splink.internals.chunking import (
|
|
14
|
+
_chunk_assignment_sql,
|
|
15
|
+
)
|
|
14
16
|
from splink.internals.database_api import DatabaseAPISubClass
|
|
15
17
|
from splink.internals.dialects import SplinkDialect
|
|
18
|
+
from splink.internals.em_sampling import _em_sample_filter_sql
|
|
16
19
|
from splink.internals.input_column import InputColumn
|
|
17
20
|
from splink.internals.misc import (
|
|
18
21
|
dedupe_preserving_order,
|
|
@@ -32,6 +35,9 @@ logger = logging.getLogger(__name__)
|
|
|
32
35
|
|
|
33
36
|
# https://stackoverflow.com/questions/39740632/python-type-hinting-without-cyclic-imports
|
|
34
37
|
if TYPE_CHECKING:
|
|
38
|
+
# Expr only exists in sqlglot >= 30.0.0
|
|
39
|
+
from sqlglot import Expr
|
|
40
|
+
|
|
35
41
|
from splink.internals.settings import LinkTypeLiteralType
|
|
36
42
|
|
|
37
43
|
user_input_link_type_options = Literal["link_only", "link_and_dedupe", "dedupe_only"]
|
|
@@ -211,10 +217,11 @@ class BlockingRule:
|
|
|
211
217
|
on
|
|
212
218
|
({self.blocking_rule_sql})
|
|
213
219
|
{where_condition}
|
|
214
|
-
{
|
|
215
|
-
|
|
216
|
-
unique_id_input_column
|
|
217
|
-
|
|
220
|
+
{
|
|
221
|
+
self.exclude_pairs_generated_by_all_preceding_rules_sql(
|
|
222
|
+
source_dataset_input_column, unique_id_input_column
|
|
223
|
+
)
|
|
224
|
+
}
|
|
218
225
|
"""
|
|
219
226
|
return sql
|
|
220
227
|
|
|
@@ -247,7 +254,7 @@ class BlockingRule:
|
|
|
247
254
|
list of tuples like [(name, name), (substr(name,1,2), substr(name,2,3))]
|
|
248
255
|
"""
|
|
249
256
|
|
|
250
|
-
def remove_table_prefix(tree:
|
|
257
|
+
def remove_table_prefix(tree: Expr) -> Expr:
|
|
251
258
|
for c in tree.find_all(Column):
|
|
252
259
|
del c.args["table"]
|
|
253
260
|
return tree
|
|
@@ -260,7 +267,7 @@ class BlockingRule:
|
|
|
260
267
|
|
|
261
268
|
rmtp = remove_table_prefix
|
|
262
269
|
|
|
263
|
-
keys_de_prefixed: list[tuple[
|
|
270
|
+
keys_de_prefixed: list[tuple[Expr, Expr]] = [
|
|
264
271
|
(rmtp(i), rmtp(j)) for (i, j) in keys_zipped
|
|
265
272
|
]
|
|
266
273
|
|
|
@@ -350,6 +357,8 @@ class ExplodingBlockingRule(BlockingRule):
|
|
|
350
357
|
input_tablename_r: str,
|
|
351
358
|
left_chunk: tuple[int, int] | None = None,
|
|
352
359
|
right_chunk: tuple[int, int] | None = None,
|
|
360
|
+
sample_threshold: int | None = None,
|
|
361
|
+
sample_modulus: int | None = None,
|
|
353
362
|
) -> str:
|
|
354
363
|
"""generates a table of the marginal id pairs from the exploded blocking rule
|
|
355
364
|
i.e. pairs are only created that match this blocking rule and NOT any of
|
|
@@ -360,6 +369,9 @@ class ExplodingBlockingRule(BlockingRule):
|
|
|
360
369
|
left side records.
|
|
361
370
|
right_chunk: Optional tuple of (chunk_number, total_chunks) for filtering
|
|
362
371
|
right side records.
|
|
372
|
+
sample_threshold: Optional integer threshold for deterministic hash
|
|
373
|
+
sampling of both sides of the join (used by blocking analysis).
|
|
374
|
+
sample_modulus: Optional modulus for the deterministic hash sample.
|
|
363
375
|
"""
|
|
364
376
|
|
|
365
377
|
unique_id_col = unique_id_input_column
|
|
@@ -373,6 +385,8 @@ class ExplodingBlockingRule(BlockingRule):
|
|
|
373
385
|
left_chunk=left_chunk,
|
|
374
386
|
right_chunk=right_chunk,
|
|
375
387
|
sql_dialect=self.sql_dialect,
|
|
388
|
+
sample_threshold=sample_threshold,
|
|
389
|
+
sample_modulus=sample_modulus,
|
|
376
390
|
)
|
|
377
391
|
|
|
378
392
|
id_expr_l = _composite_unique_id_from_nodes_sql(unique_id_input_columns, "l")
|
|
@@ -479,6 +493,8 @@ def materialise_exploded_id_tables(
|
|
|
479
493
|
unique_id_input_column: InputColumn,
|
|
480
494
|
left_chunk: tuple[int, int] | None = None,
|
|
481
495
|
right_chunk: tuple[int, int] | None = None,
|
|
496
|
+
sample_threshold: int | None = None,
|
|
497
|
+
sample_modulus: int | None = None,
|
|
482
498
|
) -> list[ExplodingBlockingRule]:
|
|
483
499
|
"""Materialise exploded ID pair tables for exploding blocking rules.
|
|
484
500
|
|
|
@@ -487,6 +503,9 @@ def materialise_exploded_id_tables(
|
|
|
487
503
|
left side records.
|
|
488
504
|
right_chunk: Optional tuple of (chunk_number, total_chunks) for filtering
|
|
489
505
|
right side records.
|
|
506
|
+
sample_threshold: Optional integer threshold for deterministic hash
|
|
507
|
+
sampling of both sides of the join (used by blocking analysis).
|
|
508
|
+
sample_modulus: Optional modulus for the deterministic hash sample.
|
|
490
509
|
"""
|
|
491
510
|
exploding_blocking_rules = [
|
|
492
511
|
br for br in blocking_rules if isinstance(br, ExplodingBlockingRule)
|
|
@@ -568,6 +587,8 @@ def materialise_exploded_id_tables(
|
|
|
568
587
|
input_tablename_r=input_tablename_r,
|
|
569
588
|
left_chunk=left_chunk,
|
|
570
589
|
right_chunk=right_chunk,
|
|
590
|
+
sample_threshold=sample_threshold,
|
|
591
|
+
sample_modulus=sample_modulus,
|
|
571
592
|
)
|
|
572
593
|
|
|
573
594
|
pipeline.enqueue_sql(sql, table_name)
|
|
@@ -680,6 +701,8 @@ def _sql_gen_where_condition(
|
|
|
680
701
|
left_chunk: tuple[int, int] | None = None,
|
|
681
702
|
right_chunk: tuple[int, int] | None = None,
|
|
682
703
|
sql_dialect: "SplinkDialect | None" = None,
|
|
704
|
+
sample_threshold: int | None = None,
|
|
705
|
+
sample_modulus: int | None = None,
|
|
683
706
|
) -> str:
|
|
684
707
|
id_expr_l = _composite_unique_id_from_nodes_sql(unique_id_cols, "l")
|
|
685
708
|
id_expr_r = _composite_unique_id_from_nodes_sql(unique_id_cols, "r")
|
|
@@ -708,6 +731,16 @@ def _sql_gen_where_condition(
|
|
|
708
731
|
unique_id_cols, chunk_num, total_chunks, "r", sql_dialect
|
|
709
732
|
)
|
|
710
733
|
|
|
734
|
+
if sample_threshold is not None and sample_modulus is not None:
|
|
735
|
+
if sql_dialect is None:
|
|
736
|
+
raise ValueError("EM sampling requires a SQL dialect")
|
|
737
|
+
where_condition += _em_sample_filter_sql(
|
|
738
|
+
unique_id_cols, sample_threshold, sample_modulus, "l", sql_dialect
|
|
739
|
+
)
|
|
740
|
+
where_condition += _em_sample_filter_sql(
|
|
741
|
+
unique_id_cols, sample_threshold, sample_modulus, "r", sql_dialect
|
|
742
|
+
)
|
|
743
|
+
|
|
711
744
|
return where_condition
|
|
712
745
|
|
|
713
746
|
|
|
@@ -721,6 +754,8 @@ def block_using_rules_sqls(
|
|
|
721
754
|
unique_id_input_column: InputColumn,
|
|
722
755
|
left_chunk: tuple[int, int] | None = None,
|
|
723
756
|
right_chunk: tuple[int, int] | None = None,
|
|
757
|
+
sample_threshold: int | None = None,
|
|
758
|
+
sample_modulus: int | None = None,
|
|
724
759
|
) -> list[dict[str, str]]:
|
|
725
760
|
"""Use the blocking rules specified in the linker's settings object to
|
|
726
761
|
generate a SQL statement that will create pairwise record comparions
|
|
@@ -751,6 +786,8 @@ def block_using_rules_sqls(
|
|
|
751
786
|
left_chunk=left_chunk,
|
|
752
787
|
right_chunk=right_chunk,
|
|
753
788
|
sql_dialect=sql_dialect,
|
|
789
|
+
sample_threshold=sample_threshold,
|
|
790
|
+
sample_modulus=sample_modulus,
|
|
754
791
|
)
|
|
755
792
|
|
|
756
793
|
# Cover the case where there are no blocking rules
|