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/execution_engine/sparkdf_execution_engine.py b/great_expectations/execution_engine/sparkdf_execution_engine.py
--- a/great_expectations/execution_engine/sparkdf_execution_engine.py
+++ b/great_expectations/execution_engine/sparkdf_execution_engine.py
@@ -17,7 +17,11 @@
 )
 from great_expectations.core.id_dict import IDDict
 from great_expectations.core.metric_domain_types import MetricDomainTypes
-from great_expectations.core.util import AzureUrl, get_or_create_spark_application
+from great_expectations.core.util import (
+    AzureUrl,
+    convert_to_json_serializable,
+    get_or_create_spark_application,
+)
 from great_expectations.exceptions import (
     BatchSpecError,
     ExecutionEngineError,
@@ -657,7 +661,9 @@ def resolve_metric_bundle(
             if not isinstance(compute_domain_kwargs, IDDict):
                 compute_domain_kwargs = IDDict(compute_domain_kwargs)
 
-            domain_id = compute_domain_kwargs.to_id()
+            domain_id = IDDict.convert_dictionary_to_id_dict(
+                data=convert_to_json_serializable(data=compute_domain_kwargs)
+            ).to_id()
             if domain_id not in aggregates:
                 aggregates[domain_id] = {
                     "column_aggregates": [],
@@ -688,6 +694,10 @@ def resolve_metric_bundle(
 
             res = df.agg(*aggregate_cols).collect()
 
+            logger.debug(
+                f"SparkDFExecutionEngine computed {len(res[0])} metrics on domain_id {IDDict.convert_dictionary_to_id_dict(data=convert_to_json_serializable(data=domain_kwargs)).to_id()}"
+            )
+
             assert (
                 len(res) == 1
             ), "all bundle-computed metrics must be single-value statistics"
@@ -695,14 +705,14 @@ def resolve_metric_bundle(
                 res[0]
             ), "unexpected number of metrics returned"
 
-            logger.debug(
-                f"SparkDFExecutionEngine computed {len(res[0])} metrics on domain_id {IDDict(domain_kwargs).to_id()}"
-            )
-
             idx: int
             metric_id: Tuple[str, str, str]
             for idx, metric_id in enumerate(aggregate["ids"]):
-                resolved_metrics[metric_id] = res[0][idx]
+                # Converting DataFrame.collect() results into JSON-serializable format produces simple data types,
+                # amenable for subsequent post-processing by higher-level "Metric" and "Expectation" layers.
+                resolved_metrics[metric_id] = convert_to_json_serializable(
+                    data=res[0][idx]
+                )
 
         return resolved_metrics
 
diff --git a/great_expectations/self_check/util.py b/great_expectations/self_check/util.py
--- a/great_expectations/self_check/util.py
+++ b/great_expectations/self_check/util.py
@@ -1630,6 +1630,7 @@ def build_sa_engine(
     df: pd.DataFrame,
     sa: ModuleType,
     schema: Optional[str] = None,
+    batch_id: Optional[str] = None,
     if_exists: str = "fail",
     index: bool = False,
     dtype: Optional[dict] = None,
@@ -1655,8 +1656,11 @@ def build_sa_engine(
     )
     batch = Batch(data=batch_data)
 
+    if batch_id is None:
+        batch_id = batch.id
+
     execution_engine = SqlAlchemyExecutionEngine(
-        engine=sqlalchemy_engine, batch_data_dict={batch.id: batch_data}
+        engine=sqlalchemy_engine, batch_data_dict={batch_id: batch_data}
     )
 
     return execution_engine


Test patch:
diff --git a/tests/execution_engine/test_sparkdf_execution_engine.py b/tests/execution_engine/test_sparkdf_execution_engine.py
--- a/tests/execution_engine/test_sparkdf_execution_engine.py
+++ b/tests/execution_engine/test_sparkdf_execution_engine.py
@@ -1094,6 +1094,76 @@ def test_resolve_metric_bundle_with_nonexistent_metric(spark_session):
         print(e)
 
 
+def test_resolve_metric_bundle_with_compute_domain_kwargs_json_serialization(
+    spark_session,
+):
+    """
+    Insures that even when "compute_domain_kwargs" has multiple keys, it will be JSON-serialized for "IDDict.to_id()".
+    """
+    engine = build_spark_engine(
+        spark=spark_session,
+        df=pd.DataFrame(
+            {
+                "names": [
+                    "Ada Lovelace",
+                    "Alan Kay",
+                    "Donald Knuth",
+                    "Edsger Dijkstra",
+                    "Guido van Rossum",
+                    "John McCarthy",
+                    "Marvin Minsky",
+                    "Ray Ozzie",
+                ]
+            }
+        ),
+        batch_id="my_id",
+    )
+
+    metrics: dict = {}
+
+    table_columns_metric: MetricConfiguration
+    results: dict
+
+    table_columns_metric, results = get_table_columns_metric(engine=engine)
+    metrics.update(results)
+
+    desired_metric = MetricConfiguration(
+        metric_name="column_values.length.max.aggregate_fn",
+        metric_domain_kwargs={
+            "column": "names",
+            "batch_id": "my_id",
+        },
+        metric_dependencies={
+            "table.columns": table_columns_metric,
+        },
+        metric_value_kwargs=None,
+    )
+
+    try:
+        results = engine.resolve_metrics(metrics_to_resolve=(desired_metric,))
+    except ge_exceptions.MetricProviderError as e:
+        assert False, str(e)
+
+    desired_metric = MetricConfiguration(
+        metric_name="column_values.length.max",
+        metric_domain_kwargs={
+            "batch_id": "my_id",
+        },
+        metric_value_kwargs=None,
+        metric_dependencies={
+            "metric_partial_fn": desired_metric,
+        },
+    )
+
+    try:
+        results = engine.resolve_metrics(
+            metrics_to_resolve=(desired_metric,), metrics=results
+        )
+        assert results == {desired_metric.id: 16}
+    except ge_exceptions.MetricProviderError as e:
+        assert False, str(e)
+
+
 # Making sure dataframe property is functional
 def test_dataframe_property_given_loaded_batch(spark_session):
     engine: SparkDFExecutionEngine = build_spark_engine(
diff --git a/tests/execution_engine/test_sqlalchemy_execution_engine.py b/tests/execution_engine/test_sqlalchemy_execution_engine.py
--- a/tests/execution_engine/test_sqlalchemy_execution_engine.py
+++ b/tests/execution_engine/test_sqlalchemy_execution_engine.py
@@ -779,6 +779,7 @@ def test_resolve_metric_bundle_with_nonexistent_metric(sa):
 
     # Ensuring a metric provider error is raised if metric does not exist
     with pytest.raises(ge_exceptions.MetricProviderError) as e:
+        # noinspection PyUnusedLocal
         res = engine.resolve_metrics(
             metrics_to_resolve=(
                 desired_metric_1,
@@ -790,6 +791,74 @@ def test_resolve_metric_bundle_with_nonexistent_metric(sa):
         print(e)
 
 
+def test_resolve_metric_bundle_with_compute_domain_kwargs_json_serialization(sa):
+    """
+    Insures that even when "compute_domain_kwargs" has multiple keys, it will be JSON-serialized for "IDDict.to_id()".
+    """
+    engine = build_sa_engine(
+        pd.DataFrame(
+            {
+                "names": [
+                    "Ada Lovelace",
+                    "Alan Kay",
+                    "Donald Knuth",
+                    "Edsger Dijkstra",
+                    "Guido van Rossum",
+                    "John McCarthy",
+                    "Marvin Minsky",
+                    "Ray Ozzie",
+                ]
+            }
+        ),
+        sa,
+        batch_id="1234",
+    )
+
+    metrics: dict = {}
+
+    table_columns_metric: MetricConfiguration
+    results: dict
+
+    table_columns_metric, results = get_table_columns_metric(engine=engine)
+    metrics.update(results)
+
+    desired_metric = MetricConfiguration(
+        metric_name="column_values.length.max.aggregate_fn",
+        metric_domain_kwargs={
+            "column": "names",
+            "batch_id": "1234",
+        },
+        metric_dependencies={
+            "table.columns": table_columns_metric,
+        },
+        metric_value_kwargs=None,
+    )
+
+    try:
+        results = engine.resolve_metrics(metrics_to_resolve=(desired_metric,))
+    except ge_exceptions.MetricProviderError as e:
+        assert False, str(e)
+
+    desired_metric = MetricConfiguration(
+        metric_name="column_values.length.max",
+        metric_domain_kwargs={
+            "batch_id": "1234",
+        },
+        metric_value_kwargs=None,
+        metric_dependencies={
+            "metric_partial_fn": desired_metric,
+        },
+    )
+
+    try:
+        results = engine.resolve_metrics(
+            metrics_to_resolve=(desired_metric,), metrics=results
+        )
+        assert results == {desired_metric.id: 16}
+    except ge_exceptions.MetricProviderError as e:
+        assert False, str(e)
+
+
 def test_get_batch_data_and_markers_using_query(sqlite_view_engine, test_df):
     my_execution_engine: SqlAlchemyExecutionEngine = SqlAlchemyExecutionEngine(
         engine=sqlite_view_engine
diff --git a/tests/expectations/metrics/test_core.py b/tests/expectations/metrics/test_core.py
--- a/tests/expectations/metrics/test_core.py
+++ b/tests/expectations/metrics/test_core.py
@@ -391,10 +391,10 @@ def test_column_value_lengths_max_metric_spark(spark_session):
         metric_domain_kwargs={
             "column": "names",
         },
+        metric_value_kwargs=None,
         metric_dependencies={
             "table.columns": table_columns_metric,
         },
-        metric_value_kwargs=None,
     )
     results = engine.resolve_metrics(metrics_to_resolve=(desired_metric,))
 
@@ -5171,11 +5171,10 @@ def test_map_compound_columns_unique_pd():
     assert metrics[unexpected_values_metric.id] == [{"a": 1, "c": 2}, {"a": 1, "c": 2}]
 
 
-def test_map_compound_columns_unique_spark(spark_session):
-    engine: SparkDFExecutionEngine = build_spark_engine(
-        spark=spark_session,
-        df=pd.DataFrame(data={"a": [0, 1, 1], "b": [1, 2, 3], "c": [0, 2, 2]}),
-        batch_id="my_id",
+def test_map_compound_columns_unique_sa(sa):
+    engine = build_sa_engine(
+        pd.DataFrame(data={"a": [0, 1, 1], "b": [1, 2, 3], "c": [0, 2, 2]}),
+        sa,
     )
 
     metrics: dict = {}
@@ -5195,6 +5194,24 @@ def test_map_compound_columns_unique_spark(spark_session):
     # Save original metrics for testing unexpected results.
     metrics_save: dict = copy.deepcopy(metrics)
 
+    prerequisite_function_metric_name: str = "compound_columns.count.map"
+
+    prerequisite_function_metric = MetricConfiguration(
+        metric_name=prerequisite_function_metric_name,
+        metric_domain_kwargs={
+            "column_list": ["a", "b"],
+        },
+        metric_value_kwargs=None,
+        metric_dependencies={
+            "table.columns": table_columns_metric,
+        },
+    )
+    results = engine.resolve_metrics(
+        metrics_to_resolve=(prerequisite_function_metric,),
+        metrics=metrics,
+    )
+    metrics.update(results)
+
     metric_name: str = "compound_columns.unique"
     condition_metric_name: str = f"{metric_name}.condition"
     unexpected_count_metric_name: str = f"{metric_name}.unexpected_count"
@@ -5210,6 +5227,7 @@ def test_map_compound_columns_unique_spark(spark_session):
         },
         metric_value_kwargs=None,
         metric_dependencies={
+            "compound_columns.count.map": prerequisite_function_metric,
             "table.columns": table_columns_metric,
         },
     )
@@ -5235,7 +5253,7 @@ def test_map_compound_columns_unique_spark(spark_session):
     )
     metrics.update(results)
 
-    # Condition metrics return "negative logic" series.
+    # Condition metrics return SQLAlchemy ColumnElement object.
     assert metrics[unexpected_count_metric.id] == 0
 
     unexpected_rows_metric = MetricConfiguration(
@@ -5256,7 +5274,7 @@ def test_map_compound_columns_unique_spark(spark_session):
     )
     metrics.update(results)
 
-    assert metrics[unexpected_rows_metric.id] == []
+    assert len(metrics[unexpected_rows_metric.id]) == 0
 
     unexpected_values_metric = MetricConfiguration(
         metric_name=unexpected_values_metric_name,
@@ -5277,13 +5295,28 @@ def test_map_compound_columns_unique_spark(spark_session):
     metrics.update(results)
 
     assert len(metrics[unexpected_values_metric.id]) == 0
-    assert metrics[unexpected_values_metric.id] == []
 
     # Restore from saved original metrics in order to start fresh on testing for unexpected results.
     metrics = copy.deepcopy(metrics_save)
 
     # Second, assert Fail (one or more unexpected results).
 
+    prerequisite_function_metric = MetricConfiguration(
+        metric_name=prerequisite_function_metric_name,
+        metric_domain_kwargs={
+            "column_list": ["a", "c"],
+        },
+        metric_value_kwargs=None,
+        metric_dependencies={
+            "table.columns": table_columns_metric,
+        },
+    )
+    results = engine.resolve_metrics(
+        metrics_to_resolve=(prerequisite_function_metric,),
+        metrics=metrics,
+    )
+    metrics.update(results)
+
     condition_metric = MetricConfiguration(
         metric_name=condition_metric_name,
         metric_domain_kwargs={
@@ -5291,6 +5324,7 @@ def test_map_compound_columns_unique_spark(spark_session):
         },
         metric_value_kwargs=None,
         metric_dependencies={
+            "compound_columns.count.map": prerequisite_function_metric,
             "table.columns": table_columns_metric,
         },
     )
@@ -5316,7 +5350,7 @@ def test_map_compound_columns_unique_spark(spark_session):
     )
     metrics.update(results)
 
-    # Condition metrics return "negative logic" series.
+    # Condition metrics return SQLAlchemy ColumnElement object.
     assert metrics[unexpected_count_metric.id] == 2
 
     unexpected_rows_metric = MetricConfiguration(
@@ -5361,15 +5395,11 @@ def test_map_compound_columns_unique_spark(spark_session):
     assert metrics[unexpected_values_metric.id] == [{"a": 1, "c": 2}, {"a": 1, "c": 2}]
 
 
-def test_map_select_column_values_unique_within_record_pd():
-    engine = build_pandas_engine(
-        pd.DataFrame(
-            data={
-                "a": [1, 1, 8, 1, 4, None, None, 7],
-                "b": [1, 2, 2, 2, 4, None, None, 1],
-                "c": [2, 3, 7, 3, 4, None, 9, 0],
-            }
-        )
+def test_map_compound_columns_unique_spark(spark_session):
+    engine: SparkDFExecutionEngine = build_spark_engine(
+        spark=spark_session,
+        df=pd.DataFrame(data={"a": [0, 1, 1], "b": [1, 2, 3], "c": [0, 2, 2]}),
+        batch_id="my_id",
     )
 
     metrics: dict = {}
@@ -5380,20 +5410,27 @@ def test_map_select_column_values_unique_within_record_pd():
     table_columns_metric, results = get_table_columns_metric(engine=engine)
     metrics.update(results)
 
+    """
+    Two tests:
+    1. Pass -- no duplicated compound column keys.
+    2. Fail -- one or more duplicated compound column keys.
+    """
+
     # Save original metrics for testing unexpected results.
     metrics_save: dict = copy.deepcopy(metrics)
 
-    metric_name: str = "select_column_values.unique.within_record"
+    metric_name: str = "compound_columns.unique"
     condition_metric_name: str = f"{metric_name}.condition"
     unexpected_count_metric_name: str = f"{metric_name}.unexpected_count"
     unexpected_rows_metric_name: str = f"{metric_name}.unexpected_rows"
     unexpected_values_metric_name: str = f"{metric_name}.unexpected_values"
 
+    # First, assert Pass (no unexpected results).
+
     condition_metric = MetricConfiguration(
         metric_name=condition_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b", "c"],
-            "ignore_row_if": "all_values_are_missing",
+            "column_list": ["a", "b"],
         },
         metric_value_kwargs=None,
         metric_dependencies={
@@ -5409,8 +5446,7 @@ def test_map_select_column_values_unique_within_record_pd():
     unexpected_count_metric = MetricConfiguration(
         metric_name=unexpected_count_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b", "c"],
-            "ignore_row_if": "all_values_are_missing",
+            "column_list": ["a", "b"],
         },
         metric_value_kwargs=None,
         metric_dependencies={
@@ -5424,25 +5460,15 @@ def test_map_select_column_values_unique_within_record_pd():
     metrics.update(results)
 
     # Condition metrics return "negative logic" series.
-    assert list(metrics[condition_metric.id][0]) == [
-        True,
-        False,
-        False,
-        False,
-        True,
-        True,
-        False,
-    ]
-    assert metrics[unexpected_count_metric.id] == 3
+    assert metrics[unexpected_count_metric.id] == 0
 
     unexpected_rows_metric = MetricConfiguration(
         metric_name=unexpected_rows_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b", "c"],
-            "ignore_row_if": "all_values_are_missing",
+            "column_list": ["a", "b"],
         },
         metric_value_kwargs={
-            "result_format": {"result_format": "SUMMARY", "partial_unexpected_count": 8}
+            "result_format": {"result_format": "SUMMARY", "partial_unexpected_count": 3}
         },
         metric_dependencies={
             "unexpected_condition": condition_metric,
@@ -5454,22 +5480,12 @@ def test_map_select_column_values_unique_within_record_pd():
     )
     metrics.update(results)
 
-    assert metrics[unexpected_rows_metric.id].equals(
-        pd.DataFrame(
-            data={"a": [1.0, 4.0, None], "b": [1.0, 4.0, None], "c": [2.0, 4.0, 9.0]},
-            index=[0, 4, 6],
-        )
-    )
-    assert len(metrics[unexpected_rows_metric.id].columns) == 3
-    pd.testing.assert_index_equal(
-        metrics[unexpected_rows_metric.id].index, pd.Index([0, 4, 6])
-    )
+    assert metrics[unexpected_rows_metric.id] == []
 
     unexpected_values_metric = MetricConfiguration(
         metric_name=unexpected_values_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b", "c"],
-            "ignore_row_if": "all_values_are_missing",
+            "column_list": ["a", "b"],
         },
         metric_value_kwargs={
             "result_format": {"result_format": "SUMMARY", "partial_unexpected_count": 3}
@@ -5484,30 +5500,18 @@ def test_map_select_column_values_unique_within_record_pd():
     )
     metrics.update(results)
 
-    assert len(metrics[unexpected_values_metric.id]) == 3
-
-    unexpected_values = []
-    for unexpected_value_dict in metrics[unexpected_values_metric.id]:
-        updated_unexpected_value_dict = {
-            key: "NULL" if np.isnan(value) else value
-            for key, value in unexpected_value_dict.items()
-        }
-        unexpected_values.append(updated_unexpected_value_dict)
-
-    assert unexpected_values == [
-        {"a": 1.0, "b": 1.0, "c": 2.0},
-        {"a": 4.0, "b": 4.0, "c": 4.0},
-        {"a": "NULL", "b": "NULL", "c": 9.0},
-    ]
+    assert len(metrics[unexpected_values_metric.id]) == 0
+    assert metrics[unexpected_values_metric.id] == []
 
     # Restore from saved original metrics in order to start fresh on testing for unexpected results.
     metrics = copy.deepcopy(metrics_save)
 
+    # Second, assert Fail (one or more unexpected results).
+
     condition_metric = MetricConfiguration(
         metric_name=condition_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b", "c"],
-            "ignore_row_if": "any_value_is_missing",
+            "column_list": ["a", "c"],
         },
         metric_value_kwargs=None,
         metric_dependencies={
@@ -5523,8 +5527,7 @@ def test_map_select_column_values_unique_within_record_pd():
     unexpected_count_metric = MetricConfiguration(
         metric_name=unexpected_count_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b", "c"],
-            "ignore_row_if": "any_value_is_missing",
+            "column_list": ["a", "c"],
         },
         metric_value_kwargs=None,
         metric_dependencies={
@@ -5538,21 +5541,12 @@ def test_map_select_column_values_unique_within_record_pd():
     metrics.update(results)
 
     # Condition metrics return "negative logic" series.
-    assert list(metrics[condition_metric.id][0]) == [
-        True,
-        False,
-        False,
-        False,
-        True,
-        False,
-    ]
     assert metrics[unexpected_count_metric.id] == 2
 
     unexpected_rows_metric = MetricConfiguration(
         metric_name=unexpected_rows_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b", "c"],
-            "ignore_row_if": "any_value_is_missing",
+            "column_list": ["a", "c"],
         },
         metric_value_kwargs={
             "result_format": {"result_format": "SUMMARY", "partial_unexpected_count": 3}
@@ -5567,21 +5561,12 @@ def test_map_select_column_values_unique_within_record_pd():
     )
     metrics.update(results)
 
-    assert metrics[unexpected_rows_metric.id].equals(
-        pd.DataFrame(
-            data={"a": [1.0, 4.0], "b": [1.0, 4.0], "c": [2.0, 4.0]}, index=[0, 4]
-        )
-    )
-    assert len(metrics[unexpected_rows_metric.id].columns) == 3
-    pd.testing.assert_index_equal(
-        metrics[unexpected_rows_metric.id].index, pd.Index([0, 4])
-    )
+    assert metrics[unexpected_rows_metric.id] == [(1, 2, 2), (1, 3, 2)]
 
     unexpected_values_metric = MetricConfiguration(
         metric_name=unexpected_values_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b", "c"],
-            "ignore_row_if": "any_value_is_missing",
+            "column_list": ["a", "c"],
         },
         metric_value_kwargs={
             "result_format": {"result_format": "SUMMARY", "partial_unexpected_count": 3}
@@ -5597,16 +5582,18 @@ def test_map_select_column_values_unique_within_record_pd():
     metrics.update(results)
 
     assert len(metrics[unexpected_values_metric.id]) == 2
-    assert metrics[unexpected_values_metric.id] == [
-        {"a": 1.0, "b": 1.0, "c": 2.0},
-        {"a": 4.0, "b": 4.0, "c": 4.0},
-    ]
+    assert metrics[unexpected_values_metric.id] == [{"a": 1, "c": 2}, {"a": 1, "c": 2}]
 
 
-def test_map_compound_columns_unique_sa(sa):
-    engine = build_sa_engine(
-        pd.DataFrame(data={"a": [0, 1, 1], "b": [1, 2, 3], "c": [0, 2, 2]}),
-        sa,
+def test_map_select_column_values_unique_within_record_pd():
+    engine = build_pandas_engine(
+        pd.DataFrame(
+            data={
+                "a": [1, 1, 8, 1, 4, None, None, 7],
+                "b": [1, 2, 2, 2, 4, None, None, 1],
+                "c": [2, 3, 7, 3, 4, None, 9, 0],
+            }
+        )
     )
 
     metrics: dict = {}
@@ -5617,49 +5604,23 @@ def test_map_compound_columns_unique_sa(sa):
     table_columns_metric, results = get_table_columns_metric(engine=engine)
     metrics.update(results)
 
-    """
-    Two tests:
-    1. Pass -- no duplicated compound column keys.
-    2. Fail -- one or more duplicated compound column keys.
-    """
-
     # Save original metrics for testing unexpected results.
     metrics_save: dict = copy.deepcopy(metrics)
 
-    prerequisite_function_metric_name: str = "compound_columns.count.map"
-
-    prerequisite_function_metric = MetricConfiguration(
-        metric_name=prerequisite_function_metric_name,
-        metric_domain_kwargs={
-            "column_list": ["a", "b"],
-        },
-        metric_value_kwargs=None,
-        metric_dependencies={
-            "table.columns": table_columns_metric,
-        },
-    )
-    results = engine.resolve_metrics(
-        metrics_to_resolve=(prerequisite_function_metric,),
-        metrics=metrics,
-    )
-    metrics.update(results)
-
-    metric_name: str = "compound_columns.unique"
+    metric_name: str = "select_column_values.unique.within_record"
     condition_metric_name: str = f"{metric_name}.condition"
     unexpected_count_metric_name: str = f"{metric_name}.unexpected_count"
     unexpected_rows_metric_name: str = f"{metric_name}.unexpected_rows"
     unexpected_values_metric_name: str = f"{metric_name}.unexpected_values"
 
-    # First, assert Pass (no unexpected results).
-
     condition_metric = MetricConfiguration(
         metric_name=condition_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b"],
+            "column_list": ["a", "b", "c"],
+            "ignore_row_if": "all_values_are_missing",
         },
         metric_value_kwargs=None,
         metric_dependencies={
-            "compound_columns.count.map": prerequisite_function_metric,
             "table.columns": table_columns_metric,
         },
     )
@@ -5672,7 +5633,8 @@ def test_map_compound_columns_unique_sa(sa):
     unexpected_count_metric = MetricConfiguration(
         metric_name=unexpected_count_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b"],
+            "column_list": ["a", "b", "c"],
+            "ignore_row_if": "all_values_are_missing",
         },
         metric_value_kwargs=None,
         metric_dependencies={
@@ -5685,16 +5647,26 @@ def test_map_compound_columns_unique_sa(sa):
     )
     metrics.update(results)
 
-    # Condition metrics return SQLAlchemy ColumnElement object.
-    assert metrics[unexpected_count_metric.id] == 0
+    # Condition metrics return "negative logic" series.
+    assert list(metrics[condition_metric.id][0]) == [
+        True,
+        False,
+        False,
+        False,
+        True,
+        True,
+        False,
+    ]
+    assert metrics[unexpected_count_metric.id] == 3
 
     unexpected_rows_metric = MetricConfiguration(
         metric_name=unexpected_rows_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b"],
+            "column_list": ["a", "b", "c"],
+            "ignore_row_if": "all_values_are_missing",
         },
         metric_value_kwargs={
-            "result_format": {"result_format": "SUMMARY", "partial_unexpected_count": 3}
+            "result_format": {"result_format": "SUMMARY", "partial_unexpected_count": 8}
         },
         metric_dependencies={
             "unexpected_condition": condition_metric,
@@ -5706,12 +5678,22 @@ def test_map_compound_columns_unique_sa(sa):
     )
     metrics.update(results)
 
-    assert len(metrics[unexpected_rows_metric.id]) == 0
+    assert metrics[unexpected_rows_metric.id].equals(
+        pd.DataFrame(
+            data={"a": [1.0, 4.0, None], "b": [1.0, 4.0, None], "c": [2.0, 4.0, 9.0]},
+            index=[0, 4, 6],
+        )
+    )
+    assert len(metrics[unexpected_rows_metric.id].columns) == 3
+    pd.testing.assert_index_equal(
+        metrics[unexpected_rows_metric.id].index, pd.Index([0, 4, 6])
+    )
 
     unexpected_values_metric = MetricConfiguration(
         metric_name=unexpected_values_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "b"],
+            "column_list": ["a", "b", "c"],
+            "ignore_row_if": "all_values_are_missing",
         },
         metric_value_kwargs={
             "result_format": {"result_format": "SUMMARY", "partial_unexpected_count": 3}
@@ -5726,37 +5708,33 @@ def test_map_compound_columns_unique_sa(sa):
     )
     metrics.update(results)
 
-    assert len(metrics[unexpected_values_metric.id]) == 0
+    assert len(metrics[unexpected_values_metric.id]) == 3
 
-    # Restore from saved original metrics in order to start fresh on testing for unexpected results.
-    metrics = copy.deepcopy(metrics_save)
+    unexpected_values = []
+    for unexpected_value_dict in metrics[unexpected_values_metric.id]:
+        updated_unexpected_value_dict = {
+            key: "NULL" if np.isnan(value) else value
+            for key, value in unexpected_value_dict.items()
+        }
+        unexpected_values.append(updated_unexpected_value_dict)
 
-    # Second, assert Fail (one or more unexpected results).
+    assert unexpected_values == [
+        {"a": 1.0, "b": 1.0, "c": 2.0},
+        {"a": 4.0, "b": 4.0, "c": 4.0},
+        {"a": "NULL", "b": "NULL", "c": 9.0},
+    ]
 
-    prerequisite_function_metric = MetricConfiguration(
-        metric_name=prerequisite_function_metric_name,
-        metric_domain_kwargs={
-            "column_list": ["a", "c"],
-        },
-        metric_value_kwargs=None,
-        metric_dependencies={
-            "table.columns": table_columns_metric,
-        },
-    )
-    results = engine.resolve_metrics(
-        metrics_to_resolve=(prerequisite_function_metric,),
-        metrics=metrics,
-    )
-    metrics.update(results)
+    # Restore from saved original metrics in order to start fresh on testing for unexpected results.
+    metrics = copy.deepcopy(metrics_save)
 
     condition_metric = MetricConfiguration(
         metric_name=condition_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "c"],
+            "column_list": ["a", "b", "c"],
+            "ignore_row_if": "any_value_is_missing",
         },
         metric_value_kwargs=None,
         metric_dependencies={
-            "compound_columns.count.map": prerequisite_function_metric,
             "table.columns": table_columns_metric,
         },
     )
@@ -5769,7 +5747,8 @@ def test_map_compound_columns_unique_sa(sa):
     unexpected_count_metric = MetricConfiguration(
         metric_name=unexpected_count_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "c"],
+            "column_list": ["a", "b", "c"],
+            "ignore_row_if": "any_value_is_missing",
         },
         metric_value_kwargs=None,
         metric_dependencies={
@@ -5782,13 +5761,22 @@ def test_map_compound_columns_unique_sa(sa):
     )
     metrics.update(results)
 
-    # Condition metrics return SQLAlchemy ColumnElement object.
+    # Condition metrics return "negative logic" series.
+    assert list(metrics[condition_metric.id][0]) == [
+        True,
+        False,
+        False,
+        False,
+        True,
+        False,
+    ]
     assert metrics[unexpected_count_metric.id] == 2
 
     unexpected_rows_metric = MetricConfiguration(
         metric_name=unexpected_rows_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "c"],
+            "column_list": ["a", "b", "c"],
+            "ignore_row_if": "any_value_is_missing",
         },
         metric_value_kwargs={
             "result_format": {"result_format": "SUMMARY", "partial_unexpected_count": 3}
@@ -5803,12 +5791,21 @@ def test_map_compound_columns_unique_sa(sa):
     )
     metrics.update(results)
 
-    assert metrics[unexpected_rows_metric.id] == [(1, 2, 2), (1, 3, 2)]
+    assert metrics[unexpected_rows_metric.id].equals(
+        pd.DataFrame(
+            data={"a": [1.0, 4.0], "b": [1.0, 4.0], "c": [2.0, 4.0]}, index=[0, 4]
+        )
+    )
+    assert len(metrics[unexpected_rows_metric.id].columns) == 3
+    pd.testing.assert_index_equal(
+        metrics[unexpected_rows_metric.id].index, pd.Index([0, 4])
+    )
 
     unexpected_values_metric = MetricConfiguration(
         metric_name=unexpected_values_metric_name,
         metric_domain_kwargs={
-            "column_list": ["a", "c"],
+            "column_list": ["a", "b", "c"],
+            "ignore_row_if": "any_value_is_missing",
         },
         metric_value_kwargs={
             "result_format": {"result_format": "SUMMARY", "partial_unexpected_count": 3}
@@ -5824,7 +5821,10 @@ def test_map_compound_columns_unique_sa(sa):
     metrics.update(results)
 
     assert len(metrics[unexpected_values_metric.id]) == 2
-    assert metrics[unexpected_values_metric.id] == [{"a": 1, "c": 2}, {"a": 1, "c": 2}]
+    assert metrics[unexpected_values_metric.id] == [
+        {"a": 1.0, "b": 1.0, "c": 2.0},
+        {"a": 4.0, "b": 4.0, "c": 4.0},
+    ]
 
 
 def test_map_select_column_values_unique_within_record_sa(sa):