直接上干货(复制到开发工具即可运行的代码)
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);
}
}