Commit 6ebad57b201989a25d35171b914269ac7876eeab

Authored by bhklein
1 parent cb9c5416

Sample ROC points at fixed FAR values for easy vertical averaging. Display conf…

…idence intervals in tables and plots.
openbr/core/eval.cpp
... ... @@ -48,22 +48,26 @@ struct OperatingPoint
48 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 53 int index = 0;
54 54 while (operatingPoints[index].FAR < FAR) {
55 55 index++;
56 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 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 270 }
267 271  
268 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 277 QString::number(1-operatingPoint.TAR)));
274 278 lines.append(QString("FAR,%1,%2").arg(QString::number(operatingPoint.score),
275   - QString::number(operatingPoint.FAR)));
  279 + QString::number(FAR)));
276 280 lines.append(QString("FRR,%1,%2").arg(QString::number(operatingPoint.score),
277 281 QString::number(1-operatingPoint.TAR)));
  282 + //multiplier roughly spans 10E-6 to 1
  283 + FAR *=1.02807;
278 284 }
  285 +
279 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 294 //Write CMC Table (CT)
288 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 300 lines.append(qPrintable(QString("CT,100,%1").arg(QString::number(getCMC(firstGenuineReturns, 100), 'f', 3))));
294 301  
295 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 306 // Attempt to read template size from enrolled gallery and write to output CSV
300 307 size_t maxSize(0);
301 308 if (target.endsWith(".gal")) {
302 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 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 315 QList<double> sampledGenuineScores; sampledGenuineScores.reserve(points);
310 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 338  
333 339 QtUtils::writeFile(csv, lines);
334 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 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 566  
561 567 float result;
562 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 572 qDebug("TAR @ FAR = 0.01: %.3f", result);
567 573 QtUtils::writeFile(csv, lines);
... ...
openbr/core/plot.cpp
... ... @@ -108,7 +108,6 @@ struct RPlot
108 108 pivotItems = QVector< QSet<QString> >(pivotHeaders.size());
109 109 foreach (const QString &fileName, files) {
110 110 QStringList pivots = getPivots(fileName, false);
111   -
112 111 // If the number of pivots don't match, abandon the directory/filename labeling scheme
113 112 if (pivots.size() != pivotHeaders.size()) {
114 113 pivots.clear();
... ... @@ -132,7 +131,6 @@ struct RPlot
132 131 minor = Pivot(i, size, pivotHeaders[i]);
133 132 }
134 133 }
135   -
136 134 const QString &smooth = destination.get<QString>("smooth", "");
137 135 major.smooth = !smooth.isEmpty() && (major.header == smooth) && (major.size > 1);
138 136 minor.smooth = !smooth.isEmpty() && (minor.header == smooth) && (minor.size > 1);
... ... @@ -177,29 +175,42 @@ struct RPlot
177 175 "TS$Y <- as.character(TS$Y)\n"
178 176 "CMC$Y <- as.numeric(as.character(CMC$Y))\n"
179 177 "\n"
  178 + "if (%1) {\n\tsummarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=.95, .drop=TRUE) {\n\t\t"
  179 + "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)"
  180 + "\n\t\t}\n\n\t\tdatac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {\n\t\t\t"
  181 + "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},"
  182 + "\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)"
  183 + "\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"
  184 + "DET <- summarySE(DET, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
  185 + "ERR <- summarySE(ERR, measurevar=\"X\", groupvars=c(\"Error\", \"%2\", \"Y\"))\n\t"
  186 + "FT <- summarySE(FT, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
  187 + "CT <- summarySE(CT, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n}\n\n"
180 188 "# Code to format FAR values\n"
181 189 "far_names <- list('0.001'=\"FAR = 0.1%\", '0.01'=\"FAR = 1%\")\n"
182 190 "far_labeller <- function(variable,value) { return(far_names[as.character(value)]) }\n"
183 191 "\n"
184 192 "# Code to format TAR@FAR table\n"
185   - "algs <- unique(FT$%1)\n"
  193 + "algs <- unique(FT$%2)\n"
186 194 "algs <- algs[!duplicated(algs)]\n"
187   - "mat <- matrix(FT$Y,nrow=6,ncol=length(algs),byrow=FALSE)\n"
  195 + "mat <- matrix(%3,nrow=6,ncol=length(algs),byrow=FALSE)\n"
188 196 "colnames(mat) <- algs \n"
189 197 "rownames(mat) <- c(\"FAR = 1e-06\", \"FAR = 1e-05\", \"FAR = 1e-04\", \"FAR = 1e-03\", \"FAR = 1e-02\", \"FAR = 1e-01\")\n"
190 198 "FTtable <- as.table(mat)\n"
191 199 "\n"
192 200 "# Code to format CMC Table\n"
193   - "mat <- matrix(CT$Y,nrow=6,ncol=length(algs),byrow=FALSE)\n"
  201 + "mat <- matrix(%4,nrow=6,ncol=length(algs),byrow=FALSE)\n"
194 202 "colnames(mat) <- algs \n"
195 203 "rownames(mat) <- c(\" Rank 1\", \"Rank 5\", \"Rank 10\", \"Rank 20\", \"Rank 50\", \"Rank 100\")\n"
196 204 "CMCtable <- as.table(mat)\n"
197 205 "\n"
198 206 "# 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)));
  207 + "if (nrow(TS) != 0) {\n\t"
  208 + "mat <- matrix(TS$Y,nrow=1,ncol=length(algs),byrow=FALSE)\n\t"
  209 + "colnames(mat) <- algs\n\t"
  210 + "rownames(mat) <- c(\"Template Size (bytes):\")\n\t"
  211 + "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),
  212 + (major.smooth || minor.smooth) ? "paste(as.character(round(FT$Y, 3)), round(FT$ci, 3), sep=\"\\u00b1\")" : "FT$Y",
  213 + (major.smooth || minor.smooth) ? "paste(as.character(round(CT$Y, 3)), round(CT$ci, 3), sep=\"\\u00b1\")" : "CT$Y")));
