Commit e095c645be17df139c566c2ee553bd1eb59b5abc

Authored by Ben Klein
2 parents cb9c5416 08742a21

Merge pull request #289 from biometrics/evalPlotting

Sample ROC points at fixed FAR values for easy vertical averaging.
openbr/core/eval.cpp
@@ -48,22 +48,26 @@ struct OperatingPoint @@ -48,22 +48,26 @@ struct OperatingPoint
48 : score(_score), FAR(_FAR), TAR(_TAR) {} 48 : score(_score), FAR(_FAR), TAR(_TAR) {}
49 }; 49 };
50 50
51 -static float getTAR(const QList<OperatingPoint> &operatingPoints, float FAR) 51 +static OperatingPoint getOperatingPoint(const QList<OperatingPoint> &operatingPoints, float FAR)
52 { 52 {
53 int index = 0; 53 int index = 0;
54 while (operatingPoints[index].FAR < FAR) { 54 while (operatingPoints[index].FAR < FAR) {
55 index++; 55 index++;
56 if (index == operatingPoints.size()) 56 if (index == operatingPoints.size())
57 - return 1; 57 + return OperatingPoint(operatingPoints.last().score, FAR, operatingPoints.last().TAR);
58 } 58 }
59 59
60 - const float x1 = (index == 0 ? 0 : operatingPoints[index-1].FAR);  
61 - const float y1 = (index == 0 ? 0 : operatingPoints[index-1].TAR);  
62 - const float x2 = operatingPoints[index].FAR;  
63 - const float y2 = operatingPoints[index].TAR;  
64 - const float m = (y2 - y1) / (x2 - x1);  
65 - const float b = y1 - m*x1;  
66 - return m * FAR + b; 60 + const float FAR1 = (index == 0 ? 0 : operatingPoints[index-1].FAR);
  61 + const float TAR1 = (index == 0 ? 0 : operatingPoints[index-1].TAR);
  62 + const float score1 = (index == 0 ? operatingPoints[index].score : operatingPoints[index-1].score);
  63 + const float FAR2 = operatingPoints[index].FAR;
  64 + const float TAR2 = operatingPoints[index].TAR;
  65 + const float score2 = operatingPoints[index].score;
  66 + const float mTAR = (TAR2 - TAR1) / (FAR2 - FAR1);
  67 + const float bTAR = TAR1 - mTAR*FAR1;
  68 + const float mScore = (score2 - score1) / (FAR2 - FAR1);
  69 + const float bScore = score1 - mScore*FAR1;
  70 + return OperatingPoint(mScore * FAR + bScore,FAR, mTAR * FAR + bTAR);
67 } 71 }
68 72
69 static float getCMC(const QVector<int> &firstGenuineReturns, int rank) 73 static float getCMC(const QVector<int> &firstGenuineReturns, int rank)
@@ -266,23 +270,26 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv, const QSt @@ -266,23 +270,26 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv, const QSt
266 } 270 }
267 271
268 // Write Detection Error Tradeoff (DET), PRE, REC 272 // Write Detection Error Tradeoff (DET), PRE, REC
269 - int points = qMin(operatingPoints.size(), Max_Points);  
270 - for (int i=0; i<points; i++) {  
271 - const OperatingPoint &operatingPoint = operatingPoints[double(i) / double(points-1) * double(operatingPoints.size()-1)];  
272 - lines.append(QString("DET,%1,%2").arg(QString::number(operatingPoint.FAR), 273 + float FAR=0.000001;
  274 + for (int i=0; i<Max_Points; i++) {
  275 + OperatingPoint operatingPoint = getOperatingPoint(operatingPoints, FAR);
  276 + lines.append(QString("DET,%1,%2").arg(QString::number(FAR),
273 QString::number(1-operatingPoint.TAR))); 277 QString::number(1-operatingPoint.TAR)));
274 lines.append(QString("FAR,%1,%2").arg(QString::number(operatingPoint.score), 278 lines.append(QString("FAR,%1,%2").arg(QString::number(operatingPoint.score),
275 - QString::number(operatingPoint.FAR))); 279 + QString::number(FAR)));
276 lines.append(QString("FRR,%1,%2").arg(QString::number(operatingPoint.score), 280 lines.append(QString("FRR,%1,%2").arg(QString::number(operatingPoint.score),
277 QString::number(1-operatingPoint.TAR))); 281 QString::number(1-operatingPoint.TAR)));
  282 + //multiplier roughly spans 10E-6 to 1
  283 + FAR *=1.02807;
278 } 284 }
  285 +
