You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单个Java类中实现Normalizer与ScoringSummary接口?需CSV解析依赖吗?

实现方案说明

一、接口实现逻辑

你可以在同一个类中实现两个接口,不过更推荐按单一职责拆分实现,可维护性更高:

1. 先实现ScoringSummary接口

先写一个不可变的实现类存储统计结果,实现ScoringSummary接口:

import java.math.BigDecimal;

public class ScoringSummaryImpl implements ScoringSummary {
    private final BigDecimal mean;
    private final BigDecimal standardDeviation;
    private final BigDecimal variance;
    private final BigDecimal median;
    private final BigDecimal min;
    private final BigDecimal max;

    public ScoringSummaryImpl(BigDecimal mean, BigDecimal standardDeviation, BigDecimal variance, BigDecimal median, BigDecimal min, BigDecimal max) {
        this.mean = mean;
        this.standardDeviation = standardDeviation;
        this.variance = variance;
        this.median = median;
        this.min = min;
        this.max = max;
    }

    @Override
    public BigDecimal mean() {
        return mean;
    }

    @Override
    public BigDecimal standardDeviation() {
        return standardDeviation;
    }

    @Override
    public BigDecimal variance() {
        return variance;
    }

    @Override
    public BigDecimal median() {
        return median;
    }

    @Override
    public BigDecimal min() {
        return min;
    }

    @Override
    public BigDecimal max() {
        return max;
    }
}

2. 实现Normalizer接口

写CsvNormalizer类实现Normalizer接口,内部处理CSV读写、数值计算逻辑,计算完成后返回ScoringSummaryImpl实例:

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Path;
import java.util.List;

public class CsvNormalizer implements Normalizer {
    private static final int SCALE = 2;
    private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;

    @Override
    public ScoringSummary zscore(Path csvPath, Path destPath, String colToStandardize) {
        // 步骤1:读取CSV,提取目标列所有数值
        List<BigDecimal> columnValues = readColumnValues(csvPath, colToStandardize);
        // 步骤2:计算统计指标
        BigDecimal mean = calculateMean(columnValues);
        BigDecimal variance = calculateVariance(columnValues, mean);
        BigDecimal std = variance.sqrt(ROUNDING_MODE).setScale(SCALE, ROUNDING_MODE);
        BigDecimal median = calculateMedian(columnValues);
        BigDecimal min = columnValues.stream().min(BigDecimal::compareTo).orElseThrow();
        BigDecimal max = columnValues.stream().max(BigDecimal::compareTo).orElseThrow();
        // 步骤3:逐行计算Z-Score,写入新CSV
        writeScaledCsv(csvPath, destPath, colToStandardize, val -> 
            val.subtract(mean).divide(std, SCALE, ROUNDING_MODE), "z_score"
        );
        // 封装返回统计结果
        return new ScoringSummaryImpl(
            mean.setScale(SCALE, ROUNDING_MODE),
            std,
            variance.setScale(SCALE, ROUNDING_MODE),
            median.setScale(SCALE, ROUNDING_MODE),
            min.setScale(SCALE, ROUNDING_MODE),
            max.setScale(SCALE, ROUNDING_MODE)
        );
    }

    @Override
    public ScoringSummary minMaxScaling(Path csvPath, Path destPath, String colToNormalize) {
        // 逻辑和zscore类似,仅计算公式替换为 (x-min)/(max-min) 即可
        List<BigDecimal> columnValues = readColumnValues(csvPath, colToNormalize);
        BigDecimal mean = calculateMean(columnValues);
        BigDecimal variance = calculateVariance(columnValues, mean);
        BigDecimal std = variance.sqrt(ROUNDING_MODE).setScale(SCALE, ROUNDING_MODE);
        BigDecimal median = calculateMedian(columnValues);
        BigDecimal min = columnValues.stream().min(BigDecimal::compareTo).orElseThrow();
        BigDecimal max = columnValues.stream().max(BigDecimal::compareTo).orElseThrow();
        BigDecimal range = max.subtract(min);
        writeScaledCsv(csvPath, destPath, colToNormalize, val -> 
            val.subtract(min).divide(range, SCALE, ROUNDING_MODE), "min_max_score"
        );
        return new ScoringSummaryImpl(
            mean.setScale(SCALE, ROUNDING_MODE),
            std,
            variance.setScale(SCALE, ROUNDING_MODE),
            median.setScale(SCALE, ROUNDING_MODE),
            min.setScale(SCALE, ROUNDING_MODE),
            max.setScale(SCALE, ROUNDING_MODE)
        );
    }

    // 以下是私有工具方法,实现读取CSV列、计算统计值、写入CSV的逻辑
    private List<BigDecimal> readColumnValues(Path csvPath, String columnName) {
        // 实现CSV读取,提取目标列的数值
    }

    private BigDecimal calculateMean(List<BigDecimal> values) {
        BigDecimal sum = values.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        return sum.divide(BigDecimal.valueOf(values.size()), SCALE, ROUNDING_MODE);
    }

    private BigDecimal calculateVariance(List<BigDecimal> values, BigDecimal mean) {
        BigDecimal sumSquareDiff = values.stream()
            .map(val -> val.subtract(mean).pow(2))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        return sumSquareDiff.divide(BigDecimal.valueOf(values.size()), SCALE, ROUNDING_MODE);
    }

    private BigDecimal calculateMedian(List<BigDecimal> values) {
        List<BigDecimal> sorted = values.stream().sorted().toList();
        int size = sorted.size();
        if (size % 2 == 1) {
            return sorted.get(size / 2);
        } else {
            return sorted.get(size / 2 - 1).add(sorted.get(size / 2))
                .divide(BigDecimal.valueOf(2), SCALE, ROUNDING_MODE);
        }
    }

    private void writeScaledCsv(Path csvPath, Path destPath, String targetCol, java.util.function.Function<BigDecimal, BigDecimal> scoreCalculator, String newColName) {
        // 实现CSV逐行读取,计算得分后写入新文件
    }
}

如果你硬要把两个接口的实现放到同一个类中,只需要让类同时implements Normalizer, ScoringSummary即可,但是需要注意统计值的存储时机,不符合单一职责原则,不推荐这么做。

二、CSV解析依赖说明

你不需要引入重型框架,有两种方案可选:

  • 推荐引入轻量CSV处理库OpenCSV,避免手动处理CSV的分隔符转义、字段含引号/换行、空值等边界问题,Maven依赖如下:
<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.6</version>
</dependency>
  • 如果不想引入第三方依赖,也可以用Java原生的BufferedReader逐行读取按逗号拆分,仅适合CSV格式非常规范、无特殊字符的场景。

内容的提问来源于stack exchange,提问作者Shahed Abd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 05:27:03