def _validate_semantic_types_dict(self, dataset):
        """
        Validates a semantic_types dict to ensure correct formatting, that all semantic_types are recognized, and that
        the semantic_types align with the column data types
        Args:
            dataset: A GE dataset
            config: A config dictionary

        Returns:
            The validated semantic_types dictionary

        """
        if not isinstance(self.semantic_types_dict, dict):
            raise ValueError(
                f"The semantic_types dict in the config must be a dictionary, but is currently a "
                f"{type(self.semantic_types_dict)}. Please reformat."
            )
        for k, v in self.semantic_types_dict.items():
            assert isinstance(v, list), (
                "Entries in semantic type dict must be lists of column names e.g. "
                "{'semantic_types': {'numeric': ['number_of_transactions']}}"
            )
            if k.upper() not in profiler_semantic_types:
                raise ValueError(
                    f"{k} is not a recognized semantic_type. Please only include one of "
                    f"{profiler_semantic_types}"
                )

        selected_columns = [
            column
            for column_list in self.semantic_types_dict.values()
            for column in column_list
        ]
        if selected_columns:
            for column in selected_columns:
                if column not in dataset.get_table_columns():
                    raise ProfilerError(f"Column {column} does not exist.")
                elif column in self.ignored_columns:
                    raise ValueError(
                        f"Column {column} is specified in both the semantic_types_dict and the list of "
                        f"ignored columns. Please remove one of these entries to proceed."
                    )

        for semantic_type, column_list in self.semantic_types_dict.items():
            for column_name in column_list:
                processed_column = self.column_info.get(column_name)
                if semantic_type == "datetime":
                    assert processed_column.get("type") in ("DATETIME", "STRING",), (
                        f"Column {column_name} must be a datetime column or a string but appears to be "
                        f"{processed_column.get('type')}"
                    )
                elif semantic_type == "numeric":
                    assert processed_column.get("type") in (
                        "INT",
                        "FLOAT",
                        "NUMERIC",
                    ), f"Column {column_name} must be an int or a float but appears to be {processed_column.get('type')}"
                elif semantic_type in ("STRING", "VALUE_SET"):
                    pass
        return self.semantic_types_dict
def _check_that_columns_exist(dataset, columns) -> None:
    if columns:
        for column in columns:
            if column not in dataset.get_table_columns():
                raise ProfilerError(f"Column {column} does not exist.")
