Log in Help
Print
Homereleasesgate-8.4-build5748-ALLpluginsInter_Annotator_Agreementsrcgateiaaplugin 〉 IaaCalculation.java
 
/**
 *
 *  Copyright (c) 1995-2012, The University of Sheffield. See the file
 *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
 *
 *  This file is part of GATE (see http://gate.ac.uk/), and is free
 *  software, licenced under the GNU Library General Public License,
 *  Version 2, June 1991 (in the distribution as file licence.html,
 *  and also available at http://gate.ac.uk/gate/licence.html).
 *
 *  $Id: IaaCalculation.java 12006 2009-12-01 17:24:28Z thomas_heitz $
 */

package gate.iaaplugin;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import gate.Annotation;
import gate.AnnotationSet;
import gate.Factory;
import gate.FeatureMap;
import gate.util.AnnotationDiffer;

/**
 * 
 * Compute the IAA(inter-annotator agreements), including the observed
 * agreement, category specific agreeement, several variants of Kappa such as
 * Cohen's Kappa, and the F-measures between the annotators (see the GATE user
 * Guide for detailed explanations about those measures).
 * 
 */
public class IaaCalculation {
  /** Non-category string. */
  public static String NONCAT = "Non-cat";
  /** Number of annotators. */
  public int numAnnotators;
  /** Number of documents. */
  public int numDocs;
  /** Number of labels. */
  public int numLabels;
  /** Name of label feature. */
  public String nameClassFeat;
  /** Array of labels. */
  public String[] labelsArr;
  /** Name of annotation type. */
  public String nameAnnType;
  /** Using label or not. */
  public boolean isUsingLabel = false;
  /**
   * Array of annotation sets, first dimension is for document, and second
   * dimension is for annotator.
   */
  public AnnotationSet[][] annsArrArr;
  /** The overall contingency table. */
  public ContingencyTable contingencyOverall;
  /** The contingency table for each pair of annotator. */
  public ContingencyTable[] contingencyTables=null;
  /** The overall F-measure. */
  public FMeasure fMeasureOverall;
  /** Fmeaures for each pair of annotator and each label. */
  public FMeasure[][] fMeasuresPairwiseLabel=null;
  /** Fmeaures for each pair of annotator and over all labels. */
  public FMeasure[] fMeasuresPairwise=null;
  /** The verbosity level for print the measures. */
  int verbosity = 2;
  /** The name of annotators. */
  public String [] annotatorNames = null;

  /** Constractor by giving the annotation sets and a list of labels. */
  public IaaCalculation(String nameAnnT, String nameF, String[] labels,
    AnnotationSet[][] annsA2, int verbsy) {
    this.nameAnnType = nameAnnT;
    this.nameClassFeat = nameF;
    this.labelsArr = labels;
    isUsingLabel = true;
    this.annsArrArr = annsA2;
    this.numLabels = labels.length;
    this.numDocs = annsA2.length; // the number of documents
    this.numAnnotators = annsA2[0].length; // the number of annotators
    this.verbosity = verbsy;
    if(this.numAnnotators < 2) {
      if(this.verbosity>0) System.out
        .println("Warning: The IAA calculation needs at least two annotation sets. ");
    }
    checkIsAnnsMissing();
  }

  /** Constructor by giving the annotation sets and the name of annotation type. */
  public IaaCalculation(String nameAnnT, AnnotationSet[][] annsA2, int verbsy) {
    this.nameAnnType = nameAnnT;
    this.numLabels = 1;
    this.labelsArr = new String[this.numLabels];
    this.labelsArr[0] = "Anns";
    isUsingLabel = false;
    this.annsArrArr = annsA2;
    this.numDocs = annsA2.length; // the number of documents
    this.numAnnotators = annsA2[0].length; // the number of annotators
    this.verbosity = verbsy;
    if(this.numAnnotators < 2) {
      if(verbosity>0) System.out
        .println("Warning: the IAA calculation needs at least two annotation sets. ");
    }
    checkIsAnnsMissing();
  }

