使用kneed寻找L曲线肘点时遭遇TypeError错误的技术求助
问题描述
我需要寻找L曲线的肘点(elbow point),将肘点的y轴值作为阈值,删除y轴值低于该阈值的点。使用Python的kneed库实现时,大部分数据正常运行,但处理少量样本数据(比如仅3个点)时抛出错误,且无法找到肘点。
我的实现函数:
def findThresholdLCurve(data:dict): if len(data) > 0: # 统计所有值并按降序排序 Occ = sorted(list(data.values()), reverse=True) x = list(range(1, len(data) + 1)) y = Occ kneedle = KneeLocator(x, y, curve="convex", direction="decreasing", online=True,interp_method="polynomial") th = kneedle.knee_y kneedle.plot_knee() return th
测试出错的数据:
data = {'apple': 0.5966618322834488, 'banana': 0.32877029629550597, 'peach': 0.5540431299774883}
错误栈:
Traceback (most recent call last): File "D:\my_study\AUDESOME_py_chatgpt\main.py", line 35, in <module> S = cal_S(O1, L) File "D:\my_study\AUDESOME_py_chatgpt\cal_S.py", line 43, in cal_S th2 = findThresholdLCurve(S[ci]) File "D:\my_study\AUDESOME_py_chatgpt\findThresholdLCurve.py", line 23, in findThresholdLCurve kneedle.plot_knee() File "D:\Anaconda\lib\site-packages\kneed\knee_locator.py", line 386, in plot_knee plt.vlines( File "D:\Anaconda\lib\site-packages\matplotlib\pyplot.py", line 3012, in vlines return gca().vlines( File "D:\Anaconda\lib\site-packages\matplotlib\__init__.py", line 1412, in inner return func(ax, *map(sanitize_sequence, args), **kwargs) File "D:\Anaconda\lib\site-packages\matplotlib\axes\_axes.py", line 1148, in vlines self.update_datalim(corners) File "D:\Anaconda\lib\site-packages\matplotlib\axes\_base.py", line 2481, in update_datalim if not np.any(np.isfinite(xys)): TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
解决方案
1. 修复kneed库的报错问题
报错核心原因:当数据量过小时,kneed无法检测到有效肘点,导致kneedle.knee_y为None,调用plot_knee()时,matplotlib尝试对None值绘制竖线,触发类型错误。
修改函数,增加肘点存在性判断,同时处理无肘点的边界情况:
def findThresholdLCurve(data:dict): if len(data) > 0: Occ = sorted(list(data.values()), reverse=True) x = list(range(1, len(data) + 1)) y = Occ kneedle = KneeLocator(x, y, curve="convex", direction="decreasing", online=True, interp_method="polynomial") th = kneedle.knee_y # 仅找到肘点时才执行绘图 if th is not None: kneedle.plot_knee() # 无肘点时自定义阈值逻辑,示例取次小值或最小值 else: th = Occ[-2] if len(Occ)>=2 else Occ[0] if Occ else None return th
2. 优化kneed检测参数适配小数据集
小数据集下默认参数灵敏度不足,可调整以下参数提升检测能力:
- 降低
S参数(灵敏度):默认1.0,调至0.5可让算法更容易识别拐点 - 更换插值方法:将
interp_method="polynomial"改为interp_method="linear",线性插值对小数据更稳定
修改后的初始化代码:
kneedle = KneeLocator(x, y, curve="convex", direction="decreasing", online=True, interp_method="linear", S=0.5)
3. 替代方法:手动计算肘点
如果kneed库仍不适用于场景,可采用以下两种手动计算方式:
方法一:最大距离法(点到直线的最大垂直距离)
计算曲线上每个点到首尾点连线的垂直距离,取距离最大的点作为肘点:
import numpy as np def find_elbow_max_distance(y): y = np.array(y) # 归一化数据消除量纲影响 y_norm = (y - y.min()) / (y.max() - y.min()) x_norm = np.arange(len(y_norm)) / (len(y_norm)-1) # 计算首尾点连线的向量 start_vec = np.array([x_norm[0], y_norm[0]]) end_vec = np.array([x_norm[-1], y_norm[-1]]) line_vec = end_vec - start_vec # 计算每个点到直线的垂直距离 distances = [] for x, y_val in zip(x_norm, y_norm): point_vec = np.array([x, y_val]) - start_vec cross_prod = np.cross(line_vec, point_vec) distance = np.abs(cross_prod) / np.linalg.norm(line_vec) distances.append(distance) # 距离最大的点即为肘点 elbow_idx = np.argmax(distances) return y[elbow_idx]
方法二:二阶导数法
通过二阶导数的突变点识别肘点:
def find_elbow_second_deriv(y): y = np.array(y) # 计算一阶、二阶导数 first_deriv = np.diff(y) second_deriv = np.diff(first_deriv) # 取二阶导数绝对值最大的点(补回diff导致的索引偏移) elbow_idx = np.argmax(np.abs(second_deriv)) + 1 return y[elbow_idx]
内容的提问来源于stack exchange,提问作者Emma_J
相关产品推荐
相关产品推荐