def _check_that_expectations_are_available(dataset, expectations) -> None:
    if expectations:
        for expectation in expectations:
            if expectation not in dataset.list_available_expectation_types():
                raise ProfilerError(
                    f"Expectation {expectation} is not available.")
    def _profile(cls, dataset, configuration=None):
        logger.debug(f"Running profiler with configuration: {configuration}")
        if configuration == "demo":
            return cls._demo_profile(dataset)

        existing_columns = dataset.get_table_columns()
        selected_columns = existing_columns
        included_expectations = []
        excluded_expectations = []

        if configuration:
            if ("included_expectations" in configuration
                    and "excluded_expectations" in configuration):
                raise ProfilerError(
                    "Please specify either `included_expectations` or `excluded_expectations`."
                )
            if "included_expectations" in configuration:
                included_expectations = configuration["included_expectations"]
                if included_expectations in [False, None, []]:
                    included_expectations = None
                _check_that_expectations_are_available(dataset,
                                                       included_expectations)
            if "excluded_expectations" in configuration:
                excluded_expectations = configuration["excluded_expectations"]
                if excluded_expectations in [False, None, []]:
                    excluded_expectations = None
                _check_that_expectations_are_available(dataset,
                                                       excluded_expectations)

            if ("included_columns" in configuration
                    and "excluded_columns" in configuration):
                raise ProfilerError(
                    "Please specify either `excluded_columns` or `included_columns`."
                )
            elif "included_columns" in configuration:
                selected_columns = configuration["included_columns"]
                if selected_columns in [False, None, []]:
                    selected_columns = []
            elif "excluded_columns" in configuration:
                excluded_columns = configuration["excluded_columns"]
                if excluded_columns in [False, None, []]:
                    excluded_columns = []
                selected_columns = set(existing_columns) - set(
                    excluded_columns)

        _check_that_columns_exist(dataset, selected_columns)
        if included_expectations is None:
            suite = cls._build_column_description_metadata(dataset)
            # remove column exist expectations
            suite.expectations = []
            return suite

        dataset.set_default_expectation_argument("catch_exceptions", False)
        dataset = cls._build_table_row_count_expectation(
            dataset,
            excluded_expectations=excluded_expectations,
            included_expectations=included_expectations,
        )
        dataset.set_config_value("interactive_evaluation", True)
        dataset = cls._build_table_column_expectations(
            dataset,
            excluded_expectations=excluded_expectations,
            included_expectations=included_expectations,
        )

        column_cache = {}
        if selected_columns:
            with tqdm(total=len(selected_columns),
                      desc="Profiling Columns",
                      delay=5) as pbar:
                for column in selected_columns:
                    pbar.set_postfix_str(column)
                    cardinality = cls._get_column_cardinality_with_caching(
                        dataset, column, column_cache)
                    column_type = cls._get_column_type_with_caching(
                        dataset, column, column_cache)

                    if cardinality in [
                            ProfilerCardinality.TWO,
                            ProfilerCardinality.VERY_FEW,
                            ProfilerCardinality.FEW,
                    ]:
                        cls._create_expectations_for_low_card_column(
                            dataset, column, column_cache)
                    elif cardinality in [
                            ProfilerCardinality.MANY,
                            ProfilerCardinality.VERY_MANY,
                            ProfilerCardinality.UNIQUE,
                    ]:
                        # TODO we will want to finesse the number and types of
                        #  expectations created here. The simple version is deny/allow list
                        #  and the more complex version is desired per column type and
                        #  cardinality. This deserves more thought on configuration.
                        dataset.expect_column_values_to_be_unique(column)

                        if column_type in [
                                ProfilerDataType.INT,
                                ProfilerDataType.FLOAT,
                        ]:
                            cls._create_expectations_for_numeric_column(
                                dataset, column)
                        elif column_type in [ProfilerDataType.DATETIME]:
                            cls._create_expectations_for_datetime_column(
                                dataset,
                                column,
                                excluded_expectations=excluded_expectations,
                                included_expectations=included_expectations,
                            )
                        elif column_type in [ProfilerDataType.STRING]:
                            cls._create_expectations_for_string_column(
                                dataset,
                                column,
                                excluded_expectations=excluded_expectations,
                                included_expectations=included_expectations,
                            )
                        elif column_type in [ProfilerDataType.UNKNOWN]:
                            logger.debug(
                                f"Skipping expectation creation for column {column} of unknown type: {column_type}"
                            )
                    pbar.update()

        if excluded_expectations:
            # NOTE: we reach into a private member here because of an expected future
            # refactor that will make the suite directly accessible
            dataset._expectation_suite.remove_all_expectations_of_type(
                excluded_expectations)
        if included_expectations:
            for expectation in dataset.get_expectation_suite(
                    discard_failed_expectations=False,
                    suppress_logging=True,
            ).expectations:
                if expectation.expectation_type not in included_expectations:
                    try:
                        dataset.remove_expectation(
                            ExpectationConfiguration(
                                expectation_type=expectation.expectation_type,
                                kwargs=expectation.kwargs,
                            ),
                            match_type="domain",
                            remove_multiple_matches=True,
                        )
                    except ValueError:
                        logger.debug(
                            f"Attempted to remove {expectation}, which was not found."
                        )

        expectation_suite = cls._build_column_description_metadata(dataset)

        return expectation_suite
    def _profile(cls, dataset, configuration=None):
        logger.debug(f"Running profiler with configuration: {configuration}")
        if configuration == "demo":
            return cls._demo_profile(dataset)

        existing_columns = dataset.get_table_columns()
        selected_columns = existing_columns
        included_expectations = []
        excluded_expectations = []

        if configuration:
            if ("included_expectations" in configuration
                    and "excluded_expectations" in configuration):
                raise ProfilerError(
                    "Please specify either `included_expectations` or `excluded_expectations`."
                )
            if "included_expectations" in configuration:
                included_expectations = configuration["included_expectations"]
                if included_expectations in [False, None, []]:
                    included_expectations = None
                _check_that_expectations_are_available(dataset,
                                                       included_expectations)
            if "excluded_expectations" in configuration:
                excluded_expectations = configuration["excluded_expectations"]
                _check_that_expectations_are_available(dataset,
                                                       excluded_expectations)

            if ("included_columns" in configuration
                    and "excluded_columns" in configuration):
                raise ProfilerError(
                    "Please specify either `excluded_columns` or `included_columns`."
                )
            elif "included_columns" in configuration:
                selected_columns = configuration["included_columns"]
            elif "excluded_columns" in configuration:
                excluded_columns = configuration["excluded_columns"]
                if excluded_columns in [False, None, []]:
                    excluded_columns = []
                selected_columns = set(existing_columns) - set(
                    excluded_columns)

        _check_that_columns_exist(dataset, selected_columns)
        if included_expectations is None:
            suite = cls._build_column_description_metadata(dataset)
            # remove column exist expectations
            suite.expectations = []
            return suite

        dataset.set_default_expectation_argument("catch_exceptions", False)
        dataset = cls._build_table_row_count_expectation(dataset,
                                                         tolerance=0.1)
        dataset.set_config_value("interactive_evaluation", True)
        dataset = cls._build_table_column_expectations(dataset)

        column_cache = {}
        if selected_columns:
            for column in selected_columns:
                cardinality = cls._get_column_cardinality_with_caching(
                    dataset, column, column_cache)
                column_type = cls._get_column_type_with_caching(
                    dataset, column, column_cache)

                if cardinality in [
                        ProfilerCardinality.TWO,
                        ProfilerCardinality.VERY_FEW,
                        ProfilerCardinality.FEW,
                ]:
                    cls._create_expectations_for_low_card_column(
                        dataset, column, column_cache)
                elif cardinality in [
                        ProfilerCardinality.MANY,
                        ProfilerCardinality.VERY_MANY,
                        ProfilerCardinality.UNIQUE,
                ]:
                    # TODO we will want to finesse the number and types of
                    #  expectations created here. The simple version is blacklisting
                    #  and the more complex version is desired per column type and
                    #  cardinality. This deserves more thought on configuration.
                    dataset.expect_column_values_to_be_unique(column)

                    if column_type in [
                            ProfilerDataType.INT, ProfilerDataType.FLOAT
                    ]:
                        cls._create_expectations_for_numeric_column(
                            dataset, column)
                    elif column_type in [ProfilerDataType.DATETIME]:
                        cls._create_expectations_for_datetime_column(
                            dataset, column)
                    elif column_type in [ProfilerDataType.STRING]:
                        cls._create_expectations_for_string_column(
                            dataset, column)
                    elif column_type in [ProfilerDataType.UNKNOWN]:
                        logger.debug(
                            f"Skipping expectation creation for column {column} of unknown type: {column_type}"
                        )

        if excluded_expectations:
            dataset = _remove_table_expectations(dataset,
                                                 excluded_expectations)
            dataset = _remove_column_expectations(dataset,
                                                  excluded_expectations)
        if included_expectations:
            for expectation in dataset.get_expectation_suite().expectations:
                if expectation.expectation_type not in included_expectations:
                    try:
                        dataset.remove_expectation(
                            expectation_type=expectation.expectation_type,
                            expectation_kwargs=expectation.kwargs,
                            column=expectation.kwargs.get("column", None),
                            remove_multiple_matches=True,
                        )
                    except ValueError:
                        logger.debug(
                            f"Attempted to remove {expectation}, which was not found."
                        )

        expectation_suite = cls._build_column_description_metadata(dataset)

        return expectation_suite