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

手动拆分拟合曲线为双色段出现间隙的问题及解决咨询

解决分段拟合曲线颜色区分与间隙问题

我需要将以下函数拟合到数据中:

def P(t, CP, Pan, tau):
    P_val = CP + Pan / (1 + t / tau)
    P_val = np.where(t >= 900, P_val - 0.8 * np.log(t / 900), P_val)
    return P_val

绘制图像时,希望对t<900和t>=900的曲线部分使用不同颜色。尝试分段绘制后,两段曲线间出现细小间隙;手动加入t=900后间隙消失,但曲线其他部分出现异常。现寻求间隙问题的解决方法,或实现多色连续曲线的替代方案(比如Matplotlib的LineCollection)。

以下是原始代码:

import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt

# Data
t2 = np.array([
    80,
    160,
    200,
    320,
    400,
    640,
    800,
    900,
    1000,
    1280,
    2000,
])

P_t2 = np.array([
    4.64,
    3.97,
    3.79,
    3.48,
    3.36,
    3.18,
    3.11,
    3.08,
    3.06,
    3.01,
    2.94,
])

t_min = 0 
t_cutoff = 7500  

mask = (t2 >= t_min) & (t2 <= t_cutoff)

t_filtered = t2[mask]
P_t_filtered = P_t2[mask]

def P(t, CP, Pan, tau):
    P_val = CP + Pan / (1 + t / tau)
    P_val = np.where(t >= 900, P_val - 0.8 * np.log(t / 900), P_val)
    return P_val

initial_guesses = [3.0, 4.0, 50.0]
bounds = ([1.35, 0, 0], [np.inf, np.inf, np.inf])

popt2, pcov2 = curve_fit(P, t_filtered, P_t_filtered, p0=initial_guesses, bounds=bounds, maxfev=5000)


t_fit2 = np.linspace(1, max(t2), 500)
P_fit2 = P(t_fit2, *popt2)


t_fit2_middle = t_fit2[(t_fit2 >= 80) & (t_fit2 <= 900)]
t_fit2_middle = np.append(t_fit2_middle, 900)               # include 900 explicitly

t_fit2_over = t_fit2[t_fit2 >= 900]
#t_fit2_over = np.append(t_fit2_over, 900)               # include 900 explicitly

P_fit2_middle = P(t_fit2_middle, *popt2)
P_fit2_over1 = P(t_fit2_over, *popt2)

#plotting
plt.plot(t_fit2_middle, P_fit2_middle, color='green', linewidth=2)
plt.plot(t_fit2_over, P_fit2_over1, color='red', linewidth=2)

plt.fill_between(t_fit2_middle, 0, P_fit2_middle, color='green', alpha=0.3, hatch='//')
plt.fill_between(t_fit2_over, P_fit2_over1, color='red', alpha=0.3, hatch='//')

plt.xlim(1, 1250)
plt.ylim(0, 8)
plt.minorticks_on()

ax = plt.gca()
ax.tick_params(labelbottom=False, labelleft=False)

plt.tight_layout()
plt.show()

解决方案

方法一:修复分段绘图的间隙问题

问题根源是手动追加t=900导致数组出现重复点,进而引发曲线异常。正确的做法是通过掩码拆分数组,确保两段曲线在t=900处自然衔接,无重复点:

# 先生成完整的拟合时间数组
t_fit2 = np.linspace(1, max(t2), 500)
P_fit2 = P(t_fit2, *popt2)

# 用掩码拆分两段:t <=900 和 t >=900
mask_middle = t_fit2 <= 900
mask_over = t_fit2 >= 900

t_fit2_middle = t_fit2[mask_middle]
t_fit2_over = t_fit2[mask_over]

P_fit2_middle = P_fit2[mask_middle]
P_fit2_over1 = P_fit2[mask_over]

替换原始代码中对应部分后,两段曲线会在t=900处完美衔接,既无间隙也无异常。

方法二:使用LineCollection实现单条多色连续曲线

如果希望用单条曲线实现颜色分段,LineCollection是更优雅的方案,步骤如下:

  1. 将拟合数据转换为线段坐标对
  2. 根据t值为每个线段分配颜色
  3. 创建LineCollection并添加到坐标轴

修改绘图部分代码:

import matplotlib.collections as mcollections

# 生成完整拟合数据
t_fit2 = np.linspace(1, max(t2), 500)
P_fit2 = P(t_fit2, *popt2)

