解决Matplotlib股票图表非交易时段时间间隔空白问题
解决股票Streamlit仪表盘图表非交易时段空白连线问题
我正在搭建股票市场的Streamlit仪表盘,股票数据仅包含每日9:00至16:00的交易时段数据。用mdates设置X轴刻度后,图表出现16:00到次日9:00的空白连线(非交易时段无数据却被强制连线),尝试过滤数据无效,附上代码和示例截图,求解决方法。
def create_plot(name:str,label:str,title_info:str,data,period_var:str): fig, ax1 = plt.subplots(figsize=(16,8)) ax1.set_ylabel(name) plt.xlabel('Datetime') plt.ylabel(label) plt.title(title_info) if period_var in ["1d"]: date_formatter = DateFormatter('%H:%M') ax1.xaxis.set_major_formatter(date_formatter) ax1.xaxis.set_major_locator(mdates.MinuteLocator(interval=30)) elif period_var in ["3d","5d"]: date_formatter = DateFormatter("%d-%H") ax1.xaxis.set_major_formatter(date_formatter) ax1.xaxis.set_major_locator(mdates.HourLocator(byhour=range(9,16),interval=2)) elif period_var in ["1mo", "3mo", "6mo"]: date_formatter = DateFormatter("%Y - %b") ax1.xaxis.set_major_formatter(date_formatter) ax1.xaxis.set_major_locator(mdates.DayLocator(interval=int(period_var[0])*2)) elif period_var in ["1y", "2y", "5y"]: date_formatter = DateFormatter("%Y-%b") ax1.xaxis.set_major_formatter(date_formatter) ax1.xaxis.set_major_locator(mdates.MonthLocator(interval=int(period_var[0]))) else: date_formatter = DateFormatter("%Y") ax1.xaxis.set_major_formatter(date_formatter) ax1.xaxis.set_major_locator(mdates.YearLocator()) ax1.plot(data.Date, data[name], label=name, marker='o', markersize=1) plt.xticks(rotation=45) plt.legend() plt.grid(True) plt.tight_layout() return fig

核心问题
Matplotlib的plot函数默认按时间序列连续绘制,哪怕相邻数据点间隔十几个小时(非交易时段),也会直接连线,导致出现跨时段的空白线条。
解决方案
在每日收盘(16:00)和次日开盘(9:00)之间插入NaN值,让Matplotlib自动断开这些位置的连线,同时保留时间轴的刻度逻辑。
修改后的完整代码
import pandas as pd import numpy as np import matplotlib.pyplot as plt import matplotlib.dates as mdates from matplotlib.dates import DateFormatter def create_plot(name:str,label:str,title_info:str,data,period_var:str): # 处理非交易时段断开连线:在每日收盘后插入NaN占位行 data['Date'] = pd.to_datetime(data['Date']) # 按日期分组,获取每个交易日的最后一条数据索引 daily_last_indices = data.groupby(data['Date'].dt.date).tail(1).index # 创建NaN行(仅需保证Date和目标列结构匹配,其他列按需补充) nan_rows = pd.DataFrame({ 'Date': data.loc[daily_last_indices, 'Date'] + pd.Timedelta(hours=1), name: np.nan }) # 合并原数据与NaN行,重新排序后绘图 combined_data = pd.concat([data, nan_rows]).sort_values('Date').reset_index(drop=True) fig, ax1 = plt.subplots(figsize=(16,8)) ax1.set_ylabel(name) plt.xlabel('Datetime') plt.ylabel(label) plt.title(title_info) if period_var in ["1d"]: date_formatter = DateFormatter('%H:%M') ax1.xaxis.set_major_formatter(date_formatter) ax1.xaxis.set_major_locator(mdates.MinuteLocator(interval=30)) elif period_var in ["3d","5d"]: date_formatter = DateFormatter("%d-%H") ax1.xaxis.set_major_formatter(date_formatter) ax1.xaxis.set_major_locator(mdates.HourLocator(byhour=range(9,16),interval=2)) elif period_var in ["1mo", "3mo", "6mo"]: date_formatter = DateFormatter("%Y - %b") ax1.xaxis.set_major_formatter(date_formatter) ax1.xaxis.set_major_locator(mdates.DayLocator(interval=int(period_var[0])*2)) elif period_var in ["1y", "2y", "5y"]: date_formatter = DateFormatter("%Y-%b") ax1.xaxis.set_major_formatter(date_formatter) ax1.xaxis.set_major_locator(mdates.MonthLocator(interval=int(period_var[0]))) else: date_formatter = DateFormatter("%Y") ax1.xaxis.set_major_formatter(date_formatter) ax1.xaxis.set_major_locator(mdates.YearLocator()) # 使用处理后的数据绘图 ax1.plot(combined_data.Date, combined_data[name], label=name, marker='o', markersize=1) plt.xticks(rotation=45) plt.legend() plt.grid(True) plt.tight_layout() return fig
补充说明
- 如果数据包含其他列,在创建
nan_rows时需要同步添加对应列的NaN值,保证数据结构一致 - 该方法既保留了时间轴的刻度逻辑,又能精准断开非交易时段的无效连线,比将X轴转为分类变量的方案更贴合股票时间序列的展示需求
内容的提问来源于stack exchange,提问作者msamedozmen
相关产品推荐
相关产品推荐

