Matplotlib时间戳转日期格式触发OverflowError问题求助
解决Matplotlib中Epoch秒级时间戳转日期格式的OverflowError问题
我想用Matplotlib把图表X轴的Epoch秒级时间戳转换成%Y-%m-%d %H:%M格式,按照官方文档操作后出现了OverflowError,相关报错回溯、代码和时间轴示例数据如下:
报错回溯
Traceback (most recent call last): File "/home/andreas/src/masiri/booking_algorythm/simple_booking.py", line 277, in <module> main() File "/home/andreas/src/masiri/booking_algorythm/simple_booking.py", line 273, in main accepted_list_of_bookings, accepted_bookings_a = process_booking_requests(randomized_list_of_bookings) File "/home/andreas/src/masiri/booking_algorythm/simple_booking.py", line 257, in process_booking_requests price = calculate_price_for_booking(trip_request, sequential_trip_requests, File "/home/andreas/src/masiri/booking_algorythm/simple_booking.py", line 226, in calculate_price_for_booking old_price = turn_booking_into_charging_plan(start_time, end_time, old_bookings_list) File "/home/andreas/src/masiri/booking_algorythm/simple_booking.py", line 209, in turn_booking_into_charging_plan plot_solution(sub_metric_v, soc_plot, charge_plot, discharge_plot, bookings_list, instability_ranges) File "/home/andreas/src/masiri/booking_algorythm/simple_booking.py", line 195, in plot_solution fig.autofmt_xdate() File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/matplotlib/figure.py", line 249, in autofmt_xdate for label in ax.get_xticklabels(which=which): File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/matplotlib/axes/_base.py", line 73, in wrapper return get_method(self)(*args, **kwargs) File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/matplotlib/axis.py", line 1381, in get_ticklabels return self.get_majorticklabels() File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/matplotlib/axis.py", line 1345, in get_majorticklabels self._update_ticks() File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/matplotlib/axis.py", line 1191, in _update_ticks major_labels = self.major.formatter.format_ticks(major_locs) File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/matplotlib/ticker.py", line 233, in format_ticks return [self(value, i) for i, value in enumerate(values)] File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/matplotlib/ticker.py", line 233, in <listcomp> return [self(value, i) for i, value in enumerate(values)] File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/matplotlib/dates.py", line 640, in __call__ result = num2date(x, self.tz).strftime(self.fmt) File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/matplotlib/dates.py", line 533, in num2date return _from_ordinalf_np_vectorized(x, tz).tolist() File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/numpy/lib/function_base.py", line 2328, in __call__ return self._vectorize_call(func=func, args=vargs) File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/numpy/lib/function_base.py", line 2411, in _vectorize_call outputs = ufunc(*inputs) File "/home/andreas/src/masiri/venv/lib/python3.10/site-packages/matplotlib/dates.py", line 354, in _from_ordinalf np.timedelta64(int(np.round(x * MUSECONDS_PER_DAY)), 'us')) OverflowError: int too big to convert
相关代码
def plot_solution(data, soc_plot,charge_plot, discharge_plot, bookings=None, boundaries=None): my_fmt = mdates.DateFormatter('%Y-%m-%d %H:%M') fig, ax_left = plt.subplots() ax_right = ax_left.twinx() ax_left.xaxis.set_major_formatter(my_fmt) ax_right.xaxis.set_major_formatter(my_fmt) time_axis = data["time"] ax_right.plot(time_axis, soc_plot, label="State of Charge") ax_left.plot(time_axis, data["price"], label="price (€/kWs)") ax_left.plot(time_axis, data["price_co2"], label="price including CO_2 costs (€/kWs)") #ax_right.plot(data["time"], charge_plot, label="charge") #ax_right.plot(data["time"], discharge_plot, label="discharge") bookings_flag = True if bookings is not None: for booking in bookings: if bookings_flag: ax_left.axvspan(booking["start"], booking["start"] + booking["duration"], facecolor='0.4', alpha=0.5, label="booking") bookings_flag = False else: ax_left.axvspan(booking["start"], booking["start"] + booking["duration"], facecolor='0.4', alpha=0.5, label="booking") boundaries_flag = True if boundaries is not None: for boundary in boundaries: if boundaries_flag: ax_left.axvspan(time_axis[boundary["start"]], time_axis[boundary["end"]], facecolor='0.9', alpha=0.5, label="boundary") boundaries_flag = False else: ax_left.axvspan(time_axis[boundary["start"]], time_axis[boundary["end"]], facecolor='0.9', alpha=0.5) ax_left.set_xlabel("time in seconds") ax_left.set_ylabel("price in €/kWs", color="blue") ax_right.set_ylabel("State of Charge (SoC), normalized", color="black") legend_1 = ax_left.legend(loc=2, borderaxespad=1.) legend_1.remove() ax_right.legend(loc=1, borderaxespad=1.) ax_right.add_artist(legend_1) fig.autofmt_xdate() plt.show(block=True)
时间轴示例数据
>>> time_axis array([1635905700, 1635906600, 1635907500, 1635908400, 1635909300, 1635910200, 1635911100, 1635912000, 1635912900, 1635913800,
问题原因
Matplotlib的DateFormatter默认处理的是Matplotlib内部的日期数值(以天为单位,从0001年开始计算),而你传入的是Epoch秒级时间戳(从1970年开始的秒数),数值过大导致转换时溢出。
解决方案
方法1:用epoch2num转换时间戳
将所有X轴相关的秒级时间戳,通过mdates.epoch2num()转换成Matplotlib可识别的日期数值,修改后的代码如下:
def plot_solution(data, soc_plot,charge_plot, discharge_plot, bookings=None, boundaries=None): my_fmt = mdates.DateFormatter('%Y-%m-%d %H:%M') fig, ax_left = plt.subplots() ax_right = ax_left.twinx() # 关键:把Epoch秒级时间戳转成Matplotlib日期数值 time_axis = mdates.epoch2num(data["time"]) ax_right.plot(time_axis, soc_plot, label="State of Charge") ax_left.plot(time_axis, data["price"], label="price (€/kWs)") ax_left.plot(time_axis, data["price_co2"], label="price including CO_2 costs (€/kWs)") bookings_flag = True if bookings is not None: for booking in bookings: # 同步转换booking的时间戳 start = mdates.epoch2num(booking["start"]) end = mdates.epoch2num(booking["start"] + booking["duration"]) if bookings_flag: ax_left.axvspan(start, end, facecolor='0.4', alpha=0.5, label="booking") bookings_flag = False else: ax_left.axvspan(start, end, facecolor='0.4', alpha=0.5) boundaries_flag = True if boundaries is not None: for boundary in boundaries: # 转换boundary对应的时间戳 start = mdates.epoch2num(data["time"][boundary["start"]]) end = mdates.epoch2num(data["time"][boundary["end"]]) if boundaries_flag: ax_left.axvspan(start, end, facecolor='0.9', alpha=0.5, label="boundary") boundaries_flag = False else: ax_left.axvspan(start, end, facecolor='0.9', alpha=0.5) ax_left.xaxis.set_major_formatter(my_fmt) ax_right.xaxis.set_major_formatter(my_fmt) ax_left.set_xlabel("time") ax_left.set_ylabel("price in €/kWs", color="blue") ax_right.set_ylabel("State of Charge (SoC), normalized", color="black") legend_1 = ax_left.legend(loc=2, borderaxespad=1.) legend_1.remove() ax_right.legend(loc=1, borderaxespad=1.) ax_right.add_artist(legend_1) fig.autofmt_xdate() plt.show(block=True)
方法2:转成numpy.datetime64类型
如果你的Python版本支持,也可以直接把时间戳转成numpy.datetime64类型,Matplotlib会自动识别这种格式:
# 替换time_axis的定义 time_axis = np.array(data["time"], dtype='datetime64[s]')
同时axvspan里的时间参数也要同步转成datetime64类型,比如:
start = np.datetime64(booking["start"], 's') end = np.datetime64(booking["start"] + booking["duration"], 's')
注意事项
所有X轴相关的时间参数(比如axvspan的起止值)必须统一格式,不能混合原始秒级戳和转换后的数值。
内容的提问来源于stack exchange,提问作者Andreas Schuldei
相关产品推荐
相关产品推荐

