You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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()

原代码问题分析

  1. 查找最接近目标Y值的逻辑错误:abs(res - abs(x))多了一层绝对值,导致比较逻辑混乱
  2. 匹配Y值时只取第一个对应X点,未考虑曲线中同一Y值可能有多个点,应该取最右侧的交点(适配退水曲线从高到低的趋势)
  3. 重复读取文件,浪费资源,X/Y已加载到内存无需二次读取
  4. 等分步进的初始化逻辑复杂且易出错

修正后的代码

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()

关键改进点

  1. 精准匹配交点:计算每个Y值与目标等分点的差值,取差值最小且最右侧的点作为交点,确保线条延伸到曲线的正确位置
  2. 简化等分逻辑:直接生成10个均匀分布的Y轴刻度值,避免复杂的步进初始化
  3. 优化数据加载:去掉冗余的csv读取逻辑,直接拆分每行数据
  4. 日期处理简化:直接从最后一个日期提取年份,代码更简洁

内容的提问来源于stack exchange,提问作者losttime

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 13:49:54