def _positive_predicted_index(predicted_label_data: pd.Series,
                              label_data: pd.Series,
                              positive_label_values: List[Any]) -> pd.Series:
    """
    creates a list of bool series for positive predicted label index based on the input data type,
    list of positive label values or intervals

    :param predicted_label_data: input data for predicted label column
    :param label_datatype:  input data for the label column
    :param positive_label_values: list of positive label values
    :return: list of positive predicted label index series
    """
    predicted_label_datatype = common.series_datatype(predicted_label_data,
                                                      positive_label_values)
    label_datatype = common.series_datatype(label_data, positive_label_values)
    if predicted_label_datatype != label_datatype:
        raise AssertionError(
            "Predicted Label Column series datatype is not the same as Label Column series"
        )
    try:
        predicted_label_data = predicted_label_data.astype(label_data.dtype)
    except ValueError as e:
        raise ValueError(
            "Labels and predicted labels cannot have different types (%s, %s)."
            % (label_data.dtype, predicted_label_data.dtype))
    if predicted_label_datatype == common.DataType.CONTINUOUS:
        data_interval_indices = _interval_index(
            label_data.append(predicted_label_data), positive_label_values)
        positive_predicted_index = _continuous_data_idx(
            predicted_label_data, data_interval_indices)
    elif predicted_label_datatype == common.DataType.CATEGORICAL and positive_label_values:
        positive_predicted_index = _categorical_data_idx(
            predicted_label_data, positive_label_values)
    else:
        raise RuntimeError(
            "Predicted Label_column data is invalid or can't be classified")
    # check if positive index boolean series has all False values
    if (~positive_predicted_index).all():
        raise ValueError(
            "No Label values are present in the predicted Label Column,"
            "Positive Predicted Index Series contains all False values")
    return positive_predicted_index
def _positive_label_index(data: pd.Series, positive_values: List[Any]) -> Tuple[pd.Series, str]:
    """
    creates a list of bool series for positive label index based on the input data type, list of positive
    label values or intervals

    :param data: input data for label column
    :param positive_values: list of positive label values
    :return: list of positive label index series, positive_label_values or intervals
    """
    data_type = common.series_datatype(data, positive_values)
    if data_type == common.DataType.CONTINUOUS:
        data_interval_indices = _interval_index(data, positive_values)
        positive_index = _continuous_data_idx(data, data_interval_indices)
        label_values_or_intervals = ",".join(map(str, data_interval_indices))
    elif data_type == common.DataType.CATEGORICAL and positive_values:
        positive_index = _categorical_data_idx(data, positive_values)
        label_values_or_intervals = ",".join(map(str, positive_values))
    else:
        raise RuntimeError("Label_column data is invalid or can't be classified")
    logger.debug(f"positive index: {positive_index}")
    logger.debug(f"label values or intervals: {label_values_or_intervals}")
    return positive_index, label_values_or_intervals