  /** Check if some annotation set for some document is missed. */
  public void checkIsAnnsMissing() {
    boolean isMissing = false;
    for(int i = 0; i < numDocs; ++i)
      for(int j = 0; j < numAnnotators; ++j)
        if(annsArrArr[i][j] == null) {
          if(verbosity>0) System.out.println("Warning: The annotation set of the "
            + "Annotator " + j + " on the document " + i + " is missed!");
          isMissing = true;
        }
    if(isMissing){
      if(verbosity>0) System.out.println("There should be " + numAnnotators
        + " Annotator(s) and " + numDocs + " document(s).");
    }
    else {
      if(verbosity>0) System.out.println("Compute the IAA for " + numAnnotators + " Annotator(s) and on "
      + numDocs + " document(s).");
    }
  }

  /** Compute the pairwise kappa for annotators. */
  public void pairwiseIaaKappa() {
    // Create one contingency object for each pair of annotators and all labels
    int num1 = numAnnotators * (numAnnotators - 1) / 2; // Number of pairs of
    // annotators
    contingencyTables = new ContingencyTable[num1];
    for(int i = 0; i < num1; ++i) {
      contingencyTables[i] = new ContingencyTable(numLabels + 1); // Count the
      // non-label
    }
    // Count the numbers for contingency table
    for(int iDoc = 0; iDoc < numDocs; ++iDoc) {
      int num11 = 0;
      for(int iAnr1 = 0; iAnr1 < numAnnotators; ++iAnr1)
        for(int iAnr2 = iAnr1 + 1; iAnr2 < numAnnotators; ++iAnr2) {
          countContingencyTableNumber(annsArrArr[iDoc][iAnr1],
            annsArrArr[iDoc][iAnr2], contingencyTables, num11);
          ++num11;
        }
    }
    // Compute the Cohen's kappa, observed agreements, and S&C's kappa
    for(int i = 0; i < num1; ++i)
      contingencyTables[i].computeKappaPairwise();
    // Compute the overall results
    ContingencyTable conOverall = new ContingencyTable(numLabels + 1);
    for(int i = 0; i < num1; ++i)
      conOverall.add(contingencyTables[i]);
    conOverall.macroAveraged(num1);
    
    contingencyOverall = conOverall;
  }
  /** Print out the results for pairwise Kappas. */
  public void printResultsPairwiseIaa() {
    // Print out the results
    if(verbosity >= 1) {
      int num1 = numAnnotators * (numAnnotators - 1) / 2; // Number of pairs of
      // annotators
      System.out.println("Overall results macro-averaged over " + num1
        + " pairs:");
      System.out.println(contingencyOverall.printResultsPairwise());
      System.out.println("Results for each pair of annotators:");
      int num11 = 0;
      for(int i = 0; i < numAnnotators; ++i)
        for(int j = i + 1; j < numAnnotators; ++j) {
          System.out.println("(" + this.annotatorNames[i] + "," + 
            this.annotatorNames[j] + "): "
            + contingencyTables[num11].printResultsPairwise());
          System.out.println("Confusion Matrix:");
          System.out.println(contingencyTables[num11]
            .printConfusionMatrix(this.labelsArr));
          if(verbosity >= 2) {
            System.out.println("Specific agreement for each label:");
            for(int jj = 0; jj < numLabels; ++jj)
              System.out.println("label=" + labelsArr[jj]
                + ": positive specific agreement = "
                + contingencyTables[num11].sAgreements[jj][0]
                + "; negative specific agreement = "
                + contingencyTables[num11].sAgreements[jj][1]);
            System.out.println("label=" + IaaCalculation.NONCAT
              + ": positive specific agreement = "
              + contingencyTables[num11].sAgreements[numLabels][0]
              + "; negative specific agreement = "
              + contingencyTables[num11].sAgreements[numLabels][1]);
          }
          ++num11;
        }
    }
  }
  /**
   * Compute the allway kappa, i.e. for more than two annotator, with extended
   * formula.
   */
  public void allwayIaaKappa() {
    // Create the contigency object for all the annotators and labels.
    ContingencyTable contingencyT = new ContingencyTable(numLabels + 1,
      numAnnotators);
    // Get the statistics for all way kappa formula
    // First create a map of label to its dimension in label array
    HashMap<String, Integer> id2Label = new HashMap<String, Integer>();
    for(int i = 0; i < numLabels; ++i)
      id2Label.put(labelsArr[i], new Integer(i));
    // The statistical quantity in the upper part of the formula
    long ySum = 0; // One term in the extended kappa formula.
    long numInstances = 0; // Total number of instances
    long numAgreements = 0; // Total number of instances agreed by all
    // annotators
    long[] numJudgementsCat = new long[numLabels + 1];
    boolean isUsingNonlabel = false;
    for(int iDoc = 0; iDoc < numDocs; ++iDoc) {// for each document
      // For each instance in one document
      if(annsArrArr[iDoc][0] != null) {
        for(Annotation ann0 : annsArrArr[iDoc][0]) {
          ++numInstances;
          int[] yc = new int[numLabels + 1];
          // First check the first annotation set
          Object obj0 = ann0.getFeatures().get(nameClassFeat);
          if(obj0 != null) {
            String label0 = obj0.toString();
            if(id2Label.containsKey(label0)) {
              ++yc[id2Label.get(label0).intValue()];
              ++(contingencyT.assignmentMatrix[id2Label.get(label0).intValue()][0]);
            }
            else {
              ++yc[numLabels];
              ++(contingencyT.assignmentMatrix[numLabels][0]);
            }
          }
          else {
            ++yc[numLabels];
            ++(contingencyT.assignmentMatrix[numLabels][0]);
          }
          // Then check other annotation sets
          Long sOffset = ann0.getStartNode().getOffset();
          Long eOffset = ann0.getEndNode().getOffset();
          for(int iJud = 1; iJud < numAnnotators; ++iJud) {
            // ?? how to determine if two annotation is the same
            if(annsArrArr[iDoc][iJud] != null) {
              AnnotationSet anns = annsArrArr[iDoc][iJud].get(sOffset, eOffset);
              if(!anns.isEmpty()) {
                for(Annotation ann2 : anns) {
                  Object obj3 = ann2.getFeatures().get(nameClassFeat);
                  if(obj3 != null) {
                    String label3 = obj3.toString();
                    if(id2Label.containsKey(label3)) {
                      ++yc[id2Label.get(label3).intValue()];
                      ++(contingencyT.assignmentMatrix[id2Label.get(label3)
                        .intValue()][iJud]);
                    }
                    else {
                      ++yc[numLabels];
                      ++(contingencyT.assignmentMatrix[numLabels][iJud]);
                    }
                  }
                  else {
                    ++yc[numLabels];
                    ++(contingencyT.assignmentMatrix[numLabels][iJud]);
                  }
                  break;
                }
              }// else System.out
              // .println("Warning: cannot compute kappa because the instances
              // are not the same for every annotators.");
            }
          }// end of the loop for judges iJud
          // Check if the current instance has been agreed by all annotators
          for(int iL = 0; iL < numLabels + 1; ++iL)
            if(yc[iL] == numAnnotators) {
              ++numAgreements;
              break;
            }
          // Finally compute the statistics
          for(int iL = 0; iL < numLabels + 1; ++iL) {
            ySum += yc[iL] * yc[iL];
            numJudgementsCat[iL] += yc[iL];
          }
          if(yc[numLabels] > 0 && !isUsingNonlabel) isUsingNonlabel = true;
        }
      }
    } // end of loop for document
    // Compute the all way kappa
    contingencyT.computeAllwayKappa(ySum, numInstances, numAgreements,
      numJudgementsCat, isUsingNonlabel);
    contingencyOverall = contingencyT;
  }
  /** Print out the results for allway Kappa. */
  public void printAllwayIaa() {
    // Print out the results
    if(verbosity >= 1) {
      System.out.println("Overall results (allWay) over " + numAnnotators
        + " annotators:");
      System.out.println(contingencyOverall.printResultsAllway());
    }
  }
  /** Compute the pairwise fmeasure for annotators. */
  public void pairwiseIaaFmeasure() {
    // Create one fmeasure object for each pair of annotators and each label
    int num1 = numAnnotators * (numAnnotators - 1) / 2;
    FMeasure[][] fMeasures = new FMeasure[num1][numLabels];
    for(int i = 0; i < num1; ++i)
      for(int j = 0; j < numLabels; ++j) {
        fMeasures[i][j] = new FMeasure();
      }
    // Count the F-measure numbers for each case
    for(int iDoc = 0; iDoc < numDocs; ++iDoc) {
      int num11 = 0;
      for(int iAnr1 = 0; iAnr1 < numAnnotators; ++iAnr1)
        for(int iAnr2 = iAnr1 + 1; iAnr2 < numAnnotators; ++iAnr2) {
          countFmeasureNumber(annsArrArr[iDoc][iAnr1], annsArrArr[iDoc][iAnr2],
            fMeasures, num11);
          ++num11;
        }
    }
    // Compute the precision, recall and F1
    for(int i = 0; i < num1; ++i)
      for(int j = 0; j < numLabels; ++j) {
        fMeasures[i][j].computeFmeasure();
        fMeasures[i][j].computeFmeasureLenient();
      }
    // Compute the averaged result over the pairs of annotators
    FMeasure fMAve = new FMeasure();
    FMeasure[] fMPair = new FMeasure[num1];
    if(isUsingLabel) {
      // Create one fmeasure for each pair of annotators for all labels
      for(int i = 0; i < num1; ++i) {
        fMPair[i] = new FMeasure();
        for(int j = 0; j < numLabels; ++j)
          fMPair[i].add(fMeasures[i][j]);
        // fMPair[i].macroAverage(numLabels);
        fMPair[i].computeFmeasure();
        fMPair[i].computeFmeasureLenient();
        fMAve.add(fMPair[i]);
      }
    }
    else {
      for(int i = 0; i < num1; ++i) {
        fMPair[i] = fMeasures[i][0];
        fMAve.add(fMeasures[i][0]);
      }
    }
    fMAve.macroAverage(num1);
    this.fMeasureOverall = fMAve;
    this.fMeasuresPairwiseLabel = fMeasures;
    this.fMeasuresPairwise = fMPair;
  }
  /** Print out the results for pairwise F-measures. */
  public void printResultsPairwiseFmeasures() {
    //  Print out the FMeasures for pairwise comparison
    if(verbosity >= 1) {
      int num1 = numAnnotators * (numAnnotators - 1) / 2;
      System.out.println("F-measures averaged over " + num1
        + " pairs of annotators.");
      System.out.println(this.fMeasureOverall.printResults());
      System.out.println("For each pair of annotators:");
      int num11 = 0;
      for(int i = 0; i < numAnnotators; ++i)
        for(int j = i + 1; j < numAnnotators; ++j) {
          System.out.println("(" + this.annotatorNames[i] + "," + 
            this.annotatorNames[j] + "): "
            + this.fMeasuresPairwise[num11].printResults());
          ++num11;
        }
      if(isUsingLabel) {
        if(verbosity >= 2) {
          System.out
            .println("For each pair of annotators, and for each label:");
          num11 = 0;
          for(int i = 0; i < numAnnotators; ++i)
            for(int j = i + 1; j < numAnnotators; ++j) {
              for(int iL = 0; iL < numLabels; ++iL)
                System.out.println("(" + this.annotatorNames[i] + "," + 
                  this.annotatorNames[j] + "), label= "
                  + labelsArr[iL] + ": " + this.fMeasuresPairwiseLabel[num11][iL].printResults());
              ++num11;
            }
        }
      }
    }
  }

