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;1
FPR;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;1
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.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;1
FPR;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;1
FPR;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;1
FPR;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;1
FPR;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;1
FPR;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.576923
TPR;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.423077
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.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.576923
TPR;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.423077
TPR;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.576923
TPR;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.423077
TPR;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.576923
TPR;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);
}
}
}
}