Java tutorial
// This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. package org.uma.jmetal.util.experiment.component; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; import org.uma.jmetal.qualityindicator.impl.GenericIndicator; import org.uma.jmetal.util.JMetalException; import org.uma.jmetal.util.experiment.Experiment; import org.uma.jmetal.util.experiment.ExperimentComponent; import java.io.*; import java.util.*; /** * This class computes the Friedman test ranking and generates a Latex script that produces a table per * quality indicator containing the ranking * * The results are a set of Latex files that are written in the directory * {@link Experiment #getExperimentBaseDirectory()}/latex. Each file is called as * FriedmanTest[indicatorName].tex * * The implementation is based on the one included in Keel: * J. Alcal-Fdez, L. Snchez, S. Garca, M.J. del Jesus, S. Ventura, J.M. Garrell, J. Otero, C. Romero, J. Bacardit, * V.M. Rivas, J.C. Fernndez, F. Herrera. * KEEL: A Software Tool to Assess Evolutionary Algorithms to Data Mining Problems. Soft Computing 13:3 (2009) 307-318 * Doi: 10.1007/s00500-008-0323-y * * @author Antonio J. Nebro <antonio@lcc.uma.es> */ public class GenerateFriedmanTestTables<Result> implements ExperimentComponent { private static final String DEFAULT_LATEX_DIRECTORY = "latex"; private final Experiment<?, Result> experiment; private String latexDirectoryName; private int numberOfAlgorithms; private int numberOfProblems; public GenerateFriedmanTestTables(Experiment<?, Result> experimentConfiguration) { this.experiment = experimentConfiguration; experiment.removeDuplicatedAlgorithms(); numberOfAlgorithms = experiment.getAlgorithmList().size(); numberOfProblems = experiment.getProblemList().size(); } @Override public void run() throws IOException { latexDirectoryName = experiment.getExperimentBaseDirectory() + "/" + DEFAULT_LATEX_DIRECTORY; for (GenericIndicator<?> indicator : experiment.getIndicatorList()) { Vector<Vector<Double>> data = readData(indicator); double[] averageRanking = computeAverageRanking(data); String fileContents = prepareFileOutputContents(averageRanking); writeLatexFile(indicator, fileContents); } } private Vector<Vector<Double>> readData(GenericIndicator<?> indicator) { Vector<Vector<Double>> data = new Vector<Vector<Double>>(); for (int algorithm = 0; algorithm < experiment.getAlgorithmList().size(); algorithm++) { String algorithmName = experiment.getAlgorithmList().get(algorithm).getTag(); data.add(new Vector<Double>()); String algorithmPath = experiment.getExperimentBaseDirectory() + "/data/" + algorithmName + "/"; for (int problem = 0; problem < experiment.getProblemList().size(); problem++) { String path = algorithmPath + experiment.getProblemList().get(problem).getName() + "/" + indicator.getName(); readDataFromFile(path, data, algorithm); } } return data; } private void readDataFromFile(String path, Vector<Vector<Double>> data, int algorithmIndex) { String string = ""; try { FileInputStream fis = new FileInputStream(path); byte[] bytes = new byte[4096]; int readBytes = 0; while (readBytes != -1) { readBytes = fis.read(bytes); if (readBytes != -1) { string += new String(bytes, 0, readBytes); } } fis.close(); } catch (IOException e) { throw new JMetalException("Error reading data ", e); } StringTokenizer lines = new StringTokenizer(string, "\n\r"); double valor = 0.0; int n = 0; while (lines.hasMoreTokens()) { valor = valor + Double.parseDouble(lines.nextToken()); n++; } if (n != 0) { (data.elementAt(algorithmIndex)).add(valor / n); } else { (data.elementAt(algorithmIndex)).add(valor); } } private double[] computeAverageRanking(Vector<Vector<Double>> data) { /*Compute the average performance per algorithm for each data set*/ double[][] mean = new double[numberOfProblems][numberOfAlgorithms]; for (int j = 0; j < numberOfAlgorithms; j++) { for (int i = 0; i < numberOfProblems; i++) { mean[i][j] = data.elementAt(j).elementAt(i); } } /*We use the Pair class to compute and order rankings*/ List<List<Pair<Integer, Double>>> order = new ArrayList<List<Pair<Integer, Double>>>(numberOfProblems); for (int i = 0; i < numberOfProblems; i++) { order.add(new ArrayList<Pair<Integer, Double>>(numberOfAlgorithms)); for (int j = 0; j < numberOfAlgorithms; j++) { order.get(i).add(new ImmutablePair<Integer, Double>(j, mean[i][j])); } Collections.sort(order.get(i), new Comparator<Pair<Integer, Double>>() { @Override public int compare(Pair<Integer, Double> pair1, Pair<Integer, Double> pair2) { if (Math.abs(pair1.getValue()) > Math.abs(pair2.getValue())) { return 1; } else if (Math.abs(pair1.getValue()) < Math.abs(pair2.getValue())) { return -1; } else { return 0; } } }); } /*building of the rankings table per algorithms and data sets*/ // Pair[][] rank = new Pair[numberOfProblems][numberOfAlgorithms]; List<List<MutablePair<Double, Double>>> rank = new ArrayList<List<MutablePair<Double, Double>>>( numberOfProblems); int position = 0; for (int i = 0; i < numberOfProblems; i++) { rank.add(new ArrayList<MutablePair<Double, Double>>(numberOfAlgorithms)); for (int j = 0; j < numberOfAlgorithms; j++) { boolean found = false; for (int k = 0; k < numberOfAlgorithms && !found; k++) { if (order.get(i).get(k).getKey() == j) { found = true; position = k + 1; } } //rank[i][j] = new Pair(position,order[i][position-1].value); rank.get(i).add(new MutablePair<Double, Double>((double) position, order.get(i).get(position - 1).getValue())); } } /*In the case of having the same performance, the rankings are equal*/ for (int i = 0; i < numberOfProblems; i++) { boolean[] hasBeenVisited = new boolean[numberOfAlgorithms]; Vector<Integer> pendingToVisit = new Vector<Integer>(); Arrays.fill(hasBeenVisited, false); for (int j = 0; j < numberOfAlgorithms; j++) { pendingToVisit.removeAllElements(); double sum = rank.get(i).get(j).getKey(); hasBeenVisited[j] = true; int ig = 1; for (int k = j + 1; k < numberOfAlgorithms; k++) { if (rank.get(i).get(j).getValue() == rank.get(i).get(k).getValue() && !hasBeenVisited[k]) { sum += rank.get(i).get(k).getKey(); ig++; pendingToVisit.add(k); hasBeenVisited[k] = true; } } sum /= (double) ig; rank.get(i).get(j).setLeft(sum); for (int k = 0; k < pendingToVisit.size(); k++) { rank.get(i).get(pendingToVisit.elementAt(k)).setLeft(sum); } } } /*compute the average ranking for each algorithm*/ double[] averageRanking = new double[numberOfAlgorithms]; for (int i = 0; i < numberOfAlgorithms; i++) { averageRanking[i] = 0; for (int j = 0; j < numberOfProblems; j++) { averageRanking[i] += rank.get(j).get(i).getKey() / ((double) numberOfProblems); } } return averageRanking; } public String prepareFileOutputContents(double[] averageRanking) { String fileContents = writeLatexHeader(); fileContents = printTableHeader(fileContents); fileContents = printTableLines(fileContents, averageRanking); fileContents = printTableTail(fileContents); fileContents = printDocumentFooter(fileContents, averageRanking); return fileContents; } /** * Write the file contents in the output file * @param indicator * @param fileContents */ private void writeLatexFile(GenericIndicator<?> indicator, String fileContents) { String outputFile = latexDirectoryName + "/FriedmanTest" + indicator.getName() + ".tex"; try { File latexOutput; latexOutput = new File(latexDirectoryName); if (!latexOutput.exists()) { latexOutput.mkdirs(); } FileOutputStream fileOutputStream = new FileOutputStream(outputFile); DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream); dataOutputStream.writeBytes(fileContents); dataOutputStream.close(); fileOutputStream.close(); } catch (IOException e) { throw new JMetalException("Error writing data ", e); } } private String writeLatexHeader() { return ("\\documentclass{article}\n" + "\\usepackage{graphicx}\n" + "\\title{Results}\n" + "\\author{}\n" + "\\date{\\today}\n" + "\\begin{document}\n" + "\\oddsidemargin 0in \\topmargin 0in" + "\\maketitle\n" + "\\\n" + "\\section{Tables}"); } private String printTableLines(String fileContents, double[] averageRanking) { String output = fileContents; for (int i = 0; i < experiment.getAlgorithmList().size(); i++) { output += "\n" + experiment.getAlgorithmList().get(i).getTag() + "&" + averageRanking[i] + "\\\\"; } return output; } private String printTableTail(String fileContents) { return fileContents + "\n" + "\\end{tabular}\n" + "\\end{table}"; } private String printTableHeader(String fileContents) { return fileContents + "\n" + ("\\begin{table}[!htp]\n" + "\\centering\n" + "\\caption{Average ranking of the algorithms}\n" + "\\begin{tabular}{c|c}\n" + "Algorithm&Ranking\\\\\n\\hline"); } private String printDocumentFooter(String fileContents, double[] averageRanking) { double term1 = (12 * (double) numberOfProblems) / (numberOfAlgorithms * (numberOfAlgorithms + 1)); double term2 = numberOfAlgorithms * (numberOfAlgorithms + 1) * (numberOfAlgorithms + 1) / (4.0); double sum = 0; for (int i = 0; i < numberOfAlgorithms; i++) { sum += averageRanking[i] * averageRanking[i]; } double friedman = (sum - term2) * term1; String output = fileContents + "\n" + "\n\nFriedman statistic considering reduction performance (distributed according to " + "chi-square with " + (numberOfAlgorithms - 1) + " degrees of freedom: " + friedman + ").\n\n"; output = output + "\n" + "\\end{document}"; return output; } }