【农气项目】基于丰谦指数的产量预报

直接上干货(复制到开发工具即可运行的代码)

1. 基础类

2. 计算有效积温、分段降水等基础数据

3. 气象数据和产量的相关性分析

4. 计算丰歉指数的产量预报

基于相似年的大概率分析法预测产量

基于加权平均法预测产量

1. 基础类

/**
 * 每日的气象数据
 */
public class DailyWeather {
    LocalDate date;
    double avgTemp;    // 平均气温(℃)
    double maxTemp;    // 最高气温(℃)
    double minTemp;    // 最低气温(℃)
    double precipitation; // 降水量(mm)
    double sunshine;   // 日照时数(h)

    // 构造函数
    public DailyWeather(LocalDate date, double avgTemp, double maxTemp, 
                       double minTemp, double precipitation, double sunshine) {
        this.date = date;
        this.avgTemp = avgTemp;
        this.maxTemp = maxTemp;
        this.minTemp = minTemp;
        this.precipitation = precipitation;
        this.sunshine = sunshine;
    }
}
//基于丰谦指数的产量预报气象数据处理结果
public class FiveDaySummary {
    LocalDate startDate;
    LocalDate endDate;
    double effectiveAccTemp;  // 有效积温
    double cumulativeAccTemp; // 累计积温
    double segmentPrecip;    // 分段降水
    double cumulativePrecip; // 累计降水
    double segmentSunshine;  // 分段日照
    double cumulativeSunshine; // 累计日照
    
    // 标准化值
    double normalizedSegmentPrecip;
    double normalizedCumulativePrecip;
    double normalizedSegmentSunshine;
    double normalizedCumulativeSunshine;

    // 构造函数
    public FiveDaySummary(LocalDate startDate, LocalDate endDate) {
        this.startDate = startDate;
        this.endDate = endDate;
    }
}

2. 计算有效积温、分段降水等基础数据

public class AgroMeteorologicalCalculator {
    // 修改1:改为实例变量,支持动态配置
    private double baseTemp;  // 发育起点温度(下限)
    private double upperTemp; // 发育上限温度(上限)

    // 修改2:通过构造函数传入温度阈值
    public AgroMeteorologicalCalculator(double baseTemp, double upperTemp) {
        if (baseTemp >= upperTemp) {
            throw new IllegalArgumentException("发育起点温度必须小于上限温度");
        }
        this.baseTemp = baseTemp;
        this.upperTemp = upperTemp;
    }

    // 修改3:重写有效积温计算方法
    private double calculateEffectiveAccTemp(double avgTemp) {
        if (avgTemp <= baseTemp) {
            return 0;
        } else if (avgTemp >= upperTemp) {
            return upperTemp - baseTemp; // 达到上限后按上限计算
        } else {
            return avgTemp - baseTemp;
        }
    }

    public List<FiveDaySummary> calculate5DayMetrics(List<DailyWeather> dailyData) {
        List<FiveDaySummary> results = new ArrayList<>();
        
        double cumulativeAccTemp = 0;
        double cumulativePrecip = 0;
        double cumulativeSunshine = 0;
        
        // 找出整个数据集的最大降水/日照用于标准化
        double maxPrecip = dailyData.stream().mapToDouble(d -> d.precipitation).max().orElse(1);
        double maxSunshine = dailyData.stream().mapToDouble(d -> d.sunshine).max().orElse(1);

        // 每5天为一个处理单元
        for (int i = 0; i < dailyData.size(); i += 5) {
            int endIndex = Math.min(i + 5, dailyData.size());
            List<DailyWeather> segment = dailyData.subList(i, endIndex);
            
            FiveDaySummary summary = new FiveDaySummary(
                segment.get(0).date, 
                segment.get(segment.size()-1).date
            );
            
            // 计算分段指标
            summary.effectiveAccTemp = segment.stream()
                    .mapToDouble(d -> calculateEffectiveAccTemp(d.avgTemp)) // 改用新方法
                    .sum();
            
            summary.segmentPrecip = segment.stream()
                .mapToDouble(d -> d.precipitation)
                .sum();
            
            summary.segmentSunshine = segment.stream()
                .mapToDouble(d -> d.sunshine)
                .sum();
            
            // 更新累计值
            cumulativeAccTemp += summary.effectiveAccTemp;
            cumulativePrecip += summary.segmentPrecip;
            cumulativeSunshine += summary.segmentSunshine;
            
            summary.cumulativeAccTemp = cumulativeAccTemp;
            summary.cumulativePrecip = cumulativePrecip;
            summary.cumulativeSunshine = cumulativeSunshine;
            
            // 计算标准化值
            summary.normalizedSegmentPrecip = summary.segmentPrecip / maxPrecip;
            summary.normalizedCumulativePrecip = summary.cumulativePrecip / maxPrecip;
            summary.normalizedSegmentSunshine = summary.segmentSunshine / maxSunshine;
            summary.normalizedCumulativeSunshine = summary.cumulativeSunshine / maxSunshine;
            
            results.add(summary);
        }
        
        return results;
    }
    