  /** Compute the fmeasure for annotators against one reference annotations. */
  public void allwayIaaFmeasure(AnnotationSet[] refAnns) {
    // Create one fmeasure object for each annotator and each label
    int num1 = numAnnotators;
    FMeasure[][] fMeasures = new FMeasure[num1][numLabels];
    for(int i = 0; i < num1; ++i)
      for(int j = 0; j < numLabels; ++j) {
        fMeasures[i][j] = new FMeasure();
      }
    // Count the F-measure numbers for each case
    for(int iDoc = 0; iDoc < numDocs; ++iDoc) {
      int num11 = 0;
      for(int iAnr1 = 0; iAnr1 < numAnnotators; ++iAnr1) {
        countFmeasureNumber(refAnns[iDoc], annsArrArr[iDoc][iAnr1], fMeasures,
          num11);
        ++num11;
      }
    }
    // Compute the precision, recall and F1
    for(int i = 0; i < num1; ++i)
      for(int j = 0; j < numLabels; ++j) {
        fMeasures[i][j].computeFmeasure();
        fMeasures[i][j].computeFmeasureLenient();
      }
    // Compute the averaged result over the pairs of annotators
    FMeasure fMAve = new FMeasure();
    FMeasure[] fMPair = new FMeasure[num1];
    if(isUsingLabel) {
      // Create one fmeasure for each pair of annotators
      for(int i = 0; i < num1; ++i) {
        fMPair[i] = new FMeasure();
        for(int j = 0; j < numLabels; ++j)
          fMPair[i].add(fMeasures[i][j]);
        // fMPair[i].macroAverage(numLabels);
        fMPair[i].computeFmeasure();
        fMPair[i].computeFmeasureLenient();
        fMAve.add(fMPair[i]);
      }
    }
    else {
      for(int i = 0; i < num1; ++i) {
        fMAve.add(fMeasures[i][0]);
        fMPair[i] = fMeasures[i][0];
      }
    }
    fMAve.macroAverage(num1);
    this.fMeasureOverall = fMAve;
    this.fMeasuresPairwise = fMPair;
    this.fMeasuresPairwiseLabel = fMeasures;
  }
  /** Print out the results for allway F-measures. */
  public void printResultsAllwayFmeasures() {
    //  Print out the FMeasures for pairwise comparison
    if(verbosity >= 1) {
      int num1 = numAnnotators;
      System.out
        .println("F-measures against the reference annotations provided:");
      System.out.println("Macro averaged over " + num1 + " pairs:");
      System.out.println(this.fMeasureOverall.printResults());
      System.out.println("For each annotator:");
      for(int i = 0; i < numAnnotators; ++i)
        System.out.println("Annotator: " + this.annotatorNames[i] + ": " + this.fMeasuresPairwise[i].printResults());
      if(isUsingLabel) {
        if(verbosity >= 2) {
          System.out
            .println("For each pair of annotators, and for each label:");
          for(int i = 0; i < numAnnotators; ++i) {
            for(int iL = 0; iL < numLabels; ++iL)
              System.out.println("Annotator: " + this.annotatorNames[i] + ", label= " + labelsArr[iL]
                + ": " + this.fMeasuresPairwiseLabel[i][iL].printResults());
          }
        }
      }
    }
  }

