Constructs an expectation using column-aggregate semantics.

        The MetaPandasDataset implementation replaces the "column" parameter supplied by the user with a pandas
        Series object containing the actual column from the relevant pandas dataframe. This simplifies the implementing
        expectation logic while preserving the standard Dataset signature and expected behavior.

        See :func:`column_aggregate_expectation <great_expectations.data_asset.dataset.Dataset.column_aggregate_expectation>` \
        for full documentation of this function.

def column_aggregate_expectation(cls, func):
        """Constructs an expectation using column-aggregate semantics.

        The MetaPandasDataset implementation replaces the "column" parameter supplied by the user with a pandas
        Series object containing the actual column from the relevant pandas dataframe. This simplifies the implementing
        expectation logic while preserving the standard Dataset signature and expected behavior.

        See :func:`column_aggregate_expectation <great_expectations.data_asset.dataset.Dataset.column_aggregate_expectation>` \
        for full documentation of this function.
        """
        if PY3:
            argspec = inspect.getfullargspec(func)[0][1:]
        else:
            argspec = inspect.getargspec(func)[0][1:]

        @cls.expectation(argspec)
        @wraps(func)
        def inner_wrapper(self, column, result_format=None, *args, **kwargs):

            if result_format is None:
                result_format = self.default_expectation_args["result_format"]

            series = self[column]
            null_indexes = series.isnull()

            element_count = int(len(series))
            nonnull_values = series[null_indexes == False]
            # Simplify this expression because the old version fails under pandas 0.21 (but only that version)
            # nonnull_count = int((null_indexes == False).sum())
            nonnull_count = len(nonnull_values)
            null_count = element_count - nonnull_count

            evaluation_result = func(self, nonnull_values, *args, **kwargs)

            if 'success' not in evaluation_result:
                raise ValueError(
                    "Column aggregate expectation failed to return required information: success")

            if ('result' not in evaluation_result) or ('observed_value' not in evaluation_result['result']):
                raise ValueError(
                    "Column aggregate expectation failed to return required information: observed_value")

            # Retain support for string-only output formats:
            result_format = parse_result_format(result_format)

            return_obj = {
                'success': bool(evaluation_result['success'])
            }

            if result_format['result_format'] == 'BOOLEAN_ONLY':
                return return_obj

            return_obj['result'] = {
                'observed_value': evaluation_result['result']['observed_value'],
                "element_count": element_count,
                "missing_count": null_count,
                "missing_percent": null_count * 1.0 / element_count if element_count > 0 else None
            }

            if result_format['result_format'] == 'BASIC':
                return return_obj

            if 'details' in evaluation_result['result']:
                return_obj['result']['details'] = evaluation_result['result']['details']

            if result_format['result_format'] in ["SUMMARY", "COMPLETE"]:
                return return_obj

            raise ValueError("Unknown result_format %s." %
                             (result_format['result_format'],))

        return inner_wrapper