package edu.stanford.nlp.parser.metrics; import edu.stanford.nlp.util.Pair; /** * A general class that scores precision, recall, and F1. The package supports incremental * computation of these statistics via the {@link #update} method. Formatted output reflecting * the current values of the statistics may be obtained via the {@link #toString} method. * * @see Evalb * @author Spence Green */ public class EvaluationMetric { private double numTestInstances = 0.0; private double exact = 0.0; private double precisions = 0.0; private double precisions2 = 0.0; private double recalls = 0.0; private double recalls2 = 0.0; private double pnums2 = 0.0; private double rnums2 = 0.0; private double f1s = 0.0; /** * Updates the evaluation statistics. Should be called once for each test example * (e.g., sentence, parse tree, etc.). * * @param curP Precision of the current test example * @param curPnum The denominator used to calculate the current precision * @param curR Recall of the current test example * @param curRnum The denominator used to calculate the current recall */ public void update(double curP, double curPnum, double curR, double curRnum) { numTestInstances += 1.0; double curF1 = (curP > 0.0 && curR > 0.0) ? 2.0 / ((1.0 / curP) + (1.0 / curR)) : 0.0; if(curF1 >= 0.9999) exact += 1.0; precisions += curP; recalls += curR; f1s += curF1; precisions2 += curPnum * curP; pnums2 += curPnum; recalls2 += curRnum * curR; rnums2 += curRnum; //Update for the toString() method to be called during running average output lastP = curP; lastR = curR; lastF1 = curF1; } /** * Returns the components of the precision. * * @return A {@link Pair} with the numerator of the precision in the first element * and the denominator of the precision in the second element. */ public Pair<Double,Double> getPFractionals() { return new Pair<>(precisions2, pnums2); } /** * Returns the components of the recall. * * @return A {@link Pair} with the numerator of the recall in the first element * and the denominator of the recall in the second element. */ public Pair<Double,Double> getRFractionals() { return new Pair<>(recalls2, rnums2); } /** * Returns the number of test instances (e.g., parse trees or sentences) used in * the calculation of the statistics. This value corresponds to the number of calls * to {@link #update}. * * @return The number of test instances */ public double getTestInstances() { return numTestInstances; } /** * A convenience method that returns the number of true positive examples from * among the test instances. Mathematically, this value is the denominator of the recall. * * @return Number of true positive examples */ public double numRelevantExamples() { return rnums2; } private double lastP = 0.0; private double lastR = 0.0; private double lastF1 = 0.0; @Override public String toString() { StringBuilder sb = new StringBuilder(); double pSent = (numTestInstances > 0.0) ? precisions / numTestInstances : 0.0; double pEvalB = (pnums2 > 0.0) ? precisions2 / pnums2 : 0.0; sb.append(String.format("P: %.2f (sent ave: %.2f) (evalb: %.2f)%n", lastP*100.0, pSent*100.0, pEvalB*100.0)); double rSent = (numTestInstances > 0.0) ? recalls / numTestInstances : 0.0; double rEvalB = (rnums2 > 0.0) ? recalls2 / rnums2 : 0.0; sb.append(String.format("R: %.2f (sent ave: %.2f) (evalb: %.2f)%n", lastR*100.0, rSent*100.0, rEvalB*100.0)); double f1Sent = (numTestInstances > 0.0) ? f1s / numTestInstances : 0.0; double f1EvalB = (pEvalB > 0.0 && rEvalB > 0.0) ? 2.0 / ((1.0 / pEvalB) + (1.0 / rEvalB)) : 0.0; sb.append(String.format("F1: %.2f (sent ave: %.2f) (evalb: %.2f)%n", lastF1*100.0, f1Sent*100.0, f1EvalB*100.0)); sb.append(String.format("Num:\t%.2f (test instances)%n", numTestInstances)); sb.append(String.format("Rel:\t%.0f (relevant examples)%n", rnums2)); sb.append(String.format("Exact:\t%.2f (test instances)%n", exact)); return sb.toString(); } }