    public static void main(String[] args) {
        // 1. 准备测试数据 (实际使用时从文件/数据库读取)
        List<DailyWeather> weatherData = new ArrayList<>();
        LocalDate date = LocalDate.of(2023, 1, 1);
        Random random = new Random();
        Calendar calendar = Calendar.getInstance();
        // 生成365天的模拟数据
        for (int i = 0; i < 365; i++) {
            weatherData.add(new DailyWeather(
                date.plusDays(i),
                15 + random.nextGaussian() * 5,   // 平均气温
                20 + random.nextGaussian() * 6,   // 最高气温
                10 + random.nextGaussian() * 4,    // 最低气温
                random.nextDouble() * 10,          // 降水量
                6 + random.nextDouble() * 6        // 日照时数
            ));
        }
        System.out.println(calendar.getTimeInMillis());
        // 2. 计算指标,传入的10和35就是有效温度条件定义里面的最高气温和最低气温
        AgroMeteorologicalCalculator calculator = new AgroMeteorologicalCalculator(10.0, 35.0);
        List<FiveDaySummary> results = calculator.calculate5DayMetrics(weatherData);
        System.out.println(calendar.getTimeInMillis());
        // 3. 输出结果
        System.out.println("起止日期\t有效积温\t累计积温\t分段降水\t累计降水\t分段日照\t累计日照\t分段降水标准化\t累计降水标准化\t分段日照标准化\t累计日照标准化");
        for (FiveDaySummary s : results) {
            System.out.printf("%s~%s\t%.1f\t%.1f\t%.1f\t%.1f\t%.1f\t%.1f\t%.3f\t%.3f\t%.3f\t%.3f\n",
                s.startDate, s.endDate,
                s.effectiveAccTemp, s.cumulativeAccTemp,
                s.segmentPrecip, s.cumulativePrecip,
                s.segmentSunshine, s.cumulativeSunshine,
                s.normalizedSegmentPrecip, s.normalizedCumulativePrecip,
                s.normalizedSegmentSunshine, s.normalizedCumulativeSunshine);
        }
    }
}

这里是每五日计算的,如果你的需求不一样直接修改for循环的i+=即可

3. 气象数据和产量的相关性分析

public class CropAnalysis {
    private final List<CropYearData> historicalData;
    private final double similarityThreshold;
    private final List<String> selectedElements;

    public CropAnalysis(List<CropYearData> data, double threshold, List<String> elements) {
        this.historicalData = new ArrayList<CropYearData>(data);
        Collections.sort(this.historicalData, new Comparator<CropYearData>() {
            @Override
            public int compare(CropYearData o1, CropYearData o2) {
                return Integer.compare(o1.getYear(), o2.getYear());
            }
        });
        this.similarityThreshold = threshold;
        this.selectedElements = new ArrayList<String>(elements);
    }

