如何在python中使用插值绘制精确召回曲线?

Posted

技术标签:

【中文标题】如何在python中使用插值绘制精确召回曲线?【英文标题】:How to draw a precision-recall curve with interpolation in python? 【发布时间】:2017-02-11 17:07:48 【问题描述】:

我使用sklearnprecision_recall_curvefunction 和matplotlib 包绘制了一条精确召回曲线。对于熟悉精确召回曲线的人来说,您知道一些科学界仅在其插值时才接受它,类似于此示例here。现在我的问题是你们中是否有人知道如何在 python 中进行插值?我一直在寻找解决方案一段时间,但没有成功!任何帮助将不胜感激。

解决方案:@francis 和 @ali_m 的两个解决方案都是正确的,共同解决了我的问题。因此,假设您从 sklearn 中的 precision_recall_curve 函数获得输出,这是我绘制图表时所做的:

precision["micro"], recall["micro"], _ = precision_recall_curve(y_test.ravel(),scores.ravel())
pr = copy.deepcopy(precision[0])
rec = copy.deepcopy(recall[0])
prInv = np.fliplr([pr])[0]
recInv = np.fliplr([rec])[0]
j = rec.shape[0]-2
while j>=0:
    if prInv[j+1]>prInv[j]:
        prInv[j]=prInv[j+1]
    j=j-1
decreasing_max_precision = np.maximum.accumulate(prInv[::-1])[::-1]
plt.plot(recInv, decreasing_max_precision, marker= markers[mcounter], label=methodNames[countOfMethods]+': AUC=0:0.2f'.format(average_precision[0]))

如果您将这些线放入 for 循环并在每次迭代时将每个方法的数据传递给它,这些线将绘制插值曲线。请注意,这不会绘制非插值精度召回曲线。

【问题讨论】:

不要在问题中提出解决方案。如果您认为应该共享信息,请为此写一个答案。 【参考方案1】:

@francis 的解决方案可以使用np.maximum.accumulate 进行矢量化。

import numpy as np
import matplotlib.pyplot as plt

recall = np.linspace(0.0, 1.0, num=42)
precision = np.random.rand(42)*(1.-recall)

# take a running maximum over the reversed vector of precision values, reverse the
# result to match the order of the recall vector
decreasing_max_precision = np.maximum.accumulate(precision[::-1])[::-1]

您还可以使用plt.step 摆脱用于绘图的for 循环:

fig, ax = plt.subplots(1, 1)
ax.hold(True)
ax.plot(recall, precision, '--b')
ax.step(recall, decreasing_max_precision, '-r')

【讨论】:

您的解决方案帮助我将@francis 的代码改编为precision_recall_curve 函数的输出。我写了解决方案。 +1,谢谢。【参考方案2】:

可以执行向后迭代以删除precision 中增加的部分。然后,可以按照 Bennett Brown 对vertical & horizontal lines in matplotlib 的回答中指定的方式绘制垂直线和水平线。

这是一个示例代码:

import numpy as np
import matplotlib.pyplot as plt

#just a dummy sample
recall=np.linspace(0.0,1.0,num=42)
precision=np.random.rand(42)*(1.-recall)
precision2=precision.copy()
i=recall.shape[0]-2

# interpolation...
while i>=0:
    if precision[i+1]>precision[i]:
        precision[i]=precision[i+1]
    i=i-1

# plotting...
fig, ax = plt.subplots()
for i in range(recall.shape[0]-1):
    ax.plot((recall[i],recall[i]),(precision[i],precision[i+1]),'k-',label='',color='red') #vertical
    ax.plot((recall[i],recall[i+1]),(precision[i+1],precision[i+1]),'k-',label='',color='red') #horizontal

ax.plot(recall,precision2,'k--',color='blue')
#ax.legend()
ax.set_xlabel("recall")
ax.set_ylabel("precision")
plt.savefig('fig.jpg')
fig.show()

这是一个结果:

【讨论】:

感谢您的回答。我还在sklearn中编写了适应precision_recall_curve输出的解决方案。

以上是关于如何在python中使用插值绘制精确召回曲线?的主要内容,如果未能解决你的问题,请参考以下文章

sklearn如何在精确召回曲线中选择阈值步骤?

Python深度学习之路-3.2PR曲线

Python深度学习之路-3.2PR曲线

Python深度学习之路-3.2PR曲线

为啥我的精确召回和 ROC 曲线不平滑?

机器学习逻辑回归分类评估方法