I have written a small program in C# to plot ROC
, PR
(equal to AP
) and PRI
(equal to API
) curves using the plot_curve()
method. (The program also calculates the approximate AUC of the curves using the area_under_curve_trapz()
method, but that isn't the main point. The main point and purpose is to plot the curves.)
Plotting ROC
and PR
curves in C# appears to be a task which has not yet been done effectively. Therefore, this algorithm is written from scratch. I also hope this algorithm will be useful for those interested in data science and machine learning, especially those using C#.
Please review and suggest any improvements in the algorithm. (In particular, I am curious about better methods of averaging the x,y coordinates for the outer-cross-validation results (i.e. nested-cross-validation), considering the variable decision boundary thresholds, i.e. the input of x,y can never be pre-determined, and using the standard 11 point averaging does not give accurate results; rather a very poor approximation. Still, it is supported by the method.)
The whole (working) algorithm is in the following file - plot.cs.It can be tested using the static plot.test_plot()
method which contains some real sample data. The generated bmp images will be loaded in the default program after execution.
Note that this was written using Visual Studio 2017, C# 7.3 and .NET 4.7.2. It may be incompatible with some lower C# or .NET versions. Please also note that this algorithm purposely ignores standard C# naming conventions.
using System;using System.Collections.Generic;using System.Diagnostics;using System.Drawing;using System.Drawing.Drawing2D;using System.Drawing.Imaging;using System.Drawing.Text;using System.IO;using System.Linq;namespace svm_compute{ public static class plot { public static void test_plot() { // this function plots sample ROC, PR and PRI (approximated interpolated) curves, using all thresholds and 11 point thresholds. var encoded_roc = @"FPR;TPR/0;0/0;0.022222/0;0.044444/0.030303;0.044444/0.030303;0.066667/0.030303;0.088889/0.030303;0.111111/0.030303;0.133333/0.030303;0.155556/0.030303;0.177778/0.060606;0.177778/0.060606;0.2/0.060606;0.222222/0.060606;0.244444/0.060606;0.266667/0.060606;0.288889/0.090909;0.288889/0.090909;0.311111/0.090909;0.333333/0.121212;0.333333/0.121212;0.355556/0.151515;0.355556/0.151515;0.377778/0.181818;0.377778/0.181818;0.4/0.181818;0.422222/0.181818;0.444444/0.181818;0.466667/0.212121;0.466667/0.212121;0.488889/0.242424;0.488889/0.242424;0.511111/0.242424;0.533333/0.242424;0.555556/0.242424;0.577778/0.242424;0.6/0.242424;0.622222/0.242424;0.644444/0.242424;0.666667/0.272727;0.666667/0.272727;0.688889/0.30303;0.688889/0.333333;0.688889/0.333333;0.711111/0.333333;0.733333/0.333333;0.755556/0.363636;0.755556/0.363636;0.777778/0.363636;0.8/0.363636;0.822222/0.393939;0.822222/0.393939;0.844444/0.393939;0.866667/0.393939;0.888889/0.424242;0.888889/0.424242;0.911111/0.424242;0.933333/0.454545;0.933333/0.484848;0.933333/0.515152;0.933333/0.515152;0.955556/0.515152;0.977778/0.545455;0.977778/0.545455;1/0.606061;1/0.636364;1/0.666667;1/0.69697;1/0.727273;1/0.757576;1/0.787879;1/0.818182;1/0.848485;1/0.878788;1/0.909091;1/0.939394;1/0.969697;1/1;1FPR;TPR/0;0/0;0.030303/0;0.060606/0;0.090909/0;0.121212/0;0.151515/0;0.181818/0;0.212121/0;0.242424/0;0.272727/0;0.30303/0;0.333333/0;0.363636/0;0.393939/0;0.454545/0.022222;0.454545/0.022222;0.484848/0.044444;0.484848/0.066667;0.484848/0.066667;0.515152/0.066667;0.545455/0.066667;0.575758/0.088889;0.575758/0.111111;0.575758/0.111111;0.606061/0.133333;0.606061/0.155556;0.606061/0.177778;0.606061/0.177778;0.636364/0.2;0.636364/0.222222;0.636364/0.244444;0.636364/0.244444;0.666667/0.266667;0.666667/0.288889;0.666667/0.311111;0.666667/0.311111;0.69697/0.311111;0.727273/0.333333;0.727273/0.333333;0.757576/0.355556;0.757576/0.377778;0.757576/0.4;0.757576/0.422222;0.757576/0.444444;0.757576/0.466667;0.757576/0.488889;0.757576/0.511111;0.757576/0.511111;0.787879/0.533333;0.787879/0.533333;0.818182/0.555556;0.818182/0.577778;0.818182/0.6;0.818182/0.622222;0.818182/0.622222;0.848485/0.644444;0.848485/0.644444;0.878788/0.666667;0.878788/0.666667;0.909091/0.688889;0.909091/0.711111;0.909091/0.711111;0.939394/0.733333;0.939394/0.755556;0.939394/0.777778;0.939394/0.8;0.939394/0.822222;0.939394/0.822222;0.969697/0.844444;0.969697/0.866667;0.969697/0.888889;0.969697/0.911111;0.969697/0.933333;0.969697/0.955556;0.969697/0.955556;1/0.977778;1/1;1FPR;TPR/0;0/0;0.022222/0;0.044444/0.030303;0.044444/0.030303;0.066667/0.030303;0.088889/0.030303;0.111111/0.030303;0.133333/0.030303;0.155556/0.030303;0.177778/0.030303;0.2/0.060606;0.2/0.060606;0.222222/0.060606;0.244444/0.060606;0.266667/0.060606;0.288889/0.060606;0.311111/0.090909;0.311111/0.121212;0.311111/0.121212;0.333333/0.121212;0.355556/0.121212;0.377778/0.121212;0.4/0.121212;0.422222/0.121212;0.444444/0.151515;0.444444/0.151515;0.466667/0.151515;0.488889/0.181818;0.488889/0.181818;0.511111/0.212121;0.511111/0.242424;0.511111/0.242424;0.533333/0.242424;0.555556/0.242424;0.577778/0.242424;0.6/0.272727;0.6/0.272727;0.622222/0.272727;0.644444/0.272727;0.666667/0.272727;0.688889/0.30303;0.688889/0.30303;0.711111/0.333333;0.711111/0.333333;0.733333/0.363636;0.733333/0.393939;0.733333/0.424242;0.733333/0.424242;0.755556/0.424242;0.777778/0.424242;0.8/0.424242;0.822222/0.424242;0.844444/0.424242;0.866667/0.424242;0.888889/0.454545;0.888889/0.484848;0.888889/0.484848;0.911111/0.515152;0.911111/0.545455;0.911111/0.545455;0.933333/0.575758;0.933333/0.606061;0.933333/0.606061;0.955556/0.666667;0.955556/0.69697;0.955556/0.69697;0.977778/0.727273;0.977778/0.757576;0.977778/0.787879;0.977778/0.787879;1/0.818182;1/0.848485;1/0.878788;1/0.909091;1/0.939394;1/0.969697;1/1;1FPR;TPR/0;0/0;0.030303/0;0.060606/0;0.090909/0;0.121212/0;0.151515/0;0.181818/0;0.212121/0.022222;0.212121/0.022222;0.242424/0.022222;0.272727/0.022222;0.30303/0.044444;0.30303/0.044444;0.333333/0.044444;0.393939/0.066667;0.393939/0.066667;0.424242/0.066667;0.454545/0.088889;0.454545/0.088889;0.484848/0.088889;0.515152/0.111111;0.515152/0.111111;0.545455/0.111111;0.575758/0.133333;0.575758/0.155556;0.575758/0.177778;0.575758/0.2;0.575758/0.222222;0.575758/0.244444;0.575758/0.266667;0.575758/0.266667;0.606061/0.266667;0.636364/0.266667;0.666667/0.288889;0.666667/0.288889;0.69697/0.311111;0.69697/0.311111;0.727273/0.333333;0.727273/0.355556;0.727273/0.377778;0.727273/0.4;0.727273/0.4;0.757576/0.422222;0.757576/0.444444;0.757576/0.466667;0.757576/0.488889;0.757576/0.488889;0.787879/0.488889;0.818182/0.511111;0.818182/0.511111;0.848485/0.533333;0.848485/0.555556;0.848485/0.555556;0.878788/0.577778;0.878788/0.6;0.878788/0.622222;0.878788/0.644444;0.878788/0.666667;0.878788/0.688889;0.878788/0.688889;0.909091/0.688889;0.939394/0.711111;0.939394/0.733333;0.939394/0.755556;0.939394/0.777778;0.939394/0.8;0.939394/0.8;0.969697/0.822222;0.969697/0.844444;0.969697/0.866667;0.969697/0.888889;0.969697/0.911111;0.969697/0.933333;0.969697/0.955556;0.969697/0.955556;1/0.977778;1/1;1FPR;TPR/0;0/0;0.022222/0;0.044444/0;0.066667/0;0.088889/0;0.111111/0;0.133333/0;0.155556/0;0.177778/0.030303;0.177778/0.030303;0.2/0.060606;0.2/0.060606;0.222222/0.060606;0.244444/0.060606;0.266667/0.090909;0.266667/0.090909;0.288889/0.090909;0.311111/0.090909;0.333333/0.121212;0.333333/0.121212;0.355556/0.121212;0.377778/0.121212;0.4/0.121212;0.422222/0.151515;0.422222/0.151515;0.444444/0.151515;0.466667/0.151515;0.488889/0.151515;0.511111/0.151515;0.533333/0.151515;0.555556/0.151515;0.577778/0.181818;0.577778/0.181818;0.6/0.181818;0.622222/0.212121;0.622222/0.212121;0.644444/0.242424;0.644444/0.272727;0.644444/0.272727;0.666667/0.272727;0.688889/0.30303;0.688889/0.333333;0.688889/0.333333;0.711111/0.333333;0.733333/0.333333;0.755556/0.333333;0.777778/0.363636;0.777778/0.393939;0.777778/0.424242;0.777778/0.424242;0.8/0.454545;0.8/0.454545;0.822222/0.454545;0.844444/0.454545;0.866667/0.454545;0.888889/0.484848;0.888889/0.484848;0.911111/0.515152;0.911111/0.545455;0.911111/0.545455;0.933333/0.575758;0.933333/0.575758;0.955556/0.606061;0.955556/0.636364;0.955556/0.666667;0.955556/0.666667;0.977778/0.69697;0.977778/0.727273;0.977778/0.757576;0.977778/0.787879;0.977778/0.818182;0.977778/0.848485;0.977778/0.878788;0.977778/0.909091;0.977778/0.909091;1/0.939394;1/0.969697;1/1;1FPR;TPR/0;0/0;0.030303/0;0.060606/0;0.090909/0.022222;0.090909/0.022222;0.121212/0.022222;0.151515/0.022222;0.181818/0.022222;0.212121/0.022222;0.242424/0.022222;0.272727/0.022222;0.30303/0.022222;0.333333/0.044444;0.333333/0.044444;0.363636/0.044444;0.393939/0.044444;0.424242/0.066667;0.424242/0.066667;0.454545/0.088889;0.454545/0.088889;0.484848/0.088889;0.515152/0.111111;0.515152/0.111111;0.545455/0.133333;0.545455/0.155556;0.545455/0.177778;0.545455/0.2;0.545455/0.2;0.575758/0.222222;0.575758/0.222222;0.606061/0.222222;0.636364/0.222222;0.666667/0.244444;0.666667/0.266667;0.666667/0.288889;0.666667/0.311111;0.666667/0.311111;0.69697/0.311111;0.727273/0.333333;0.727273/0.355556;0.727273/0.355556;0.757576/0.355556;0.787879/0.377778;0.787879/0.377778;0.818182/0.4;0.818182/0.422222;0.818182/0.422222;0.848485/0.444444;0.848485/0.466667;0.848485/0.488889;0.848485/0.511111;0.848485/0.533333;0.848485/0.555556;0.848485/0.577778;0.848485/0.577778;0.878788/0.6;0.878788/0.622222;0.878788/0.644444;0.878788/0.666667;0.878788/0.666667;0.909091/0.688889;0.909091/0.711111;0.909091/0.733333;0.909091/0.733333;0.939394/0.755556;0.939394/0.777778;0.939394/0.8;0.939394/0.8;0.969697/0.822222;0.969697/0.822222;1/0.844444;1/0.866667;1/0.888889;1/0.911111;1/0.933333;1/0.955556;1/0.977778;1/1;1FPR;TPR/0;0/0;0.022222/0;0.044444/0;0.066667/0;0.088889/0;0.111111/0;0.133333/0;0.155556/0;0.177778/0;0.2/0;0.222222/0;0.244444/0;0.266667/0;0.288889/0;0.311111/0;0.333333/0.030303;0.333333/0.030303;0.355556/0.030303;0.377778/0.030303;0.4/0.030303;0.422222/0.060606;0.422222/0.060606;0.444444/0.060606;0.466667/0.060606;0.488889/0.090909;0.488889/0.090909;0.511111/0.090909;0.533333/0.090909;0.555556/0.121212;0.555556/0.121212;0.577778/0.151515;0.577778/0.151515;0.6/0.151515;0.622222/0.181818;0.622222/0.181818;0.644444/0.181818;0.666667/0.212121;0.666667/0.212121;0.688889/0.242424;0.688889/0.242424;0.711111/0.272727;0.711111/0.30303;0.711111/0.333333;0.711111/0.363636;0.711111/0.363636;0.733333/0.363636;0.755556/0.393939;0.755556/0.393939;0.777778/0.424242;0.777778/0.424242;0.8/0.454545;0.8/0.454545;0.822222/0.484848;0.822222/0.515152;0.822222/0.545455;0.822222/0.545455;0.844444/0.545455;0.866667/0.545455;0.888889/0.545455;0.911111/0.575758;0.911111/0.606061;0.911111/0.606061;0.933333/0.636364;0.933333/0.666667;0.933333/0.666667;0.955556/0.69697;0.955556/0.727273;0.955556/0.727273;0.977778/0.757576;0.977778/0.757576;1/0.787879;1/0.818182;1/0.848485;1/0.878788;1/0.909091;1/0.939394;1/0.969697;1/1;1FPR;TPR/0;0/0;0.030303/0;0.060606/0;0.090909/0;0.121212/0;0.151515/0;0.181818/0;0.212121/0;0.242424/0.022222;0.242424/0.022222;0.272727/0.044444;0.272727/0.044444;0.30303/0.044444;0.333333/0.066667;0.333333/0.066667;0.363636/0.066667;0.393939/0.088889;0.393939/0.088889;0.424242/0.088889;0.454545/0.111111;0.454545/0.133333;0.454545/0.155556;0.454545/0.177778;0.454545/0.177778;0.484848/0.177778;0.515152/0.177778;0.545455/0.2;0.545455/0.2;0.575758/0.222222;0.575758/0.222222;0.606061/0.244444;0.606061/0.244444;0.636364/0.266667;0.636364/0.288889;0.636364/0.288889;0.666667/0.288889;0.69697/0.288889;0.727273/0.288889;0.757576/0.311111;0.757576/0.311111;0.787879/0.333333;0.787879/0.333333;0.818182/0.355556;0.818182/0.377778;0.818182/0.377778;0.848485/0.4;0.848485/0.422222;0.848485/0.422222;0.878788/0.444444;0.878788/0.444444;0.909091/0.466667;0.909091/0.488889;0.909091/0.511111;0.909091/0.511111;0.939394/0.533333;0.939394/0.555556;0.939394/0.577778;0.939394/0.577778;0.969697/0.6;0.969697/0.622222;0.969697/0.644444;0.969697/0.666667;0.969697/0.666667;1/0.688889;1/0.711111;1/0.733333;1/0.755556;1/0.777778;1/0.8;1/0.822222;1/0.844444;1/0.866667;1/0.888889;1/0.911111;1/0.933333;1/0.955556;1/0.977778;1/1;1"; var encoded_pr = @"TPR;PPV/0;1/0.022222;1/0.044444;1/0.044444;0.666667/0.066667;0.75/0.088889;0.8/0.111111;0.833333/0.133333;0.857143/0.155556;0.875/0.177778;0.888889/0.177778;0.8/0.2;0.818182/0.222222;0.833333/0.244444;0.846154/0.266667;0.857143/0.288889;0.866667/0.288889;0.8125/0.311111;0.823529/0.333333;0.833333/0.333333;0.789474/0.355556;0.8/0.355556;0.761905/0.377778;0.772727/0.377778;0.73913/0.4;0.75/0.422222;0.76/0.444444;0.769231/0.466667;0.777778/0.466667;0.75/0.488889;0.758621/0.488889;0.733333/0.511111;0.741935/0.533333;0.75/0.555556;0.757576/0.577778;0.764706/0.6;0.771429/0.622222;0.777778/0.644444;0.783784/0.666667;0.789474/0.666667;0.769231/0.688889;0.775/0.688889;0.756098/0.688889;0.738095/0.711111;0.744186/0.733333;0.75/0.755556;0.755556/0.755556;0.73913/0.777778;0.744681/0.8;0.75/0.822222;0.755102/0.822222;0.74/0.844444;0.745098/0.866667;0.75/0.888889;0.754717/0.888889;0.740741/0.911111;0.745455/0.933333;0.75/0.933333;0.736842/0.933333;0.724138/0.933333;0.711864/0.955556;0.716667/0.977778;0.721311/0.977778;0.709677/1;0.714286/1;0.692308/1;0.681818/1;0.671642/1;0.661765/1;0.652174/1;0.642857/1;0.633803/1;0.625/1;0.616438/1;0.608108/1;0.6/1;0.592105/1;0.584416/1;0.576923TPR;PPV/0;1/0.030303;1/0.060606;1/0.090909;1/0.121212;1/0.151515;1/0.181818;1/0.212121;1/0.242424;1/0.272727;1/0.30303;1/0.333333;1/0.363636;1/0.393939;1/0.454545;1/0.454545;0.9375/0.484848;0.941176/0.484848;0.888889/0.484848;0.842105/0.515152;0.85/0.545455;0.857143/0.575758;0.863636/0.575758;0.826087/0.575758;0.791667/0.606061;0.8/0.606061;0.769231/0.606061;0.740741/0.606061;0.714286/0.636364;0.724138/0.636364;0.7/0.636364;0.677419/0.636364;0.65625/0.666667;0.666667/0.666667;0.647059/0.666667;0.628571/0.666667;0.611111/0.69697;0.621622/0.727273;0.631579/0.727273;0.615385/0.757576;0.625/0.757576;0.609756/0.757576;0.595238/0.757576;0.581395/0.757576;0.568182/0.757576;0.555556/0.757576;0.543478/0.757576;0.531915/0.757576;0.520833/0.787879;0.530612/0.787879;0.52/0.818182;0.529412/0.818182;0.519231/0.818182;0.509434/0.818182;0.5/0.818182;0.490909/0.848485;0.5/0.848485;0.491228/0.878788;0.5/0.878788;0.491525/0.909091;0.5/0.909091;0.491803/0.909091;0.483871/0.939394;0.492063/0.939394;0.484375/0.939394;0.476923/0.939394;0.469697/0.939394;0.462687/0.939394;0.455882/0.969697;0.463768/0.969697;0.457143/0.969697;0.450704/0.969697;0.444444/0.969697;0.438356/0.969697;0.432432/0.969697;0.426667/1;0.434211/1;0.428571/1;0.423077TPR;PPV/0;1/0.022222;1/0.044444;1/0.044444;0.666667/0.066667;0.75/0.088889;0.8/0.111111;0.833333/0.133333;0.857143/0.155556;0.875/0.177778;0.888889/0.2;0.9/0.2;0.818182/0.222222;0.833333/0.244444;0.846154/0.266667;0.857143/0.288889;0.866667/0.311111;0.875/0.311111;0.823529/0.311111;0.777778/0.333333;0.789474/0.355556;0.8/0.377778;0.809524/0.4;0.818182/0.422222;0.826087/0.444444;0.833333/0.444444;0.8/0.466667;0.807692/0.488889;0.814815/0.488889;0.785714/0.511111;0.793103/0.511111;0.766667/0.511111;0.741935/0.533333;0.75/0.555556;0.757576/0.577778;0.764706/0.6;0.771429/0.6;0.75/0.622222;0.756757/0.644444;0.763158/0.666667;0.769231/0.688889;0.775/0.688889;0.756098/0.711111;0.761905/0.711111;0.744186/0.733333;0.75/0.733333;0.733333/0.733333;0.717391/0.733333;0.702128/0.755556;0.708333/0.777778;0.714286/0.8;0.72/0.822222;0.72549/0.844444;0.730769/0.866667;0.735849/0.888889;0.740741/0.888889;0.727273/0.888889;0.714286/0.911111;0.719298/0.911111;0.706897/0.911111;0.694915/0.933333;0.7/0.933333;0.688525/0.933333;0.677419/0.955556;0.68254/0.955556;0.661538/0.955556;0.651515/0.977778;0.656716/0.977778;0.647059/0.977778;0.637681/0.977778;0.628571/1;0.633803/1;0.625/1;0.616438/1;0.608108/1;0.6/1;0.592105/1;0.584416/1;0.576923TPR;PPV/0;1/0.030303;1/0.060606;1/0.090909;1/0.121212;1/0.151515;1/0.181818;1/0.212121;1/0.212121;0.875/0.242424;0.888889/0.272727;0.9/0.30303;0.909091/0.30303;0.833333/0.333333;0.846154/0.393939;0.866667/0.393939;0.8125/0.424242;0.823529/0.454545;0.833333/0.454545;0.789474/0.484848;0.8/0.515152;0.809524/0.515152;0.772727/0.545455;0.782609/0.575758;0.791667/0.575758;0.76/0.575758;0.730769/0.575758;0.703704/0.575758;0.678571/0.575758;0.655172/0.575758;0.633333/0.575758;0.612903/0.606061;0.625/0.636364;0.636364/0.666667;0.647059/0.666667;0.628571/0.69697;0.638889/0.69697;0.621622/0.727273;0.631579/0.727273;0.615385/0.727273;0.6/0.727273;0.585366/0.727273;0.571429/0.757576;0.581395/0.757576;0.568182/0.757576;0.555556/0.757576;0.543478/0.757576;0.531915/0.787879;0.541667/0.818182;0.55102/0.818182;0.54/0.848485;0.54902/0.848485;0.538462/0.848485;0.528302/0.878788;0.537037/0.878788;0.527273/0.878788;0.517857/0.878788;0.508772/0.878788;0.5/0.878788;0.491525/0.878788;0.483333/0.909091;0.491803/0.939394;0.5/0.939394;0.492063/0.939394;0.484375/0.939394;0.476923/0.939394;0.469697/0.939394;0.462687/0.969697;0.470588/0.969697;0.463768/0.969697;0.457143/0.969697;0.450704/0.969697;0.444444/0.969697;0.438356/0.969697;0.432432/0.969697;0.426667/1;0.434211/1;0.428571/1;0.423077TPR;PPV/0;1/0.022222;1/0.044444;1/0.066667;1/0.088889;1/0.111111;1/0.133333;1/0.155556;1/0.177778;1/0.177778;0.888889/0.2;0.9/0.2;0.818182/0.222222;0.833333/0.244444;0.846154/0.266667;0.857143/0.266667;0.8/0.288889;0.8125/0.311111;0.823529/0.333333;0.833333/0.333333;0.789474/0.355556;0.8/0.377778;0.809524/0.4;0.818182/0.422222;0.826087/0.422222;0.791667/0.444444;0.8/0.466667;0.807692/0.488889;0.814815/0.511111;0.821429/0.533333;0.827586/0.555556;0.833333/0.577778;0.83871/0.577778;0.8125/0.6;0.818182/0.622222;0.823529/0.622222;0.8/0.644444;0.805556/0.644444;0.783784/0.644444;0.763158/0.666667;0.769231/0.688889;0.775/0.688889;0.756098/0.688889;0.738095/0.711111;0.744186/0.733333;0.75/0.755556;0.755556/0.777778;0.76087/0.777778;0.744681/0.777778;0.729167/0.777778;0.714286/0.8;0.72/0.8;0.705882/0.822222;0.711538/0.844444;0.716981/0.866667;0.722222/0.888889;0.727273/0.888889;0.714286/0.911111;0.719298/0.911111;0.706897/0.911111;0.694915/0.933333;0.7/0.933333;0.688525/0.955556;0.693548/0.955556;0.68254/0.955556;0.671875/0.955556;0.661538/0.977778;0.666667/0.977778;0.656716/0.977778;0.647059/0.977778;0.637681/0.977778;0.628571/0.977778;0.619718/0.977778;0.611111/0.977778;0.60274/0.977778;0.594595/1;0.6/1;0.592105/1;0.584416/1;0.576923TPR;PPV/0;1/0.030303;1/0.060606;1/0.090909;1/0.090909;0.75/0.121212;0.8/0.151515;0.833333/0.181818;0.857143/0.212121;0.875/0.242424;0.888889/0.272727;0.9/0.30303;0.909091/0.333333;0.916667/0.333333;0.846154/0.363636;0.857143/0.393939;0.866667/0.424242;0.875/0.424242;0.823529/0.454545;0.833333/0.454545;0.789474/0.484848;0.8/0.515152;0.809524/0.515152;0.772727/0.545455;0.782609/0.545455;0.75/0.545455;0.72/0.545455;0.692308/0.545455;0.666667/0.575758;0.678571/0.575758;0.655172/0.606061;0.666667/0.636364;0.677419/0.666667;0.6875/0.666667;0.666667/0.666667;0.647059/0.666667;0.628571/0.666667;0.611111/0.69697;0.621622/0.727273;0.631579/0.727273;0.615385/0.727273;0.6/0.757576;0.609756/0.787879;0.619048/0.787879;0.604651/0.818182;0.613636/0.818182;0.6/0.818182;0.586957/0.848485;0.595745/0.848485;0.583333/0.848485;0.571429/0.848485;0.56/0.848485;0.54902/0.848485;0.538462/0.848485;0.528302/0.848485;0.518519/0.878788;0.527273/0.878788;0.517857/0.878788;0.508772/0.878788;0.5/0.878788;0.491525/0.909091;0.5/0.909091;0.491803/0.909091;0.483871/0.909091;0.47619/0.939394;0.484375/0.939394;0.476923/0.939394;0.469697/0.939394;0.462687/0.969697;0.470588/0.969697;0.463768/1;0.471429/1;0.464789/1;0.458333/1;0.452055/1;0.445946/1;0.44/1;0.434211/1;0.428571/1;0.423077TPR;PPV/0;1/0.022222;1/0.044444;1/0.066667;1/0.088889;1/0.111111;1/0.133333;1/0.155556;1/0.177778;1/0.2;1/0.222222;1/0.244444;1/0.266667;1/0.288889;1/0.311111;1/0.333333;1/0.333333;0.9375/0.355556;0.941176/0.377778;0.944444/0.4;0.947368/0.422222;0.95/0.422222;0.904762/0.444444;0.909091/0.466667;0.913043/0.488889;0.916667/0.488889;0.88/0.511111;0.884615/0.533333;0.888889/0.555556;0.892857/0.555556;0.862069/0.577778;0.866667/0.577778;0.83871/0.6;0.84375/0.622222;0.848485/0.622222;0.823529/0.644444;0.828571/0.666667;0.833333/0.666667;0.810811/0.688889;0.815789/0.688889;0.794872/0.711111;0.8/0.711111;0.780488/0.711111;0.761905/0.711111;0.744186/0.711111;0.727273/0.733333;0.733333/0.755556;0.73913/0.755556;0.723404/0.777778;0.729167/0.777778;0.714286/0.8;0.72/0.8;0.705882/0.822222;0.711538/0.822222;0.698113/0.822222;0.685185/0.822222;0.672727/0.844444;0.678571/0.866667;0.684211/0.888889;0.689655/0.911111;0.694915/0.911111;0.683333/0.911111;0.672131/0.933333;0.677419/0.933333;0.666667/0.933333;0.65625/0.955556;0.661538/0.955556;0.651515/0.955556;0.641791/0.977778;0.647059/0.977778;0.637681/1;0.642857/1;0.633803/1;0.625/1;0.616438/1;0.608108/1;0.6/1;0.592105/1;0.584416/1;0.576923TPR;PPV/0;1/0.030303;1/0.060606;1/0.090909;1/0.121212;1/0.151515;1/0.181818;1/0.212121;1/0.242424;1/0.242424;0.888889/0.272727;0.9/0.272727;0.818182/0.30303;0.833333/0.333333;0.846154/0.333333;0.785714/0.363636;0.8/0.393939;0.8125/0.393939;0.764706/0.424242;0.777778/0.454545;0.789474/0.454545;0.75/0.454545;0.714286/0.454545;0.681818/0.454545;0.652174/0.484848;0.666667/0.515152;0.68/0.545455;0.692308/0.545455;0.666667/0.575758;0.678571/0.575758;0.655172/0.606061;0.666667/0.606061;0.645161/0.636364;0.65625/0.636364;0.636364/0.636364;0.617647/0.666667;0.628571/0.69697;0.638889/0.727273;0.648649/0.757576;0.657895/0.757576;0.641026/0.787879;0.65/0.787879;0.634146/0.818182;0.642857/0.818182;0.627907/0.818182;0.613636/0.848485;0.622222/0.848485;0.608696/0.848485;0.595745/0.878788;0.604167/0.878788;0.591837/0.909091;0.6/0.909091;0.588235/0.909091;0.576923/0.909091;0.566038/0.939394;0.574074/0.939394;0.563636/0.939394;0.553571/0.939394;0.54386/0.969697;0.551724/0.969697;0.542373/0.969697;0.533333/0.969697;0.52459/0.969697;0.516129/1;0.52381/1;0.515625/1;0.507692/1;0.5/1;0.492537/1;0.485294/1;0.478261/1;0.471429/1;0.464789/1;0.458333/1;0.452055/1;0.445946/1;0.44/1;0.434211/1;0.428571/1;0.423077"; var p11 = false; var average_for_closest_points = true; var open_bmp = true; plot_curve(perf_curve_types.roc, encoded_roc, $@"c:\svm_compute\charts\{nameof(perf_curve_types.roc)}.bmp", p11, average_for_closest_points, open_bmp); plot_curve(perf_curve_types.pr, encoded_pr, $@"c:\svm_compute\charts\{nameof(perf_curve_types.pr)}.bmp", p11, average_for_closest_points, open_bmp); plot_curve(perf_curve_types.pri_from_pr, encoded_pr, $@"c:\svm_compute\charts\{nameof(perf_curve_types.pri_from_pr)}.bmp", p11, average_for_closest_points, open_bmp); p11 = true; plot_curve(perf_curve_types.roc, encoded_roc, $@"c:\svm_compute\charts\{nameof(perf_curve_types.roc)}{(p11 ? "_p11" : "")}.bmp", p11, average_for_closest_points, open_bmp); plot_curve(perf_curve_types.pr, encoded_pr, $@"c:\svm_compute\charts\{nameof(perf_curve_types.pr)}{(p11 ? "_p11" : "")}.bmp", p11, average_for_closest_points, open_bmp); plot_curve(perf_curve_types.pri_from_pr, encoded_pr, $@"c:\svm_compute\charts\{nameof(perf_curve_types.pri_from_pr)}{(p11 ? "_p11" : "")}.bmp", p11, average_for_closest_points, open_bmp); } public enum perf_curve_types : int { pr = 1, pri = 2, pri_from_pr = 3, roc = 4 } public static double scale(double value, double min, double max, double min_scale, double max_scale) { return (max_scale - min_scale) * ((value - min) / (max - min)) + min_scale; } public static double area_under_curve_trapz(List<(double x, double y)> coordinate_list) { coordinate_list = coordinate_list.Distinct().ToList(); coordinate_list = coordinate_list.OrderBy(a => a.x).ThenBy(a => a.y).ToList(); var auc = coordinate_list.Select((c, i) => i >= coordinate_list.Count - 1 ? 0 : (coordinate_list[i + 1].x - coordinate_list[i].x) * ((coordinate_list[i].y + coordinate_list[i + 1].y) / 2)).Sum(); return auc; } public static void plot_curve(perf_curve_types perf_curve_type, string encoded_plot_data, string save_filename, bool convert_to_p11 = false, bool average_for_closest_points = true, bool open_bmp = true) { // the data is in the format of x_title;y_title/x;y/x;y/x;y/... // each line represents a different curve. usually from outer-cross-validation, there would be 5 or 10 curves (although it could be any number >= 2). if the input the average value from cross-validation, there would only be one curve. // note, it is not necessary that the number of values is equal per curve. however, they should all be x,y values of either ROC or PR curves - other types of curves are not necessarily supported as there is code here specific to ROC and PR. if (perf_curve_type == 0) { throw new ArgumentException("Invalid performance curve type specified.", nameof(perf_curve_type)); } if (string.IsNullOrWhiteSpace(save_filename)) { throw new ArgumentException("BMP filename required to save curve.", nameof(save_filename)); } if (string.IsNullOrWhiteSpace(encoded_plot_data)) { throw new ArgumentException("Plot x,y coordinate data is missing.", nameof(encoded_plot_data)); } List<(string x_title, string y_title, List<(double x, double y)> xy)> parsed_xy = encoded_plot_data.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .Select(line => { var headers = line.Trim().Split('/').First().Split(';'); var r = (x_title: headers[0], y_title: headers[1], xy: line.Trim().Split('/').Skip(1).Select(b => (x: double.Parse(""+ b.Split(';')[0]), y: double.Parse(""+ b.Split(';')[1]))).ToList()); r.xy = r.xy.OrderBy(a => a.x).ThenBy(a => perf_curve_type == perf_curve_types.roc ? a.y : 1 - a.y).ToList(); return r; }).ToList(); if (convert_to_p11) { // convert the x,y coordinate to the 11 point thresholds (useful for comparison of different classification models, where thresholds are unlikely to be meaningful) var points11 = new[] { 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 }; for (var index = 0; index < parsed_xy.Count; index++) { if (perf_curve_type == perf_curve_types.roc) { parsed_xy[index] = (parsed_xy[index].x_title, parsed_xy[index].y_title, points11.Select(t => (t, parsed_xy[index].xy.Where(a => a.x <= t).Max(a => a.y))).ToList()); } else { parsed_xy[index] = (parsed_xy[index].x_title, parsed_xy[index].y_title, points11.Select(t => (t, parsed_xy[index].xy.Where(a => a.x >= t).Max(a => a.y))).ToList()); } } } if (perf_curve_type == perf_curve_types.pri_from_pr) { // interpolate the x,y values (also for comparison of classifiation models) var last_y = 0d; parsed_xy = parsed_xy.Select(a => (x_title: a.x_title, y_title: a.y_title, xy: a.xy.Select((b, i) => { //x = TPR; y = PPV var max_ppv = a.xy.Where(c => c.x >= b.x).Max(c => c.y); if (double.IsNaN(max_ppv)) max_ppv = last_y; last_y = max_ppv; return (x: b.x, y: max_ppv); }).ToList())).ToList(); } (string x_title, string y_title, List<(double x, double y)> xy) average_xy2 = (parsed_xy.First().x_title, parsed_xy.First().y_title, new List<(double x, double y)>()); if (average_for_closest_points) { // make average curve for all outer-cv curves var all_xy = parsed_xy.SelectMany(a => a.xy).OrderBy(a => a.x).ThenBy(a => perf_curve_type == perf_curve_types.roc ? a.y : 1 - a.y).Distinct().ToList(); var closest_to_all_xy = all_xy.Select(z => parsed_xy.Select(a => a.xy.OrderBy(b => Math.Abs(b.x - z.x) + Math.Abs(b.y - z.y)).First()).ToList()).ToList(); average_xy2.xy = closest_to_all_xy.Select(a => (x: a.Average(b => b.x), y: a.Average(b => b.y))).Distinct().OrderBy(a => a.x).ThenBy(a => perf_curve_type == perf_curve_types.roc ? a.y : 1 - a.y).ToList(); average_xy2.xy = average_xy2.xy.Distinct().OrderBy(a => a.x).ThenBy(a => perf_curve_type == perf_curve_types.roc ? a.y : 1 - a.y).ToList(); } List<(string x_title, string y_title, List<(double x, double y)> xy)> data = parsed_xy; var average_curves = 0; if (average_xy2.xy != null && average_xy2.xy.Count > 0) { data.Add(average_xy2); average_curves++; } var graph_title = perf_curve_type.ToString().Replace("_", "").ToUpperInvariant() +""+ string.Join("", string.Join("|", data.Select(a => a.x_title +"/"+ a.y_title).Distinct().ToList()), "Curve"); var axis_x_title = string.Join("", string.Join("|", data.Select(a => a.x_title).Distinct().ToList()), "(X)"); var axis_y_title = string.Join("", string.Join("|", data.Select(a => a.y_title).Distinct().ToList()), "(Y)"); var width_x = 2000; // could be specified as a parameter var height_y = 2000; // could be specified as a parameter var bg_color = Color.Transparent; var fg_color = Color.White; // not in use - using transparent instead var grid_color = Color.LightSlateGray; var title_color = Color.White; var axis_start_color = Color.Blue; var axis_end_color = Color.Red; var bmp = new Bitmap(width_x, height_y); var graph_width_x = (double)bmp.Width * 0.8; // could be specified as a parameter var graph_height_y = (double)bmp.Height * 0.8; // could be specified as a parameter var graph_start_x = ((double)width_x - (double)graph_width_x) / (double)2; var graph_end_x = (double)graph_start_x + (double)graph_width_x; var graph_start_y = ((double)height_y - (double)graph_height_y) / (double)2; var graph_end_y = (double)graph_start_y + (double)graph_height_y; program.WriteLine($@"{nameof(graph_start_x)}={graph_start_x}, {nameof(graph_start_y)}={graph_start_y}, {nameof(graph_end_x)}={graph_end_x}, {nameof(graph_end_y)}={graph_end_y}"); using (var bmp_gfx = Graphics.FromImage(bmp)) { ///////////////////////////////////////////////////////// // set bg colour, and other gfx parameters bmp_gfx.Clear(bg_color); bmp_gfx.SmoothingMode = SmoothingMode.AntiAlias; bmp_gfx.InterpolationMode = InterpolationMode.HighQualityBicubic; bmp_gfx.PixelOffsetMode = PixelOffsetMode.HighQuality; bmp_gfx.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; StringFormat format = new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center, }; ///////////////////////////////////////////////////////// // draw title { var title_font = new Font("Tahoma", (int)graph_start_y / 4, GraphicsUnit.Pixel); var title_brush = new SolidBrush(title_color); bmp_gfx.DrawString(graph_title, title_font, title_brush, new RectangleF(0, 0, bmp.Width, (int)graph_start_y), format); } ///////////////////////////////////////////////////////// // draw diagonal graph grid lines { bmp_gfx.DrawLine(new Pen(grid_color, 2), (float)graph_start_x, (float)graph_start_y, (float)graph_end_x, (float)graph_end_y); bmp_gfx.DrawLine(new Pen(grid_color, 2), (float)graph_start_x, (float)graph_end_y, (float)graph_end_x, (float)graph_start_y); } ///////////////////////////////////////////////////////// // draw x axis title { var axis_x_font = new Font("Tahoma", (int)((height_y - graph_end_y) / 4), GraphicsUnit.Pixel); var axis_x_title_size = bmp_gfx.MeasureString(axis_x_title, axis_x_font); var axis_x_title_start_x = (float)(((double)width_x / (double)2) - ((double)axis_x_title_size.Width / (double)2)); var axis_x_title_start_y = (float)((double)height_y - ((double)axis_x_title_size.Height)); var axis_x_brush = new LinearGradientBrush(new RectangleF(axis_x_title_start_x, axis_x_title_start_y, axis_x_title_size.Width, axis_x_title_size.Height), axis_start_color, axis_end_color, LinearGradientMode.Horizontal); bmp_gfx.DrawString(axis_x_title, axis_x_font, axis_x_brush, axis_x_title_start_x, axis_x_title_start_y); } ///////////////////////////////////////////////////////// // draw y axis title { var axis_y_font = new Font("Tahoma", (int)graph_start_x / 4, GraphicsUnit.Pixel); var axis_y_title_size = bmp_gfx.MeasureString(axis_y_title, axis_y_font); //, new StringFormat(StringFormatFlags.DirectionVertical)); var axis_y_title_start_x = (float)0; // (axis_y_title_size.Height/2); var axis_y_title_start_y = (float)((bmp.Height / 2) + (axis_y_title_size.Width / 2)); var axis_y_title_end_x = axis_y_title_start_x + axis_y_title_size.Height; var axis_y_title_end_y = axis_y_title_start_y + axis_y_title_size.Width; bmp_gfx.TranslateTransform(axis_y_title_start_x, axis_y_title_start_y); bmp_gfx.RotateTransform(90 * 3); var axis_y_brush = new LinearGradientBrush(new RectangleF(0, 0, axis_y_title_size.Width, axis_y_title_size.Height), axis_start_color, axis_end_color, LinearGradientMode.Horizontal); bmp_gfx.DrawString(axis_y_title, axis_y_font, (Brush)axis_y_brush, 0, 0); bmp_gfx.ResetTransform(); } ///////////////////////////////////////////////////////// // draw grid lines horizonal (x changes, y in min to max) { var ticks_axis_x_brush = new LinearGradientBrush(new Rectangle((int)0, (int)0, (int)bmp.Width, bmp.Height), axis_start_color, axis_end_color, LinearGradientMode.Horizontal); var ticks_axis_x_font = new Font("Tahoma", (int)((height_y - graph_end_y) / 4), GraphicsUnit.Pixel); for (var x = 0.0; x <= 1.0; x += 0.1) { var x1 = scale(x, 0.0, 1.0, graph_start_x, graph_end_x); var x2 = scale(x, 0.0, 1.0, graph_start_x, graph_end_x); var y1 = scale(0.0, 0.0, 1.0, graph_start_y, graph_end_y); var y2 = scale(1.0, 0.0, 1.0, graph_start_y, graph_end_y); program.WriteLine($@"{nameof(x1)}={x1}, {nameof(y1)}={y1}, {nameof(x2)}={x2}, {nameof(y2)}={y2}"); bmp_gfx.DrawLine(new Pen(grid_color, 2), (float)x1, (float)y1, (float)x2, (float)y2); var tick_text_size = bmp_gfx.MeasureString(x.ToString("0.0"), ticks_axis_x_font); // swap width and height, since it is rotated to side bmp_gfx.TranslateTransform((float)x1 - (tick_text_size.Height / 2), (float)(graph_end_y + (tick_text_size.Width * 1.1))); bmp_gfx.RotateTransform(90 * 3); bmp_gfx.DrawString(x.ToString("0.0"), ticks_axis_x_font, ticks_axis_x_brush, 0, 0); // tick_text_size.Height);//, new StringFormat(StringFormatFlags.DirectionVertical)); bmp_gfx.ResetTransform(); } } ///////////////////////////////////////////////////////// // draw grid lines vertical (y changes, x is min to max) { var ticks_axis_y_brush = new LinearGradientBrush(new Rectangle((int)0, (int)0, (int)bmp.Width, bmp.Height), axis_start_color, axis_end_color, LinearGradientMode.Horizontal); var ticks_axis_y_font = new Font("Tahoma", (int)((graph_start_x) / 4), GraphicsUnit.Pixel); for (var y = 0.0; y <= 1.0; y += 0.1) { var x1 = scale(0.0, 0.0, 1.0, graph_start_x, graph_end_x); var x2 = scale(1.0, 0.0, 1.0, graph_start_x, graph_end_x); var y1 = scale(y, 0.0, 1.0, graph_start_y, graph_end_y); var y2 = scale(y, 0.0, 1.0, graph_start_y, graph_end_y); program.WriteLine($@"{nameof(x1)}={x1}, {nameof(y1)}={y1}, {nameof(x2)}={x2}, {nameof(y2)}={y2}"); bmp_gfx.DrawLine(new Pen(grid_color, 2), (float)x1, (float)y1, (float)x2, (float)y2); var tick_text_size = bmp_gfx.MeasureString((1 - y).ToString("0.0"), ticks_axis_y_font); bmp_gfx.DrawString((1 - y).ToString("0.0"), ticks_axis_y_font, ticks_axis_y_brush, (float)(graph_start_x - (tick_text_size.Width * 1.0)), (float)(y2 - (tick_text_size.Height / 2))); } } ///////////////////////////////////////////////////////// // draw data var color_rnd = new Random(2); for (var i = 0; i < data.Count; i++) { var is_average_curve = i >= data.Count - average_curves; var c = is_average_curve ? Color.White : Color.FromArgb(color_rnd.Next(0, 255), color_rnd.Next(0, 255), color_rnd.Next(0, 255)); var auc = area_under_curve_trapz(data[i].xy); auc = Math.Round(auc, 2); var auc_str = "AUC "+ auc.ToString("0.00"); var auc_str_font = new Font("Tahoma", (int)((graph_start_x) / 4), GraphicsUnit.Pixel); var auc_str_size = bmp_gfx.MeasureString(auc_str, auc_str_font); bmp_gfx.DrawString(auc_str, auc_str_font, new SolidBrush(c), (int)(width_x - auc_str_size.Width), (int)(graph_start_y + (auc_str_size.Height * i))); for (var a = 1; a < data[i].xy.Count; a++) { var xy1 = data[i].xy[a - 1]; var xy2 = data[i].xy[a]; var x1 = scale(xy1.x, 0.0, 1.0, graph_start_x, graph_end_x); var y1 = scale((1 - xy1.y), 0.0, 1.0, graph_start_y, graph_end_y); var x2 = scale(xy2.x, 0.0, 1.0, graph_start_x, graph_end_x); var y2 = scale((1 - xy2.y), 0.0, 1.0, graph_start_y, graph_end_y); var point_size = 20; var pen_size = 4; bmp_gfx.DrawLine(new Pen(c, pen_size), (float)x1, (float)y1, (float)x2, (float)y2); if (a == 1) { bmp_gfx.DrawEllipse(new Pen(c, pen_size), (int)x1 - (point_size / 2), (int)y1 - (point_size / 2), point_size, point_size); } bmp_gfx.DrawEllipse(new Pen(c, pen_size), (int)x2 - (point_size / 2), (int)y2 - (point_size / 2), point_size, point_size); } } } Directory.CreateDirectory(Path.GetDirectoryName(save_filename)); bmp.Save(save_filename, ImageFormat.Bmp); if (open_bmp) { Process.Start(save_filename); } } }}