Repository: great-expectations/great_expectations

Issue:
Use cleaner solution for non-truncating division in python 2
Prefer `from __future__ import division` to `1.*x/y`


Discussion:
Done inline during improve_partitions push.

Resolving patch:
diff --git a/great_expectations/rule_based_profiler/data_assistant/data_assistant.py b/great_expectations/rule_based_profiler/data_assistant/data_assistant.py
--- a/great_expectations/rule_based_profiler/data_assistant/data_assistant.py
+++ b/great_expectations/rule_based_profiler/data_assistant/data_assistant.py
@@ -115,6 +115,29 @@ def get_table_row_count_metric_multi_batch_parameter_builder(
                 json_serialize=json_serialize,
             )
 
+        def get_column_value_counts_metric_multi_batch_parameter_builder(
+            self,
+            json_serialize: Union[str, bool] = True,
+        ) -> ParameterBuilder:
+            """
+            This method instantiates one commonly used "MetricMultiBatchParameterBuilder" with specified directives.
+            """
+            metric_name: str = "column.value_counts"
+            return MetricMultiBatchParameterBuilder(
+                name=metric_name,
+                metric_name=metric_name,
+                metric_domain_kwargs=DOMAIN_KWARGS_PARAMETER_FULLY_QUALIFIED_NAME,
+                metric_value_kwargs={
+                    "sort": "value",
+                },
+                enforce_numeric_metric=False,
+                replace_nan_with_zero=False,
+                reduce_scalar_metric=False,
+                evaluation_parameter_builder_configs=None,
+                json_serialize=json_serialize,
+                data_context=None,
+            )
+
         def get_column_distinct_values_count_metric_multi_batch_parameter_builder(
             self,
             json_serialize: Union[str, bool] = True,
@@ -312,6 +335,7 @@ def build_numeric_metric_range_multi_batch_parameter_builder(
                 round_decimals=f"{VARIABLES_KEY}round_decimals",
                 evaluation_parameter_builder_configs=None,
                 json_serialize=json_serialize,
+                data_context=None,
             )
 
     commonly_used_parameter_builders: CommonlyUsedParameterBuilders = (
@@ -388,9 +412,10 @@ def _build_profiler(self) -> None:
             rules = profiler.rules
             self._add_rules_to_profiler(rules=rules)
 
-        self._validate_profiler_rule_name_uniqueness()
+        custom_rules: List[Rule] = self.rules
+        self._validate_profiler_rule_name_uniqueness(custom_rules=custom_rules)
 
-        self._add_rules_to_profiler(rules=self.rules)
+        self._add_rules_to_profiler(rules=custom_rules)
 
         custom_variables: Optional[Dict[str, Any]] = self.variables
         if custom_variables is None:
@@ -613,22 +638,24 @@ def get_rule_variables_and_validation_parameter_builders_from_self_initializing_
         ] = rule.expectation_configuration_builders[0].validation_parameter_builders
         return variables, validation_parameter_builders
 
-    def _validate_profiler_rule_name_uniqueness(self) -> None:
+    def _validate_profiler_rule_name_uniqueness(
+        self,
+        custom_rules: List[Rule],
+    ) -> None:
         """
         This private utility method insures that all "Rule" objects in underlying "BaseRuleBasedProfiler" are unique.
         """
-        rule: Rule
-
         profiler_rules: List[Rule] = self.profiler.rules
         if profiler_rules is None:
             profiler_rules = []
 
-        profiler_rule_names: Set[str] = {rule.name for rule in profiler_rules}
-
-        custom_rules: List[Rule] = self.rules
         if custom_rules is None:
             custom_rules = []
 
+        rule: Rule
+
+        profiler_rule_names: Set[str] = {rule.name for rule in profiler_rules}
+
         custom_rule_names: Set[str] = {rule.name for rule in custom_rules}
 
         common_rule_names: Set[str] = profiler_rule_names & custom_rule_names
diff --git a/great_expectations/rule_based_profiler/data_assistant/onboarding_data_assistant.py b/great_expectations/rule_based_profiler/data_assistant/onboarding_data_assistant.py
--- a/great_expectations/rule_based_profiler/data_assistant/onboarding_data_assistant.py
+++ b/great_expectations/rule_based_profiler/data_assistant/onboarding_data_assistant.py
@@ -6,12 +6,21 @@
 from great_expectations.rule_based_profiler.data_assistant.data_assistant import (
     build_map_metric_rule,
 )
-from great_expectations.rule_based_profiler.domain_builder import ColumnDomainBuilder
+from great_expectations.rule_based_profiler.domain_builder import (
+    CategoricalColumnDomainBuilder,
+    ColumnDomainBuilder,
+)
 from great_expectations.rule_based_profiler.expectation_configuration_builder import (
     DefaultExpectationConfigurationBuilder,
     ExpectationConfigurationBuilder,
 )
-from great_expectations.rule_based_profiler.parameter_builder import ParameterBuilder
+from great_expectations.rule_based_profiler.helpers.cardinality_checker import (
+    CardinalityLimitMode,
+)
+from great_expectations.rule_based_profiler.parameter_builder import (
+    ParameterBuilder,
+    ValueSetMultiBatchParameterBuilder,
+)
 from great_expectations.rule_based_profiler.rule import Rule
 from great_expectations.rule_based_profiler.types import (
     DOMAIN_KWARGS_PARAMETER_FULLY_QUALIFIED_NAME,
@@ -124,6 +133,7 @@ def rules(self) -> Optional[List[Rule]]:
         )
         numeric_rule: Rule = self._build_numeric_rule()
         datetime_rule: Rule = self._build_datetime_rule()
+        categorical_rule: Rule = self._build_categorical_rule()
 
         return [
             column_value_uniqueness_rule,
@@ -131,6 +141,7 @@ def rules(self) -> Optional[List[Rule]]:
             column_value_nonnullity_rule,
             numeric_rule,
             datetime_rule,
+            categorical_rule,
         ]
 
     def _build_data_assistant_result(
@@ -195,7 +206,7 @@ def _build_numeric_rule() -> Rule:
             json_serialize=True
         )
 
-        # Step-3: Declare "ParameterBuilder" for every "validation" need "ExpectationConfigurationBuilder" objects.
+        # Step-3: Declare "ParameterBuilder" for every "validation" need in "ExpectationConfigurationBuilder" objects.
 
         column_min_values_range_parameter_builder_for_validations: ParameterBuilder = DataAssistant.commonly_used_parameter_builders.build_numeric_metric_range_multi_batch_parameter_builder(
             metric_name="column.min",
@@ -470,7 +481,7 @@ def _build_datetime_rule() -> Rule:
             json_serialize=True
         )
 
-        # Step-3: Declare "ParameterBuilder" for every "validation" need "ExpectationConfigurationBuilder" objects.
+        # Step-3: Declare "ParameterBuilder" for every "validation" need in "ExpectationConfigurationBuilder" objects.
 
         column_min_values_range_parameter_builder_for_validations: ParameterBuilder = DataAssistant.commonly_used_parameter_builders.build_numeric_metric_range_multi_batch_parameter_builder(
             metric_name="column.min",
@@ -590,3 +601,154 @@ def _build_datetime_rule() -> Rule:
         )
 
         return rule
+
+    @staticmethod
+    def _build_categorical_rule() -> Rule:
+        """
+        This method builds "Rule" object focused on emitting "ExpectationConfiguration" objects for categorical columns.
+        """
+
+        # Step-1: Instantiate "CategoricalColumnDomainBuilder" for selecting columns containing "FEW" discrete values.
+
+        categorical_column_type_domain_builder: CategoricalColumnDomainBuilder = (
+            CategoricalColumnDomainBuilder(
+                include_column_names=None,
+                exclude_column_names=None,
+                include_column_name_suffixes=None,
+                exclude_column_name_suffixes=None,
+                semantic_type_filter_module_name=None,
+                semantic_type_filter_class_name=None,
+                include_semantic_types=None,
+                exclude_semantic_types=None,
+                allowed_semantic_types_passthrough=None,
+                limit_mode=CardinalityLimitMode.FEW,
+                max_unique_values=None,
+                max_proportion_unique=None,
+                data_context=None,
+            )
+        )
+
+        # Step-2: Declare "ParameterBuilder" for every metric of interest.
+
+        column_distinct_values_count_metric_multi_batch_parameter_builder_for_metrics: ParameterBuilder = DataAssistant.commonly_used_parameter_builders.get_column_distinct_values_count_metric_multi_batch_parameter_builder(
+            json_serialize=True
+        )
+        column_value_counts_metric_multi_batch_parameter_builder_for_metrics: ParameterBuilder = DataAssistant.commonly_used_parameter_builders.get_column_value_counts_metric_multi_batch_parameter_builder(
+            json_serialize=True
+        )
+
+        # Step-3: Declare "ParameterBuilder" for every "validation" need in "ExpectationConfigurationBuilder" objects.
+
+        value_set_multi_batch_parameter_builder_for_validations: ParameterBuilder = (
+            ValueSetMultiBatchParameterBuilder(
+                name="value_set_estimator",
+                metric_domain_kwargs=DOMAIN_KWARGS_PARAMETER_FULLY_QUALIFIED_NAME,
+                metric_value_kwargs=None,
+                evaluation_parameter_builder_configs=None,
+                json_serialize=True,
+                data_context=None,
+            )
+        )
+        column_distinct_values_count_range_parameter_builder_for_validations: ParameterBuilder = DataAssistant.commonly_used_parameter_builders.build_numeric_metric_range_multi_batch_parameter_builder(
+            metric_name="column.distinct_values.count",
+            metric_value_kwargs=None,
+            json_serialize=True,
+        )
+        column_unique_proportion_range_parameter_builder_for_validations: ParameterBuilder = DataAssistant.commonly_used_parameter_builders.build_numeric_metric_range_multi_batch_parameter_builder(
+            metric_name="column.unique_proportion",
+            metric_value_kwargs=None,
+            json_serialize=True,
+        )
+
+        validation_parameter_builder_configs: Optional[List[ParameterBuilderConfig]]
+
+        # Step-4: Pass "validation" "ParameterBuilderConfig" objects to every "DefaultExpectationConfigurationBuilder", responsible for emitting "ExpectationConfiguration" (with specified "expectation_type").
+
+        validation_parameter_builder_configs = [
+            ParameterBuilderConfig(
+                **value_set_multi_batch_parameter_builder_for_validations.to_json_dict(),
+            ),
+        ]
+        expect_column_values_to_be_in_set_expectation_configuration_builder: DefaultExpectationConfigurationBuilder = DefaultExpectationConfigurationBuilder(
+            expectation_type="expect_column_values_to_be_in_set",
+            validation_parameter_builder_configs=validation_parameter_builder_configs,
+            column=f"{DOMAIN_KWARGS_PARAMETER_FULLY_QUALIFIED_NAME}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}column",
+            value_set=f"{value_set_multi_batch_parameter_builder_for_validations.fully_qualified_parameter_name}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}{FULLY_QUALIFIED_PARAMETER_NAME_VALUE_KEY}",
+            mostly=f"{VARIABLES_KEY}mostly",
+            meta={
+                "profiler_details": f"{value_set_multi_batch_parameter_builder_for_validations.fully_qualified_parameter_name}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}{FULLY_QUALIFIED_PARAMETER_NAME_METADATA_KEY}",
+            },
+        )
+
+        validation_parameter_builder_configs = [
+            ParameterBuilderConfig(
+                **column_distinct_values_count_range_parameter_builder_for_validations.to_json_dict(),
+            ),
+        ]
+        expect_column_unique_value_count_to_be_between_expectation_configuration_builder: DefaultExpectationConfigurationBuilder = DefaultExpectationConfigurationBuilder(
+            expectation_type="expect_column_unique_value_count_to_be_between",
+            validation_parameter_builder_configs=validation_parameter_builder_configs,
+            column=f"{DOMAIN_KWARGS_PARAMETER_FULLY_QUALIFIED_NAME}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}column",
+            min_value=f"{column_distinct_values_count_range_parameter_builder_for_validations.fully_qualified_parameter_name}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}{FULLY_QUALIFIED_PARAMETER_NAME_VALUE_KEY}[0]",
+            max_value=f"{column_distinct_values_count_range_parameter_builder_for_validations.fully_qualified_parameter_name}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}{FULLY_QUALIFIED_PARAMETER_NAME_VALUE_KEY}[1]",
+            strict_min=f"{VARIABLES_KEY}strict_min",
+            strict_max=f"{VARIABLES_KEY}strict_max",
+            meta={
+                "profiler_details": f"{column_distinct_values_count_range_parameter_builder_for_validations.fully_qualified_parameter_name}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}{FULLY_QUALIFIED_PARAMETER_NAME_METADATA_KEY}",
+            },
+        )
+
+        validation_parameter_builder_configs = [
+            ParameterBuilderConfig(
+                **column_unique_proportion_range_parameter_builder_for_validations.to_json_dict(),
+            ),
+        ]
+        expect_column_proportion_of_unique_values_to_be_between_expectation_configuration_builder: DefaultExpectationConfigurationBuilder = DefaultExpectationConfigurationBuilder(
+            expectation_type="expect_column_proportion_of_unique_values_to_be_between",
+            validation_parameter_builder_configs=validation_parameter_builder_configs,
+            column=f"{DOMAIN_KWARGS_PARAMETER_FULLY_QUALIFIED_NAME}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}column",
+            min_value=f"{column_unique_proportion_range_parameter_builder_for_validations.fully_qualified_parameter_name}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}{FULLY_QUALIFIED_PARAMETER_NAME_VALUE_KEY}[0]",
+            max_value=f"{column_unique_proportion_range_parameter_builder_for_validations.fully_qualified_parameter_name}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}{FULLY_QUALIFIED_PARAMETER_NAME_VALUE_KEY}[1]",
+            strict_min=f"{VARIABLES_KEY}strict_min",
+            strict_max=f"{VARIABLES_KEY}strict_max",
+            meta={
+                "profiler_details": f"{column_unique_proportion_range_parameter_builder_for_validations.fully_qualified_parameter_name}{FULLY_QUALIFIED_PARAMETER_NAME_SEPARATOR_CHARACTER}{FULLY_QUALIFIED_PARAMETER_NAME_METADATA_KEY}",
+            },
+        )
+
+        # Step-5: Instantiate and return "Rule" object, comprised of "variables", "domain_builder", "parameter_builders", and "expectation_configuration_builders" components.
+
+        variables: dict = {
+            "mostly": 1.0,
+            "strict_min": False,
+            "strict_max": False,
+            "false_positive_rate": 0.05,
+            "quantile_statistic_interpolation_method": "auto",
+            "estimator": "bootstrap",
+            "n_resamples": 9999,
+            "random_seed": None,
+            "include_estimator_samples_histogram_in_details": False,
+            "truncate_values": {
+                "lower_bound": 0.0,
+                "upper_bound": None,
+            },
+            "round_decimals": 1,
+        }
+        parameter_builders: List[ParameterBuilder] = [
+            column_distinct_values_count_metric_multi_batch_parameter_builder_for_metrics,
+            column_value_counts_metric_multi_batch_parameter_builder_for_metrics,
+        ]
+        expectation_configuration_builders: List[ExpectationConfigurationBuilder] = [
+            expect_column_values_to_be_in_set_expectation_configuration_builder,
+            expect_column_unique_value_count_to_be_between_expectation_configuration_builder,
+            expect_column_proportion_of_unique_values_to_be_between_expectation_configuration_builder,
+        ]
+        rule: Rule = Rule(
+            name="categorical_rule",
+            variables=variables,
+            domain_builder=categorical_column_type_domain_builder,
+            parameter_builders=parameter_builders,
+            expectation_configuration_builders=expectation_configuration_builders,
+        )
+
+        return rule


Test patch:
diff --git a/tests/rule_based_profiler/data_assistant/test_onboarding_data_assistant.py b/tests/rule_based_profiler/data_assistant/test_onboarding_data_assistant.py
--- a/tests/rule_based_profiler/data_assistant/test_onboarding_data_assistant.py
+++ b/tests/rule_based_profiler/data_assistant/test_onboarding_data_assistant.py
@@ -89,7 +89,7 @@ def test_onboarding_data_assistant_result_serialization(
         bobby_onboarding_data_assistant_result.to_json_dict()
         == onboarding_data_assistant_result_as_dict
     )
-    assert len(bobby_onboarding_data_assistant_result.profiler_config.rules) == 6
+    assert len(bobby_onboarding_data_assistant_result.profiler_config.rules) == 7
 
 
 def test_onboarding_data_assistant_result_batch_id_to_batch_identifier_display_name_map_coverage(