I know that if I have some function f(x) that describes a curve, I can approximate the area under the curve using the trapezoid rule as follows:

def auc(f, a, b, n): 
    subinterval = (b - a) / n 
    s = f(a) + f(b)
    i = 1
    while i < n: 
        s += 2 * f(a + i * subinterval) 
        i += 1
    return (subinterval / 2) * s 

However, I am trying to implement the trapezoid rule to approximate the area under the ROC curve. I don't have the function f(x), but rather true positive rates and false positive rates at thresholds from 0 to 1 spaced by .01. I tried to implement the rule following this guide https://byjus.com/maths/trapezoidal-rule/ as so:

def roc_auc(tprs, fprs):
    y_sum = max(tprs) + min(tprs)
    for i in range(1, len(fprs)-1):
        y_sum += 2*tprs[i]
    interval = (max(fprs) - min(fprs)) / len(fprs)
    return ((interval / 2) * y_sum) / 100

However, when I test it against auc functions implemented in numpy, scikit, etc. I get different values than the one I calculate so I know I'm doing something wrong. Can anyone tell me where I'm going wrong?

  • 1
    $\begingroup$ Just as a note, the trapezoidal rule doesn't approximate the AUC, it computes it exactly. $\endgroup$
    – Calimo
    Mar 6 '21 at 15:53
  • $\begingroup$ Can you see what I've done incorrectly? $\endgroup$ Mar 6 '21 at 21:52
  • $\begingroup$ You're approximating the ROC curve, so quite obviously you're going to get different values than ROC implementations. It's very difficult to answer such a vague question... $\endgroup$
    – Calimo
    Mar 9 '21 at 7:37
  • $\begingroup$ Feel free to provide an explanation if there is something I'm misunderstanding. Otherwise your comments are not productive. I realize that the values will be different. However, the magnitude of the difference suggests to me that my implementation is incorrect. Someone else took the time to give an explanation instead of a petty non-answer so no need to respond further $\endgroup$ Mar 9 '21 at 17:58

You're assuming that the points are equally spaced along the fpr axis, which is generally not true. See e.g. the "Uniform grid" vs "Nonuniform grid" sections of the wikipedia article. You need something like

delta_xs = np.diff(fpr)
left_endpoints_y = tpr[:-1]
right_endpoints_y = tpr[1:]
trap_areas = 0.5 * (left_endpoints_y + right_endpoints_y) * delta_xs
area = trap_areas.sum()

(That's more verbose than it needs to be, probably not the most efficient, and I don't know what order your fpr/tpr lists are, so it'll need some finagling.)

Using thresholds at 0.01 spacing is a little unusual too: the ROC curve is represented by using every predicted probability as a threshold (together with $\pm\infty$, or a convention that $(0,0)$ and $(1,1)$ are on the curve).

  • $\begingroup$ "Using thresholds at 0.01 spacing is a little unusual too" is such an understatement... $\endgroup$
    – Calimo
    Mar 9 '21 at 7:38
  • 1
    $\begingroup$ Ahh I see thank you! Thats very good to know about the threshholds. I knew there was some other method for choosing them but didn't see an explanation for how anywhere. So in that case, I would have a threshhold for every prediction with a unique probability? $\endgroup$ Mar 9 '21 at 18:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.