279 // Write FAR/TAR Table (FT) 286 // Write FAR/TAR Table (FT)
280 - lines.append(qPrintable(QString("FT,0.000001,%1").arg(QString::number(getTAR(operatingPoints, 0.000001), 'f', 3))));  
281 - lines.append(qPrintable(QString("FT,0.00001,%1").arg(QString::number(getTAR(operatingPoints, 0.00001), 'f', 3))));  
282 - lines.append(qPrintable(QString("FT,0.0001,%1").arg(QString::number(getTAR(operatingPoints, 0.0001), 'f', 3))));  
283 - lines.append(qPrintable(QString("FT,0.001,%1").arg(QString::number(getTAR(operatingPoints, 0.001), 'f', 3))));  
284 - lines.append(qPrintable(QString("FT,0.01,%1").arg(QString::number(getTAR(operatingPoints, 0.01), 'f', 3))));  
285 - lines.append(qPrintable(QString("FT,0.1,%1").arg(QString::number(getTAR(operatingPoints, 0.1), 'f', 3)))); 287 + lines.append(qPrintable(QString("FT,0.000001,%1").arg(QString::number(getOperatingPoint(operatingPoints, 0.000001).TAR, 'f', 3))));
  288 + lines.append(qPrintable(QString("FT,0.00001,%1").arg(QString::number(getOperatingPoint(operatingPoints, 0.00001).TAR, 'f', 3))));
  289 + lines.append(qPrintable(QString("FT,0.0001,%1").arg(QString::number(getOperatingPoint(operatingPoints, 0.0001).TAR, 'f', 3))));
  290 + lines.append(qPrintable(QString("FT,0.001,%1").arg(QString::number(getOperatingPoint(operatingPoints, 0.001).TAR, 'f', 3))));
  291 + lines.append(qPrintable(QString("FT,0.01,%1").arg(QString::number(getOperatingPoint(operatingPoints, 0.01).TAR, 'f', 3))));
  292 + lines.append(qPrintable(QString("FT,0.1,%1").arg(QString::number(getOperatingPoint(operatingPoints, 0.1).TAR, 'f', 3))));
286 293
287 //Write CMC Table (CT) 294 //Write CMC Table (CT)
288 lines.append(qPrintable(QString("CT,1,%1").arg(QString::number(getCMC(firstGenuineReturns, 1), 'f', 3)))); 295 lines.append(qPrintable(QString("CT,1,%1").arg(QString::number(getCMC(firstGenuineReturns, 1), 'f', 3))));
@@ -293,19 +300,18 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv, const QSt @@ -293,19 +300,18 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv, const QSt
293 lines.append(qPrintable(QString("CT,100,%1").arg(QString::number(getCMC(firstGenuineReturns, 100), 'f', 3)))); 300 lines.append(qPrintable(QString("CT,100,%1").arg(QString::number(getCMC(firstGenuineReturns, 100), 'f', 3))));
294 301
295 // Write FAR/TAR Bar Chart (BC) 302 // Write FAR/TAR Bar Chart (BC)
296 - lines.append(qPrintable(QString("BC,0.001,%1").arg(QString::number(getTAR(operatingPoints, 0.001), 'f', 3))));  
297 - lines.append(qPrintable(QString("BC,0.01,%1").arg(QString::number(result = getTAR(operatingPoints, 0.01), 'f', 3)))); 303 + lines.append(qPrintable(QString("BC,0.001,%1").arg(QString::number(getOperatingPoint(operatingPoints, 0.001).TAR, 'f', 3))));
  304 + lines.append(qPrintable(QString("BC,0.01,%1").arg(QString::number(result = getOperatingPoint(operatingPoints, 0.01).TAR, 'f', 3))));