# 将数据转为线段列表:每个线段是[(x1,y1), (x2,y2)]
points = np.column_stack((t_fit2, P_fit2))
segments = np.array([points[i:i+2] for i in range(len(points)-1)])

# 为线段分配颜色:t<900用绿色,否则用红色
colors = ['green' if t <900 else 'red' for t in t_fit2[:-1]]

# 创建并添加LineCollection
lc = mcollections.LineCollection(segments, colors=colors, linewidth=2)
ax = plt.gca()
ax.add_collection(lc)

# 保留填充和数据点绘制
plt.fill_between(t_fit2[t_fit2<=900], 0, P_fit2[t_fit2<=900], color='green', alpha=0.3, hatch='//')
plt.fill_between(t_fit2[t_fit2>=900], 0, P_fit2[t_fit2>=900], color='red', alpha=0.3, hatch='//')
plt.scatter(t_filtered, P_t_filtered, color='black', zorder=5)

这种方法生成的是连续的单条曲线,自动在t=900处切换颜色,彻底避免间隙问题。


完整修正代码

import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import matplotlib.collections as mcollections

# Data
t2 = np.array([
    80,
    160,
    200,
    320,
    400,
    640,
    800,
    900,
    1000,
    1280,
    2000,
])

P_t2 = np.array([
    4.64,
    3.97,
    3.79,
    3.48,
    3.36,
    3.18,
    3.11,
    3.08,
    3.06,
    3.01,
    2.94,
])

t_min = 0 
t_cutoff = 7500  

mask = (t2 >= t_min) & (t2 <= t_cutoff)
t_filtered = t2[mask]
P_t_filtered = P_t2[mask]

def P(t, CP, Pan, tau):
    P_val = CP + Pan / (1 + t / tau)
    P_val = np.where(t >= 900, P_val - 0.8 * np.log(t / 900), P_val)
    return P_val

initial_guesses = [3.0, 4.0, 50.0]
bounds = ([1.35, 0, 0], [np.inf, np.inf, np.inf])
popt2, pcov2 = curve_fit(P, t_filtered, P_t_filtered, p0=initial_guesses, bounds=bounds, maxfev=5000)

# 方法一:分段绘图(简单直观)
plt.figure()
t_fit2 = np.linspace(1, max(t2), 500)
P_fit2 = P(t_fit2, *popt2)

mask_middle = t_fit2 <= 900
mask_over = t_fit2 >= 900

t_fit2_middle = t_fit2[mask_middle]
t_fit2_over = t_fit2[mask_over]

P_fit2_middle = P_fit2[mask_middle]
P_fit2_over1 = P_fit2[mask_over]

plt.plot(t_fit2_middle, P_fit2_middle, color='green', linewidth=2)
plt.plot(t_fit2_over, P_fit2_over1, color='red', linewidth=2)

plt.fill_between(t_fit2_middle, 0, P_fit2_middle, color='green', alpha=0.3, hatch='//')
plt.fill_between(t_fit2_over, 0, P_fit2_over1, color='red', alpha=0.3, hatch='//')
plt.scatter(t_filtered, P_t_filtered, color='black', zorder=5)

plt.xlim(1, 1250)
plt.ylim(0, 8)
plt.minorticks_on()
ax = plt.gca()
ax.tick_params(labelbottom=False, labelleft=False)
plt.tight_layout()
plt.show()

# 方法二:LineCollection多色曲线
plt.figure()
t_fit2 = np.linspace(1, max(t2), 500)
P_fit2 = P(t_fit2, *popt2)

points = np.column_stack((t_fit2, P_fit2))
segments = np.array([points[i:i+2] for i in range(len(points)-1)])
colors = ['green' if t <900 else 'red' for t in t_fit2[:-1]]

lc = mcollections.LineCollection(segments, colors=colors, linewidth=2)
ax = plt.gca()
ax.add_collection(lc)

plt.fill_between(t_fit2[t_fit2<=900], 0, P_fit2[t_fit2<=900], color='green', alpha=0.3, hatch='//')
plt.fill_between(t_fit2[t_fit2>=900], 0, P_fit2[t_fit2>=900], color='red', alpha=0.3, hatch='//')
plt.scatter(t_filtered, P_t_filtered, color='black', zorder=5)

plt.xlim(1, 1250)
plt.ylim(0, 8)
plt.minorticks_on()
ax.tick_params(labelbottom=False, labelleft=False)
plt.tight_layout()
plt.show()

内容的提问来源于stack exchange,提问作者Márton Horváth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 09:38:11