/***********************************************************************
This file is part of KEEL-software, the Data Mining tool for regression,
classification, clustering, pattern mining and so on.
Copyright (C) 2004-2010
F. Herrera (herrera@decsai.ugr.es)
L. Sánchez (luciano@uniovi.es)
J. Alcalá-Fdez (jalcala@decsai.ugr.es)
S. García (sglopez@ujaen.es)
A. Fernández (alberto.fernandez@ujaen.es)
J. Luengo (julianlm@decsai.ugr.es)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/
**********************************************************************/
/**
* File: Multiple.java
*
* This class performs several statistical comparisons between NxN methods
*
* @author Written by Joaqu�n Derrac (University of Granada) 29/04/2010
* @version 1.1
* @since JDK1.5
*/
package keel.GraphInterKeel.statistical.tests;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.*;
import keel.GraphInterKeel.statistical.Configuration;
import org.core.*;
public class Multiple {
private static boolean Iman, Nemenyi, Bonferroni, Holm, Hoch, Hommel, Scha, Berg; //post-hoc methods to apply
private static final int MAX_ALGORITHMS = 9;
/**
* Builder
*/
public Multiple(){
}//end-method
/**
* <p>
* In this method, all possible post hoc statistical test between more than three algorithms results
* are executed, according to the configuration file
* @param algorithms A vector of String with the names of the algorithms
* </p>
*/
public static void doMultiple(double data[][], String algorithms[]) {
String outputFileName = Configuration.getPath();
String outputString = new String("");
outputString = header();
outputString += runMultiple(data, algorithms);
Files.writeFile(outputFileName, outputString);
}//end-method
/**
* This method runs the multiple comparison tests
*
* @param code A value to codify which post-hoc methods apply
* @param results Array with the results of the methods
* @param algorithmName Array with the name of the methods employed
*
* @return A string with the contents of the test in LaTeX format
*/
private static String runMultiple(double[][] results,String algorithmName[]) {
int i, j, k;
int posicion;
double mean[][];
MultiplePair orden[][];
MultiplePair rank[][];
boolean encontrado;
int ig;
double sum;
boolean visto[];
Vector<Integer> porVisitar;
double Rj[];
double friedman;
double sumatoria=0;
double termino1, termino2, termino3;
double iman;
boolean vistos[];
int pos, tmp, counter;
String cad;
double maxVal;
double Pi[];
double ALPHAiHolm[];
double ALPHAiShaffer[];
String ordenAlgoritmos[];
double ordenRankings[];
int order[];
double adjustedP[][];
double SE;
boolean parar;
Vector<Integer> indices = new Vector<Integer>();
Vector<Vector<Relation>> exhaustiveI = new Vector<Vector<Relation>>();
boolean[][] cuadro;
double minPi, tmpPi, maxAPi,tmpAPi;
Relation[] parejitas;
Vector<Integer> T;
int Tarray[];
DecimalFormat nf4 = (DecimalFormat) DecimalFormat.getInstance();
nf4.setMaximumFractionDigits(4);
nf4.setMinimumFractionDigits(0);
DecimalFormatSymbols dfs = nf4.getDecimalFormatSymbols();
dfs.setDecimalSeparator('.');
nf4.setDecimalFormatSymbols(dfs);
DecimalFormat nf6 = (DecimalFormat) DecimalFormat.getInstance();
nf6.setMaximumFractionDigits(6);
nf6.setMinimumFractionDigits(0);
nf6.setDecimalFormatSymbols(dfs);
String out="";
int nDatasets = Configuration.getNDatasets();
Iman = Configuration.isIman();
Nemenyi = Configuration.isNemenyi();
Bonferroni = Configuration.isBonferroni();
Holm = Configuration.isHolm();
Hoch = Configuration.isHochberg();
Hommel = Configuration.isHommel();
Scha = Configuration.isShaffer();
Berg = Configuration.isBergman();
mean = new double[nDatasets][algorithmName.length];
//Maximize performance
if(Configuration.getObjective()==1){
/*Compute the average performance per algorithm for each data set*/
for (i=0; i<nDatasets; i++) {
for (j=0; j<algorithmName.length; j++) {
mean[i][j] = results[j][i];
}
}
}
//Minimize performance
else{
double maxValue=Double.MIN_VALUE;
/*Compute the average performance per algorithm for each data set*/
for (i=0; i<nDatasets; i++) {
for (j=0; j<algorithmName.length; j++) {
if(results[j][i]>maxValue){
maxValue=results[j][i];
}
mean[i][j] = (-1.0 * results[j][i]);
}
}
for (i=0; i<nDatasets; i++) {
for (j=0; j<algorithmName.length; j++) {
mean[i][j] += maxValue;
}
}
}
/*We use the pareja structure to compute and order rankings*/
orden = new MultiplePair[nDatasets][algorithmName.length];
for (i=0; i<nDatasets; i++) {
for (j=0; j<algorithmName.length; j++){
orden[i][j] = new MultiplePair (j,mean[i][j]);
}
Arrays.sort(orden[i]);
}
/*building of the rankings table per algorithms and data sets*/
rank = new MultiplePair[nDatasets][algorithmName.length];
posicion = 0;
for (i=0; i<nDatasets; i++) {
for (j=0; j<algorithmName.length; j++){
encontrado = false;
for (k=0; k<algorithmName.length && !encontrado; k++) {
if (orden[i][k].indice == j) {
encontrado = true;
posicion = k+1;
}
}
rank[i][j] = new MultiplePair(posicion,orden[i][posicion-1].valor);
}
}
/*In the case of having the same performance, the rankings are equal*/
for (i=0; i<nDatasets; i++) {
visto = new boolean[algorithmName.length];
porVisitar= new Vector<Integer>();
Arrays.fill(visto,false);
for (j=0; j<algorithmName.length; j++) {
porVisitar.removeAllElements();
sum = rank[i][j].indice;
visto[j] = true;
ig = 1;
for (k=j+1;k<algorithmName.length;k++) {
if (rank[i][j].valor == rank[i][k].valor && !visto[k]) {
sum += rank[i][k].indice;
ig++;
porVisitar.add(new Integer(k));
visto[k] = true;
}
}
sum /= (double)ig;
rank[i][j].indice = sum;
for (k=0; k<porVisitar.size(); k++) {
rank[i][((Integer)porVisitar.elementAt(k)).intValue()].indice = sum;
}
}
}
/*compute the average ranking for each algorithm*/
Rj = new double[algorithmName.length];
for (i=0; i<algorithmName.length; i++){
Rj[i] = 0;
for (j=0; j<nDatasets; j++) {
Rj[i] += rank[j][i].indice / ((double)nDatasets);
}
}
/*Print the average ranking per algorithm*/
out+="\n\nAverage ranks obtained by applying the Friedman procedure\n\n";
out+="\\begin{table}[!htp]\n" +
"\\centering\n" +
"\\begin{tabular}{|c|c|}\\hline\n" +
"Algorithm&Ranking\\\\\\hline\n";
for (i=0; i<algorithmName.length;i++) {
out+=(String)algorithmName[i]+" & "+nf4.format(Rj[i])+"\\\\\n";
}
out+="\\hline\n\\end{tabular}\n\\caption{Average Rankings of the algorithms}\n\\end{table}";
/*Compute the Friedman statistic*/
termino1 = (12*(double)nDatasets)/((double)algorithmName.length*((double)algorithmName.length+1));
termino2 = (double)algorithmName.length*((double)algorithmName.length+1)*((double)algorithmName.length+1)/(4.0);
for (i=0; i<algorithmName.length;i++) {
sumatoria += Rj[i]*Rj[i];
}
friedman = (sumatoria - termino2) * termino1;
out+="\n\nFriedman statistic considering reduction performance (distributed according to chi-square with "
+(algorithmName.length-1)+" degrees of freedom: "+nf6.format(friedman)+".\n\n";
double pFriedman;
pFriedman = ChiSq(friedman, (algorithmName.length-1));
out+="P-value computed by Friedman Test: " + pFriedman +".\\newline\n\n";
/*Compute the Iman-Davenport statistic*/
if(Iman){
iman = ((nDatasets-1)*friedman)/(nDatasets*(algorithmName.length-1) - friedman);
out+="Iman and Davenport statistic considering reduction performance (distributed according to F-distribution with "
+(algorithmName.length-1)+" and "+ (algorithmName.length-1)*(nDatasets-1) +" degrees of freedom: "+nf6.format(iman)+".\n\n";
double pIman;
pIman = FishF(iman, (algorithmName.length-1),(algorithmName.length-1) * (nDatasets - 1));
out+="P-value computed by Iman and Daveport Test: " + pIman +".\\newline\n\n";
}
termino3 = Math.sqrt((double)algorithmName.length*((double)algorithmName.length+1)/(6.0*(double)nDatasets));
out+="\n\n\\pagebreak\n\n";
/************ NxN COMPARISON **************/
out+="\\section{Post hoc comparisons}";
out+="\n\nResults achieved on post hoc comparisons for $\\alpha = 0.05$, $\\alpha = 0.10$ and adjusted p-values.\n\n";
/*Compute the unadjusted p_i value for each comparison alpha=0.05*/
Pi = new double[(int)combinatoria(2,algorithmName.length)];
ALPHAiHolm = new double[(int)combinatoria(2,algorithmName.length)];
ALPHAiShaffer = new double[(int)combinatoria(2,algorithmName.length)];
ordenAlgoritmos = new String[(int)combinatoria(2,algorithmName.length)];
ordenRankings = new double[(int)combinatoria(2,algorithmName.length)];
order = new int[(int)combinatoria(2,algorithmName.length)];
parejitas = new Relation[(int)combinatoria(2,algorithmName.length)];
T = new Vector<Integer>();
T = trueHShaffer(algorithmName.length);
Tarray = new int[T.size()];
for (i=0; i<T.size(); i++) {
Tarray[i] = ((Integer)T.elementAt(i)).intValue();
}
Arrays.sort(Tarray);
SE = termino3;
vistos = new boolean[(int)combinatoria(2,algorithmName.length)];
for (i=0, k=0; i<algorithmName.length;i++) {
for (j=i+1; j<algorithmName.length;j++,k++) {
ordenRankings[k] = Math.abs(Rj[i] -Rj[j]);
ordenAlgoritmos[k] = (String)algorithmName[i] + " vs. " + (String)algorithmName[j];
parejitas[k] = new Relation(i,j);
}
}
Arrays.fill(vistos,false);
for (i=0; i<ordenRankings.length; i++) {
for (j=0;vistos[j]==true;j++);
pos = j;
maxVal = ordenRankings[j];
for (j=j+1;j<ordenRankings.length;j++) {
if (vistos[j] == false && ordenRankings[j] > maxVal) {
pos = j;
maxVal = ordenRankings[j];
}
}
vistos[pos] = true;
order[i] = pos;
}
/*Computing the logically related hypotheses tests (Shaffer and Bergmann-Hommel)*/
pos = 0;
tmp = Tarray.length-1;
for (i=0; i<order.length; i++) {
Pi[i] = 2*CDF_Normal.normp((-1)*Math.abs((ordenRankings[order[i]])/SE));
ALPHAiHolm[i] = 0.05/((double)order.length-(double)i);
ALPHAiShaffer[i] = 0.05/((double)order.length-(double)Math.max(pos,i));
if (i == pos && Pi[i] <= ALPHAiShaffer[i]) {
tmp--;
pos = (int)combinatoria(2,algorithmName.length) - Tarray[tmp];
}
}
out+="\\subsection{P-values for $\\alpha=0.05$}\n\n";
int count=4;
if(Holm){
count++;
}
if(Scha){
count++;
}
out+="\\begin{table}[!htp]\n\\centering\\scriptsize\n" +
"\\begin{tabular}{"+printC(count)+"}\n" +
"$i$&algorithms&$z=(R_0 - R_i)/SE$&$p$";
if(Holm){
out+="&Holm";
}
if(Scha){
out+="&Shaffer";
}
out+="\\\\\n\\hline";
for (i=0; i<order.length; i++) {
out+=(order.length-i) + "&" + ordenAlgoritmos[order[i]] + "&" +
nf6.format(Math.abs((ordenRankings[order[i]])/SE)) + "&" +
nf6.format(Pi[i]);
if(Holm){
out+="&" + nf6.format(ALPHAiHolm[i]);
}
if(Scha){
out+="&" + nf6.format(ALPHAiShaffer[i]);
}
out+="\\\\\n";
}
out+="\\hline\n" + "\\end{tabular}\n\\caption{P-values Table for $\\alpha=0.05$}\n" + "\\end{table}";
/*Compute the rejected hipotheses for each test*/
if(Nemenyi){
out+="Nemenyi's procedure rejects those hypotheses that have an unadjusted p-value $\\le"+nf6.format(0.05/(double)(order.length))+"$.\n\n";
}
if(Holm){
parar = false;
for (i=0; i<order.length && !parar; i++) {
if (Pi[i] > ALPHAiHolm[i]) {
out+="Holm's procedure rejects those hypotheses that have an unadjusted p-value $\\le"+nf6.format(ALPHAiHolm[i])+"$.\n\n";
parar = true;
}
}
}
if(Scha){
parar = false;
for (i=0; i<order.length && !parar; i++) {
if (Pi[i] <= ALPHAiShaffer[i]) {
out+="Shaffer's procedure rejects those hypotheses that have an unadjusted p-value $\\le"+nf6.format(ALPHAiShaffer[i])+"$.\n\n";
parar = true;
}
}
}
/*For Bergmann-Hommel's procedure, 9 algorithms could suppose intense computation*/
if ((algorithmName.length <= MAX_ALGORITHMS)&&(Berg)) {
for (i=0; i<algorithmName.length; i++) {
indices.add(new Integer(i));
}
exhaustiveI = obtainExhaustive(indices);
cuadro = new boolean[algorithmName.length][algorithmName.length];
for (i=0; i<algorithmName.length; i++) {
Arrays.fill(cuadro[i], false);
}
for (i=0; i<exhaustiveI.size(); i++) {
minPi = 2*CDF_Normal.normp((-1)*Math.abs(Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(0)).i] - Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(0)).j])/SE);
for (j=1; j<((Vector<Relation>)exhaustiveI.elementAt(i)).size(); j++) {
tmpPi = 2*CDF_Normal.normp((-1)*Math.abs(Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(j)).i] - Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(j)).j])/SE);
if (tmpPi < minPi) {
minPi = tmpPi;
}
}
if (minPi > (0.05/((double)((Vector<Relation>)exhaustiveI.elementAt(i)).size()))) {
for (j=0; j<((Vector<Relation>)exhaustiveI.elementAt(i)).size(); j++) {
cuadro[((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(j)).i][((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(j)).j] = true;
}
}
}
if(Berg){
cad="";
cad+="Bergmann's procedure rejects these hypotheses:\n\n";
cad+="\\begin{itemize}\n\n";
counter=0;
for (i=0; i<cuadro.length;i++) {
for (j=i+1; j<cuadro.length;j++) {
if (cuadro[i][j] == false) {
cad+="\\item "+algorithmName[i]+" vs. "+algorithmName[j]+"\n\n";
counter++;
}
}
}
cad+="\\end{itemize}\n\n";
if(counter>0){
out+=cad;
}
else{
out+="Bergmann's procedure does not reject any hypotheses.\n\n";
}
}
}
out+="\\pagebreak\n\n";
out+="\\subsection{P-values for $\\alpha=0.10$}\n\n";
/*Compute the unadjusted p_i value for each comparison alpha=0.10*/
Pi = new double[(int)combinatoria(2,algorithmName.length)];
ALPHAiHolm = new double[(int)combinatoria(2,algorithmName.length)];
ALPHAiShaffer = new double[(int)combinatoria(2,algorithmName.length)];
ordenAlgoritmos = new String[(int)combinatoria(2,algorithmName.length)];
ordenRankings = new double[(int)combinatoria(2,algorithmName.length)];
order = new int[(int)combinatoria(2,algorithmName.length)];
SE = termino3;
vistos = new boolean[(int)combinatoria(2,algorithmName.length)];
for (i=0, k=0; i<algorithmName.length;i++) {
for (j=i+1; j<algorithmName.length;j++,k++) {
ordenRankings[k] = Math.abs(Rj[i] -Rj[j]);
ordenAlgoritmos[k] = (String)algorithmName[i] + " vs. " + (String)algorithmName[j];
}
}
Arrays.fill(vistos,false);
for (i=0; i<ordenRankings.length; i++) {
for (j=0;vistos[j]==true;j++);
pos = j;
maxVal = ordenRankings[j];
for (j=j+1;j<ordenRankings.length;j++) {
if (vistos[j] == false && ordenRankings[j] > maxVal) {
pos = j;
maxVal = ordenRankings[j];
}
}
vistos[pos] = true;
order[i] = pos;
}
/*Computing the logically related hypotheses tests (Shaffer and Bergmann-Hommel)*/
pos = 0;
tmp = Tarray.length-1;
for (i=0; i<order.length; i++) {
Pi[i] = 2*CDF_Normal.normp((-1)*Math.abs((ordenRankings[order[i]])/SE));
ALPHAiHolm[i] = 0.1/((double)order.length-(double)i);
ALPHAiShaffer[i] = 0.1/((double)order.length-(double)Math.max(pos,i));
if (i == pos && Pi[i] <= ALPHAiShaffer[i]) {
tmp--;
pos = (int)combinatoria(2,algorithmName.length) - Tarray[tmp];
}
}
count=4;
if(Holm){
count++;
}
if(Scha){
count++;
}
out+="\\begin{table}[!htp]\n\\centering\\scriptsize\n" +
"\\begin{tabular}{"+printC(count)+"}\n" +
"$i$&algorithms&$z=(R_0 - R_i)/SE$&$p$";
if(Holm){
out+="&Holm";
}
if(Scha){
out+="&Shaffer";
}
out+="\\\\\n\\hline";
for (i=0; i<order.length; i++) {
out+=(order.length-i) + "&" + ordenAlgoritmos[order[i]] + "&" +
nf6.format(Math.abs((ordenRankings[order[i]])/SE)) + "&" +
nf6.format(Pi[i]);
if(Holm){
out+="&" + nf6.format(ALPHAiHolm[i]);
}
if(Scha){
out+="&" + nf6.format(ALPHAiShaffer[i]);
}
out+="\\\\\n";
}
out+="\\hline\n" + "\\end{tabular}\n\\caption{P-values Table for $\\alpha=0.10$}\n" + "\\end{table}";
/*Compute the rejected hipotheses for each test*/
if(Nemenyi){
out+="Nemenyi's procedure rejects those hypotheses that have an unadjusted p-value $\\le"+nf6.format(0.10/(double)(order.length))+"$.\n\n";
}
if(Holm){
parar = false;
for (i=0; i<order.length && !parar; i++) {
if (Pi[i] > ALPHAiHolm[i]) {
out+="Holm's procedure rejects those hypotheses that have an unadjusted p-value $\\le"+nf6.format(ALPHAiHolm[i])+"$.\n\n";
parar = true;
}
}
}
if(Scha){
parar = false;
for (i=0; i<order.length && !parar; i++) {
if (Pi[i] <= ALPHAiShaffer[i]) {
out+="Shaffer's procedure rejects those hypotheses that have an unadjusted p-value $\\le"+nf6.format(ALPHAiShaffer[i])+"$.\n\n";
parar = true;
}
}
}
/*For Bergmann-Hommel's procedure, 9 algorithms could suppose intense computation*/
if ((algorithmName.length <= MAX_ALGORITHMS)&(Berg)) {
indices.removeAllElements();
for (i=0; i<algorithmName.length; i++) {
indices.add(new Integer(i));
}
exhaustiveI = obtainExhaustive(indices);
cuadro = new boolean[algorithmName.length][algorithmName.length];
for (i=0; i<algorithmName.length; i++) {
Arrays.fill(cuadro[i], false);
}
for (i=0; i<exhaustiveI.size(); i++) {
minPi = 2*CDF_Normal.normp((-1)*Math.abs(Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(0)).i] - Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(0)).j])/SE);
for (j=1; j<((Vector<Relation>)exhaustiveI.elementAt(i)).size(); j++) {
tmpPi = 2*CDF_Normal.normp((-1)*Math.abs(Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(j)).i] - Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(j)).j])/SE);
if (tmpPi < minPi) {
minPi = tmpPi;
}
}
if (minPi > 0.1/((double)((Vector<Relation>)exhaustiveI.elementAt(i)).size())) {
for (j=0; j<((Vector<Relation>)exhaustiveI.elementAt(i)).size(); j++) {
cuadro[((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(j)).i][((Relation)((Vector<Relation>)exhaustiveI.elementAt(i)).elementAt(j)).j] = true;
}
}
}
if(Berg){
cad="";
cad+="Bergmann's procedure rejects these hypotheses:\n\n";
cad+="\\begin{itemize}\n\n";
counter=0;
for (i=0; i<cuadro.length;i++) {
for (j=i+1; j<cuadro.length;j++) {
if (cuadro[i][j] == false) {
cad+="\\item "+algorithmName[i]+" vs. "+algorithmName[j]+"\n\n";
counter++;
}
}
}
cad+="\\end{itemize}\n\n";
if(counter>0){
out+=cad;
}
else{
out+="Bergmann's procedure does not reject any hypotheses.\n\n";
}
}
}
out+="\\pagebreak\n\n";
/************ ADJUSTED P-VALUES NxN COMPARISON **************/
out+="\\subsection{Adjusted p-values}\n\n";
adjustedP = new double[Pi.length][4];
pos = 0;
tmp = Tarray.length-1;
for (i=0; i<adjustedP.length; i++) {
adjustedP[i][0] = Pi[i] * (double)(adjustedP.length);
adjustedP[i][1] = Pi[i] * (double)(adjustedP.length-i);
adjustedP[i][2] = Pi[i] * ((double)adjustedP.length-(double)Math.max(pos,i));
if (i == pos) {
tmp--;
pos = (int)combinatoria(2,algorithmName.length) - Tarray[tmp];
}
if (algorithmName.length <= MAX_ALGORITHMS) {
maxAPi = Double.MIN_VALUE;
minPi = Double.MAX_VALUE;
for (j=0; j<exhaustiveI.size(); j++) {
if (exhaustiveI.elementAt(j).toString().contains(parejitas[order[i]].toString())) {
minPi = 2*CDF_Normal.normp((-1)*Math.abs(Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(j)).elementAt(0)).i] - Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(j)).elementAt(0)).j])/SE);
for (k=1; k<((Vector<Relation>)exhaustiveI.elementAt(j)).size(); k++) {
tmpPi = 2*CDF_Normal.normp((-1)*Math.abs(Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(j)).elementAt(k)).i] - Rj[((Relation)((Vector<Relation>)exhaustiveI.elementAt(j)).elementAt(k)).j])/SE);
if (tmpPi < minPi) {
minPi = tmpPi;
}
}
tmpAPi = minPi * (double)(((Vector<Relation>)exhaustiveI.elementAt(j)).size());
if (tmpAPi > maxAPi) {
maxAPi = tmpAPi;
}
}
}
adjustedP[i][3] = maxAPi;
}
}
for (i=1; i<adjustedP.length; i++) {
if (adjustedP[i][1] < adjustedP[i-1][1])
adjustedP[i][1] = adjustedP[i-1][1];
if (adjustedP[i][2] < adjustedP[i-1][2])
adjustedP[i][2] = adjustedP[i-1][2];
if (adjustedP[i][3] < adjustedP[i-1][3])
adjustedP[i][3] = adjustedP[i-1][3];
}
count=3;
if(Nemenyi){
count++;
}
if(Holm){
count++;
}
if(Scha){
count++;
}
if(Berg){
count++;
}
out+="\\begin{table}[!htp]\n\\centering\\scriptsize\n" +
"\\begin{tabular}{"+printC(count)+"}\n" +
"i&hypothesis&unadjusted $p$";
if(Nemenyi){
out+="&$p_{Neme}$";
}
if(Holm){
out+="&$p_{Holm}$";
}
if(Scha){
out+="&$p_{Shaf}$";
}
if(Berg){
out+="&$p_{Berg}$";
}
out+="\\\\\n\\hline";
for (i=0; i<Pi.length; i++) {
out+=(i+1) + "&" + algorithmName[parejitas[order[i]].i] + " vs ." + algorithmName[parejitas[order[i]].j] + "&" + nf6.format(Pi[i]);
if(Nemenyi){
out+="&" + nf6.format(adjustedP[i][0]);
}
if(Holm){
out+="&" + nf6.format(adjustedP[i][1]);
}
if(Scha){
out+="&" + nf6.format(adjustedP[i][2]);
}
if(Berg){
out+="&" + nf6.format(adjustedP[i][3]);
}
out+="\\\\\n";
}
out+="\\hline\n" + "\\end{tabular}\n\\caption{Adjusted $p$-values}\n" + "\\end{table}\n\n";
out+="\\end{landscape}\n\\end{document}";
return out;
}//end-method
/**
* Obtain all exhaustive comparisons possible from an array of indexes
*
* @param indices A verctos of indexes.
*
* @return A vector with vectors containing all the possible relations between the indexes
*/
@SuppressWarnings("unchecked")
public static Vector<Vector<Relation>> obtainExhaustive (Vector<Integer> indices) {
Vector<Vector<Relation>> result = new Vector<Vector<Relation>>();
int i,j,k;
String binario;
boolean[] number = new boolean[indices.size()];
Vector<Integer> ind1, ind2;
Vector<Relation> set = new Vector<Relation>();
Vector<Vector<Relation>> res1, res2;
Vector<Relation> temp;
Vector<Relation> temp2;
Vector<Relation> temp3;
ind1 = new Vector<Integer>();
ind2 = new Vector<Integer>();
temp = new Vector<Relation>();
temp2 = new Vector<Relation>();
temp3 = new Vector<Relation>();
for (i=0; i<indices.size();i++) {
for (j=i+1; j<indices.size();j++) {
set.addElement(new Relation(((Integer)indices.elementAt(i)).intValue(),((Integer)indices.elementAt(j)).intValue()));
}
}
if (set.size()>0)
result.addElement(set);
for (i=1; i<(int)(Math.pow(2, indices.size()-1)); i++) {
Arrays.fill(number, false);
ind1.removeAllElements();
ind2.removeAllElements();
temp.removeAllElements();
temp2.removeAllElements();
temp3.removeAllElements();
binario = Integer.toString(i, 2);
for (k=0; k<number.length-binario.length();k++) {
number[k] = false;
}
for (j=0; j<binario.length();j++,k++) {
if (binario.charAt(j) == '1')
number[k] = true;
}
for (j=0; j<number.length; j++) {
if (number[j] == true) {
ind1.addElement(new Integer(((Integer)indices.elementAt(j)).intValue()));
} else {
ind2.addElement(new Integer(((Integer)indices.elementAt(j)).intValue()));
}
}
res1 = obtainExhaustive (ind1);
res2 = obtainExhaustive (ind2);
for (j=0; j<res1.size();j++) {
result.addElement(new Vector<Relation>((Vector<Relation>)res1.elementAt(j)));
}
for (j=0; j<res2.size();j++) {
result.addElement(new Vector<Relation>((Vector<Relation>)res2.elementAt(j)));
}
for (j=0; j<res1.size();j++) {
temp = (Vector<Relation>)((Vector<Relation>)res1.elementAt(j)).clone();
for (k=0; k<res2.size();k++) {
temp2 = (Vector<Relation>)temp.clone();
temp3 = (Vector<Relation>) ((Vector<Relation>)res2.elementAt(k)).clone();
if (((Relation)temp2.elementAt(0)).i < ((Relation)temp3.elementAt(0)).i) {
temp2.addAll((Vector<Relation>)temp3);
result.addElement(new Vector<Relation>(temp2));
} else {
temp3.addAll((Vector<Relation>)temp2);
result.addElement(new Vector<Relation>(temp3));
}
}
}
}
for (i=0;i<result.size();i++) {
if (((Vector<Relation>)result.elementAt(i)).toString().equalsIgnoreCase("[]")) {
result.removeElementAt(i);
i--;
}
}
for (i=0;i<result.size();i++) {
for (j=i+1; j<result.size(); j++) {
if (((Vector<Relation>)result.elementAt(i)).toString().equalsIgnoreCase(((Vector<Relation>)result.elementAt(j)).toString())) {
result.removeElementAt(j);
j--;
}
}
}
return result;
}//end-method
/**
* Computes the (N/M) combinatory number
*
* @param n N value
* @param m M value
*
* @return The (N/M) combinatory number
*/
public static double combinatoria (int m, int n) {
double result = 1;
int i;
if (n >= m) {
for (i=1; i<=m; i++)
result *= (double)(n-m+i)/(double)i;
} else {
result = 0;
}
return result;
}//end-method
/**
* Computes the trueHShaffer distribution from a given parameter.
*
* @param k K parameter
*
* @return The trueHShaffer distribution
*/
public static Vector<Integer> trueHShaffer (int k) {
Vector<Integer> number;
int j;
Vector<Integer> tmp, tmp2;
int p;
number = new Vector<Integer>();
tmp = new Vector<Integer>();
if (k <= 1) {
number.addElement(new Integer(0));
} else {
for (j=1; j<=k; j++) {
tmp = trueHShaffer (k-j);
tmp2 = new Vector<Integer>();
for (p=0; p<tmp.size(); p++) {
tmp2.addElement(((Integer)(tmp.elementAt(p))).intValue() + (int)combinatoria(2,j));
}
number = unionVectores (number,tmp2);
}
}
return number;
}//end-method
/**
* Joins two vectors
*
* @param a First vector
* @param b Second vector
*
* @return The joint of both vectors
*/
public static Vector<Integer> unionVectores (Vector<Integer> a, Vector<Integer> b) {
int i;
for (i=0; i<b.size(); i++) {
if (a.contains(new Integer((Integer)(b.elementAt(i)))) == false) {
a.addElement(b.elementAt(i));
}
}
return a;
}//end-method
/**
* Chi square distribution
*
* @param x Chi^2 value
* @param n Degrees of freedom
*
* @return P-value associated
*/
private static double ChiSq(double x, int n) {
if (n == 1 & x > 1000) {
return 0;
}
if (x > 1000 | n > 1000) {
double q = ChiSq((x - n) * (x - n) / (2 * n), 1) / 2;
if (x > n) {
return q;
}
{
return 1 - q;
}
}
double p = Math.exp( -0.5 * x);
if ((n % 2) == 1) {
p = p * Math.sqrt(2 * x / Math.PI);
}
double k = n;
while (k >= 2) {
p = p * x / k;
k = k - 2;
}
double t = p;
double a = n;
while (t > 0.0000000001 * p) {
a = a + 2;
t = t * x / a;
p = p + t;
}
return 1 - p;
}
/**
* Fisher distribution
*
* @param f Fisher value
* @param n1 N1 value
* @param n2 N2 value
*
* @return P-value associated
*/
private static double FishF(double f, int n1, int n2) {
double x = n2 / (n1 * f + n2);
if ((n1 % 2) == 0) {
return StatCom(1 - x, n2, n1 + n2 - 4, n2 - 2) * Math.pow(x, n2 / 2.0);
}
if ((n2 % 2) == 0) {
return 1 -
StatCom(x, n1, n1 + n2 - 4, n1 - 2) *
Math.pow(1 - x, n1 / 2.0);
}
double th = Math.atan(Math.sqrt(n1 * f / (1.0*n2)));
double a = th / (Math.PI / 2.0);
double sth = Math.sin(th);
double cth = Math.cos(th);
if (n2 > 1) {
a = a +
sth * cth * StatCom(cth * cth, 2, n2 - 3, -1) / (Math.PI / 2.0);
}
if (n1 == 1) {
return 1 - a;
}
double c = 4 * StatCom(sth * sth, n2 + 1, n1 + n2 - 4, n2 - 2) * sth *
Math.pow(cth, n2) / Math.PI;
if (n2 == 1) {
return 1 - a + c / 2.0;
}
int k = 2;
while (k <= (n2 - 1) / 2.0) {
c = c * k / (k - .5);
k = k + 1;
}
return 1 - a + c;
}
/**
* StatCom distribution
*
* @param q q parameter
* @param i i parameter
* @param j j parameter
* @param b b parameter
*
* @return P-value associated
*/
private static double StatCom(double q, int i, int j, int b) {
double zz = 1;
double z = zz;
int k = i;
while (k <= j) {
zz = zz * q * k / (k - b);
z = z + zz;
k = k + 2;
}
return z;
}
/**
* Prints as many "c" as desired
*
* @param n Number of "c" to print
*
* @return A string with all the "c"s
*/
public static String printC(int n){
String out="";
for(int i=0;i<n;i++){
out+="c";
}
return out;
}//end-method
/**
* <p>
* This method composes the header of the LaTeX file where the results are saved
* </p>
* @return A string with the header of the LaTeX file
*/
private static String header() {
String output = new String("");
output += "\\documentclass[a4paper,10pt]{article}\n";
output += "\\usepackage{graphicx}\n";
output += "\\usepackage{lscape}\n";
output += "\\title{Output tables for the test of Multiple comparisons.}\n";
output += "\\author{}\n\\date{\\today}\n\\begin{document}\n";
output += "\\begin{landscape}\n\\pagestyle{empty}\n\\maketitle\n\\thispagestyle{empty}\n\\section{Average rankings of Friedman test}\n\n";
return output;
}//end-method
}//end-class