  /** Count the f-measure numbers given two annotation set. */
  public void countFmeasureNumber(AnnotationSet annsOriginal,
    AnnotationSet annsTest, FMeasure[][] fMeasures, int num11) {
    // if(annsOriginal != null && annsTest != null) {
    if(isUsingLabel) {
      HashSet<String> signSet = new HashSet<String>();
      signSet.add(nameClassFeat);
      
      //System.out.println("nameClassFeat=*"+nameClassFeat+"*");
      
      // Create an annotationDiffer()
      AnnotationDiffer annDiff = new AnnotationDiffer();
      annDiff.setSignificantFeaturesSet(signSet);
      for(int iLabel = 0; iLabel < numLabels; ++iLabel) {
        // Get key and response annotation sets by ann type and feature
        FeatureMap featMap = Factory.newFeatureMap();
        featMap.put(nameClassFeat, labelsArr[iLabel]);
        if(annsOriginal != null && annsTest != null) {
          AnnotationSet keyAnns = annsOriginal.get(nameAnnType, featMap);
          AnnotationSet responseAnns = annsTest.get(nameAnnType, featMap);
          // Apply the AnnotationDiffer()
          annDiff.calculateDiff(keyAnns, responseAnns);
          
          //System.out.println("label="+labelsArr[iLabel]+", correct="+annDiff.getCorrectMatches()+"*");
          
          // Add the number
          fMeasures[num11][iLabel].correct += annDiff.getCorrectMatches();
          fMeasures[num11][iLabel].partialCor += annDiff
            .getPartiallyCorrectMatches();
          fMeasures[num11][iLabel].missing += annDiff.getMissing();
          fMeasures[num11][iLabel].spurious += annDiff.getSpurious();
        }
        else if(annsOriginal == null && annsTest != null) {
          AnnotationSet responseAnns = annsTest.get(nameAnnType, featMap);
          // Add the number
          if(responseAnns != null)
            fMeasures[num11][iLabel].spurious += responseAnns.size();
        }
        else if(annsOriginal != null && annsTest == null) {
          AnnotationSet keyAnns = annsOriginal.get(nameAnnType, featMap);
          // Add the number
          if(keyAnns != null)
            fMeasures[num11][iLabel].missing += keyAnns.size();
        }
      }
    }
    else {
      HashSet<String> signSet = new HashSet<String>();
      AnnotationDiffer annDiff = new AnnotationDiffer();
      annDiff.setSignificantFeaturesSet(signSet);
      int iLabel = 0;
      if(annsOriginal != null && annsTest != null) {
        // Get key and response annotation sets by ann type and feature
        AnnotationSet keyAnns = annsOriginal.get(nameAnnType);
        AnnotationSet responseAnns = annsTest.get(nameAnnType);
        // Apply the AnnotationDiffer()
        annDiff.calculateDiff(keyAnns, responseAnns);
        // Add the number
        fMeasures[num11][iLabel].correct += annDiff.getCorrectMatches();
        fMeasures[num11][iLabel].partialCor += annDiff
          .getPartiallyCorrectMatches();
        fMeasures[num11][iLabel].missing += annDiff.getMissing();
        fMeasures[num11][iLabel].spurious += annDiff.getSpurious();
      }
      else if(annsOriginal == null && annsTest != null) {
        AnnotationSet responseAnns = annsTest.get(nameAnnType);
        // Add the number
        if(responseAnns != null)
          fMeasures[num11][iLabel].spurious += responseAnns.size();
      }
      else if(annsOriginal != null && annsTest == null) {
        AnnotationSet keyAnns = annsOriginal.get(nameAnnType);
        // Add the number
        if(keyAnns != null)
          fMeasures[num11][iLabel].missing += keyAnns.size();
      }
    }
    // }
  }