    public void analyze(int targetYear) {
        CropYearData target = getYearData(targetYear);
        
        System.out.println("\n作物产量分析报告 - 目标年份: " + targetYear);
        System.out.println("分析要素: " + selectedElements);
        printSeparator();
        System.out.println("| 气象要素      | 年份 | 欧氏距离 | 相关系数 | 综合聚类指标 | 产量变化% | 是否相似年 |");
        printSeparator();

        // 分要素计算
        for (String element : selectedElements) {
            // 计算各项指标
            Map<Integer, Double> distances = calculateDistances(element, target);
            Map<Integer, Double> correlations = calculateCorrelations(element);
            Map<Integer, Double> clusters = performClustering(element);
            Map<Integer, Double> yieldChanges = calculateYieldChanges();
            Map<Integer, Boolean> similarities = calculateSimilarities(distances);

            // 打印该要素结果(跳过第一年)
            for (int i = 1; i < historicalData.size() - 1; i++) {
                int year = historicalData.get(i).getYear();
                System.out.printf("| %-13s | %4d | %8.2f | %8.4f | %12.2f | %8.2f | %10s |\n",
                    element,
                    year,
                    distances.get(year),
                    correlations.get(year),
                    clusters.get(year),
                    yieldChanges.get(year),
                    similarities.get(year) ? "是" : "否"
                );
            }
            printSeparator();
        }
    }

    // ====================== 指标计算方法 ======================
    private Map<Integer, Double> calculateDistances(String element, CropYearData target) {
        EuclideanDistance distance = new EuclideanDistance();
        Map<Integer, Double> results = new HashMap<Integer, Double>();
        double targetValue = target.getFeature(element);
        
        for (CropYearData data : historicalData) {
            if (data.getYear() == target.getYear()) continue;
            results.put(data.getYear(), 
                Math.abs(targetValue - data.getFeature(element))); // 简化为一维距离
        }
        return results;
    }

    private Map<Integer, Double> calculateCorrelations(String element) {
        PearsonsCorrelation pc = new PearsonsCorrelation();
        Map<Integer, Double> results = new HashMap<Integer, Double>();
        
        // 提取该要素所有年份数据
        double[] allValues = new double[historicalData.size()];
        for (int i = 0; i < historicalData.size(); i++) {
            allValues[i] = historicalData.get(i).getFeature(element);
        }
        
        // 滑动窗口计算(窗口大小=3)
        for (int i = 2; i < historicalData.size(); i++) {
            int year = historicalData.get(i).getYear();
            double[] window = {
                historicalData.get(i-2).getFeature(element),
                historicalData.get(i-1).getFeature(element),
                historicalData.get(i).getFeature(element)
            };
            results.put(year, pc.correlation(new double[]{0,1,2}, window));
        }
        // 前两年用全局相关
        double[] indices = new double[allValues.length];
        for (int i = 0; i < indices.length; i++) indices[i] = i;
        double globalCorr = pc.correlation(indices, allValues);
        results.put(historicalData.get(0).getYear(), globalCorr);
        results.put(historicalData.get(1).getYear(), globalCorr);
        
        return results;
    }

    private Map<Integer, Double> performClustering(String element) {
        List<DoublePoint> points = new ArrayList<DoublePoint>();
        for (CropYearData data : historicalData) {
            points.add(new DoublePoint(new double[]{data.getFeature(element)}));
        }
        
        KMeansPlusPlusClusterer<DoublePoint> clusterer = new KMeansPlusPlusClusterer<DoublePoint>(2, 100);
        List<CentroidCluster<DoublePoint>> clusters = clusterer.cluster(points);
        
        // 计算最大距离用于标准化
        double maxDist = 0;
        for (int i = 0; i < clusters.size(); i++) {
            for (int j = i+1; j < clusters.size(); j++) {
                double d = new EuclideanDistance().compute(
                    clusters.get(i).getCenter().getPoint(),
                    clusters.get(j).getCenter().getPoint());
                maxDist = Math.max(maxDist, d);
            }
        }
        if (maxDist == 0) maxDist = 1;
        
        // 构建结果
        Map<Integer, Double> results = new HashMap<Integer, Double>();
        for (int i = 0; i < clusters.size(); i++) {
            for (DoublePoint point : clusters.get(i).getPoints()) {
                int idx = points.indexOf(point);
                int year = historicalData.get(idx).getYear();
                double dist = new EuclideanDistance().compute(
                    point.getPoint(), 
                    clusters.get(i).getCenter().getPoint());
                results.put(year, 1 - (dist / maxDist));
            }
        }
        return results;
    }

