Log in Help
Print
Homereleasesgate-8.4-build5748-ALLpluginsInter_Annotator_Agreementsrcgateiaaplugin 〉 ContingencyTable.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: ContingencyTable.java 12128 2010-01-05 13:47:20Z ggorrell $
 */

package gate.iaaplugin;

/**
 * Get a contingency table (or confusion matrix)and compute the IAA measures
 * such as observed agreement, category specific agreements, Cohen's Kappa, and
 * S&C's Kappa. It also includes a few methods for adding and averaging the
 * measures and printing the results.
 */
public class ContingencyTable {
  /** Number of category. */
  public int numCats;
  /** Number of annotators. */
  public int numJudges;
  /** Number of annotators. */
  // int numAnnotators;
  /* Confusion matrix. */
  public float[][] confusionMatrix;
  /** The observed agreement. */
  public float observedAgreement = 0;
  /** Indicate if the agreement is available or not. */
  public boolean isAgreementAvailable = false;
  /** Cohen's kappa. */
  public float kappaCohen = 0;
  /** Scott's pi or Siegel & Castellan's kappa */
  public float kappaPi = 0;
  /** Assignment matrix for computing the all way kappa. */
  float[][] assignmentMatrix;
  /** Davies and Fleiss's extension of Cohen's kappa. */
  public float kappaDF = 0;
  /** Extension of Scott's pi for more than two judges. */
  public float kappaSC = 0;
  /** Positive and negative specific agreement for each category. */
  public float[][] sAgreements;

  /** Constructor for pairise case. */
  public ContingencyTable(int numC) {
    this.numCats = numC;
    this.confusionMatrix = new float[numC][numC];
    isAgreementAvailable = false;
  }

  /** Constructor with number of annotators. */
  public ContingencyTable(int numC, int numJ) {
    this.numCats = numC;
    this.numJudges = numJ;
    this.assignmentMatrix = new float[numC][numJ];
    isAgreementAvailable = false;
  }

  /** Compute the kappa for two annotators. */
  public void computeKappaPairwise() {
    // Compute the agreement
    if(!isAgreementAvailable) computeObservedAgreement();
    // compute the agreement by chance
    // Get the marginal sum for each annotator
    float[] marginalArrayC = new float[numCats];
    float[] marginalArrayR = new float[numCats];
    float totalSum = 0;
    for(int i = 0; i < numCats; ++i) {
      float sum = 0;
      for(int j = 0; j < numCats; ++j)
        sum += confusionMatrix[i][j];
      marginalArrayC[i] = sum;
      totalSum += sum;
      sum = 0;
      for(int j = 0; j < numCats; ++j)
        sum += confusionMatrix[j][i];
      marginalArrayR[i] = sum;
    }
    // Compute Cohen's p(E)
    float pE = 0;
    if(totalSum > 0) {
      float doubleSum = totalSum * totalSum;
      for(int i = 0; i < numCats; ++i)
        pE += (marginalArrayC[i] * marginalArrayR[i]) / doubleSum;
    }
    // Compute Cohen's Kappa
    if(totalSum > 0)
      kappaCohen = (observedAgreement - pE) / (1 - pE);
    else kappaCohen = 0;
    // Compute S&C's chance agreement
    pE = 0;
    if(totalSum > 0) {
      float doubleSum = 2 * totalSum;
      for(int i = 0; i < numCats; ++i) {
        float p = (marginalArrayC[i] + marginalArrayR[i]) / doubleSum;
        pE += p * p;
      }
    }
    if(totalSum > 0)
      kappaPi = (observedAgreement - pE) / (1 - pE);
    else kappaPi = 0;
    // Compute the specific agreement for each label using marginal sums
    sAgreements = new float[numCats][2];
    for(int i = 0; i < numCats; ++i) {
      if(marginalArrayC[i] + marginalArrayR[i]>0) 
        sAgreements[i][0] = (2 * confusionMatrix[i][i])
          / (marginalArrayC[i] + marginalArrayR[i]);
      else sAgreements[i][0] = 0.0f;
      if(2 * totalSum - marginalArrayC[i] - marginalArrayR[i]>0)
        sAgreements[i][1] = (2 * (totalSum - marginalArrayC[i]
          - marginalArrayR[i] + confusionMatrix[i][i]))
          / (2 * totalSum - marginalArrayC[i] - marginalArrayR[i]);
      else sAgreements[i][1] = 0.0f;
    }
  }

  /** Compute the observed agreement. */
  public void computeObservedAgreement() {
    float sumAgreed = 0;
    float sumTotal = 0;
    for(int i = 0; i < numCats; ++i) {
      sumAgreed += confusionMatrix[i][i];
      for(int j = 0; j < numCats; ++j)
        sumTotal += confusionMatrix[i][j];
    }
    if(sumTotal > 0.0)
      observedAgreement = sumAgreed / sumTotal;
    else observedAgreement = 0;
    isAgreementAvailable = true;
  }

