splink 5.0.0.dev2__tar.gz → 5.0.0.dev3__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.
Files changed (165) hide show
  1. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/PKG-INFO +1 -1
  2. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/pyproject.toml +1 -1
  3. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/__init__.py +1 -1
  4. splink-5.0.0.dev3/splink/backends/duckdb.py +6 -0
  5. splink-5.0.0.dev3/splink/backends/spark.py +5 -0
  6. splink-5.0.0.dev3/splink/exploratory.py +4 -0
  7. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/accuracy.py +6 -11
  8. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/blocking.py +14 -2
  9. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/blocking_analysis.py +30 -26
  10. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/clustering.py +2 -1
  11. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/comparison.py +7 -6
  12. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/comparison_level.py +19 -16
  13. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/comparison_vector_values.py +17 -14
  14. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/completeness.py +2 -1
  15. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/connected_components.py +2 -1
  16. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/database_api.py +1 -12
  17. splink-5.0.0.dev3/splink/internals/duckdb/database_api_with_profiling.py +71 -0
  18. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/em_training_session.py +9 -6
  19. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/estimate_u.py +69 -29
  20. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/expectation_maximisation.py +5 -4
  21. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/labelling_tool.py +2 -4
  22. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/linker.py +8 -8
  23. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/linker_components/inference.py +55 -73
  24. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/linker_components/table_management.py +0 -41
  25. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/linker_components/training.py +5 -12
  26. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/m_from_labels.py +2 -3
  27. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/m_training.py +8 -9
  28. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/misc.py +28 -11
  29. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/pipeline.py +7 -4
  30. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/predict.py +34 -19
  31. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/profile_data.py +3 -3
  32. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/settings_validation/settings_column_cleaner.py +3 -3
  33. splink-5.0.0.dev3/splink/internals/spark/database_api_with_profiling.py +174 -0
  34. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/splink_dataframe.py +3 -2
  35. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/term_frequencies.py +55 -114
  36. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/vertically_concatenate.py +25 -104
  37. splink-5.0.0.dev2/splink/backends/duckdb.py +0 -3
  38. splink-5.0.0.dev2/splink/backends/spark.py +0 -4
  39. splink-5.0.0.dev2/splink/exploratory.py +0 -5
  40. splink-5.0.0.dev2/splink/internals/default_from_jsonschema.py +0 -20
  41. splink-5.0.0.dev2/splink/internals/validate_jsonschema.py +0 -44
  42. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/.gitignore +0 -0
  43. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/LICENSE +0 -0
  44. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/README.md +0 -0
  45. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/backends/postgres.py +0 -0
  46. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/backends/sqlite.py +0 -0
  47. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/blocking_analysis.py +0 -0
  48. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/blocking_rule_library.py +0 -0
  49. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/clustering.py +0 -0
  50. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/comparison_level_library.py +0 -0
  51. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/comparison_library.py +0 -0
  52. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/datasets.py +0 -0
  53. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/__init__.py +0 -0
  54. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/block_from_labels.py +0 -0
  55. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/blocking_rule_creator.py +0 -0
  56. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/blocking_rule_creator_utils.py +0 -0
  57. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/blocking_rule_library.py +0 -0
  58. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/cache_dict_with_logging.py +0 -0
  59. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/charts.py +0 -0
  60. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/chunking.py +0 -0
  61. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/cluster_studio.py +0 -0
  62. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/column_expression.py +0 -0
  63. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/comparison_creator.py +0 -0
  64. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/comparison_level_composition.py +0 -0
  65. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/comparison_level_creator.py +0 -0
  66. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/comparison_level_library.py +0 -0
  67. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/comparison_level_sql.py +0 -0
  68. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/comparison_library.py +0 -0
  69. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/comparison_vector_distribution.py +0 -0
  70. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/constants.py +0 -0
  71. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/cost_of_blocking_rules.py +0 -0
  72. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/databricks/__init__.py +0 -0
  73. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/databricks/enable_splink.py +0 -0
  74. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/datasets/__init__.py +0 -0
  75. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/datasets/metadata.py +0 -0
  76. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/datasets/splink_datasets.py +0 -0
  77. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/datasets/utils.py +0 -0
  78. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/dialects.py +0 -0
  79. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/duckdb/__init__.py +0 -0
  80. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/duckdb/database_api.py +0 -0
  81. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/duckdb/dataframe.py +0 -0
  82. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/duckdb/duckdb_helpers/__init__.py +0 -0
  83. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/duckdb/duckdb_helpers/duckdb_helpers.py +0 -0
  84. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/edge_metrics.py +0 -0
  85. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/exceptions.py +0 -0
  86. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/DEPENDENCY_LICENSES.txt +0 -0
  87. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/accuracy_chart.json +0 -0
  88. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/blocking_rule_generated_comparisons.json +0 -0
  89. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/comparator_score_chart.json +0 -0
  90. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/comparator_score_threshold_chart.json +0 -0
  91. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/completeness.json +0 -0
  92. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/m_u_parameters_interactive_history.json +0 -0
  93. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/match_weight_histogram.json +0 -0
  94. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/match_weights_interactive_history.json +0 -0
  95. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/match_weights_waterfall.json +0 -0
  96. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/parameter_estimate_comparisons.json +0 -0
  97. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/phonetic_match_chart.json +0 -0
  98. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/precision_recall.json +0 -0
  99. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/probability_two_random_records_match_iteration.json +0 -0
  100. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/profile_data.json +0 -0
  101. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/roc.json +0 -0
  102. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/tf_adjustment_chart.json +0 -0
  103. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/threshold_selection_tool.json +0 -0
  104. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/chart_defs/unlinkables_chart_def.json +0 -0
  105. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/external_js/d3@7.8.5 +0 -0
  106. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/external_js/stdlib.js@5.8.3 +0 -0
  107. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/external_js/vega-embed@6.20.2 +0 -0
  108. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/external_js/vega-lite@5.2.0 +0 -0
  109. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/external_js/vega@5.31.0 +0 -0
  110. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/labelling_tool/slt.js +0 -0
  111. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/labelling_tool/template.j2 +0 -0
  112. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/settings_jsonschema.json +0 -0
  113. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/spark_jars/scala-udf-similarity-0.1.2_spark3.x.jar +0 -0
  114. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/spark_jars/scala-udf-similarity-0.2.0_spark4.x.jar +0 -0
  115. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/splink_cluster_studio/cluster_template.j2 +0 -0
  116. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/splink_cluster_studio/custom.css +0 -0
  117. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/splink_comparison_viewer/custom.css +0 -0
  118. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/splink_comparison_viewer/template.j2 +0 -0
  119. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/splink_vis_utils/splink_vis_utils.js +0 -0
  120. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/files/templates/single_chart_template.html +0 -0
  121. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/find_brs_with_comparison_counts_below_threshold.py +0 -0
  122. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/find_matches_to_new_records.py +0 -0
  123. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/graph_metrics.py +0 -0
  124. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/input_column.py +0 -0
  125. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/linker_components/__init__.py +0 -0
  126. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/linker_components/clustering.py +0 -0
  127. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/linker_components/evaluation.py +0 -0
  128. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/linker_components/misc.py +0 -0
  129. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/linker_components/visualisations.py +0 -0
  130. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/logging_messages.py +0 -0
  131. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/lower_id_on_lhs.py +0 -0
  132. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/m_u_records_to_parameters.py +0 -0
  133. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/match_weights_histogram.py +0 -0
  134. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/one_to_one_clustering.py +0 -0
  135. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/optimise_cost_of_brs.py +0 -0
  136. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/parse_sql.py +0 -0
  137. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/postgres/__init__.py +0 -0
  138. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/postgres/database_api.py +0 -0
  139. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/postgres/dataframe.py +0 -0
  140. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/realtime.py +0 -0
  141. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/settings.py +0 -0
  142. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/settings_creator.py +0 -0
  143. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/settings_validation/__init__.py +0 -0
  144. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/settings_validation/log_invalid_columns.py +0 -0
  145. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/settings_validation/settings_validation_log_strings.py +0 -0
  146. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/settings_validation/valid_types.py +0 -0
  147. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/similarity_analysis.py +0 -0
  148. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/spark/__init__.py +0 -0
  149. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/spark/database_api.py +0 -0
  150. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/spark/dataframe.py +0 -0
  151. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/spark/jar_location.py +0 -0
  152. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/spark/spark_helpers/__init__.py +0 -0
  153. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/spark/spark_helpers/custom_spark_dialect.py +0 -0
  154. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/spark/spark_helpers/version.py +0 -0
  155. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/splink_comparison_viewer.py +0 -0
  156. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/splinkdataframe_utils.py +0 -0
  157. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/sql_transform.py +0 -0
  158. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/sqlite/__init__.py +0 -0
  159. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/sqlite/database_api.py +0 -0
  160. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/sqlite/dataframe.py +0 -0
  161. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/testing.py +0 -0
  162. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/unique_id_concat.py +0 -0
  163. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/unlinkables.py +0 -0
  164. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/internals/waterfall_chart.py +0 -0
  165. {splink-5.0.0.dev2 → splink-5.0.0.dev3}/splink/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splink