203 214  
204 215 // Open output device
205 216 file.write(qPrintable(QString("\n"
... ... @@ -231,8 +242,9 @@ struct RPlot
231 242 "print(title(\"Table of True Accept Rates at various False Accept Rates\"))\n"
232 243 "print(textplot(CMCtable))\n"
233 244 "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";
  245 + "if (nrow(TS) != 0) {\n\t"
  246 + "print(textplot(TStable, cex=1.15))\n\t"
  247 + "print(title(\"Template Size by Algorithm\"))\n}\n";
236 248 file.write(qPrintable(textplot.arg(PRODUCT_NAME, PRODUCT_VERSION)));
237 249 }
238 250  
... ... @@ -281,11 +293,12 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
281 293  
282 294 RPlot p(files, destination);
283 295  
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())) +
  296 + p.file.write(qPrintable(QString("qplot(X, 1-Y, data=DET, geom=\"line\", main=\"%1\"").arg(rocOpts.get<QString>("title",QString())) +
285 297 (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +
286 298 (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) +
287 299 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +
288   - (p.major.size > 1 ? getScale("colour", "Algorithm", p.major.size) : QString()) +
  300 + ((p.major.smooth || p.minor.smooth) ? " + 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()) +
  301 + (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
289 302 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +
290 303 QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format()))") +
291 304 (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 +306,14 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
293 306 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 307 " 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 308  
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\"") +
  309 + p.file.write(qPrintable(QString("qplot(X, Y, data=DET, geom=\"line\"") +
297 310 (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +
298 311 (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) +
299 312 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()) +
  313 + ((p.major.smooth || p.minor.smooth) ? " + 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()) +
  314 + (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
301 315 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +
  316 + QString(" + theme(legend.position=%1)").arg(rocOpts.contains("legendPosition") ? "c"+QtUtils::toString(rocOpts.get<QPointF>("legendPosition")) : "'bottom'") +
302 317 QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format())) + scale_y_log10(labels=trans_format(\"log10\", math_format())) + annotation_logticks()\n\n")));
303 318  
304 319 p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") +
... ... @@ -311,7 +326,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
311 326 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 327 "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 328 (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()) +
  329 + (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +
315 330 (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +
316 331 (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 332 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 +335,14 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
320 335 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 336 (p.major.size > 1 ? QString(", fill=factor(%1)").arg(p.major.header) : QString()) +
322 337 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +
323   - (p.major.size > 1 ? getScale("fill", "Algorithm", p.major.size) : QString()) +
  338 + (p.major.size > 1 ? getScale("fill", p.major.header, p.major.size) : QString()) +
324 339 (p.minor.size > 1 ? QString(" + facet_grid(%2 ~ X)").arg(p.minor.header) : QString(" + facet_grid(. ~ X, labeller=far_labeller)")) +
325 340 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 341  
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\"") +
  342 + p.file.write(qPrintable(QString("qplot(X, Y, data=ERR, geom=\"line\", linetype=Error") +
328 343 ((p.flip ? p.major.size : p.minor.size) > 1 ? QString(", colour=factor(%1)").arg(p.flip ? p.major.header : p.minor.header) : QString()) +
329 344 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()) +
  345 + ((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 346 QString(" + scale_y_log10(labels=percent) + annotation_logticks(sides=\"l\")") +
332 347 ((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 348 QString(" + theme(aspect.ratio=1)\n\n")));
... ...