  /** Count the contingency numbers given two annotation set. */
  public void countContingencyTableNumber(AnnotationSet annsOriginal,
    AnnotationSet annsTest, ContingencyTable[] contingencyTableNumbers,
    int num11) {
    // if(annsOriginal != null && annsTest != null) {
    HashSet<String> signSet = new HashSet<String>();
    // signSet.add(nameClassFeat);
    if(isUsingLabel) {
      // For each pair of categories
      for(int iLabel = 0; iLabel < numLabels; ++iLabel) {
        for(int iL2 = 0; iL2 < numLabels; ++iL2) {
          // Get annotation set containing one label
          if(annsOriginal != null && annsTest != null) {
            FeatureMap featMap = Factory.newFeatureMap();
            featMap.put(nameClassFeat, labelsArr[iLabel]);
            AnnotationSet keyAnns = annsOriginal.get(nameAnnType, featMap);
            // Get annotation set containing another label
            featMap.clear();
            featMap.put(nameClassFeat, labelsArr[iL2]);
            AnnotationSet responseAnns = annsTest.get(nameAnnType, featMap);
            // Apply the AnnotationDiffer(), not sure if span is harmful for
            // some
            // application
            AnnotationDiffer annDiff = new AnnotationDiffer();
            // An empty signSet, just count the span
            annDiff.setSignificantFeaturesSet(signSet);
            annDiff.calculateDiff(keyAnns, responseAnns);
            // Add the number
            contingencyTableNumbers[num11].confusionMatrix[iLabel][iL2] += annDiff
              .getCorrectMatches();
            // For the un-matched annotations
            // if(iLabel==iL2) {
            // contingencyTableNumbers[num11].confusionMatrix[numLabels][iL2] +=
            // annDiff.getSpurious();
            // contingencyTableNumbers[num11].confusionMatrix[iLabel][numLabels]
            // +=
            // annDiff.getMissing();
            // }
          }
        }
      }
      // For the pair of the non-category and one category
      HashSet<String> id2Label = new HashSet<String>();
      for(int i = 0; i < numLabels; ++i)
        id2Label.add(labelsArr[i]);
      // Extract those annotation without label and with label but without a
      // value in label list as non-label set.
      HashSet<Annotation> keyAnnsNonlabel = new HashSet<Annotation>();
      HashSet<Annotation> testAnnsNonlabel = new HashSet<Annotation>();
      if(annsOriginal != null && annsTest != null) {
        for(Annotation ann : annsOriginal) {
          if(!ann.getFeatures().containsKey(nameClassFeat)
            || !id2Label.contains(ann.getFeatures().get(nameClassFeat)))
            keyAnnsNonlabel.add(ann);
        }
        for(Annotation ann : annsTest) {
          if(!ann.getFeatures().containsKey(nameClassFeat)
            || !id2Label.contains(ann.getFeatures().get(nameClassFeat)))
            testAnnsNonlabel.add(ann);
        }
        for(int iLabel = 0; iLabel < numLabels; ++iLabel) {
          FeatureMap featMap = Factory.newFeatureMap();
          featMap.put(nameClassFeat, labelsArr[iLabel]);
          // for the key set with label
          AnnotationSet keyAnns = annsOriginal.get(nameAnnType, featMap);
          AnnotationDiffer annDiff = new AnnotationDiffer();
          // An empty signSet, just count the span
          annDiff.setSignificantFeaturesSet(signSet);
          annDiff.calculateDiff(keyAnns, testAnnsNonlabel);
          contingencyTableNumbers[num11].confusionMatrix[iLabel][numLabels] += annDiff
            .getCorrectMatches();
          // For the test set with label
          AnnotationSet testAnns = annsTest.get(nameAnnType, featMap);
          AnnotationDiffer annDiff1 = new AnnotationDiffer();
          // An empty signSet, just count the span
          annDiff1.setSignificantFeaturesSet(signSet);
          annDiff1.calculateDiff(testAnns, keyAnnsNonlabel);
          contingencyTableNumbers[num11].confusionMatrix[numLabels][iLabel] += annDiff1
            .getCorrectMatches();
        }
        // For two non-category
        AnnotationDiffer annDiff = new AnnotationDiffer();
        // An empty signSet, just count the span
        annDiff.setSignificantFeaturesSet(signSet);
        annDiff.calculateDiff(keyAnnsNonlabel, testAnnsNonlabel);
        contingencyTableNumbers[num11].confusionMatrix[numLabels][numLabels] += annDiff
          .getCorrectMatches();
      }
      else if(annsOriginal == null && annsTest != null) {
        for(Annotation ann : annsTest) {
          if(!ann.getFeatures().containsKey(nameClassFeat)
            || !id2Label.contains(ann.getFeatures().get(nameClassFeat)))
            testAnnsNonlabel.add(ann);
        }
        for(int iLabel = 0; iLabel < numLabels; ++iLabel) {
          FeatureMap featMap = Factory.newFeatureMap();
          featMap.put(nameClassFeat, labelsArr[iLabel]);
          // For the test set with label
          AnnotationSet testAnns = annsTest.get(nameAnnType, featMap);
          if(testAnns != null)
            contingencyTableNumbers[num11].confusionMatrix[numLabels][iLabel] += testAnns
              .size();
        }
        // For two non-category
        if(testAnnsNonlabel !=null)
          contingencyTableNumbers[num11].confusionMatrix[numLabels][numLabels] += testAnnsNonlabel
            .size();
      }
      else if(annsOriginal != null && annsTest == null) {
        for(Annotation ann : annsOriginal) {
          if(!ann.getFeatures().containsKey(nameClassFeat)
            || !id2Label.contains(ann.getFeatures().get(nameClassFeat)))
            keyAnnsNonlabel.add(ann);
        }
        for(int iLabel = 0; iLabel < numLabels; ++iLabel) {
          FeatureMap featMap = Factory.newFeatureMap();
          featMap.put(nameClassFeat, labelsArr[iLabel]);
          // For the test set with label
          AnnotationSet keyAnns = annsOriginal.get(nameAnnType, featMap);
          if(keyAnns != null)
            contingencyTableNumbers[num11].confusionMatrix[iLabel][numLabels] += keyAnns
              .size();
        }
        // For two non-category
        if(keyAnnsNonlabel != null)
        contingencyTableNumbers[num11].confusionMatrix[numLabels][numLabels] += keyAnnsNonlabel
          .size();
      }
    }
    else { // if not use labels
      int numL = 0;
      // For each pair of categories
      if(annsOriginal != null && annsTest != null) {
        AnnotationSet keyAnns = annsOriginal.get(nameAnnType);
        // Get annotation set containing another label
        AnnotationSet responseAnns = annsTest.get(nameAnnType);
        // Apply the AnnotationDiffer(), not sure if span is harmful for some
        // application
        AnnotationDiffer annDiff = new AnnotationDiffer();
        // An empty signSet, just count the span
        annDiff.setSignificantFeaturesSet(signSet);
        annDiff.calculateDiff(keyAnns, responseAnns);
        // Add the number
        contingencyTableNumbers[num11].confusionMatrix[numL][numL] += annDiff
          .getCorrectMatches();
        // For the un-matched annotations
        contingencyTableNumbers[num11].confusionMatrix[numLabels][numL] += annDiff
          .getSpurious();
        contingencyTableNumbers[num11].confusionMatrix[numL][numLabels] += annDiff
          .getMissing();
      }
      else if(annsOriginal == null && annsTest != null) {
        // Get annotation set containing another label
        AnnotationSet responseAnns = annsTest.get(nameAnnType);
        // For the un-matched annotations
        if(responseAnns != null)
          contingencyTableNumbers[num11].confusionMatrix[numLabels][numL] += responseAnns
            .size();
      }
      else if(annsOriginal != null && annsTest == null) {
        // Get annotation set containing another label
        AnnotationSet keyAnns = annsOriginal.get(nameAnnType);
        // For the un-matched annotations
        if(keyAnns != null)
          contingencyTableNumbers[num11].confusionMatrix[numL][numLabels] += keyAnns
            .size();
      }
    }
  }