298 305
299 // Attempt to read template size from enrolled gallery and write to output CSV 306 // Attempt to read template size from enrolled gallery and write to output CSV
300 size_t maxSize(0); 307 size_t maxSize(0);
301 if (target.endsWith(".gal")) { 308 if (target.endsWith(".gal")) {
302 foreach (const Template &t, TemplateList::fromGallery(target)) maxSize = max(maxSize, t.bytes()); 309 foreach (const Template &t, TemplateList::fromGallery(target)) maxSize = max(maxSize, t.bytes());
  310 + lines.append(QString("TS,,%1").arg(QString::number(maxSize)));
303 } 311 }
304 312
305 - lines.append(QString("TS,,%1").arg(QString::number(maxSize)));  
306 -  
307 // Write SD & KDE 313 // Write SD & KDE
308 - points = qMin(qMin(Max_Points, genuines.size()), impostors.size()); 314 + int points = qMin(qMin(Max_Points, genuines.size()), impostors.size());
309 QList<double> sampledGenuineScores; sampledGenuineScores.reserve(points); 315 QList<double> sampledGenuineScores; sampledGenuineScores.reserve(points);
310 QList<double> sampledImpostorScores; sampledImpostorScores.reserve(points); 316 QList<double> sampledImpostorScores; sampledImpostorScores.reserve(points);
311 317
@@ -332,10 +338,10 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv, const QSt @@ -332,10 +338,10 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv, const QSt
332 338
333 QtUtils::writeFile(csv, lines); 339 QtUtils::writeFile(csv, lines);
334 if (maxSize > 0) qDebug("Template Size: %i bytes", (int)maxSize); 340 if (maxSize > 0) qDebug("Template Size: %i bytes", (int)maxSize);
335 - qDebug("TAR @ FAR = 0.01: %.3f",getTAR(operatingPoints, 0.01));  
336 - qDebug("TAR @ FAR = 0.001: %.3f",getTAR(operatingPoints, 0.001));  
337 - qDebug("TAR @ FAR = 0.0001: %.3f",getTAR(operatingPoints, 0.0001));  
338 - qDebug("TAR @ FAR = 0.00001: %.3f",getTAR(operatingPoints, 0.00001)); 341 + qDebug("TAR @ FAR = 0.01: %.3f",getOperatingPoint(operatingPoints, 0.01).TAR);
  342 + qDebug("TAR @ FAR = 0.001: %.3f",getOperatingPoint(operatingPoints, 0.001).TAR);
  343 + qDebug("TAR @ FAR = 0.0001: %.3f",getOperatingPoint(operatingPoints, 0.0001).TAR);
  344 + qDebug("TAR @ FAR = 0.00001: %.3f",getOperatingPoint(operatingPoints, 0.00001).TAR);