    private Map<Integer, Double> calculateYieldChanges() {
        Map<Integer, Double> changes = new HashMap<Integer, Double>();
        for (int i = 1; i < historicalData.size(); i++) {
            double prev = historicalData.get(i-1).getYield();
            changes.put(historicalData.get(i).getYear(), 
                (historicalData.get(i).getYield() - prev) / prev * 100);
        }
        return changes;
    }

    private Map<Integer, Boolean> calculateSimilarities(Map<Integer, Double> distances) {
        double maxDist = 0;
        for (Double d : distances.values()) {
            maxDist = Math.max(maxDist, d);
        }
        if (maxDist == 0) maxDist = 1;
        
        Map<Integer, Boolean> results = new HashMap<Integer, Boolean>();
        for (Entry<Integer, Double> entry : distances.entrySet()) {
            results.put(entry.getKey(), (entry.getValue() / maxDist) <= similarityThreshold);
        }
        return results;
    }

    // ====================== 辅助方法 ======================
    private void printSeparator() {
        System.out.println("-------------------------------------------------------------------------------");
    }

    private CropYearData getYearData(int year) {
        for (CropYearData data : historicalData) {
            if (data.getYear() == year) return data;
        }
        throw new IllegalArgumentException("无该年份数据: " + year);
    }

    // ====================== 数据类 ======================
    public static class CropYearData {
        private final int year;
        private final double yield;
        private final Map<String, Double> features;

        public CropYearData(int year, double yield, Map<String, Double> features) {
            this.year = year;
            this.yield = yield;
            this.features = features;
        }
        public double getFeature(String name) {
            Double value = features.get(name);
            return value != null ? value : 0.0;
        }
        public int getYear() { return year; }
        public double getYield() { return yield; }
    }

    // ====================== 虚拟数据生成 ======================
    private static List<CropYearData> generateDemoData() {
        List<CropYearData> data = new ArrayList<CropYearData>();
        Random random = new Random(42);
        
        // 生成2011-2020年数据
        for (int year = 2011; year <= 2020; year++) {
            Map<String, Double> features = new HashMap<String, Double>();
            features.put("有效积温", 2500.0 + (year - 2011) * 50 + random.nextInt(100) - 50);
            features.put("累计积温", 3200.0 + (year - 2011) * 60 + random.nextInt(120) - 60);
            features.put("分段累计降水", 350.0 + (year - 2011) * 10 + random.nextInt(40) - 20);
            features.put("累计降水", 480.0 + (year - 2011) * 15 + random.nextInt(50) - 25);
            features.put("分段累计日照", 1200.0 + (year - 2011) * 30 + random.nextInt(80) - 40);
            features.put("累计日照", 1800.0 + (year - 2011) * 40 + random.nextInt(100) - 50);
            
            data.add(new CropYearData(
                year,
                500 + (year - 2011) * 15 + random.nextInt(30) - 15,
                features
            ));
        }
        
        // 添加2021年数据(无产量)
        Map<String, Double> featuresNew = new HashMap<String, Double>();
        featuresNew.put("有效积温", 2600.0 + random.nextInt(100) - 50);
        featuresNew.put("累计积温", 3300.0 + random.nextInt(120) - 60);
        featuresNew.put("分段累计降水", 380.0 + random.nextInt(40) - 20);
        featuresNew.put("累计降水", 500.0 + random.nextInt(50) - 25);
        featuresNew.put("分段累计日照", 1250.0 + random.nextInt(80) - 40);
        featuresNew.put("累计日照", 1850.0 + random.nextInt(100) - 50);
        data.add(new CropYearData(2021, Double.NaN, featuresNew));
        
        return data;
    }

    // ====================== 使用示例 ======================
    public static void main(String[] args) {
        // 1. 生成虚拟数据
        List<CropYearData> demoData = generateDemoData();
        
        // 2. 选择要分析的要素(可自由调整)
        List<String> elementsToAnalyze = Arrays.asList("有效积温", "累计积温", "分段累计降水"); // 示例选择2个要素
        
        // 3. 执行分析
        CropAnalysis analyzer = new CropAnalysis(demoData, 0.35, elementsToAnalyze);
        analyzer.analyze(2021);
    }
}