3
- Version: 5.0.0.dev2
3
+ Version: 5.0.0.dev3
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "splink"
3
- version = "5.0.0.dev2"
3
+ version = "5.0.0.dev3"
4
4
  description = "Fast probabilistic data linkage at scale"
5
5
  authors = [
6
6
  { name = "Robin Linacre", email = "robinlinacre@hotmail.com" },
@@ -56,7 +56,7 @@ def __getattr__(name):
56
56
  raise AttributeError(f"module 'splink' has no attribute '{name}'") from None
57
57
 
58
58
 
59
- __version__ = "5.0.0.dev2"
59
+ __version__ = "5.0.0.dev3"
60
60
 
61
61
 
62
62
  __all__ = [
@@ -0,0 +1,6 @@
1
+ from splink.internals.duckdb.database_api import DuckDBAPI
2
+ from splink.internals.duckdb.database_api_with_profiling import (
3
+ DuckDBAPIWithProfiling,
4
+ )
5
+
6
+ __all__ = ["DuckDBAPI", "DuckDBAPIWithProfiling"]
@@ -0,0 +1,5 @@
1
+ from splink.internals.spark.database_api import SparkAPI
2
+ from splink.internals.spark.database_api_with_profiling import SparkAPIWithProfiling
3
+ from splink.internals.spark.jar_location import similarity_jar_location
4
+
5
+ __all__ = ["similarity_jar_location", "SparkAPI", "SparkAPIWithProfiling"]
@@ -0,0 +1,4 @@
1
+ from .internals.completeness import completeness_chart
2
+ from .internals.profile_data import profile_columns
3
+
4
+ __all__ = ["completeness_chart", "profile_columns"]
@@ -14,8 +14,8 @@ from splink.internals.predict import predict_from_comparison_vectors_sqls_using_
14
14
  from splink.internals.splink_dataframe import SplinkDataFrame
15
15
  from splink.internals.sql_transform import move_l_r_table_prefix_to_column_suffix
16
16
  from splink.internals.vertically_concatenate import (
17
- compute_df_concat,
18
- compute_df_concat_with_tf,
17
+ enqueue_df_concat,
18
+ enqueue_df_concat_with_tf,
19
19
  )
20
20
 
21
21
  if TYPE_CHECKING:
@@ -316,9 +316,7 @@ def truth_space_table_from_labels_table(
316
316
  match_weight_round_to_nearest: Optional[float] = None,
317
317
  ) -> SplinkDataFrame:
318
318
  pipeline = CTEPipeline()
319
-
320
- nodes_with_tf = compute_df_concat_with_tf(linker, pipeline)
321
- pipeline = CTEPipeline([nodes_with_tf])
319
+ enqueue_df_concat_with_tf(linker, pipeline)
322
320
 
323
321
  sqls = predictions_from_sample_of_pairwise_labels_sql(linker, labels_tablename)
324
322
  pipeline.enqueue_list_of_sqls(sqls)
@@ -351,13 +349,11 @@ def truth_space_table_from_labels_column(
351
349
  group_by_statement = "group by source_dataset"
352
350
 
353
351
  pipeline = CTEPipeline()
354
- concat = compute_df_concat(linker, pipeline)
355
-
356
- pipeline = CTEPipeline([concat])
352
+ enqueue_df_concat(linker, pipeline)
357
353
 
358
354
  sql = f"""
359
355
  select count(*) as count
360
- from {concat.physical_name}
356
+ from __splink__df_concat
361
357
  {group_by_statement}
362
358
  """
363
359
 
@@ -451,8 +447,7 @@ def prediction_errors_from_labels_table(
451
447
  threshold_match_probability: float = 0.5,
452
448
  ) -> SplinkDataFrame:
453
449
  pipeline = CTEPipeline()
454
- nodes_with_tf = compute_df_concat_with_tf(linker, pipeline)
455
- pipeline = CTEPipeline([nodes_with_tf])
450
+ enqueue_df_concat_with_tf(linker, pipeline)
456
451
 
457
452
  sqls = predictions_from_sample_of_pairwise_labels_sql(linker, labels_tablename)
458
453
 
@@ -14,7 +14,11 @@ from splink.internals.chunking import _chunk_assignment_sql
14
14
  from splink.internals.database_api import DatabaseAPISubClass
15
15
  from splink.internals.dialects import SplinkDialect
16
16
  from splink.internals.input_column import InputColumn
17
- from splink.internals.misc import dedupe_preserving_order, ensure_is_list
17
+ from splink.internals.misc import (
18
+ dedupe_preserving_order,
19
+ ensure_is_list,
20
+ join_sql_with_union_all,
21
+ )
18
22
  from splink.internals.parse_sql import parse_columns_in_sql
19
23
  from splink.internals.pipeline import CTEPipeline
20
24
  from splink.internals.splink_dataframe import SplinkDataFrame
@@ -117,6 +121,10 @@ class BlockingRule:
117
121
  def sqlglot_dialect(self) -> str:
118
122
  return SplinkDialect.from_string(self._sql_dialect_str).sqlglot_dialect
119
123
 
124
+ @property
125
+ def requires_blocking_input_materialisation(self) -> bool:
126
+ return False
127
+
120
128
  @property
121
129
  def sql_dialect(self) -> SplinkDialect:
122
130
  return SplinkDialect.from_string(self._sql_dialect_str)
@@ -403,6 +411,10 @@ class ExplodingBlockingRule(BlockingRule):
403
411
 
404
412
  return "false"
405
413
 
414
+ @property
415
+ def requires_blocking_input_materialisation(self) -> bool:
416
+ return True
417
+
406
418
  def create_blocked_pairs_sql(
407
419
  self,
408
420
  *,
@@ -760,7 +772,7 @@ def block_using_rules_sqls(
760
772
  )
761
773
  br_sqls.append(sql)
762
774
 
763
- sql = " UNION ALL ".join(br_sqls)
775
+ sql = join_sql_with_union_all(br_sqls)
764
776
 
765
777
  if any(isinstance(br, ExplodingBlockingRule) for br in blocking_rules):
766
778
  sqls.append(
@@ -15,7 +15,6 @@ from typing import (
15
15
  cast,
16
16
  )
17
17
 
18
- import duckdb
19
18
  import sqlglot
20
19
 
21
20
  from splink.internals.blocking import (
@@ -77,22 +76,23 @@ def _count_comparisons_from_blocking_rule_pre_filter_conditions_sqls(
77
76
  input_tablename_l = "__splink__df_concat"
78
77
  input_tablename_r = "__splink__df_concat"
79
78
 
80
- sql = blocking_rule.create_blocking_input_sql(
81
- input_tablename=input_tablename_l,
82
- input_columns=input_dataframes[0].columns,
83
- )
79
+ if blocking_rule.requires_blocking_input_materialisation:
80
+ sql = blocking_rule.create_blocking_input_sql(
81
+ input_tablename=input_tablename_l,
82
+ input_columns=input_dataframes[0].columns,
83
+ )
84
84
 
85
- sqls.append({"sql": sql, "output_table_name": "__splink__br_input_l"})
85
+ sqls.append({"sql": sql, "output_table_name": "__splink__br_input_l"})
86
86
 
87
- sql = blocking_rule.create_blocking_input_sql(
88
- input_tablename=input_tablename_r,
89
- input_columns=input_dataframes[0].columns,
90
- )
87
+ sql = blocking_rule.create_blocking_input_sql(
88
+ input_tablename=input_tablename_r,
89
+ input_columns=input_dataframes[0].columns,
90
+ )
91
91
 
92
- sqls.append({"sql": sql, "output_table_name": "__splink__br_input_r"})
92
+ sqls.append({"sql": sql, "output_table_name": "__splink__br_input_r"})
93
93
 
94
- input_tablename_l = "__splink__br_input_l"
95
- input_tablename_r = "__splink__br_input_r"
94
+ input_tablename_l = "__splink__br_input_l"
95
+ input_tablename_r = "__splink__br_input_r"
96
96
 
97
97
  l_cols_sel = []
98
98
  r_cols_sel = []
@@ -380,18 +380,19 @@ def _cumulative_comparisons_to_be_scored_from_blocking_rules(
380
380
  """
381
381
  pipeline.enqueue_sql(sql, "__splink__df_count_cumulative_blocks")
382
382
 
383
- # TODO: duckdb (if available) or arrow (if not)
384
- result_df = db_api.sql_pipeline_to_splink_dataframe(pipeline).as_pandas_dataframe()
385
- # TODO: resuse connexion if available - see similar code in EM training
386
- con = duckdb.connect()
383
+ result_df = db_api.sql_pipeline_to_splink_dataframe(pipeline).as_duckdbpyrelation()
384
+ con = db_api.duckdb_con
385
+ con.register("result_df", result_df)
387
386
 
388
387
  # The above table won't include rules that have no matches
388
+ all_rules_table_name = "__splink__df_blocking_rule_counts"
389
389
  con.execute(
390
- "CREATE TABLE all_rules "
391
- "(match_key VARCHAR, blocking_rule VARCHAR, cartesian BIGINT);"
390
+ f"CREATE OR REPLACE TABLE {all_rules_table_name} "
391
+ "(match_key BIGINT, blocking_rule VARCHAR, cartesian BIGINT);"
392
392
  )
393
393
  con.executemany(
394
- "INSERT INTO all_rules VALUES ($match_key, $blocking_rule, $cartesian);",
394
+ f"INSERT INTO {all_rules_table_name} VALUES "
395
+ "($match_key, $blocking_rule, $cartesian);",
395
396
  [
396
397
  {
397
398
  "match_key": str(i),
@@ -402,8 +403,8 @@ def _cumulative_comparisons_to_be_scored_from_blocking_rules(
402
403
  ],
403
404
  )
404
405
 
406
+ table_name = "__splink__cumulative_blocking_rule_counts"
405
407
  if len(result_df) > 0:
406
- table_name = "__splink__cumulative_blocking_rule_counts"
407
408
  con.register(table_name, result_df)
408
409
  sql = f"""
409
410
  WITH simple_counts AS (
@@ -413,7 +414,7 @@ def _cumulative_comparisons_to_be_scored_from_blocking_rules(
413
414
  cast(rules.match_key as int) as match_key,
414
415
  rules.cartesian
415
416
  FROM
416
- all_rules AS rules
417
+ {all_rules_table_name} AS rules
417
418
  LEFT JOIN
418
419
  {table_name} AS counts
419
420
  ON
@@ -437,7 +438,7 @@ def _cumulative_comparisons_to_be_scored_from_blocking_rules(
437
438
  else:
438
439
  # TODO: can we join onto empty arrow table? if so, we don't need separate case
439
440
  # simpler sql as we have no data to join onto
440
- sql = """
441
+ sql = f"""
441
442
  select
442
443
  blocking_rule,
443
444
  0 as row_count,
@@ -446,15 +447,18 @@ def _cumulative_comparisons_to_be_scored_from_blocking_rules(
446
447
  match_key,
447
448
  0 as start,
448
449
  from
449
- all_rules
450
+ {all_rules_table_name}
450
451
  """
451
452
 
452
453
  [b.drop_materialised_id_pairs_dataframe() for b in exploding_br_with_id_tables]
453
454
  complete_df = con.sql(sql)
454
-
455
- return cast(
455
+ counts_data = cast(
456
456
  list[CumulativeComparisonRecord], record_dicts_from_relation(complete_df)
457
457
  )
458
+ # clean up temporary tables
459
+ con.execute(f"DROP VIEW IF EXISTS {table_name}")
460
+ con.execute(f"DROP TABLE {all_rules_table_name}")
461
+ return counts_data
458
462
 
459
463
 
460
464
  def _count_comparisons_generated_from_blocking_rule(
@@ -9,6 +9,7 @@ from splink.internals.database_api import AcceptableInputTableType, DatabaseAPIS
9
9
  from splink.internals.input_column import InputColumn
10
10
  from splink.internals.misc import (
11
11
  ascii_uid,
12
+ join_sql_with_union_all,
12
13
  prob_to_match_weight,
13
14
  threshold_args_to_match_prob,
14
15
  threshold_args_to_match_prob_list,
@@ -338,7 +339,7 @@ def _generate_cluster_summary_stats_sql(
338
339
  for threshold in thresholds
339
340
  ]
340
341
 
341
- sql = "\nUNION ALL\n".join(select_statements)
342
+ sql = join_sql_with_union_all(select_statements)
342
343
 
343
344
  return sql
344
345
 
@@ -7,6 +7,7 @@ from splink.internals.dialects import SplinkDialect
7
7
  from splink.internals.input_column import InputColumn
8
8
  from splink.internals.misc import (
9
9
  dedupe_preserving_order,
10
+ indent_sql,
10
11
  join_list_with_commas_final_and,
11
12
  )
12
13
 
@@ -161,8 +162,8 @@ class Comparison:
161
162
  sqls = [
162
163
  cl._when_then_comparison_vector_value_sql for cl in self.comparison_levels
163
164
  ]
164
- sql = " ".join(sqls)
165
- sql = f"CASE {sql} END as {self._gamma_column_name}"
165
+ sql = "\n".join(sqls)
166
+ sql = f"CASE\n{indent_sql(sql)}\nEND as {self._gamma_column_name}"
166
167
 
167
168
  return sql
168
169
 
@@ -253,8 +254,8 @@ class Comparison:
253
254
  cl._match_weight_sql(self._gamma_column_name)
254
255
  for cl in self.comparison_levels
255
256
  ]
256
- sql = " ".join(sqls)
257
- sql = f"CASE {sql} END as {self._mw_column_name}"
257
+ sql = "\n".join(sqls)
258
+ sql = f"CASE\n{indent_sql(sql)}\nEND as {self._mw_column_name}"
258
259
  output_cols.append(sql)
259
260
 
260
261
  if self._has_tf_adjustments:
@@ -262,8 +263,8 @@ class Comparison:
262
263
  cl._tf_adjustment_sql(self._gamma_column_name, self.comparison_levels)
263
264
  for cl in self.comparison_levels
264
265
  ]
265
- sql = " ".join(sqls)
266
- sql = f"\nCASE {sql} END as {self._mw_tf_adj_column_name}"
266
+ sql = "\n".join(sqls)
267
+ sql = f"CASE\n{indent_sql(sql)}\nEND as {self._mw_tf_adj_column_name}"
267
268
  output_cols.append(sql)
268
269
  output_cols.append(self._gamma_column_name)
269
270
 
@@ -10,7 +10,7 @@ from textwrap import dedent
10
10
  from typing import Any, Optional, Union, cast
11
11
 
12
12
  import sqlglot
13
- from sqlglot.expressions import Column, Identifier
13
+ from sqlglot.expressions import Column, Expression, Identifier
14
14
  from sqlglot.optimizer.normalize import normalize
15
15
  from sqlglot.optimizer.simplify import simplify
16
16
 
@@ -84,12 +84,12 @@ def _exact_match_colname(sql_syntax_tree):
84
84
  return cols[0]
85
85
 
86
86
 
87
- def _get_and_subclauses(expr: sqlglot.Expression) -> list[sqlglot.Expression]:
87
+ def _get_and_subclauses(expr: Expression) -> list[Expression]:
88
88
  # get list of subclauses joined together by 'AND' at top-level
89
89
  # e.g. 'A AND B AND C' -> ['A', 'B', 'C']
90
90
  # or if no AND, return expression as a list, e.g. 'A' -> ['A']
91
91
  if isinstance(expr, sqlglot.exp.And):
92
- return list(expr.flatten())
92
+ return [cast(Expression, subclause) for subclause in expr.flatten()]
93
93
  return [expr]
94
94
 
95
95
 
@@ -660,11 +660,10 @@ class ComparisonLevel:
660
660
 
661
661
  def _match_weight_sql(self, gamma_column_name: str) -> str:
662
662
  sql = f"""
663
- WHEN
664
- {gamma_column_name} = {self.comparison_vector_value}
665
- THEN cast({self._match_weight} as float8)
663
+ WHEN {gamma_column_name} = {self.comparison_vector_value} THEN
664
+ cast({self._match_weight} as float8)
666
665
  """
667
- return dedent(sql)
666
+ return dedent(sql).strip()
668
667
 
669
668
  def _tf_adjustment_sql(
670
669
  self, gamma_column_name: str, comparison_levels: list[ComparisonLevel]
@@ -689,7 +688,9 @@ class ComparisonLevel:
689
688
  or self._tf_adjustment_weight == 0
690
689
  or self._is_else_level
691
690
  ):
692
- sql = f"WHEN {gamma_colname_value_is_this_level} then cast(0 as float8)"
691
+ sql = f"""
692
+ WHEN {gamma_colname_value_is_this_level} THEN cast(0 as float8)
693
+ """
693
694
  else:
694
695
  tf_adj_col = self._tf_adjustment_input_column
695
696
 
@@ -714,14 +715,16 @@ class ComparisonLevel:
714
715
 
715
716
  log2_u_prob = math.log2(u_prob_exact_match)
716
717
 
717
- sql = f"""WHEN {gamma_colname_value_is_this_level} then
718
- (CASE WHEN {tf_adjustment_exists}
719
- THEN
720
- cast({self._tf_adjustment_weight} as float8) * (
721
- cast({log2_u_prob} as float8) - log2({tf_u_value_sql})
722
- )
723
- ELSE cast(0 as float8)
724
- END)"""
718
+ sql = f"""
719
+ WHEN {gamma_colname_value_is_this_level} THEN
720
+ CASE
721
+ WHEN {tf_adjustment_exists} THEN
722
+ cast({self._tf_adjustment_weight} as float8) * (
723
+ cast({log2_u_prob} as float8) - log2({tf_u_value_sql})
724
+ )
725
+ ELSE cast(0 as float8)
726
+ END
727
+ """
725
728
  return dedent(sql).strip()
726
729
 
727
730
  def as_dict(self):
@@ -4,6 +4,7 @@ import logging
4
4
  from typing import List, Optional
5
5
 
6
6
  from splink.internals.input_column import InputColumn
7
+ from splink.internals.misc import indent_sql
7
8
  from splink.internals.unique_id_concat import (
8
9
  _composite_unique_id_from_nodes_sql,
9
10
  )
@@ -22,15 +23,15 @@ def compute_comparison_vector_values_sql(
22
23
  See [the fastlink paper](https://imai.fas.harvard.edu/research/files/linkage.pdf)
23
24
  for more details of what is meant by comparison vectors.
24
25
  """
25
- select_cols_expr = ",".join(columns_to_select_for_comparison_vector_values)
26
-
26
+ select_columns = list(columns_to_select_for_comparison_vector_values)
27
27
  if include_clerical_match_score:
28
- clerical_match_score = ", clerical_match_score"
29
- else:
30
- clerical_match_score = ""
28
+ select_columns.append("clerical_match_score")
29
+
30
+ select_cols_expr = ",\n".join(indent_sql(col) for col in select_columns)
31
31
 
32
32
  sql = f"""
33
- select {select_cols_expr} {clerical_match_score}
33
+ select
34
+ {select_cols_expr}
34
35
  from __splink__df_blocked
35
36
  """
36
37
 
@@ -61,7 +62,8 @@ def compute_comparison_vector_values_from_id_pairs_sqls(
61
62
  else:
62
63
  unique_id_columns = [unique_id_input_column]
63
64
 
64
- select_cols_expr = ", \n".join(columns_to_select_for_blocking)
65
+ select_columns = [*columns_to_select_for_blocking, "b.match_key"]
66
+ select_cols_expr = ",\n".join(indent_sql(col) for col in select_columns)
65
67
 
66
68
  # Where there are large numbers of unmatched records, the DuckDB query planner
67
69
  # can struggle with the double inner join below. It should
@@ -101,7 +103,8 @@ def compute_comparison_vector_values_from_id_pairs_sqls(
101
103
  # using the __splink__blocked_id_pairs as an associated (junction) table
102
104
  # That is, it does the join, but doesn't compute the comparison vectors
103
105
  sql = f"""
104
- select {select_cols_expr}, b.match_key
106
+ select
107
+ {select_cols_expr}
105
108
  from __splink__blocked_id_pairs as b
106
109
  inner join {input_tablename_l} as l
107
110
  on {uid_l_expr} = b.join_key_l
@@ -111,16 +114,16 @@ def compute_comparison_vector_values_from_id_pairs_sqls(
111
114
 
112
115
  sqls.append({"sql": sql, "output_table_name": "blocked_with_cols"})
113
116
 
114
- select_cols_expr = ", \n".join(columns_to_select_for_comparison_vector_values)
115
-
117
+ select_columns = list(columns_to_select_for_comparison_vector_values)
116
118
  if include_clerical_match_score:
117
- clerical_match_score = ", clerical_match_score"
118
- else:
119
- clerical_match_score = ""
119
+ select_columns.append("clerical_match_score")
120
+
121
+ select_cols_expr = ",\n".join(indent_sql(col) for col in select_columns)
120
122
 
121
123
  # The second table computes the comparison vectors from these aliases
122
124
  sql = f"""
123
- select {select_cols_expr} {clerical_match_score}
125
+ select
126
+ {select_cols_expr}
124
127
  from blocked_with_cols
125
128
  """
126
129
 
@@ -7,6 +7,7 @@ from splink.internals.charts import (
7
7
  )
8
8
  from splink.internals.database_api import DatabaseAPISubClass
9
9
  from splink.internals.input_column import InputColumn
10
+ from splink.internals.misc import join_sql_with_union_all
10
11
  from splink.internals.pipeline import CTEPipeline
11
12
  from splink.internals.splink_dataframe import SplinkDataFrame
12
13
  from splink.internals.splinkdataframe_utils import (
@@ -75,7 +76,7 @@ def completeness_data(
75
76
  """
76
77
  sqls.append(sql)
77
78
 
78
- sql = " union all ".join(sqls)
79
+ sql = join_sql_with_union_all(sqls)
79
80
 
80
81
  pipeline.enqueue_sql(sql, "__splink__df_all_column_completeness")
81
82
 
@@ -16,6 +16,7 @@ import time
16
16
  from typing import Optional
17
17
 
18
18
  from splink.internals.database_api import DatabaseAPISubClass
19
+ from splink.internals.misc import join_sql_with_union_all
19
20
  from splink.internals.pipeline import CTEPipeline
20
21
  from splink.internals.splink_dataframe import SplinkDataFrame
21
22
 
@@ -316,7 +317,7 @@ def solve_connected_components(
316
317
 
317
318
  pipeline = CTEPipeline()
318
319
 
319
- sql = " UNION ALL ".join(
320
+ sql = join_sql_with_union_all(
320
321
  [
321
322
  f"""select node_id as {node_id_column_name}, representative as cluster_id
322
323
  from {t.physical_name}"""
@@ -16,7 +16,6 @@ from typing import (
16
16
  )
17
17
 
18
18
  import duckdb
19
- import sqlglot
20
19
 
21
20
  from splink.internals.cache_dict_with_logging import CacheDictWithLogging
22
21
  from splink.internals.logging_messages import execute_sql_logging_message_info, log_sql
@@ -106,16 +105,6 @@ class DatabaseAPI(ABC, Generic[TablishType]):
106
105
  try:
107
106
  return self._execute_sql_against_backend(final_sql)
108
107
  except Exception as e:
109
- # Parse our SQL through sqlglot to pretty print
110
- try:
111
- final_sql = sqlglot.parse_one(
112
- final_sql,
113
- read=self.sql_dialect.sqlglot_dialect,
114
- ).sql(pretty=True)
115
- # if sqlglot produces any errors, just report the raw SQL
116
- except Exception:
117
- pass
118
-
119
108
  raise SplinkException(
120
109
  f"Error executing the following sql for table "
121
110
  f"`{templated_name}`({physical_name}):\n{final_sql}"
@@ -311,7 +300,7 @@ class DatabaseAPI(ABC, Generic[TablishType]):
311
300
  # returns sql
312
301
  # sensible default:
313
302
  self.delete_table_from_database(physical_name)
314
- sql = f"CREATE TABLE {physical_name} AS {sql}"
303
+ sql = f"CREATE TABLE {physical_name} AS\n{sql}"
315
304
  return sql
316
305
 
317
306
  def _cleanup_for_execute_sql(
@@ -0,0 +1,71 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from os import PathLike
5
+ from pathlib import Path
6
+ from typing import Union
7
+
8
+ import duckdb
9
+
10
+ from .database_api import DuckDBAPI
11
+ from .dataframe import DuckDBDataFrame
12
+
13
+
14
+ class DuckDBAPIWithProfiling(DuckDBAPI):
15
+ def __init__(
16
+ self,
17
+ connection: Union[str, duckdb.DuckDBPyConnection] = ":memory:",
18
+ output_schema: str = None,
19
+ query_profiling_dir: str | PathLike[str] = "tmp_query_profiling",
20
+ ):
21
+ super().__init__(connection=connection, output_schema=output_schema)
22
+ self.query_profiling_dir = Path(query_profiling_dir)
23
+ self.query_profiling_dir.mkdir(parents=True, exist_ok=True)
24
+ self._query_profile_counter = 0
25
+ self._pending_profile_path: Path | None = None
26
+ self._pending_profile_sql: str | None = None
27
+
28
+ def _should_profile_sql(self, sql: str) -> bool:
29
+ stripped_sql = sql.lstrip().upper()
30
+ return stripped_sql.startswith("SELECT") or stripped_sql.startswith("WITH")
31
+
32
+ def _next_query_profile_path(self, templated_name: str) -> Path:
33
+ self._query_profile_counter += 1
34
+ safe_name = re.sub(r"[^A-Za-z0-9_.-]+", "_", templated_name).strip("_")
35
+ if not safe_name:
36
+ safe_name = "query"
37
+ filename = f"{self._query_profile_counter:04d}_{safe_name}_duckdb.txt"
38
+ return self.query_profiling_dir / filename
39
+
40
+ def _setup_for_execute_sql(self, sql: str, physical_name: str) -> str:
41
+ if self._should_profile_sql(sql):
42
+ self._pending_profile_path = self._next_query_profile_path(physical_name)
43
+ self._pending_profile_sql = sql
44
+ else:
45
+ self._pending_profile_path = None
46
+ self._pending_profile_sql = None
47
+
48
+ return super()._setup_for_execute_sql(sql, physical_name)
49
+
50
+ def _cleanup_for_execute_sql(
51
+ self, table: duckdb.DuckDBPyRelation, templated_name: str, physical_name: str
52
+ ) -> DuckDBDataFrame:
53
+ try:
54
+ output_df = self.table_to_splink_dataframe(templated_name, physical_name)
55
+ if self._pending_profile_path is not None and self._pending_profile_sql:
56
+ explain_result = super()._execute_sql_against_backend(
57
+ f"EXPLAIN ANALYZE {self._pending_profile_sql}"
58
+ )
59
+ rows = explain_result.fetchall()
60
+ if len(rows) == 1 and len(rows[0]) == 2:
61
+ profile_text = rows[0][1]
62
+ else:
63
+ profile_text = "\n".join(
64
+ " | ".join("" if value is None else str(value) for value in row)
65
+ for row in rows
66
+ )
67
+ self._pending_profile_path.write_text(profile_text, encoding="utf-8")
68
+ return output_df
69
+ finally:
70
+ self._pending_profile_path = None
71
+ self._pending_profile_sql = None
@@ -26,7 +26,10 @@ from splink.internals.settings import (
26
26
  Settings,
27
27
  TrainingSettings,
28
28
  )
29
- from splink.internals.vertically_concatenate import compute_df_concat_with_tf
29
+ from splink.internals.vertically_concatenate import (
30
+ enqueue_df_concat,
31
+ enqueue_df_concat_with_tf,
32
+ )
30
33
 
31
34
  from .database_api import DatabaseAPISubClass
32
35
  from .exceptions import EMTrainingException
@@ -195,13 +198,12 @@ class EMTrainingSession:
195
198
  self._training_log_message()
196
199
 
197
200
  pipeline = CTEPipeline()
198
- nodes_with_tf = compute_df_concat_with_tf(self._original_linker, pipeline)
199
- pipeline = CTEPipeline([nodes_with_tf])
201
+ enqueue_df_concat(self._original_linker, pipeline)
200
202
 
201
203
  orig_settings = self._original_linker._settings_obj
202
204
  sqls = block_using_rules_sqls(
203
- input_tablename_l="__splink__df_concat_with_tf",
204
- input_tablename_r="__splink__df_concat_with_tf",
205
+ input_tablename_l="__splink__df_concat",
206
+ input_tablename_r="__splink__df_concat",
205
207
  blocking_rules=[self._blocking_rule_for_training],
206
208
  link_type=orig_settings._link_type,
207
209
  source_dataset_input_column=orig_settings.column_info_settings.source_dataset_input_column,
@@ -211,7 +213,8 @@ class EMTrainingSession:
211
213
 
212
214
  blocked_pairs = self.db_api.sql_pipeline_to_splink_dataframe(pipeline)
213
215
 
214
- pipeline = CTEPipeline([blocked_pairs, nodes_with_tf])
216
+ pipeline = CTEPipeline([blocked_pairs])
217
+ enqueue_df_concat_with_tf(self._original_linker, pipeline)
215
218
 
216
219
  sqls = compute_comparison_vector_values_from_id_pairs_sqls(
217
220
  orig_settings._columns_to_select_for_blocking,