def bias_report(
    df: pd.DataFrame,
    facet_column: FacetColumn,
    label_column: LabelColumn,
    stage_type: StageType,
    predicted_label_column: LabelColumn = None,
    metrics: List[Any] = ["all"],
    group_variable: Optional[pd.Series] = None,
) -> List[Dict]:
    """
    Run full bias report on a dataset.

    The report computes the bias metric for multi-facet, and multi-class inputs by
    computing the sensitive_facet_index, positive_label_index, and positive_predicted_label_index by collapsing the
    multiple categories into two, as indicated by the facet_column, label_column, and predicted_label_column respectively.

    :param df: Dataset as a pandas.DataFrame
    :param facet_column: description of column to consider for Bias analysis
    :param label_column: description of column which has the labels.
    :param stage_type: pre_training or post_training for which bias metrics is computed
    :param predicted_label_column: description of column with predicted labels
    :param metrics: list of metrics names to provide bias metrics
    :param group_variable: data series for the group variable
    :return: list of dictionaries with metrics for different label values
    """
    if facet_column:
        if facet_column.name not in df.columns:
            raise ValueError("Facet column {} is not present in the dataset".format(facet_column.name))
    if not label_column.positive_label_values:
        raise ValueError("Positive label values or thresholds are empty for Label column")
    if isinstance(predicted_label_column, LabelColumn) and predicted_label_column.positive_label_values:
        if predicted_label_column.positive_label_values != label_column.positive_label_values:
            raise ValueError(
                "Positive predicted label values or threshold should be empty or same as label values or thresholds"
            )
    if not isinstance(stage_type, StageType):
        raise ValueError("stage_type should be a Enum value of StageType")
    if not predicted_label_column and stage_type == StageType.POST_TRAINING:
        raise ValueError("predicted_label_column has to be provided for Post training metrics")
    data_series: pd.Series = df[facet_column.name]
    df = df.drop(facet_column.name, 1)
    label_series: pd.Series = label_column.data
    positive_label_index, label_values = _positive_label_index(
        data=label_series, positive_values=label_column.positive_label_values
    )
    if label_column.name in df.columns:
        df = df.drop(label_column.name, 1)

    metrics_to_run = []
    if predicted_label_column and stage_type == StageType.POST_TRAINING:
        post_training_metrics = (
            smclarify.bias.metrics.POSTTRAINING_METRICS
            if metrics == ["all"]
            else fetch_metrics_to_run(smclarify.bias.metrics.POSTTRAINING_METRICS, metrics)
        )
        metrics_to_run.extend(post_training_metrics)
        predicted_label_series = predicted_label_column.data
        positive_predicted_label_index = _positive_predicted_index(
            predicted_label_data=predicted_label_series,
            label_data=label_series,
            positive_label_values=label_column.positive_label_values,
        )
        if predicted_label_column.name in df.columns:
            df = df.drop(predicted_label_column.name, 1)
    else:
        positive_predicted_label_index = [None]
        pre_training_metrics = (
            smclarify.bias.metrics.PRETRAINING_METRICS
            if metrics == ["all"]
            else fetch_metrics_to_run(smclarify.bias.metrics.PRETRAINING_METRICS, metrics)
        )
        metrics_to_run.extend(pre_training_metrics)
    metrics_to_run.sort(key=_metric_name_comparator)

    facet_dtype = common.series_datatype(data_series, facet_column.sensitive_values)
    data_series_cat: pd.Series  # Category series
    # result values can be str for label_values or dict for metrics
    result: MetricResult
    facet_metric: FacetReport
    metrics_result = []
    if facet_dtype == common.DataType.CATEGORICAL:
        data_series_cat = data_series.astype("category")
        # pass the values for metric one vs all case
        facet_values_list = (
            [[val] for val in list(data_series.unique())]
            if not facet_column.sensitive_values
            else [facet_column.sensitive_values]
        )
        for facet_values in facet_values_list:
            # list of metrics with values
            metrics_list = []
            for metric in metrics_to_run:
                result = _categorical_metric_call_wrapper(
                    metric,
                    df,
                    data_series_cat,
                    facet_values,
                    positive_label_index,
                    positive_predicted_label_index,
                    group_variable,
                )
                metrics_list.append(result)
            facet_metric = FacetReport(facet_value_or_threshold=",".join(map(str, facet_values)), metrics=metrics_list)
            metrics_result.append(facet_metric.toJson())
        logger.debug("metric_result: %s", str(metrics_result))
        return metrics_result

    elif facet_dtype == common.DataType.CONTINUOUS:
        facet_interval_indices = _interval_index(data_series, facet_column.sensitive_values)
        facet_continuous_column = FacetContinuousColumn(facet_column.name, facet_interval_indices)
        logger.info(f"Threshold Interval indices: {facet_interval_indices}")
        # list of metrics with values
        metrics_list = []
        for metric in metrics_to_run:
            result = _continuous_metric_call_wrapper(
                metric,
                df,
                data_series,
                facet_continuous_column.interval_indices,
                positive_label_index,
                positive_predicted_label_index,
                group_variable,
            )
            metrics_list.append(result)
        facet_metric = FacetReport(
            facet_value_or_threshold=",".join(map(str, facet_interval_indices)), metrics=metrics_list
        )
        metrics_result.append(facet_metric.toJson())
        logger.debug("metric_result:", metrics_result)
        return metrics_result
    else:
        raise RuntimeError("facet_column data is invalid or can't be classified")