diff --git a/meteoinfo-common/src/main/java/org/meteoinfo/common/util/GlobalUtil.java b/meteoinfo-common/src/main/java/org/meteoinfo/common/util/GlobalUtil.java
index f1a95e4f..9640c282 100644
--- a/meteoinfo-common/src/main/java/org/meteoinfo/common/util/GlobalUtil.java
+++ b/meteoinfo-common/src/main/java/org/meteoinfo/common/util/GlobalUtil.java
@@ -43,6 +43,8 @@ import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
diff --git a/meteoinfo-console/src/main/java/org/meteoinfo/console/JConsole.java b/meteoinfo-console/src/main/java/org/meteoinfo/console/JConsole.java
index 73616a08..ee5e39ae 100644
--- a/meteoinfo-console/src/main/java/org/meteoinfo/console/JConsole.java
+++ b/meteoinfo-console/src/main/java/org/meteoinfo/console/JConsole.java
@@ -652,6 +652,10 @@ public class JConsole extends JScrollPane
}
private void append(String string) {
+ if (string.length() > 10000) {
+ string = string.substring(0, 10000);
+ }
+ string = StringUtil.unicodeToString(string);
int slen = textLength();
if (slen > this.maxLength) {
text.setText("");
diff --git a/meteoinfo-console/src/main/java/org/meteoinfo/console/StringUtil.java b/meteoinfo-console/src/main/java/org/meteoinfo/console/StringUtil.java
index a27d10aa..60db1fbf 100644
--- a/meteoinfo-console/src/main/java/org/meteoinfo/console/StringUtil.java
+++ b/meteoinfo-console/src/main/java/org/meteoinfo/console/StringUtil.java
@@ -29,6 +29,8 @@ package org.meteoinfo.console;
import java.util.StringTokenizer;
import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class StringUtil {
@@ -91,6 +93,26 @@ public class StringUtil {
return sb.toString();
}
+ /**
+ * Convert unicode string to character string
+ *
+ * @param unicodeString Unicode string
+ * @return Character string
+ */
+ public static String unicodeToString(String unicodeString) {
+ Pattern pattern = Pattern.compile("\\\\u[0-9a-fA-F]{4}");
+ Matcher matcher = pattern.matcher(unicodeString);
+ StringBuffer builder = new StringBuffer();
+ while (matcher.find()) {
+ String unicodeSequence = matcher.group();
+ char unicode = (char) Integer.parseInt(unicodeSequence.substring(2), 16);
+ matcher.appendReplacement(builder, Character.toString(unicode));
+ }
+ matcher.appendTail(builder);
+
+ return builder.toString();
+ }
+
/**
* Split a filename into dirName, baseName
*
diff --git a/meteoinfo-lab/milconfig.xml b/meteoinfo-lab/milconfig.xml
index 69ac815d..9dec9803 100644
--- a/meteoinfo-lab/milconfig.xml
+++ b/meteoinfo-lab/milconfig.xml
@@ -1,11 +1,6 @@
-
-
-
-
-
-
+
@@ -13,22 +8,23 @@
-
-
+
+
+
+
+
+
+
-
-
-
+
-
-
-
+
@@ -36,5 +32,5 @@
-
+
diff --git a/meteoinfo-math/src/main/java/org/meteoinfo/math/stats/regression/StepwiseRegression.java b/meteoinfo-math/src/main/java/org/meteoinfo/math/stats/regression/StepwiseRegression.java
new file mode 100644
index 00000000..e24e1dca
--- /dev/null
+++ b/meteoinfo-math/src/main/java/org/meteoinfo/math/stats/regression/StepwiseRegression.java
@@ -0,0 +1,118 @@
+package org.meteoinfo.math.stats.regression;
+
+import org.apache.commons.math4.legacy.stat.regression.OLSMultipleLinearRegression;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StepwiseRegression {
+ private static final double F_THRESHOLD = 4.0; // F test threshold, can be adjusted as needed
+
+ public static void main(String[] args) {
+ // sample data
+ double[][] xData = {
+ {1, 2, 3, 4},
+ {2, 3, 4, 5},
+ {3, 4, 5, 6},
+ {4, 5, 6, 7},
+ {5, 6, 7, 8}
+ };
+ double[] yData = {1, 2, 3, 4, 5};
+
+ // stepwise regression
+ StepwiseRegression stepwiseRegression = new StepwiseRegression();
+ List selectedVariables = stepwiseRegression.stepwise(xData, yData);
+
+ System.out.println("Selected Variables: " + selectedVariables);
+ }
+
+ public List stepwise(double[][] xData, double[] yData) {
+ List variables = new ArrayList<>();
+ for (int i = 0; i < xData[0].length; i++) {
+ variables.add(i);
+ }
+
+ List selectedVariables = new ArrayList<>();
+ boolean changed;
+
+ do {
+ changed = false;
+
+ // forward selection
+ double bestFValue = Double.NEGATIVE_INFINITY;
+ int bestVariable = -1;
+ for (int var : variables) {
+ if (!selectedVariables.contains(var)) {
+ List tempVars = new ArrayList<>(selectedVariables);
+ tempVars.add(var);
+ double fValue = calculateFValue(xData, yData, tempVars);
+ if (fValue > bestFValue) {
+ bestFValue = fValue;
+ bestVariable = var;
+ }
+ }
+ }
+
+ if (bestFValue > F_THRESHOLD) {
+ selectedVariables.add(bestVariable);
+ changed = true;
+ }
+
+ // backward remove
+ bestFValue = Double.NEGATIVE_INFINITY;
+ bestVariable = -1;
+ for (int var : selectedVariables) {
+ List tempVars = new ArrayList<>(selectedVariables);
+ tempVars.remove(Integer.valueOf(var));
+ double fValue = calculateFValue(xData, yData, tempVars);
+ if (fValue > bestFValue) {
+ bestFValue = fValue;
+ bestVariable = var;
+ }
+ }
+
+ if (bestFValue > F_THRESHOLD && selectedVariables.size() > 1) {
+ selectedVariables.remove(Integer.valueOf(bestVariable));
+ changed = true;
+ }
+ } while (changed);
+
+ return selectedVariables;
+ }
+
+ private double calculateFValue(double[][] xData, double[] yData, List variables) {
+ int n = xData.length;
+ int k = variables.size();
+
+ double[][] xMatrix = new double[n][k + 1];
+ for (int i = 0; i < n; i++) {
+ xMatrix[i][0] = 1; // intercept
+ for (int j = 0; j < k; j++) {
+ xMatrix[i][j + 1] = xData[i][variables.get(j)];
+ }
+ }
+
+ OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression();
+ regression.newSampleData(yData, xMatrix);
+
+ double sse = regression.calculateResidualSumOfSquares();
+ double sst = calculateSST(yData);
+
+ double fValue = ((sst - sse) / k) / (sse / (n - k - 1));
+ return fValue;
+ }
+
+ private double calculateSST(double[] yData) {
+ double mean = 0;
+ for (double y : yData) {
+ mean += y;
+ }
+ mean /= yData.length;
+
+ double sst = 0;
+ for (double y : yData) {
+ sst += Math.pow(y - mean, 2);
+ }
+ return sst;
+ }
+}