对于相关性分析的气象基础数据均可自由调整 

4. 计算丰歉指数的产量预报

基于相似年的大概率分析法预测产量

public class YieldForecast {
    private final Map<Integer, Double> historicalYields;
    private final Map<Integer, Boolean> similarYears;

    public YieldForecast(Map<Integer, Double> historicalYields, 
                        Map<Integer, Boolean> similarYears) {
        this.historicalYields = new HashMap<>(historicalYields);
        this.similarYears = new HashMap<>(similarYears);
    }

    /**
     * 基于相似年的大概率分析法预测产量
     * @param baseYear 基准年份(通常为目标年份-1)
     * @return 预测结果(含预测产量、置信度等)
     */
    public ForecastResult predict(int baseYear) {
        // 1. 获取基准年产量
        double baseYield = historicalYields.get(baseYear);
        
        // 2. 筛选相似年
        List<Integer> similarYearsList = new ArrayList<>();
        for (Map.Entry<Integer, Boolean> entry : similarYears.entrySet()) {
            if (entry.getValue() && historicalYields.containsKey(entry.getKey())) {
                similarYearsList.add(entry.getKey());
            }
        }

        // 3. 计算相似年产量变化率
        DescriptiveStatistics stats = new DescriptiveStatistics();
        for (int year : similarYearsList) {
            double prevYield = historicalYields.get(year - 1);
            double change = (historicalYields.get(year) - prevYield) / prevYield * 100;
            stats.addValue(change);
        }

        // 4. 大概率分析
        double predictedChange = stats.getN() > 0 ? getMostProbableChange(stats) : 0;
        double predictedYield = baseYield * (1 + predictedChange / 100);
        double confidence = calculateConfidence(stats);

        return new ForecastResult(
            predictedYield,
            predictedChange,
            confidence,
            similarYearsList,
            baseYield
        );
    }

    // ====================== 核心计算方法 ======================
    private double getMostProbableChange(DescriptiveStatistics stats) {
        // 简化版众数计算(实际项目可用更精确算法)
        double[] values = stats.getValues();
        Map<Double, Integer> freqMap = new HashMap<>();
        for (double v : values) {
            int count = freqMap.containsKey(v) ? freqMap.get(v) : 0;
            freqMap.put(v, count + 1);
        }
        
        double mode = 0;
        int maxCount = 0;
        for (Map.Entry<Double, Integer> entry : freqMap.entrySet()) {
            if (entry.getValue() > maxCount) {
                mode = entry.getKey();
                maxCount = entry.getValue();
            }
        }
        return mode;
    }

    private double calculateConfidence(DescriptiveStatistics stats) {
        if (stats.getN() == 0) return 0;
        // 置信度公式:100*(1 - 标准差/√样本数)
        return Math.max(0, 100 * (1 - stats.getStandardDeviation() / Math.sqrt(stats.getN())));
    }

    // ====================== 数据类 ======================
    public static class ForecastResult {
        public final double predictedYield;  // 预测产量
        public final double yieldChange;     // 预测变化率(%)
        public final double confidence;      // 置信度(0-100)
        public final List<Integer> similarYearsUsed; // 使用的相似年
        public final double baseYield;       // 基准年产量

        public ForecastResult(double predictedYield, double yieldChange, 
                            double confidence, List<Integer> similarYearsUsed,
                            double baseYield) {
            this.predictedYield = predictedYield;
            this.yieldChange = yieldChange;
            this.confidence = confidence;
            this.similarYearsUsed = similarYearsUsed;
            this.baseYield = baseYield;
        }

        public void printReport() {
            System.out.println("\n产量预测报告");
            System.out.println("==========================================");
            System.out.printf("基准年产量: %.2f\n", baseYield);
            System.out.printf("预测产量: %.2f (变化率: %.1f%%)\n", 
                predictedYield, yieldChange);
            System.out.printf("置信度: %.1f%%\n", confidence);
            System.out.println("使用的相似年: " + similarYearsUsed);
            System.out.println("==========================================");
        }
    }

