如何让matplotlib的ax.hlines延伸至曲线交点,将图表等分为10份?
解决Matplotlib日期轴图表的等分水平线绘制问题
我需要将图表的Y轴等分为10份,让每条水平线从X轴起点延伸至与曲线的交点。目前绘制的线条杂乱,尝试通过查找最接近目标值的日期定位交点但效果不佳,未找到适配日期轴场景的解决方案。
原代码如下:
import matplotlib.pyplot as plt import matplotlib.dates as mdates import datetime as dt import csv dates = [] Y = [] date = 0 with open('data.txt', 'r') as datafile: plotting = csv.reader(datafile, delimiter=',') for ROWS in datafile.readlines(): dates.append(ROWS.split(' ')[0]) Y.append(int(ROWS.split(' ')[1])) date = ROWS.split(' ')[0] date = dt.datetime.strptime(date, "%d.%m.%Y") date = date.year date = str(date) fig, ax = plt.subplots() X = [dt.datetime.strptime(d,'%d.%m.%Y').date() for d in dates] plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%d.%m.%Y')) plt.gca().xaxis.set_major_locator(mdates.DayLocator()) plt.plot(X, Y, "-o") plt.xlim(min(X), max(X)) # plt.ylim(min(Y), max(Y)) y_min = min(Y) y_max = max(Y) step = 0 res = y_max old_step = (y_max - y_min) / 9 new_date = dt.datetime.strptime('31.05.2007', '%d.%m.%Y') with open('data.txt', 'r') as datafile: for i in range(10): k = 0 res = res - step max_date = min(Y, key=lambda x: abs(res - abs(x))) for j in Y: if Y[k] == max_date: new_date = X[k] k += 1 ax.hlines(res, min(X), new_date, color='black', linestyle='--') if res == y_max: step = old_step plt.gcf().autofmt_xdate() ax.grid() plt.title('График спада паводка '+date+' г.') plt.xlabel('T') plt.ylabel('Q м³/c') plt.show()
原代码问题分析
- 查找最接近目标Y值的逻辑错误:
abs(res - abs(x))多了一层绝对值,导致比较逻辑混乱 - 匹配Y值时只取第一个对应X点,未考虑曲线中同一Y值可能有多个点,应该取最右侧的交点(适配退水曲线从高到低的趋势)
- 重复读取文件,浪费资源,X/Y已加载到内存无需二次读取
- 等分步进的初始化逻辑复杂且易出错
修正后的代码
import matplotlib.pyplot as plt import matplotlib.dates as mdates import datetime as dt # 加载数据 dates = [] Y = [] with open('data.txt', 'r') as datafile: for row in datafile.readlines(): date_str, y_val = row.strip().split(' ') dates.append(date_str) Y.append(int(y_val)) # 处理日期格式 X = [dt.datetime.strptime(d, '%d.%m.%Y').date() for d in dates] year = dt.datetime.strptime(dates[-1], "%d.%m.%Y").year # 创建图表 fig, ax = plt.subplots() ax.plot(X, Y, "-o") # 设置X轴日期格式 ax.xaxis.set_major_formatter(mdates.DateFormatter('%d.%m.%Y')) ax.xaxis.set_major_locator(mdates.DayLocator()) ax.set_xlim(min(X), max(X)) # 计算Y轴10个等分点 y_min = min(Y) y_max = max(Y) # 生成10个均匀分布的Y值(包括首尾) y_ticks = [y_max - i * (y_max - y_min)/9 for i in range(10)] # 绘制每条水平线到曲线交点 for y_val in y_ticks: # 找到所有Y值中最接近当前y_val的点,取最右侧的那个(适配退水曲线) diffs = [abs(y - y_val) for y in Y] min_diff = min(diffs) closest_indices = [idx for idx, d in enumerate(diffs) if d == min_diff] target_x = X[max(closest_indices)] # 绘制水平线:从X轴起点到目标交点 ax.hlines(y_val, min(X), target_x, color='black', linestyle='--') # 美化图表 fig.autofmt_xdate() ax.grid() ax.set_title(f'График спада паводка {year} г.') ax.set_xlabel('T') ax.set_ylabel('Q м³/c') plt.show()
关键改进点
- 精准匹配交点:计算每个Y值与目标等分点的差值,取差值最小且最右侧的点作为交点,确保线条延伸到曲线的正确位置
- 简化等分逻辑:直接生成10个均匀分布的Y轴刻度值,避免复杂的步进初始化
- 优化数据加载:去掉冗余的csv读取逻辑,直接拆分每行数据
- 日期处理简化:直接从最后一个日期提取年份,代码更简洁
内容的提问来源于stack exchange,提问作者losttime
相关产品推荐
相关产品推荐

