【农气项目】基于关键因子的产量预报
直接上干货(复制到开发工具即可运行的代码)
1. 气象数据旬膨化
2. 计算气象产量,并预报趋势产量
3. 气象数据与产量信息相关性分析
4. 气象产量预报
1. 气象数据旬膨化
public class WeatherDataProcessor {public enum InflationOption {NO_INFLATION, TWO_WEEKS, THREE_WEEKS}public static class WeatherRecord {private final LocalDate date;private final double avgTemperature;private final double precipitation;private final double sunshine;public WeatherRecord(LocalDate date, double avgTemperature, double precipitation, double sunshine) {this.date = date;this.avgTemperature = avgTemperature;this.precipitation = precipitation;this.sunshine = sunshine;}public LocalDate getDate() {return date;}public double getAvgTemperature() {return avgTemperature;}public double getPrecipitation() {return precipitation;}public double getSunshine() {return sunshine;}}public static class DecadalResult {private final int sequence;private final int year;private final int month;private final int decade;private final String decadeName;private final boolean isInflated;private final double avgTemperature;private final double precipitation;private final double sunshine;public DecadalResult(int sequence, int year, int month, int decade, String decadeName, boolean isInflated,double avgTemperature, double precipitation, double sunshine) {this.sequence = sequence;this.year = year;this.month = month;this.decade = decade;this.decadeName = decadeName;this.isInflated = isInflated;this.avgTemperature = avgTemperature;this.precipitation = precipitation;this.sunshine = sunshine;}@Overridepublic String toString() {return String.format("%d\t%d\t%s\t%.1f\t%.1f\t%.1f", year, sequence, decadeName, avgTemperature,precipitation, sunshine);}}public static void main(String[] args) {List<WeatherRecord> data = generateTestData(LocalDate.of(2010, 12, 1), LocalDate.of(2011, 4, 3));List<DecadalResult> results = process(data, InflationOption.THREE_WEEKS);System.out.println("年份\t旬序\t旬名称\t\t气温(℃)\t降水(mm)\t日照(h)");results.forEach(System.out::println);// 验证三旬膨化结果数量long inflatedCount = results.stream().filter(r -> r.isInflated).count();System.out.println("膨化结果数: " + inflatedCount);}public static List<DecadalResult> process(List<WeatherRecord> data, InflationOption option) {if (data == null || data.isEmpty())return new ArrayList<>();final LocalDate minDate = getMinDate(data);final LocalDate maxDate = getMaxDate(data);List<DecadalResult> allResults = new ArrayList<>();// 处理不膨化结果List<DecadalResult> normalResults = processNormalData(data, minDate, maxDate);allResults.addAll(normalResults);// 处理膨化结果if (option != InflationOption.NO_INFLATION) {int startSequence = normalResults.size() + 1;List<DecadalResult> inflatedResults = processInflatedData(data, option, minDate, maxDate, startSequence);allResults.addAll(inflatedResults);}return allResults;}private static List<DecadalResult> processNormalData(List<WeatherRecord> data, LocalDate minDate,LocalDate maxDate) {List<DecadalResult> results = new ArrayList<>();Map<Integer, List<WeatherRecord>> dataByYear = groupByYear(data);int sequence = 1; // 旬序从1开始for (Map.Entry<Integer, List<WeatherRecord>> entry : dataByYear.entrySet()) {int year = entry.getKey();List<WeatherRecord> yearData = entry.getValue();int minMonth = getMinMonth(yearData);int maxMonth = getMaxMonth(yearData);for (int month = minMonth; month <= maxMonth; month++) {YearMonth yearMonth = YearMonth.of(year, month);LocalDate monthStart = yearMonth.atDay(1);LocalDate monthEnd = yearMonth.atEndOfMonth();LocalDate actualMonthStart = monthStart.isBefore(minDate) ? minDate : monthStart;LocalDate actualMonthEnd = monthEnd.isAfter(maxDate) ? maxDate : monthEnd;for (int decade = 1; decade <= 3; decade++) {LocalDate decadeStart = calculateDecadeStart(year, month, decade);LocalDate decadeEnd = calculateDecadeEnd(year, month, decade);// 严格检查旬完整性(必须包含完整的旬)if (!isCompleteDecade(decadeStart, decadeEnd, actualMonthStart, actualMonthEnd)) {continue;}List<WeatherRecord> filtered = filterRecords(yearData, decadeStart, decadeEnd);if (filtered.isEmpty())continue;String decadeName = getNormalDecadeName(month, decade);results.add(new DecadalResult(sequence++, year, month, decade, decadeName, false,calculateAvg(filtered), calculateSum(filtered, true), calculateSum(filtered, false)));}}}return results;}// 检查旬是否完整private static boolean isCompleteDecade(LocalDate decadeStart, LocalDate decadeEnd, LocalDate dataStart,LocalDate dataEnd) {// 旬的起始日必须是旬的第一天(如1日、11日、21日)boolean isStartValid = (decadeStart.getDayOfMonth() == 1) || (decadeStart.getDayOfMonth() == 11)|| (decadeStart.getDayOfMonth() == 21);// 旬必须完全包含在数据范围内return isStartValid && !decadeStart.isBefore(dataStart) && !decadeEnd.isAfter(dataEnd);}private static LocalDate getInflatedStart(int year, int month, int decade, InflationOption option,LocalDate minDate) {if (option == InflationOption.TWO_WEEKS) {
// 两旬膨化:获取前一旬的开始日期int prevDecade = decade - 1;int prevMonth = month;if (prevDecade < 1) {prevMonth--;prevDecade = 3;if (prevMonth < 1)return null; // 超出年份范围}return calculateDecadeStart(year, prevMonth, prevDecade);} else if (option == InflationOption.THREE_WEEKS) {
// 三旬膨化:获取前两旬的开始日期int prevDecade1 = decade - 1;int prevMonth1 = month;if (prevDecade1 < 1) {prevMonth1--;prevDecade1 = 3;if (prevMonth1 < 1)return null; // 超出年份范围}int prevDecade2 = prevDecade1 - 1;int prevMonth2 = prevMonth1;if (prevDecade2 < 1) {prevMonth2--;prevDecade2 = 3;if (prevMonth2 < 1)return null; // 超出年份范围}return calculateDecadeStart(year, prevMonth2, prevDecade2);}return null;}// ...(其他保持不变的方法)...private static List<DecadalResult> processInflatedData(List<WeatherRecord> data, InflationOption option,LocalDate minDate, LocalDate maxDate, int startSequence) {List<DecadalResult> results = new ArrayList<>();Map<Integer, List<WeatherRecord>> dataByYear = groupByYear(data);int currentSequence = startSequence;// 先统计有效不膨化旬数int validDecadeCount = countValidDecades(dataByYear, minDate, maxDate);// 计算最大膨化结果数int maxInflated = (option == InflationOption.TWO_WEEKS) ? validDecadeCount - 1 : validDecadeCount - 2;if (maxInflated <= 0)return results;// 处理膨化数据outer: for (Map.Entry<Integer, List<WeatherRecord>> entry : dataByYear.entrySet()) {int year = entry.getKey();List<WeatherRecord> yearData = entry.getValue();int minMonth = getMinMonth(yearData);int maxMonth = getMaxMonth(yearData);for (int month = minMonth; month <= maxMonth; month++) {for (int decade = 1; decade <= 3; decade++) {if (results.size() >= maxInflated)break outer;LocalDate currentEnd = calculateDecadeEnd(year, month, decade);LocalDate inflatedStart = getInflatedStart(year, month, decade, option, minDate);if (inflatedStart != null && !inflatedStart.isBefore(minDate) && !currentEnd.isAfter(maxDate)) {List<WeatherRecord> filtered = filterRecords(yearData, inflatedStart, currentEnd);if (!filtered.isEmpty()) {results.add(new DecadalResult(currentSequence++, year, month, decade,"膨化第" + (currentSequence - startSequence) + "旬", true, calculateAvg(filtered),calculateSum(filtered, true), calculateSum(filtered, false)));}}}}}return results;}//统计有效旬数(与processNormalData逻辑一致)private static int countValidDecades(Map<Integer, List<WeatherRecord>> dataByYear, LocalDate minDate,LocalDate maxDate) {int count = 0;for (Map.Entry<Integer, List<WeatherRecord>> entry : dataByYear.entrySet()) {int year = entry.getKey();List<WeatherRecord> yearData = entry.getValue();int minMonth = getMinMonth(yearData);int maxMonth = getMaxMonth(yearData);for (int month = minMonth; month <= maxMonth; month++) {for (int decade = 1; decade <= 3; decade++) {LocalDate start = calculateDecadeStart(year, month, decade);LocalDate end = calculateDecadeEnd(year, month, decade);if (isCompleteDecade(start, end, minDate, maxDate)) {count++;}}}}return count;}private static String getNormalDecadeName(int month, int decade) {switch (decade) {case 1:return month + "月上旬";case 2:return month + "月中旬";case 3:return month + "月下旬";default:throw new IllegalArgumentException("无效旬号: " + decade);}}private static LocalDate getMinDate(List<WeatherRecord> data) {LocalDate min = null;for (WeatherRecord record : data) {if (min == null || record.getDate().isBefore(min)) {min = record.getDate();}}return min != null ? min : LocalDate.now();}private static LocalDate getMaxDate(List<WeatherRecord> data) {LocalDate max = null;for (WeatherRecord record : data) {if (max == null || record.getDate().isAfter(max)) {max = record.getDate();}}return max != null ? max : LocalDate.now();}private static Map<Integer, List<WeatherRecord>> groupByYear(List<WeatherRecord> data) {Map<Integer, List<WeatherRecord>> map = new HashMap<>();for (WeatherRecord record : data) {int year = record.getDate().getYear();if (!map.containsKey(year)) {map.put(year, new ArrayList<>());}map.get(year).add(record);}return map;}private static int getMinMonth(List<WeatherRecord> yearData) {int min = 12;for (WeatherRecord record : yearData) {min = Math.min(min, record.getDate().getMonthValue());}return min;}private static int getMaxMonth(List<WeatherRecord> yearData) {int max = 1;for (WeatherRecord record : yearData) {max = Math.max(max, record.getDate().getMonthValue());}return max;}private static List<WeatherRecord> filterRecords(List<WeatherRecord> data, LocalDate start, LocalDate end) {List<WeatherRecord> result = new ArrayList<>();for (WeatherRecord record : data) {LocalDate date = record.getDate();if (!date.isBefore(start) && !date.isAfter(end)) {result.add(record);}}return result;}private static double calculateAvg(List<WeatherRecord> records) {double sum = 0;int count = 0;for (WeatherRecord record : records) {sum += record.getAvgTemperature();count++;}return count == 0 ? 0 : sum / count;}private static double calculateSum(List<WeatherRecord> records, boolean isPrecipitation) {double sum = 0;for (WeatherRecord record : records) {sum += isPrecipitation ? record.getPrecipitation() : record.getSunshine();}return sum;}private static LocalDate calculateDecadeStart(int year, int month, int decade) {int day = (decade - 1) * 10 + 1;int maxDay = YearMonth.of(year, month).lengthOfMonth();return LocalDate.of(year, month, Math.min(day, maxDay));}private static LocalDate calculateDecadeEnd(int year, int month, int decade) {int endDay = decade * 10;int maxDay = YearMonth.of(year, month).lengthOfMonth();return LocalDate.of(year, month, Math.min(endDay, maxDay));}private static List<WeatherRecord> generateTestData(LocalDate start, LocalDate end) {List<WeatherRecord> data = new ArrayList<>();LocalDate current = start;while (!current.isAfter(end)) {double seasonFactor = Math.sin((current.getDayOfYear() - 105) / 58.0);double temp = 5.0 + 25.0 * seasonFactor + (Math.random() * 4 - 2);double precip = 1.0 * (1 + seasonFactor) * (0.5 + Math.random());double sunshine = 6 + 6 * seasonFactor + (Math.random() * 3 - 1.5);data.add(new WeatherRecord(current, temp, precip, sunshine));current = current.plusDays(1);}return data;}
}
2. 计算气象产量,并预报趋势产量
public class YieldDecomposition {public enum SmoothingOption {THREE_YEAR(3), FIVE_YEAR(5);public final int windowSize;SmoothingOption(int size) { this.windowSize = size; }}public static class YieldData {private final int year;private final double yield;public YieldData(int year, double yield) {this.year = year;this.yield = yield;}public int getYear() { return year; }public double getYield() { return yield; }}public static class DecomposedYield {private final int year;private final Double socialYield;private final Double trendYield;private final Double weatherYield;private final Double relativeYield;public DecomposedYield(int year, Double social, Double trend, Double weather, Double relative) {this.year = year;this.socialYield = social;this.trendYield = trend;this.weatherYield = weather;this.relativeYield = relative;}@Overridepublic String toString() {return String.format("%d\t%s\t%s\t%s\t%s",year,socialYield != null ? String.format("%.2f", socialYield) : "",trendYield != null ? String.format("%.2f", trendYield) : "",weatherYield != null ? String.format("%.2f", weatherYield) : "",relativeYield != null ? String.format("%.1f%%", relativeYield) : "");}}public static void main(String[] args) {// 示例数据(2011-2020)List<YieldData> yields = Arrays.asList(new YieldData(2011, 5.2), new YieldData(2012, 5.5),new YieldData(2013, 5.1), new YieldData(2014, 5.8),new YieldData(2015, 6.0), new YieldData(2016, 5.7),new YieldData(2017, 6.2), new YieldData(2018, 6.5),new YieldData(2019, 6.3), new YieldData(2020, 6.4));// 预报年份设置int forecastYear = 2025;// 3年滑动窗口分析System.out.println("=== 3年滑动窗口分析 ===");process(yields, SmoothingOption.THREE_YEAR, forecastYear);// 5年滑动窗口分析System.out.println("\n=== 5年滑动窗口分析 ===");process(yields, SmoothingOption.FIVE_YEAR, forecastYear);}private static void process(List<YieldData> yields, SmoothingOption option, int forecastYear) {// 1. 产量分解List<DecomposedYield> decomposition = decomposeYield(yields, option);// 2. 获取建模有效数据(排除窗口期不足的年份)List<YieldData> modelData = yields.subList(option.windowSize - 1, yields.size());// 3. 打印趋势产量公式printTrendFormula(modelData);// 4. 预报指定年份double forecast = calculateTrendForecast(modelData, forecastYear);System.out.printf("预报 %d 年趋势产量: %.2f\n", forecastYear, forecast);// 5. 打印分解结果printDecompositionResults(decomposition);}private static List<DecomposedYield> decomposeYield(List<YieldData> yields, SmoothingOption option) {List<DecomposedYield> results = new ArrayList<>();int windowSize = option.windowSize;// 计算趋势产量(仅使用有效数据)List<YieldData> modelData = yields.subList(windowSize - 1, yields.size());Map<Integer, Double> trendYields = calculateTrendYield(modelData);for (int i = 0; i < yields.size(); i++) {YieldData current = yields.get(i);Double social = (i >= windowSize - 1) ? calculateMovingAverage(yields, i, windowSize) : null;if (social == null) {results.add(new DecomposedYield(current.year, current.yield, null, null, null));} else {Double trend = trendYields.get(current.year);Double weather = trend != null ? current.yield - social : null;Double relative = weather != null ? (weather / social) * 100 : null;results.add(new DecomposedYield(current.year, social, trend, weather, relative));}}return results;}private static double calculateMovingAverage(List<YieldData> yields, int endIndex, int windowSize) {return yields.subList(endIndex - windowSize + 1, endIndex + 1).stream().mapToDouble(YieldData::getYield).average().orElse(0);}private static Map<Integer, Double> calculateTrendYield(List<YieldData> yields) {double[] coeff = calculateRegressionCoefficients(yields);int baseYear = yields.get(0).getYear();return yields.stream().collect(Collectors.toMap(YieldData::getYear,yd -> coeff[0] + coeff[1] * (yd.getYear() - baseYear)));}private static double[] calculateRegressionCoefficients(List<YieldData> yields) {int n = yields.size();int baseYear = yields.get(0).getYear();double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;for (YieldData yd : yields) {double x = yd.getYear() - baseYear;double y = yd.getYield();sumX += x;sumY += y;sumXY += x * y;sumX2 += x * x;}double b = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);double a = (sumY - b * sumX) / n;return new double[]{a, b};}private static double calculateTrendForecast(List<YieldData> yields, int year) {double[] coeff = calculateRegressionCoefficients(yields);return coeff[0] + coeff[1] * (year - yields.get(0).getYear());}private static void printTrendFormula(List<YieldData> yields) {double[] coeff = calculateRegressionCoefficients(yields);System.out.printf("趋势产量公式: y = %.4f + %.4f * (年份 - %d)\n",coeff[0], coeff[1], yields.get(0).getYear());}private static void printDecompositionResults(List<DecomposedYield> results) {System.out.println("\n产量分解结果:");System.out.println("年份\t社会产量\t趋势产量\t气象产量\t相对产量");results.forEach(System.out::println);}
}
3. 气象数据与产量信息相关性分析
public class FlexibleYieldCorrelation {public static class DecadalAnalysis {private final String decadeName;private final double[] temperatures;private final double[] yields;public DecadalAnalysis(String name, double[] temps, double[] yields) {if (temps.length != yields.length) {throw new IllegalArgumentException("气温和产量数据长度必须相同");}this.decadeName = name;this.temperatures = temps;this.yields = yields;}public CorrelationResult analyze(double significanceLevel) {PearsonsCorrelation pc = new PearsonsCorrelation();double r = pc.correlation(temperatures, yields);TTest tTest = new TTest();double pValue = tTest.pairedTTest(temperatures, yields);boolean isSignificant = pValue < significanceLevel;return new CorrelationResult(decadeName, r, pValue, isSignificant);}}public static class CorrelationResult {public final String decadeName;public final double correlation;public final double pValue;public final boolean isSignificant;public CorrelationResult(String name, double r, double p, boolean significant) {this.decadeName = name;this.correlation = r;this.pValue = p;this.isSignificant = significant;}@Overridepublic String toString() {return String.format("%-8s\t%.4f\t%.4f\t%s",decadeName, correlation, pValue, isSignificant ? "是" : "否");}}public static void main(String[] args) {// 示例使用:动态输入数据Map<String, double[]> tempData = new LinkedHashMap<>();tempData.put("1月上旬", new double[]{2.1, 2.3, 2.0, 2.4, 2.2});tempData.put("1月中旬", new double[]{2.3, 2.5, 2.2, 2.6, 2.4});tempData.put("1月下旬", new double[]{2.5, 2.7, 2.4, 2.8, 2.6});tempData.put("1月1旬", new double[]{2.5, 2.7, 2.4, 2.8, 2.6});tempData.put("1月2旬", new double[]{2.5, 2.7, 2.4, 2.8, 2.6});tempData.put("1月3旬", new double[]{2.5, 2.7, 2.4, 2.8, 2.6});tempData.put("1月4旬", new double[]{2.5, 2.7, 2.4, 2.8, 2.6});tempData.put("1月5旬", new double[]{2.5, 2.7, 2.4, 2.8, 2.6});double[] yields = {0.23, -0.37, 0.33, 0.17, -0.13}; // 对应年份的气象产量double significanceLevel = 0.05; // 可配置的显著性水平// 分析处理List<CorrelationResult> results = new ArrayList<>();for (Map.Entry<String, double[]> entry : tempData.entrySet()) {DecadalAnalysis analysis = new DecadalAnalysis(entry.getKey(), entry.getValue(), yields);results.add(analysis.analyze(significanceLevel));}// 输出结果System.out.println("旬名称\t\t相关系数\tP值\t显著性");results.stream().sorted((a,b) -> Double.compare(Math.abs(b.correlation), Math.abs(a.correlation))).forEach(System.out::println);}
}
4. 气象产量预报
public class YieldForecastSystem {// 气象产量预报结果类public static class MeteoYieldResult {public final int year;public final double actualMeteoYield;public final double predictedMeteoYield;public final double errorPercentage;public MeteoYieldResult(int year, double actual, double predicted) {this.year = year;this.actualMeteoYield = actual;this.predictedMeteoYield = predicted;this.errorPercentage = Math.abs((actual - predicted) / actual) * 100;}@Overridepublic String toString() {return String.format("%d\t%.2f\t%.2f\t%.1f%%",year, actualMeteoYield, predictedMeteoYield, errorPercentage);}}// 气象产量预报模型public static class MeteoYieldModel {private OLSMultipleLinearRegression regression;private List<String> factorNames;private double[] coefficients;public void train(double[] meteoYields, double[][] factors, List<String> factorNames) {this.factorNames = factorNames;regression = new OLSMultipleLinearRegression();regression.newSampleData(meteoYields, factors);this.coefficients = regression.estimateRegressionParameters();}public double predict(double[] inputFactors) {if (coefficients == null) throw new IllegalStateException("模型未训练");double prediction = coefficients[0]; // 截距项for (int i = 0; i < inputFactors.length; i++) {prediction += coefficients[i+1] * inputFactors[i];}return prediction;}public void printModelSummary() {System.out.println("\n=== 气象产量模型摘要 ===");System.out.println("截距项: " + coefficients[0]);for (int i = 0; i < factorNames.size(); i++) {System.out.printf("气象因子[%s] 系数: %.4f%n", factorNames.get(i), coefficients[i+1]);}System.out.println("R²: " + regression.calculateRSquared());}}public static void main(String[] args) {// 历史数据(2015-2020)int[] trainYears = {2015, 2016, 2017, 2018, 2019, 2020};double[] meteoYields = {0.2, -0.2, 0.2, 0.4, 0.1, 0.1}; // 气象产量(实际产量-趋势产量)// 气象因子数据(每个内层数组代表一个因子多年的数据)List<String> factorNames = Arrays.asList("1月上旬降水", "2月中旬气温", "3月下旬日照");double[][] historicalFactors = {{120, 115, 130, 125, 110, 140}, // 1月上旬降水(mm){22.1, 21.8, 22.5, 23.0, 22.7, 22.9}, // 2月中旬气温(℃){8.2, 7.9, 8.5, 8.7, 8.3, 8.6} // 3月下旬日照(h/d)};// 预报年份(2021年)的气象因子数据int forecastYear = 2021;double[] forecastFactors = {135, 23.2, 8.8}; // 2021年的因子数据// 转换为回归所需的格式(行为年份,列为因子)double[][] factorMatrix = transposeMatrix(historicalFactors);// 创建并训练模型MeteoYieldModel model = new MeteoYieldModel();model.train(meteoYields, factorMatrix, factorNames);model.printModelSummary();// 交叉验证(留一法)System.out.println("\n=== 历史气象产量验证 ===");System.out.println("年份\t实际气象产\t预测气象产\t误差率");List<MeteoYieldResult> results = new ArrayList<>();for (int i = 0; i < trainYears.length; i++) {// 创建训练集(排除当前年份)double[] trainMeteoYields = removeElement(meteoYields, i);double[][] trainFactors = removeRow(factorMatrix, i);// 训练临时模型MeteoYieldModel tempModel = new MeteoYieldModel();tempModel.train(trainMeteoYields, trainFactors, factorNames);// 预测当前年份double prediction = tempModel.predict(factorMatrix[i]);results.add(new MeteoYieldResult(trainYears[i], meteoYields[i], prediction));}// 输出验证结果results.forEach(System.out::println);// 计算平均误差double avgError = results.stream().mapToDouble(r -> r.errorPercentage).average().orElse(0);System.out.printf("%n历史平均误差: %.1f%%%n", avgError);// 预报新年份double forecastMeteoYield = model.predict(forecastFactors);System.out.printf("%n=== %d年气象产量预报 ===%n", forecastYear);System.out.println("气象因子值:");for (int i = 0; i < factorNames.size(); i++) {System.out.printf("%s: %.1f%n", factorNames.get(i), forecastFactors[i]);}System.out.printf("预测气象产量: %.2f 吨/公顷%n", forecastMeteoYield);}// 矩阵转置(因子数据预处理)private static double[][] transposeMatrix(double[][] matrix) {double[][] transposed = new double[matrix[0].length][matrix.length];for (int i = 0; i < matrix.length; i++) {for (int j = 0; j < matrix[i].length; j++) {transposed[j][i] = matrix[i][j];}}return transposed;}// 工具方法:从数组中移除指定元素private static double[] removeElement(double[] array, int index) {double[] newArray = new double[array.length - 1];System.arraycopy(array, 0, newArray, 0, index);System.arraycopy(array, index + 1, newArray, index, array.length - index - 1);return newArray;}// 工具方法:从矩阵中移除指定行private static double[][] removeRow(double[][] matrix, int rowIndex) {double[][] newMatrix = new double[matrix.length - 1][];System.arraycopy(matrix, 0, newMatrix, 0, rowIndex);System.arraycopy(matrix, rowIndex + 1, newMatrix, rowIndex, matrix.length - rowIndex - 1);return newMatrix;}
}