001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.experiment.evaluation.retrieval.analysers; 031 032import gnu.trove.map.hash.TObjectDoubleHashMap; 033import gnu.trove.procedure.TDoubleProcedure; 034import gnu.trove.procedure.TObjectDoubleProcedure; 035 036import java.io.File; 037import java.io.FileWriter; 038import java.io.IOException; 039 040import net.sf.jasperreports.engine.JRException; 041import net.sf.jasperreports.engine.JasperPrint; 042 043import org.apache.commons.math.stat.descriptive.DescriptiveStatistics; 044import org.openimaj.data.identity.Identifiable; 045import org.openimaj.experiment.evaluation.AnalysisResult; 046 047import com.googlecode.jatl.Html; 048 049/** 050 * {@link AnalysisResult} used with {@link PrecisionAtN} to hold the P@N 051 * precision after N documents have been retrieved. 052 * <p> 053 * Provides both per-query precisions and descriptive statistics over all 054 * queries. 055 * 056 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 057 * 058 * @param <QUERY> 059 * Type of query 060 */ 061public class PrecisionAtNResult<QUERY> implements AnalysisResult { 062 TObjectDoubleHashMap<QUERY> allScores = new TObjectDoubleHashMap<QUERY>(); 063 int N; 064 065 /** 066 * Construct with the given N. 067 * 068 * @param N 069 * number of retrieved documents at which precision is calculated 070 */ 071 public PrecisionAtNResult(int N) { 072 this.N = N; 073 } 074 075 private DescriptiveStatistics computeStats() { 076 final DescriptiveStatistics ds = new DescriptiveStatistics(); 077 078 allScores.forEachValue(new TDoubleProcedure() { 079 @Override 080 public boolean execute(double value) { 081 ds.addValue(value); 082 return true; 083 } 084 }); 085 086 return ds; 087 } 088 089 void writeHTML(File file, final String title, final String info) throws IOException { 090 FileWriter fw = null; 091 try { 092 fw = new FileWriter(file); 093 094 new Html(fw) { 095 { 096 html(); 097 head(); 098 title(title); 099 end(); 100 body(); 101 h1().text(title).end(); 102 div().text(info).end(); 103 hr(); 104 pre().text(PrecisionAtNResult.this.toString()).end(); 105 endAll(); 106 } 107 }; 108 } finally { 109 if (fw != null) 110 fw.close(); 111 } 112 } 113 114 @Override 115 public String toString() { 116 return getSummaryReport(); 117 } 118 119 @Override 120 public JasperPrint getSummaryReport(String title, String info) throws JRException { 121 // FIXME 122 throw new UnsupportedOperationException(); 123 } 124 125 @Override 126 public JasperPrint getDetailReport(String title, String info) throws JRException { 127 // FIXME 128 throw new UnsupportedOperationException(); 129 } 130 131 @Override 132 public String getSummaryReport() { 133 final StringBuilder outBuffer = new StringBuilder(); 134 135 final DescriptiveStatistics ds = computeStats(); 136 outBuffer.append("Aggregate P@" + N + " Statistics:\n"); 137 outBuffer.append(String.format("%-15s\t%6d\n", "num_q", ds.getN())); 138 outBuffer.append(String.format("%-15s\t%6.4f\n", "min", ds.getMin())); 139 outBuffer.append(String.format("%-15s\t%6.4f\n", "max", ds.getMax())); 140 outBuffer.append(String.format("%-15s\t%6.4f\n", "mean", ds.getMean())); 141 outBuffer.append(String.format("%-15s\t%6.4f\n", "std dev", ds.getStandardDeviation())); 142 outBuffer.append(String.format("%-15s\t%6.4f\n", "median", ds.getPercentile(50))); 143 outBuffer.append(String.format("%-15s\t%6.4f\n", "skewness", ds.getSkewness())); 144 outBuffer.append(String.format("%-15s\t%6.4f\n", "kurtosis", ds.getKurtosis())); 145 146 return outBuffer.toString(); 147 } 148 149 @Override 150 public String getDetailReport() { 151 final StringBuilder outBuffer = new StringBuilder(); 152 153 allScores.forEachEntry(new TObjectDoubleProcedure<QUERY>() { 154 @Override 155 public boolean execute(QUERY a, double b) { 156 String id; 157 if (a instanceof Identifiable) 158 id = ((Identifiable) a).getID(); 159 else 160 id = a.toString(); 161 162 outBuffer.append(String.format("P@%-11s\t%10s\t%6.4f\n", N, id, b)); 163 164 return true; 165 } 166 }); 167 outBuffer.append("\n"); 168 169 outBuffer.append(getSummaryReport()); 170 171 return outBuffer.toString(); 172 } 173}