  /**
   * Check if the annotation task is suitable for computing kappa by checking if
   * the annotation sets contain the same annotations.
   */
  public static boolean isSameInstancesForAnnotators(AnnotationSet[] annsA, int vsy) {
    int numAnnotators = annsA.length;
    if(annsA[0] == null) return false;
    for(Annotation ann : annsA[0]) {
      for(int iJud = 1; iJud < numAnnotators; ++iJud) {
        if(annsA[iJud] == null) return false;
        boolean isContained = false;
        for(Annotation ann1 : annsA[iJud]) {
          // If the ann is not the same
          if(ann.coextensive(ann1)) {
            isContained = true;
            break;
          }
        }
        if(!isContained) {
          if(vsy>0)
          System.out.println("The " + iJud + " annotator cause different");
          return false;
        }
      }// end of the loop for annotators
    }// end of loop for each annotation in one document
    // If the annotated instances are the same for every annotators.
    return true;
  }

  /** Collect the labels into a list and sort it alphabetically. */
  public static ArrayList<String> collectLabels(AnnotationSet[][] annsA2,
    String nameF) {
    ArrayList<String> labelsSet = new ArrayList<String>();
    int numD = annsA2.length;
    int numJ = annsA2[0].length;
    for(int iDoc = 0; iDoc < numD; ++iDoc)
      for(int iJud = 0; iJud < numJ; ++iJud) {
        if(annsA2[iDoc][iJud] != null)
          for(Annotation ann : annsA2[iDoc][iJud]) {
            if(ann.getFeatures().containsKey(nameF)) {
              String label = ann.getFeatures().get(nameF).toString();
              if(label != null && !labelsSet.contains(label))
                labelsSet.add(label);
            }
          }// end of the loop for annotation
      }// end of the loop for annotators
    // If the annotated instances are the same for every annotators.
    Collections.sort(labelsSet);
    return labelsSet;
  }
  
  public void setAnnotatorNames(String [] names) {
    this.annotatorNames = names;
  }
}