339 345
340 qDebug("\nRetrieval Rate @ Rank = %d: %.3f", Report_Retrieval, getCMC(firstGenuineReturns, Report_Retrieval)); 346 qDebug("\nRetrieval Rate @ Rank = %d: %.3f", Report_Retrieval, getCMC(firstGenuineReturns, Report_Retrieval));
341 347
@@ -560,8 +566,8 @@ float InplaceEval(const QString &amp;simmat, const QString &amp;target, const QString &amp;q @@ -560,8 +566,8 @@ float InplaceEval(const QString &amp;simmat, const QString &amp;target, const QString &amp;q
560 566
561 float result; 567 float result;
562 // Write FAR/TAR Bar Chart (BC) 568 // Write FAR/TAR Bar Chart (BC)
563 - lines.append(qPrintable(QString("BC,0.001,%1").arg(QString::number(getTAR(operatingPoints, 0.001), 'f', 3))));  
564 - lines.append(qPrintable(QString("BC,0.01,%1").arg(QString::number(result = getTAR(operatingPoints, 0.01), 'f', 3)))); 569 + lines.append(qPrintable(QString("BC,0.001,%1").arg(QString::number(getOperatingPoint(operatingPoints, 0.001).TAR, 'f', 3))));
  570 + lines.append(qPrintable(QString("BC,0.01,%1").arg(QString::number(result = getOperatingPoint(operatingPoints, 0.01).TAR, 'f', 3))));
565 571
566 qDebug("TAR @ FAR = 0.01: %.3f", result); 572 qDebug("TAR @ FAR = 0.01: %.3f", result);
567 QtUtils::writeFile(csv, lines); 573 QtUtils::writeFile(csv, lines);
openbr/core/plot.cpp
@@ -65,6 +65,8 @@ struct RPlot @@ -65,6 +65,8 @@ struct RPlot
65 QFile file; 65 QFile file;
66 QStringList pivotHeaders; 66 QStringList pivotHeaders;
67 QVector< QSet<QString> > pivotItems; 67 QVector< QSet<QString> > pivotItems;
  68 + float confidence;
  69 +
68 bool flip; 70 bool flip;
69 71
70 struct Pivot 72 struct Pivot
@@ -108,7 +110,6 @@ struct RPlot @@ -108,7 +110,6 @@ struct RPlot
108 pivotItems = QVector< QSet<QString> >(pivotHeaders.size()); 110 pivotItems = QVector< QSet<QString> >(pivotHeaders.size());
109 foreach (const QString &fileName, files) { 111 foreach (const QString &fileName, files) {
110 QStringList pivots = getPivots(fileName, false); 112 QStringList pivots = getPivots(fileName, false);
111 -  
112 // If the number of pivots don't match, abandon the directory/filename labeling scheme 113 // If the number of pivots don't match, abandon the directory/filename labeling scheme
113 if (pivots.size() != pivotHeaders.size()) { 114 if (pivots.size() != pivotHeaders.size()) {
114 pivots.clear(); 115 pivots.clear();
@@ -132,8 +133,13 @@ struct RPlot @@ -132,8 +133,13 @@ struct RPlot
132 minor = Pivot(i, size, pivotHeaders[i]); 133 minor = Pivot(i, size, pivotHeaders[i]);
133 } 134 }
134 } 135 }
135 -  
136 const QString &smooth = destination.get<QString>("smooth", ""); 136 const QString &smooth = destination.get<QString>("smooth", "");
  137 + if (destination.contains(QString("confidence"))) {
  138 + const QString &CI = destination.get<QString>("confidence");
  139 + confidence = !CI.isEmpty() ? CI.toFloat()/100.0 : 0.95;
  140 + } else {
  141 + confidence = 0.95;
  142 + }
137 major.smooth = !smooth.isEmpty() && (major.header == smooth) && (major.size > 1); 143 major.smooth = !smooth.isEmpty() && (major.header == smooth) && (major.size > 1);
138 minor.smooth = !smooth.isEmpty() && (minor.header == smooth) && (minor.size > 1); 144 minor.smooth = !smooth.isEmpty() && (minor.header == smooth) && (minor.size > 1);
139 if (major.smooth) major.size = 1; 145 if (major.smooth) major.size = 1;
@@ -177,29 +183,43 @@ struct RPlot @@ -177,29 +183,43 @@ struct RPlot
177 "TS$Y <- as.character(TS$Y)\n" 183 "TS$Y <- as.character(TS$Y)\n"
178 "CMC$Y <- as.numeric(as.character(CMC$Y))\n" 184 "CMC$Y <- as.numeric(as.character(CMC$Y))\n"
179 "\n" 185 "\n"
  186 + "if (%1) {\n\tsummarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=%5, .drop=TRUE) {\n\t\t"
  187 + "require(plyr)\n\n\t\tlength2 <- function (x, na.rm=FALSE) {\n\t\t\tif (na.rm) sum(!is.na(x))\n\t\t\telse length(x)"
  188 + "\n\t\t}\n\n\t\tdatac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {\n\t\t\t"
  189 + "c(N=length2(xx[[col]], na.rm=na.rm), mean=mean(xx[[col]], na.rm=na.rm), sd=sd(xx[[col]], na.rm=na.rm))\n\t\t\t},"
  190 + "\n\t\t\tmeasurevar\n\t\t)\n\n\t\tdatac <- rename(datac, c(\"mean\" = measurevar))\n\t\tdatac$se <- datac$sd / sqrt(datac$N)"
  191 + "\n\t\tciMult <- qt(conf.interval/2 + .5, datac$N-1)\n\t\tdatac$ci <- datac$se * ciMult\n\n\t\treturn(datac)\n\t}\n\t"
  192 + "DET <- summarySE(DET, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
  193 + "ERR <- summarySE(ERR, measurevar=\"X\", groupvars=c(\"Error\", \"%2\", \"Y\"))\n\t"
  194 + "FT <- summarySE(FT, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
  195 + "CT <- summarySE(CT, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n}\n\n"
180 "# Code to format FAR values\n" 196 "# Code to format FAR values\n"
181 "far_names <- list('0.001'=\"FAR = 0.1%\", '0.01'=\"FAR = 1%\")\n" 197 "far_names <- list('0.001'=\"FAR = 0.1%\", '0.01'=\"FAR = 1%\")\n"
182 "far_labeller <- function(variable,value) { return(far_names[as.character(value)]) }\n" 198 "far_labeller <- function(variable,value) { return(far_names[as.character(value)]) }\n"
183 "\n" 199 "\n"
184 "# Code to format TAR@FAR table\n" 200 "# Code to format TAR@FAR table\n"
185 - "algs <- unique(FT$%1)\n" 201 + "algs <- unique(FT$%2)\n"
186 "algs <- algs[!duplicated(algs)]\n" 202 "algs <- algs[!duplicated(algs)]\n"
187 - "mat <- matrix(FT$Y,nrow=6,ncol=length(algs),byrow=FALSE)\n" 203 + "mat <- matrix(%3,nrow=6,ncol=length(algs),byrow=FALSE)\n"
188 "colnames(mat) <- algs \n" 204 "colnames(mat) <- algs \n"
189 "rownames(mat) <- c(\"FAR = 1e-06\", \"FAR = 1e-05\", \"FAR = 1e-04\", \"FAR = 1e-03\", \"FAR = 1e-02\", \"FAR = 1e-01\")\n" 205 "rownames(mat) <- c(\"FAR = 1e-06\", \"FAR = 1e-05\", \"FAR = 1e-04\", \"FAR = 1e-03\", \"FAR = 1e-02\", \"FAR = 1e-01\")\n"
190 "FTtable <- as.table(mat)\n" 206 "FTtable <- as.table(mat)\n"
191 "\n" 207 "\n"
192 "# Code to format CMC Table\n" 208 "# Code to format CMC Table\n"
193 - "mat <- matrix(CT$Y,nrow=6,ncol=length(algs),byrow=FALSE)\n" 209 + "mat <- matrix(%4,nrow=6,ncol=length(algs),byrow=FALSE)\n"
194 "colnames(mat) <- algs \n" 210 "colnames(mat) <- algs \n"
195 "rownames(mat) <- c(\" Rank 1\", \"Rank 5\", \"Rank 10\", \"Rank 20\", \"Rank 50\", \"Rank 100\")\n" 211 "rownames(mat) <- c(\" Rank 1\", \"Rank 5\", \"Rank 10\", \"Rank 20\", \"Rank 50\", \"Rank 100\")\n"
196 "CMCtable <- as.table(mat)\n" 212 "CMCtable <- as.table(mat)\n"
197 "\n" 213 "\n"
198 "# Code to format Template Size Table\n" 214 "# Code to format Template Size Table\n"
199 - "mat <- matrix(TS$Y,nrow=1,ncol=length(algs),byrow=FALSE)\n"  
200 - "colnames(mat) <- algs\n"  
201 - "rownames(mat) <- c(\"Template Size (bytes):\")\n"  
202 - "TStable <- as.table(mat)\n").arg(major.header))); 215 + "if (nrow(TS) != 0) {\n\t"
  216 + "mat <- matrix(TS$Y,nrow=1,ncol=length(algs),byrow=FALSE)\n\t"
  217 + "colnames(mat) <- algs\n\t"
  218 + "rownames(mat) <- c(\"Template Size (bytes):\")\n\t"
  219 + "TStable <- as.table(mat)\n}\n").arg(((major.smooth || minor.smooth) ? "TRUE" : "FALSE"), major.size > 1 ? major.header : (minor.header.isEmpty() ? major.header : minor.header),
  220 + (major.smooth || minor.smooth) && confidence != 0 ? "paste(as.character(round(FT$Y, 3)), round(FT$ci, 3), sep=\"\\u00b1\")" : "FT$Y",
  221 + (major.smooth || minor.smooth) && confidence != 0 ? "paste(as.character(round(CT$Y, 3)), round(CT$ci, 3), sep=\"\\u00b1\")" : "CT$Y",
  222 + QString::number(confidence))));
