4. 对数坐标下零值与负值导致的绘图中断
问题定位:对数轴为什么“画不出来”
对数坐标的数学定义域是 x > 0。当数据列中出现 0 或负值时,不同绘图库的处理方式并不一致:有的在坐标变换阶段直接抛出异常(如 ValueError: Data has no positive values, and therefore cannot be log-scaled),有的把非正值静默映射为 NaN 从而“丢点”,还有的只保留正值但把整条折线断开。因此排障的第一步不是急着改数据,而是先确认是报错还是丢点,再确认非正值的数量与位置。
下面用一段可复现的示例数据来演示。假设我们有一组随时间变化的浓度测量值,其中混入了 0 和负值(仪器下限截断或背景扣除后的结果):
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 示例数据:含 0 与负值
t = np.arange(1, 13)
conc = np.array([5.2, 3.8, 2.1, 0.0, 0.9, -0.4, 0.6, 0.35, 0.12, 0.0, 0.05, 0.02])
df = pd.DataFrame({"t": t, "conc": conc})
print("非正值数量:", (df["conc"] <= 0).sum())
print("非正值位置:", df.loc[df["conc"] <= 0, "t"].tolist())
# 非正值数量: 3
# 非正值位置: [4, 6, 10]
直接调用 ax.set_yscale("log") 时,Matplotlib 会忽略非正值并在控制台给出警告,折线在对应位置断开;若整列都非正,则直接报错。定位阶段应始终先打印非正值的数量、索引和原始值,而不是只看图。
三种方案的实现与选择依据
方案 A(过滤)最稳妥,前提是你能确认非正值属于无效数据。实现时用布尔掩码过滤,并保留原始索引以便追溯:
mask = df["conc"] > 0
df_pos = df[mask].copy()
ax.plot(df_pos["t"], df_pos["conc"], marker="o")
ax.set_yscale("log")
# 缺口处折线断开,符合“数据缺失”的语义
方案 B(伪对数 symlog)适合零和负值本身有物理意义(如背景扣除后的净信号)。linthresh 决定线性区宽度,应取略大于最小正值的量级,避免线性区吞掉有效数据:
ax.plot(df["t"], df["conc"], marker="o")
ax.set_yscale("symlog", linthresh=0.1)
ax.axhline(0, color="gray", lw=0.8, ls="--")
方案 C(分段坐标)在需要同时展示大正值与负值时使用,通常通过自定义 FuncScale 或双轴实现,成本最高,仅在 A、B 都不满足语义时采用。
验证:变换后数值与视觉位置是否一致
无论选哪种方案,都要做一次“数值—位置”核对,防止坐标变换悄悄改变了数据含义。对纯对数轴,取两个已知点计算其在轴上的相对位置:
import numpy as np
# 对数轴上,值 v 的位置比例 = (log10(v) - log10(ymin)) / (log10(ymax) - log10(ymin))
ymin, ymax = 0.01, 10
for v in [0.02, 0.12, 5.2]:
frac = (np.log10(v) - np.log10(ymin)) / (np.log10(ymax) - np.log10(ymin))
print(f"v={v:>5} 位置比例={frac:.3f}")
# v= 0.02 位置比例=0.101
# v= 0.12 位置比例=0.360
# v= 5.2 位置比例=0.905
把这三个比例与图中数据点的实际像素位置对照,若偏差超过 1~2 像素,说明存在额外的坐标变换或数据被意外修改。对 symlog,则要额外确认 0 恰好落在 y=0 的线性段中心,且 -0.4 与 0.4 关于零线对称。
t 做插值,产生虚假的连续折线。过滤后应显式 reset_index(drop=True) 或始终按 t 列对齐。排障检查清单
| 检查项 | 判断依据 | 处理动作 |
|---|---|---|
| 是否报错 | 控制台出现 log 相关 ValueError | 确认整列是否全为非正 |
| 非正值数量与位置 | (df[col] <= 0).sum() | 记录索引,判断是否无效数据 |
| 丢点还是断线 | 点数少于原始行数 | 检查是否被静默转 NaN |
| 方案语义匹配 | 零/负值是否有物理意义 | A / B / C 三选一 |
| 数值—位置一致性 | log10 比例 vs 像素位置 | 偏差大则排查额外变换 |