Implementing the Trapezoid rule without the formula for the curve

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?

• Just as a note, the trapezoidal rule doesn't approximate the AUC, it computes it exactly. Mar 6 '21 at 15:53
• Can you see what I've done incorrectly? Mar 6 '21 at 21:52
• 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... Mar 9 '21 at 7:37
• 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 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).

• "Using thresholds at 0.01 spacing is a little unusual too" is such an understatement... Mar 9 '21 at 7:38
• 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? Mar 9 '21 at 18:01