203 223
204 // Open output device 224 // Open output device
205 file.write(qPrintable(QString("\n" 225 file.write(qPrintable(QString("\n"
@@ -231,8 +251,9 @@ struct RPlot @@ -231,8 +251,9 @@ struct RPlot
231 "print(title(\"Table of True Accept Rates at various False Accept Rates\"))\n" 251 "print(title(\"Table of True Accept Rates at various False Accept Rates\"))\n"
232 "print(textplot(CMCtable))\n" 252 "print(textplot(CMCtable))\n"
233 "print(title(\"Table of retrieval rate at various ranks\"))\n" 253 "print(title(\"Table of retrieval rate at various ranks\"))\n"
234 - "print(textplot(TStable, cex=1.15))\n"  
235 - "print(title(\"Template Size by Algorithm\"))\n"; 254 + "if (nrow(TS) != 0) {\n\t"
  255 + "print(textplot(TStable, cex=1.15))\n\t"
  256 + "print(title(\"Template Size by Algorithm\"))\n}\n";
236 file.write(qPrintable(textplot.arg(PRODUCT_NAME, PRODUCT_VERSION))); 257 file.write(qPrintable(textplot.arg(PRODUCT_NAME, PRODUCT_VERSION)));
237 } 258 }
238 259
@@ -281,11 +302,12 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show) @@ -281,11 +302,12 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
281 302
282 RPlot p(files, destination); 303 RPlot p(files, destination);
283 304
284 - p.file.write(qPrintable(QString("qplot(X, 1-Y, data=DET%1, main=\"%2\"").arg((p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : ", geom=\"line\"", rocOpts.get<QString>("title",QString())) + 305 + p.file.write(qPrintable(QString("qplot(X, 1-Y, data=DET, geom=\"line\", main=\"%1\"").arg(rocOpts.get<QString>("title",QString())) +
285 (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) + 306 (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +
286 (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) + 307 (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) +
287 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") + 308 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +
288 - (p.major.size > 1 ? getScale("colour", "Algorithm", p.major.size) : QString()) + 309 + ((p.major.smooth || p.minor.smooth) && p.confidence != 0 ? " + geom_errorbar(data=DET[seq(1, NROW(DET), by = 29),], aes(x=X, ymin=(1-Y)-ci, ymax=(1-Y)+ci), width=0.1, alpha=I(1/2))" : QString()) +
  310 + (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
289 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + 311 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +
290 QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format()))") + 312 QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format()))") +
291 (rocOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent) + coord_cartesian(ylim=%1)").arg("c"+QtUtils::toString(rocOpts.get<QPointF>("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) + 313 (rocOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent) + coord_cartesian(ylim=%1)").arg("c"+QtUtils::toString(rocOpts.get<QPointF>("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) +
@@ -293,12 +315,14 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show) @@ -293,12 +315,14 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
293 QString(" + theme(legend.title = element_text(size = %1), plot.title = element_text(size = %1), axis.text = element_text(size = %1), axis.title.x = element_text(size = %1), axis.title.y = element_text(size = %1)," 315 QString(" + theme(legend.title = element_text(size = %1), plot.title = element_text(size = %1), axis.text = element_text(size = %1), axis.title.x = element_text(size = %1), axis.title.y = element_text(size = %1),"
294 " legend.position=%2, legend.background = element_rect(fill = 'white'), panel.grid.major = element_line(colour = \"gray\"), panel.grid.minor = element_line(colour = \"gray\", linetype = \"dashed\"), legend.text = element_text(size = %1))\n\n").arg(QString::number(rocOpts.get<float>("textSize",12)), rocOpts.contains("legendPosition") ? "c"+QtUtils::toString(rocOpts.get<QPointF>("legendPosition")) : "'bottom'"))); 316 " legend.position=%2, legend.background = element_rect(fill = 'white'), panel.grid.major = element_line(colour = \"gray\"), panel.grid.minor = element_line(colour = \"gray\", linetype = \"dashed\"), legend.text = element_text(size = %1))\n\n").arg(QString::number(rocOpts.get<float>("textSize",12)), rocOpts.contains("legendPosition") ? "c"+QtUtils::toString(rocOpts.get<QPointF>("legendPosition")) : "'bottom'")));
295 317
296 - p.file.write(qPrintable(QString("qplot(X, Y, data=DET%1").arg((p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : ", geom=\"line\"") + 318 + p.file.write(qPrintable(QString("qplot(X, Y, data=DET, geom=\"line\"") +
297 (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) + 319 (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +
298 (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) + 320 (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) +
299 QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") + 321 QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") +
300 - (p.major.size > 1 ? getScale("colour", "Algorithm", p.major.size) : QString()) + 322 + ((p.major.smooth || p.minor.smooth) && p.confidence != 0 ? " + geom_errorbar(data=DET[seq(1, NROW(DET), by = 29),], aes(x=X, ymin=Y-ci, ymax=Y+ci), width=0.1, alpha=I(1/2))" : QString()) +
  323 + (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
301 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + 324 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +
  325 + QString(" + theme(legend.position=%1)").arg(rocOpts.contains("legendPosition") ? "c"+QtUtils::toString(rocOpts.get<QPointF>("legendPosition")) : "'bottom'") +
302 QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format())) + scale_y_log10(labels=trans_format(\"log10\", math_format())) + annotation_logticks()\n\n"))); 326 QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format())) + scale_y_log10(labels=trans_format(\"log10\", math_format())) + annotation_logticks()\n\n")));
303 327
304 p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") + 328 p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") +
@@ -311,7 +335,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show) @@ -311,7 +335,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
311 QString(((p.major.smooth || p.minor.smooth) ? (!uncertainty ? " + stat_summary(geom=\"line\", fun.y=mean, size=%1)" : " + stat_summary(geom=\"line\", fun.y=min, aes(linetype=\"Min/Max\"), size=%1) + stat_summary(geom=\"line\", " 335 QString(((p.major.smooth || p.minor.smooth) ? (!uncertainty ? " + stat_summary(geom=\"line\", fun.y=mean, size=%1)" : " + stat_summary(geom=\"line\", fun.y=min, aes(linetype=\"Min/Max\"), size=%1) + stat_summary(geom=\"line\", "
312 "fun.y=max, aes(linetype=\"Min/Max\"), size=%1) + stat_summary(geom=\"line\", fun.y=mean, aes(linetype=\"Mean\"), size=%1) + scale_linetype_manual(\"Legend\", values=c(\"Mean\"=1, \"Min/Max\"=2))") : " + geom_line(size=%1)")).arg(QString::number(cmcOpts.get<float>("thickness",1))) + 336 "fun.y=max, aes(linetype=\"Min/Max\"), size=%1) + stat_summary(geom=\"line\", fun.y=mean, aes(linetype=\"Mean\"), size=%1) + scale_linetype_manual(\"Legend\", values=c(\"Mean\"=1, \"Min/Max\"=2))") : " + geom_line(size=%1)")).arg(QString::number(cmcOpts.get<float>("thickness",1))) +
313 (minimalist ? "" : " + scale_x_log10(labels=c(1,5,10,50,100), breaks=c(1,5,10,50,100)) + annotation_logticks(sides=\"b\")") + 337 (minimalist ? "" : " + scale_x_log10(labels=c(1,5,10,50,100), breaks=c(1,5,10,50,100)) + annotation_logticks(sides=\"b\")") +
314 - (p.major.size > 1 ? getScale("colour", "Algorithm", p.major.size) : QString()) + 338 + (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
315 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + 339 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +
316 (cmcOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent) + coord_cartesian(ylim=%1)").arg("c"+QtUtils::toString(cmcOpts.get<QPointF>("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) + 340 (cmcOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent) + coord_cartesian(ylim=%1)").arg("c"+QtUtils::toString(cmcOpts.get<QPointF>("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) +
317 QString(" + theme_minimal() + theme(legend.title = element_text(size = %1), plot.title = element_text(size = %1), axis.text = element_text(size = %1), axis.title.x = element_text(size = %1), axis.title.y = element_text(size = %1)," 341 QString(" + theme_minimal() + theme(legend.title = element_text(size = %1), plot.title = element_text(size = %1), axis.text = element_text(size = %1), axis.title.x = element_text(size = %1), axis.title.y = element_text(size = %1),"
@@ -320,14 +344,14 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show) @@ -320,14 +344,14 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
320 p.file.write(qPrintable(QString("qplot(factor(%1)%2, data=BC, %3").arg(p.major.smooth ? (p.minor.header.isEmpty() ? "Algorithm" : p.minor.header) : p.major.header, (p.major.smooth || p.minor.smooth) ? ", Y" : "", (p.major.smooth || p.minor.smooth) ? "geom=\"boxplot\"" : "geom=\"bar\", position=\"dodge\", weight=Y") + 344 p.file.write(qPrintable(QString("qplot(factor(%1)%2, data=BC, %3").arg(p.major.smooth ? (p.minor.header.isEmpty() ? "Algorithm" : p.minor.header) : p.major.header, (p.major.smooth || p.minor.smooth) ? ", Y" : "", (p.major.smooth || p.minor.smooth) ? "geom=\"boxplot\"" : "geom=\"bar\", position=\"dodge\", weight=Y") +
321 (p.major.size > 1 ? QString(", fill=factor(%1)").arg(p.major.header) : QString()) + 345 (p.major.size > 1 ? QString(", fill=factor(%1)").arg(p.major.header) : QString()) +
322 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") + 346 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +
323 - (p.major.size > 1 ? getScale("fill", "Algorithm", p.major.size) : QString()) + 347 + (p.major.size > 1 ? getScale("fill", p.major.header, p.major.size) : QString()) +
324 (p.minor.size > 1 ? QString(" + facet_grid(%2 ~ X)").arg(p.minor.header) : QString(" + facet_grid(. ~ X, labeller=far_labeller)")) + 348 (p.minor.size > 1 ? QString(" + facet_grid(%2 ~ X)").arg(p.minor.header) : QString(" + facet_grid(. ~ X, labeller=far_labeller)")) +
325 QString(" + scale_y_continuous(labels=percent) + theme(legend.position=\"none\", axis.text.x=element_text(angle=-90, hjust=0))%1").arg((p.major.smooth || p.minor.smooth) ? "" : " + geom_text(data=BC, aes(label=Y, y=0.05))") + "\n\n")); 349 QString(" + scale_y_continuous(labels=percent) + theme(legend.position=\"none\", axis.text.x=element_text(angle=-90, hjust=0))%1").arg((p.major.smooth || p.minor.smooth) ? "" : " + geom_text(data=BC, aes(label=Y, y=0.05))") + "\n\n"));
326 350
327 - p.file.write(qPrintable(QString("qplot(X, Y, data=ERR%1, linetype=Error").arg((p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : ", geom=\"line\"") + 351 + p.file.write(qPrintable(QString("qplot(X, Y, data=ERR, geom=\"line\", linetype=Error") +
328 ((p.flip ? p.major.size : p.minor.size) > 1 ? QString(", colour=factor(%1)").arg(p.flip ? p.major.header : p.minor.header) : QString()) + 352 ((p.flip ? p.major.size : p.minor.size) > 1 ? QString(", colour=factor(%1)").arg(p.flip ? p.major.header : p.minor.header) : QString()) +
329 QString(", xlab=\"Score\", ylab=\"Error Rate\") + theme_minimal()") + 353 QString(", xlab=\"Score\", ylab=\"Error Rate\") + theme_minimal()") +
330 - ((p.flip ? p.major.size : p.minor.size) > 1 ? getScale("colour", p.flip ? "Algorithm" : "Algorithm", p.flip ? p.major.size : p.minor.size) : QString()) + 354 + ((p.flip ? p.major.size : p.minor.size) > 1 ? getScale("colour", p.flip ? p.major.header : p.minor.header, p.flip ? p.major.size : p.minor.size) : QString()) +
331 QString(" + scale_y_log10(labels=percent) + annotation_logticks(sides=\"l\")") + 355 QString(" + scale_y_log10(labels=percent) + annotation_logticks(sides=\"l\")") +
332 ((p.flip ? p.minor.size : p.major.size) > 1 ? QString(" + facet_wrap(~ %1, scales=\"free_x\")").arg(p.flip ? p.minor.header : p.major.header) : QString()) + 356 ((p.flip ? p.minor.size : p.major.size) > 1 ? QString(" + facet_wrap(~ %1, scales=\"free_x\")").arg(p.flip ? p.minor.header : p.major.header) : QString()) +
333 QString(" + theme(aspect.ratio=1)\n\n"))); 357 QString(" + theme(aspect.ratio=1)\n\n")));