Skip to content

熵值法综合指标

熵值法客观赋权:极差标准化(区分正 / 负向指标)→ 比重 pij → 熵值 ej=1lnnipijlnpij → 权重 wj=(1ej)/j(1ej) → 加权综合得分。

核心代码

py
def build_entropy_index(
    *,
    df: pd.DataFrame,
    params: dict[str, Any],
    column_descriptions: dict[str, str],
) -> tuple[pd.DataFrame, dict[str, Any], list[str], dict[str, str]]:
    """熵值法综合指标。"""
    indicators = require_numeric_columns(df, normalize_string_list(params.get('columns')), label=_('指标变量'))
    new_name = ensure_new_column_absent(df, params.get('new_name') or 'entropy_score')
    directions = params.get('directions') if isinstance(params.get('directions'), dict) else {}
    working, valid_columns, warnings = _prepare_indicator_frame(
        df=df,
        indicators=indicators,
        missing_strategy=str(params.get('missing_strategy') or 'drop').strip().lower(),
        standardize=False,
    )
    normalized = pd.DataFrame(index=working.index)
    removed_columns = []

    for column in valid_columns:
        series = working[column]
        maximum = series.max()
        minimum = series.min()
        if maximum == minimum:
            removed_columns.append(column)
            continue
        direction = str(directions.get(column) or 'positive').strip().lower()
        if direction == 'negative':
            normalized[column] = (maximum - series) / (maximum - minimum)
        else:
            normalized[column] = (series - minimum) / (maximum - minimum)

    normalized = normalized.drop(columns=removed_columns, errors='ignore')
    if normalized.empty:
        raise DataProcessingValidationError(_('所有指标都没有有效波动,无法计算熵值法'))
    if removed_columns:
        warnings.append(_('以下指标为常数列,已自动剔除: %(columns)s') % {'columns': ', '.join(removed_columns)})

    adjusted = normalized.replace(0, 1e-12)
    probability = adjusted.div(adjusted.sum(axis=0), axis=1)
    sample_size = len(probability)
    entropy_coefficient = 1.0 / np.log(sample_size) if sample_size > 1 else 0.0
    entropy = -(probability * np.log(probability)).sum(axis=0) * entropy_coefficient
    divergence = 1 - entropy
    if divergence.sum() == 0:
        raise DataProcessingValidationError(_('熵值法权重无法计算,所有指标信息量都为 0'))
    weights = divergence / divergence.sum()
    scores = normalized.mul(weights, axis=1).sum(axis=1)

    result_df = df.copy()
    result_df[new_name] = np.nan
    result_df.loc[scores.index, new_name] = scores

    descriptions = dict(column_descriptions)
    descriptions[new_name] = _('熵值法综合得分')
    details = {
        'created_column': new_name,
        'weights': {column: round(float(value), 6) for column, value in weights.items()},
        'entropy': {column: round(float(value), 6) for column, value in entropy.items()},
        'directions': {
            column: str(directions.get(column) or 'positive').strip().lower()
            for column in normalized.columns
        },
        'removed_constant_columns': removed_columns,
        'score_summary': _build_score_summary(scores),
        'score_histogram': _build_histogram(scores),
        'used_columns': list(normalized.columns),
    }
    return result_df, details, warnings, descriptions

极小样本复现

样本 entropy_indicators.csv 共 30 行 × 5 项指标(ind5 为负向指标)。

bash
python examples/run_entropy.py

期望输出:

============================================================
熵值法综合指标(5 项指标,ind5 负向)
============================================================
样本量 N = 30
------------------------------------------------------------
指标                   权重           熵值
ind1           0.141727     0.968064
ind2           0.273399     0.938394
ind3           0.1797      0.959507
ind4           0.123396     0.972195
ind5           0.281778     0.936505
------------------------------------------------------------
权重合计 = 1.000000
得分范围: 0.283177 ~ 0.826239

Released under the AGPL-3.0 License.