我目前正在尝试使用plotly 将线图(最终具有多条迹线)绘制为图形对象。它基于时间序列股票市场价格,并且由于我只需要/拥有某些日期的数据,因此我需要根据日期或日期时间值排除 x 轴上的不相关范围。我的数据集可以这样考虑:
d = {'plotPrice': [107, 107.1, 107.2, 106.9, 107, 106.5, 106, 106.2, 106.8, 107],
'dateTime': [pd.Timestamp('2023-10-24 08:01:56.668000'), pd.Timestamp('2023-10-24 09:25:58.272'), pd.Timestamp('2023-10-26 11:42:09.199'),
pd.Timestamp('2023-10-26 14:54:06.095'),pd.Timestamp('2023-10-26 15:25:50.430'),pd.Timestamp('2023-10-30 09:15:33.546'),
pd.Timestamp('2023-10-30 11:34:44.861'),pd.Timestamp('2023-10-30 14:18:27.706'),pd.Timestamp('2023-10-30 15:02:33.771'),
pd.Timestamp('2023-10-30 16:30:37.412')]}
df = pd.DataFrame(data=d)
数据基于刻度数据,因此没有以相等大小的间隔组织,例如 1 分钟或类似的间隔,并且由于其他原因,我无法按时间间隔对其进行聚合。这意味着我需要将 xaxis 识别为
type=date
轴才能正确缩放。我当前通过绘图和应用范围中断进行绘图的函数如下所示:
def generate_plotly_line_plot(df, titlestr, ccy,saveBool=False):
# Figure and Layout
fig = go.Figure()
fig.update_layout(
autosize=False,
width=1000,
height=500,
margin=dict(
l=50,
r=50,
b=50,
t=50,
pad=4
),
title=go.layout.Title(text=titlestr),
showlegend=True,
hovermode='x unified'
)
# Format x and y axis
dateTime_date = df['dateTime'].apply(lambda x: x.date()).unique()
df_date_range = pd.date_range(df['dateTime'].min(), df['dateTime'].max(), freq='1D').to_series().reset_index(
drop=True).apply(lambda x: x.date())
exclude_dates = [x.strftime('%Y-%m-%d') for x in df_date_range if x not in dateTime_date]
fig.update_xaxes(
title_text='Date',
rangebreaks=[
dict(values=exclude_dates)
],
rangeslider_visible = True
)
# Add Traces
for plot_col in df.columns[df.columns!='dateTime']:
fig.add_trace(go.Scatter(x=df['dateTime'],
y=df[plot_col],
mode='lines',
name=plot_col,
line=dict(
width=.75
))
)
fig.update_yaxes(
title_text="Stock Price in "+ccy)
fig.show()
generate_plotly_line_plot(df=df[['plotPrice', 'dateTime']],titlestr='Test', ccy='EUR')
现在的问题是,这似乎实现了范围划分,但没有删除 x 轴上的排除区域,导致所需日期之间存在巨大差距。有没有办法让绘图正确地从 x 轴上消除范围间隙间隙?
提前致谢!
我已经尝试过以下操作:
dict(bounds=[16.5, 9], pattern="hour")
或 dict(bounds=["sat", "mon"])
的形式使用区间突破,但这对我不起作用,因为我还需要在正常交易周期间排除特定日期。exclude_dates
重新格式化为字符串,包括小时、分钟、秒和毫秒。
这会导致绘图仅显示第一个日期,而不显示整个时间段。exclude_dates
,即传递更多值作为 exclude_dates
。
这导致绘图也仅显示第一天的数据。尝试使用
rangebreaks
以不同的方式重建时间序列图后,我无法让它工作。
然后我切换回使用
brokenaxes
包,对于更高级的绘图,使用 matplotlib 中的 transform=ax.transAxes
转换。这至少允许我根据日期时间正确缩放 x 轴,同时每天打破 x 轴。请注意,我的数据已经排除了周末和选定的日期。每个脚本看起来都有点像这样:
断斧:
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from brokenaxes import brokenaxes
def matplotlib_price_plot(df):
# Init
fig = plt.figure(figsize=(20, 10))
x_major_formatter = mdates.DateFormatter("%Y-%m-%d\n%H:%M")
# Get ax breaks
date_ranges = []
for d in df['date'].unique():
temp = df[df['date']==d]
date_ranges.append((temp['dateTime'].iloc[0], temp['dateTime'].iloc[-1]))
date_ranges = tuple(date_ranges)
# Init broken ax
bax = brokenaxes(xlims=date_ranges, wspace=.03)
# Plot values
bax.plot(df['dateTime'], df['price'], c='darkorange', label='price', alpha=.5)
bax.set_ylabel('Price in EUR')
bax.legend()
# Format Ticks
for ax in bax.axs:
ax.grid(which='major', axis='both')
ax.xaxis.set_tick_params(labelsize=6)
ax.xaxis.set_major_formatter(x_major_formatter)
return
matplotlib
(ax.transAxes)
:
注意:这里我需要删除/覆盖各个轴以获得所需的结果,因为您实际上将多个单独的子图组合成一个图形。
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib as mpl
def matplotlib_sector_price_plot(df):
# Get ax breaks
date_ranges = []
for d in df['date'].unique():
temp = df[df['date']==d]
date_ranges.append((temp.index[0], temp.index[-1]))
date_ranges = tuple(date_ranges)
# Init fig
fig, axs = plt.subplots(1, len(date_ranges), sharex=False, figsize=(25, 10))
x_major_formatter = mdates.DateFormatter("%d-%m\n%H:%M")
stockTicker = 'CFR SW'
stockCurrency = 'CHF'
sectorTicker = 'ESIC GR'
# Check whether 1-D or more and adjust tick size
if len(axs) == 1:
hour_tick_size = 1
else:
hour_tick_size = 2
# Plot values
for ax, dr in zip(axs, date_ranges):
temp = df[(df.index>=dr[0])&(df.index<=dr[1])]
ax.set_xlim(dr)
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
d = 0.005
if ax == axs[0] and len(axs) > 1: # First subplot for multi day
ax.plot(temp['lastPx'], c='mediumblue', label=stockTicker + ' (Last)', linewidth=.75)
ax.spines['right'].set_visible(False)
ax.tick_params(labelright='off')
ax.yaxis.tick_left()
ax.set_ylabel('Stock Price in ' + stockCurrency)
ax.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.2f}'))
ax.tick_params(axis='x', labelrotation=0)
ax.yaxis.set_major_locator(mpl.ticker.LinearLocator(5))
ax.xaxis.set_major_formatter(x_major_formatter)
ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(1,25),interval=hour_tick_size))
ax.grid(which='major', axis='x')
ax2 = ax.twinx()
ax2.plot(temp['lastMidPt'], c='gold', label=sectorTicker + ' (Mid)', linewidth=.75)
ax2.spines['right'].set_visible(False)
ax2.tick_params(labelright='off')
ax2.yaxis.set_ticks([])
ax2.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.4f}'))
ax.plot((1 - d, 1 + d), (-d, +d), **kwargs)
ax.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs)
elif ax == axs[-1] and len(axs) > 1: # Last subplot for multi day
line1 = ax.plot(temp['lastPx'], c='mediumblue', label=stockTicker + ' (Last)', linewidth=.75)
ax.spines['left'].set_visible(False)
ax.yaxis.tick_right()
ax.yaxis.set_ticks_position('none')
ax.yaxis.set_ticks([])
ax.xaxis.set_major_formatter(x_major_formatter)
ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(1,25),interval=hour_tick_size))
ax.tick_params(axis='x', labelrotation=0)
ax.grid(which='major', axis='both')
ax2 = ax.twinx()
line2 = ax2.plot(temp['lastMidPt'], c='gold', label=sectorTicker + ' (Mid)', linewidth=.75)
ax2.spines['left'].set_visible(False)
ax2.tick_params(labelleft='off')
ax2.set_ylabel('ETF Price in EUR')
ax2.yaxis.tick_right()
ax2.yaxis.set_major_locator(mpl.ticker.LinearLocator(5))
ax2.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.4f}'))
ax.plot((-d, +d), (1 - d, 1 + d), **kwargs)
ax.plot((-d, +d), (-d, +d), **kwargs)
elif len(axs) == 1: # 1-D plot
line1 = ax.plot(temp['lastPx'], c='mediumblue', label=stockTicker + ' (Last)', linewidth=.75)
ax.set_ylabel('Stock Price in ' + stockCurrency)
ax.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.2f}'))
ax.tick_params(axis='x', labelrotation=0)
ax.yaxis.set_major_locator(mpl.ticker.LinearLocator(5))
ax.xaxis.set_major_formatter(x_major_formatter)
ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(1, 25), interval=hour_tick_size))
ax.grid(True, which='major', axis='both')
ax2 = ax.twinx()
line2 = ax2.plot(temp['lastMidPt'], c='gold', label=sectorTicker + ' (Mid)', linewidth=.75)
ax2.yaxis.set_major_locator(mpl.ticker.LinearLocator(5))
ax2.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.4f}'))
ax2.xaxis.set_major_locator(mdates.HourLocator(byhour=range(1, 25), interval=hour_tick_size))
ax2.grid(True, which='major', axis='both')
else: # Middle subplots for multi day
ax.plot(temp['lastPx'], c='mediumblue', label=stockTicker + ' (Last)', linewidth=.75)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.tick_params(labelright='off')
ax.yaxis.set_ticks_position('none')
ax.yaxis.set_ticks([])
ax.xaxis.set_major_formatter(x_major_formatter)
ax.tick_params(axis='x', labelrotation=0)
ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(1,25),interval=hour_tick_size))
ax.grid(which='major', axis='both')
ax2 = ax.twinx()
ax2.plot(temp['lastMidPt'], c='gold', label=sectorTicker + ' (Mid)', linewidth=.75)
ax2.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax2.yaxis.set_ticks_position('none')
ax2.yaxis.set_ticks([])
ax2.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.4f}'))
ax.plot((1 - d, 1 + d), (-d, +d), **kwargs)
ax.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs)
ax.plot((-d, +d), (1 - d, 1 + d), **kwargs)
ax.plot((-d, +d), (-d, +d), **kwargs)
lines = line1+line2
labs = [l.get_label() for l in lines]
axs[-1].legend(lines, labs, loc='upper right')
fig.tight_layout()
plt.show()
return
总的来说,结果并不相同,因为绘图是静态的并且缺少绘图功能,但至少从可视化的角度来看是我想要实现的。