  /** Compute the all way kappa. */
  public void computeAllwayKappa(long ySum, long numInstances,
    long numAgreements, long[] numJudgesCat, boolean isUsingNonlabel) {
    // Compute cohen's kappa using the extended formula.
    float[] pc = new float[numCats];
    for(int j = 0; j < numJudges; ++j) {
      for(int i = 0; i < numCats; ++i)
        pc[i] += assignmentMatrix[i][j];
      float sum = 0;
      for(int i = 0; i < numCats; ++i)
        sum += assignmentMatrix[i][j];
      if(sum > 0) for(int i = 0; i < numCats; ++i)
        assignmentMatrix[i][j] /= sum;
    }
    float sum = 0;
    for(int i = 0; i < numCats; ++i)
      sum += pc[i];
    if(sum > 0) for(int i = 0; i < numCats; ++i)
      pc[i] /= sum;
    float term1, term2;
    term1 = 0;
    for(int i = 0; i < numCats; ++i)
      term1 += pc[i] * (1 - pc[i]);
    term2 = 0;
    for(int i = 0; i < numCats; ++i)
      for(int j = 0; j < numJudges; ++j)
        term2 += (assignmentMatrix[i][j] - pc[i])
          * (assignmentMatrix[i][j] - pc[i]);
    if(numInstances > 0 && numJudges > 1 && (term1 > 0 || term2 > 0))
      kappaDF = 1f - (numInstances * numJudges * numJudges - ySum)
        / (numInstances * (numJudges * (numJudges - 1) * term1 + term2));
    else kappaDF = 0;
    // Compute the observed agreement and the S&C kappa
    if(numInstances == 0 || numJudges == 0) {
      observedAgreement = 0;
      kappaSC = 0;
    }
    else {
      observedAgreement = (float)numAgreements / numInstances;
      // Compute the kappa of S&C
      float pE = 0;
      float dNum = numInstances * numJudges;
      for(int i = 0; i < numCats; ++i) {
        float s = numJudgesCat[i] / dNum;
        float sR = s;
        for(int j = 1; j < numJudges; ++j)
          sR *= s;
        pE += sR;
      }
      kappaSC = (observedAgreement - pE) / (1 - pE);
    }
  }

  /** Print out the results. */
  public String printResultsPairwise() {
    StringBuffer logMessage = new StringBuffer();
    logMessage.append("Observed agreement: " + observedAgreement + ";  ");
    logMessage.append("Cohen's kappa: " + kappaCohen + "; ");
    logMessage.append("Scott's pi: " + kappaPi + "\n");
    return logMessage.toString();
  }

  /** Print out the confusion matrix. */
  public String printConfusionMatrix(String[] labelsArr) {
    StringBuffer logMessage = new StringBuffer();
    // logMessage.append("----------------------------------------------\n");
    logMessage.append("\t\t|");
    int numL = labelsArr.length;
    for(int i = 0; i < numL; ++i) {
      logMessage.append("\t" + labelsArr[i] + "\t|");
    }
    logMessage.append("\t " + IaaCalculation.NONCAT + " \t|\n");
    // logMessage.append("----------------------------------------------\n");
    for(int i = 0; i < numL; ++i) {
      logMessage.append("\t" + labelsArr[i] + "\t|");
      for(int j = 0; j < numL; ++j)
        logMessage.append("\t" + this.confusionMatrix[i][j] + "\t|");
      logMessage.append("\t" + this.confusionMatrix[i][numL] + "\t|\n");
    }
    logMessage.append("\t" + IaaCalculation.NONCAT + "\t|");
    for(int j = 0; j < numL; ++j)
      logMessage.append("\t" + this.confusionMatrix[numL][j] + "\t|");
    logMessage.append("\t" + this.confusionMatrix[numL][numL] + "\t|\n");
    // logMessage.append("----------------------------------------------\n");
    return logMessage.toString();
  }

  /** Print out the results. */
  public String printResultsAllway() {
    StringBuffer logMessage = new StringBuffer();
    logMessage.append("Observed agreement: " + observedAgreement + "; ");
    logMessage.append("Cohen's kappa extended for  " + numJudges
      + " annotators: " + kappaDF + "; ");
    logMessage.append("S&C kappa for " + numJudges + " annotators: " + kappaSC
      + "\n");
    return logMessage.toString();
  }

  /** Add the results. */
  public void add(ContingencyTable another) {
    this.kappaCohen += another.kappaCohen;
    this.kappaDF += another.kappaDF;
    this.kappaPi += another.kappaPi;
    this.kappaSC += another.kappaSC;
    this.observedAgreement += another.observedAgreement;
  }

  /** Compute the macro averaged results. */
  public void macroAveraged(int num) {
    this.kappaCohen /= num;
    this.kappaDF /= num;
    this.kappaPi /= num;
    this.kappaSC /= num;
    this.observedAgreement /= num;
  }
}