在利用python的matplotlib.pyplot绘制的散点图中,我们可以将鼠标移动到任意位置,图像中会自动显示当前鼠标所在带你的坐标,但是我们无法准确的获取到散点图中某一个数据点的准确坐标。本文将介绍一种基于欧氏距离的获取散点图中鼠标选中点的准确数据坐标的方法,并将介绍距离阈值的确定方法。本方法属笔者在解决实际问题的过程中探索结果,如有不妥,请大神指正!
(1)PyCharm Edition(下载地址:http://www.jetbrains.com/pycharm/)
(2)matplotlib(下载地址:https://pypi.org/project/matplotlib/)
(1)先获取鼠标点击的位置坐标,计算该位置坐标跟散点图中所有数据点的欧式距离,找到距离的最小值对应的数据点即为鼠标选中的点。但是在这个过程中可能会出现鼠标点击空白处依然有选中数据点的情况,为了解决这个问题,我们需要先设计距离阈值(一般为数据的直径长度),当最小距离小于阈值,则采纳该点,否则放弃采纳,即本次点击不合法。
(2)源代码
def get_inflectionPoint_button0(self):
'''
获取拐点
:return:
'''
if self.mainWindow_lineEdit0_text == "":
QMessageBox.warning(self, "警告!", "请选择源文件!", QMessageBox.Cancel)
return
if self.mainWindow_lineEdit1_text == "":
QMessageBox.warning(self, "警告!", "请提取数据!", QMessageBox.Cancel)
return
if len(path_separate_line_file_name) <= 0:
QMessageBox.warning(self, "警告!", "请提取数据!", QMessageBox.Cancel)
return
if len(path_separate_line_file_name) > 0:
data_list_SJ = pd.read_excel(path_separate_line_file_name)['Time']
data_list_YB = pd.read_excel(path_separate_line_file_name)['Ea - Axial Strain']
data_list_YL = pd.read_excel(path_separate_line_file_name)['Sd - Deviator Stress']
data_list_Axial_Load = pd.read_excel(path_separate_line_file_name)['Axial Load']
data_list_Axial_Def1 = pd.read_excel(path_separate_line_file_name)['Axial Def. 1']
data_list_Axial_Def2 = pd.read_excel(path_separate_line_file_name)['Axial def.2']
data_list_circumferce_def = pd.read_excel(path_separate_line_file_name)['circumferce def ']
data_list_Avg_Axial_Def = pd.read_excel(path_separate_line_file_name)['Avg. Axial Def.']
data_list_YBC = []
for i in range(len(data_list_YB)):
if i == 0:
data_list_YBC.append(0)
else:
data_list_YBC.append(float(data_list_YB[i] - data_list_YB[i - 1]))
# 删除第一行元素
data_list_SJ = np.array(data_list_SJ[1:])
data_list_YBC = np.array(data_list_YBC[1:])
# 绘图
plt.close('all')
mpl.rcParams['font.sans-serif']=['SimHei'] #指定默认字体 SimHei为黑体
mpl.rcParams['axes.unicode_minus']=False #用来正常显示负号
plt.scatter(data_list_SJ,data_list_YBC,s=20, c="#ff1212", marker='.')
plt.xlim(0,)
plt.ylim(0,)
plt.xlabel(u"时间(s)") #X轴标签
plt.ylabel("轴向应变差(%)") #Y轴标签
# plt.title("Sd - Deviator Stress(%)") #标题
clicked_point = plt.ginput(1)
print("clicked", clicked_point)
plt.show()
if len(clicked_point) == 0:
QMessageBox.warning(self, "警告!", "请在坐标系内选点!", QMessageBox.Cancel)
return
distance_list = []
for i in range(len(data_list_SJ)):
distance_list.append(self.compute_distance([clicked_point[0][0],clicked_point[0][1] * 25000],[data_list_SJ[i],data_list_YBC[i] * 25000]))
print(float(min(distance_list)))
if float(min(distance_list)) > 200.0:
QMessageBox.warning(self, "警告!", "您选择的点距离真实数据太远!", QMessageBox.Cancel)
return
min_distance_index = distance_list.index(min(distance_list))
global inflectionPoint_index1
inflectionPoint_index1 = min_distance_index + 1
slope_SJ = data_list_SJ[min_distance_index]
slope_YBC = '%.4f'%data_list_YBC[min_distance_index]
label_text = str(slope_SJ) + ':' + str(slope_YBC)
self.lineEdit0.setText(label_text)
self.lineEdit5_0_0.setText(str(data_list_Axial_Load[inflectionPoint_index1]))
self.lineEdit5_0_1.setText(str(data_list_Axial_Def1[inflectionPoint_index1]))
self.lineEdit5_0_2.setText(str(data_list_Axial_Def2[inflectionPoint_index1]))
self.lineEdit5_0_3.setText(str(data_list_circumferce_def[inflectionPoint_index1]))
self.lineEdit5_0_4.setText(str(data_list_Avg_Axial_Def[inflectionPoint_index1]))
# print(data_list_YL)
data_list_YL = np.array(data_list_YL).tolist()
data_list_SJ = np.array(data_list_SJ).tolist()
# print(data_list_YL)
# print(data_list_SJ.index(slope_SJ))
global slope_YL1
slope_YL1 = data_list_YL[data_list_SJ.index(slope_SJ) + 1]
# print(slope_YL1)
else:
return
(3)实验结果
在鼠标点击获取数据点坐标的这个过程中,得到鼠标点击位置的坐标是关键,另外,关于距离阈值的确定,有时候可能需要经验判断,该值不能太小,否则会造成选点困难的问题,也不能太大,否则会造成点击空白依然有效的情况(笔者建议把该值确定为数据点的直径大小)。