    // ====================== 虚拟数据生成 ======================
    public static Map<Integer, Double> generateHistoricalYields() {
        Map<Integer, Double> data = new HashMap<>();
        Random rand = new Random(42);
        // 2011-2020年产量数据(含随机波动)
        data.put(2011, 500.0);
        data.put(2012, 520.0 + rand.nextDouble()*10-5);  // 515-525
        data.put(2013, 480.0 + rand.nextDouble()*10-5);  // 475-485
        data.put(2014, 550.0 + rand.nextDouble()*15-7); // 543-557
        data.put(2015, 530.0 + rand.nextDouble()*12-6); // 524-536
        data.put(2016, 600.0 + rand.nextDouble()*20-10); // 590-610
        data.put(2017, 580.0 + rand.nextDouble()*15-7); // 573-587
        data.put(2018, 620.0 + rand.nextDouble()*25-12); // 608-632
        data.put(2019, 610.0 + rand.nextDouble()*18-9); // 601-619
        data.put(2020, 650.0 + rand.nextDouble()*30-15); // 635-665
        return data;
    }

    // ====================== 使用示例 ======================
    public static void main(String[] args) {
        // 1. 准备数据(假设来自前期的相似年分析)
        Map<Integer, Double> yields = generateHistoricalYields();
        
        /**
         * 这个是根据前一步的计算结果来的,都有哪些年是相似年
         */
        Map<Integer, Boolean> similarYears = new HashMap<>();
        similarYears.put(2012, true);   // 是相似年
        similarYears.put(2013, false);  // 不是相似年
        similarYears.put(2015, true);
        similarYears.put(2017, true);
        similarYears.put(2019, true);

        // 2. 创建预测器
        YieldForecast forecast = new YieldForecast(yields, similarYears);

        // 3. 执行预测(以2020年为基准,预测2021年)
        ForecastResult result = forecast.predict(2020);

        // 4. 输出结果
        result.printReport();
    }
}

基于加权平均法预测产量

public class YieldForecastWeighted {

    /**
     * 等权重预测方法
     * @param baseYield 基准年产量
     * @param similarYears 相似年数据(需包含所有气象要素和△Y%)
     */
    public static double predict(double baseYield, 
                               List<Map<String, Double>> similarYears) {
        
        // 1. 检查数据有效性
        if (similarYears.isEmpty()) {
            throw new IllegalArgumentException("无有效相似年数据");
        }

        // 2. 动态获取所有气象要素名称(排除△Y%)
        Set<String> allElements = new HashSet<>();
        for (Map<String, Double> yearData : similarYears) {
            for (String key : yearData.keySet()) {
                if (!"△Y%".equals(key)) {
                    allElements.add(key);
                }
            }
        }

        // 3. 计算各年综合相关系数(等权重平均)
        List<Double> yearWeights = new ArrayList<>();
        for (Map<String, Double> yearData : similarYears) {
            double sum = 0;
            int count = 0;
            for (String element : allElements) {
                sum += yearData.getOrDefault(element, 0.0);
                count++;
            }
            yearWeights.add(sum / count); // 等权重平均
        }

        // 4. 加权平均计算
        double weightedSum = 0;
        double totalWeight = 0;
        for (int i = 0; i < similarYears.size(); i++) {
            double changeRate = similarYears.get(i).get("△Y%");
            weightedSum += changeRate * yearWeights.get(i);
            totalWeight += yearWeights.get(i);
        }

        return baseYield * (1 + weightedSum / 100 / totalWeight);
    }

    public static void main(String[] args) {
        // 示例数据(可包含任意数量气象要素)
        List<Map<String, Double>> data = new ArrayList<>();
        data.add(new HashMap<String, Double>() {{
            put("积温", 0.52); put("降水", 0.45); put("日照", -0.58); 
            put("风速", 0.31); put("△Y%", -3.73);
        }});
        data.add(new HashMap<String, Double>() {{
            put("积温", 0.76); put("降水", 0.39); put("湿度", 0.12);
            put("△Y%", -0.20);
        }});

        double result = predict(650.0, data);
        System.out.printf("预测产量: %.2f\n", result);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值