datetime64作x轴绘渐变填充图报isfinite不支持类型错误
渐变填充折线图datetime64类型横轴报错问题
问题复现代码
需要实现带渐变填充效果的折线图,测试代码如下:
import numpy as np import matplotlib.pyplot as plt import matplotlib.colors as mcolors from matplotlib.patches import Polygon def main(): # float类型x可正常运行 # x = np.array([0,1,2]) # datetime64类型x触发报错 x = np.array('2015-12-23 12:51:00',dtype='datetime64[ns]') x = np.insert(x, 0, ('2015-12-24 12:51:00')) x = np.insert(x, 0, ('2015-12-25 12:51:00')) y = np.array([1,2,99]) gradient_fill(x, y) plt.show() def gradient_fill(x, y, fill_color=None, ax=None, **kwargs): """ 绘制折线,折线下方填充线性透明度渐变效果 参数说明 ---------- x, y : 类数组结构 折线对应的数据点 fill_color : matplotlib支持的颜色格式(字符串、元组)或None 填充色,若为None则默认使用折线本身的颜色 ax : matplotlib Axes实例 绘图使用的坐标轴,若为None则使用当前pyplot默认坐标轴 其余参数会直接透传给matplotlib的``plot``方法 返回值 ------- line : Line2D实例 绘制得到的折线对象 im : AxesImage实例 被裁剪到折线下方区域的透明渐变层对象 """ if ax is None: ax = plt.gca() line, = ax.plot(x, y, **kwargs) if fill_color is None: fill_color = line.get_color() zorder = line.get_zorder() alpha = line.get_alpha() alpha = 1.0 if alpha is None else alpha z = np.empty((100, 1, 4), dtype=float) rgb = mcolors.colorConverter.to_rgb(fill_color) z[:,:,:3] = rgb z[:,:,-1] = np.linspace(0, alpha, 100)[:,None] xmin, xmax, ymin, ymax = x.min(), x.max(), y.min(), y.max() im = ax.imshow(z, aspect='auto', extent=[xmin, xmax, ymin, ymax], origin='lower', zorder=zorder) xy = np.column_stack([x, y]) xy = np.vstack([[xmin, ymin], xy, [xmax, ymin], [xmin, ymin]]) clip_path = Polygon(xy, facecolor='none', edgecolor='none', closed=True) ax.add_patch(clip_path) im.set_clip_path(clip_path) ax.autoscale(True) return line, im main()
报错信息
x为float类型时代码运行正常,使用datetime64类型作为横轴数据时,抛出如下错误:
if not np.any(np.isfinite(xys)): TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
报错原因
NumPy 1.17之后确实已经支持直接对datetime64数组调用np.isfinite,当前报错和NumPy的版本修复无关,核心问题出在坐标拼接逻辑:
- 用
np.column_stack([x, y])拼接datetime64类型的x数组和float类型的y数组时,numpy无法找到兼容的通用数据类型,会把拼接后的数组降级为object类型 - Matplotlib初始化
Polygon对象时,会对传入的坐标数组调用np.isfinite做有效性校验,而np.isfinite不支持object类型数组的逐元素判断,因此抛出类型错误。
解决方法
只需要修改gradient_fill函数中构造渐变层和裁剪路径的逻辑,将datetime类型的x转换为Matplotlib内部使用的数值型日期表示即可,不需要调整绘图的主逻辑,也不需要升降级依赖版本,操作步骤如下:
- 首先在文件头部导入Matplotlib的日期转换工具:
from matplotlib.dates import date2num
- 替换
gradient_fill函数中构造imshow范围和裁剪路径的代码段,将原始datetime64的x转换为数值型日期值再参与拼接:
# 将datetime64转换为matplotlib内部使用的数值日期格式 x_num = date2num(x) xmin, xmax = x_num.min(), x_num.max() ymin, ymax = y.min(), y.max() im = ax.imshow(z, aspect='auto', extent=[xmin, xmax, ymin, ymax], origin='lower', zorder=zorder) xy = np.column_stack([x_num, y]) xy = np.vstack([[xmin, ymin], xy, [xmax, ymin], [xmin, ymin]]) clip_path = Polygon(xy, facecolor='none', edgecolor='none', closed=True)
注意:ax.plot(x, y, **kwargs)这行不需要修改,直接传入原始datetime64格式的x即可,Matplotlib会自动完成日期轴的刻度识别和格式化显示,不会影响最终的轴展示效果。
内容的提问来源于stack exchange,提问作者Freude
相关产品推荐
相关产品推荐

