经过长时间的搜索,我找不到任何线程/讨论来帮助我使 autoscaling 与plotly一起工作。
这个想法是,当我使用 x 范围滑块浏览数据时,每次移动滑块时 y 轴都会动态重新缩放。
这是我的代码片段:
fig = plt.figure()
# Layout format and buttons
layout = dict(
xaxis=dict(
rangeselector=dict(
buttons=list([
dict(count=1,
label='1m',
step='month',
stepmode='backward'),
dict(count=6,
label='6m',
step='month',
stepmode='backward'),
dict(count=1,
label='YTD',
step='year',
stepmode='todate'),
dict(count=1,
label='1y',
step='year',
stepmode='backward'),
dict(step='all')
])
),
rangeslider=dict(
visible = True
),
type='date'
)
)
# Plot open, high, low, close for each coins
for c in dic.keys():
ohlc = dic[c][['open','high','low','close']].copy()
candlestick = go.Candlestick(x = ohlc.index,
open = ohlc['open'],
high = ohlc['high'],
low = ohlc['low'],
close = ohlc['close'],
name = 'OHLC')
fig = go.Figure(data = [candlestick],
layout = layout)
fig.update_yaxes(showgrid=True,
zeroline=False,
showticklabels=True,
showspikes=True,
spikemode='across',
spikesnap='cursor',
spikethickness=1,
showline=True,
spikedash='dot',
spikecolor="#707070",
autorange = True, # DOESN'T WORK....
fixedrange= False # DOESN'T WORK....
)
fig.update_xaxes(showgrid=True,
zeroline=False,
showticklabels=True,
showspikes=True,
spikemode='across',
spikesnap='cursor',
spikethickness=1,
showline=True,
spikedash='dot',
spikecolor="#707070",
type='date',
)
fig.update_layout(title = 'OHLC for: '+c,
height = 750,
plot_bgcolor="#FFFFFF",
hovermode="x",
hoverdistance=100,
spikedistance=1000,
xaxis_rangeslider_visible=True,
dragmode='pan',
)
fig.show()
我尝试使用
autorange = True
和 fixedrange = False
但显然这没有任何作用,原因我不明白。
预先感谢您的帮助!
Plotly.py 的交互式渲染器使用 Plotly.js 显示数字,因此我们可以挂钩
plotly_relayout
事件,并在需要时通过将适当的 javascript 代码传递给 .show()
方法来重新缩放 y 轴范围,这要归功于 post_script
参数。我在这里提供了一个 JS/Plotly.js 解决方案,然后是 Python 解决方案,因为它包含嵌入 JS 代码。
请注意,您可能需要根据实际 OHLC 数据分辨率调整
formatDateRange()
函数(在这两种情况下)(请参阅评论)。
Plotly.js:
Plotly.newPlot('<plot_id>', data, layout).then(gd => {
gd.on('plotly_relayout', (e) => {
if (e['yaxis.range'] || e['yaxis.autorange'] || e.autosize || e.width || e.height) {
return;
}
if (e['xaxis.autorange']) {
Plotly.relayout(gd, {'yaxis.autorange': true});
return;
}
// NB. `formatDateRange()` depends on your OHLC data resolution (assuming one
// index per day here, formatted as 'yyyy-MM-dd'; but could be per hour, etc.) :
// - Slider/zoom range uses datetime format 'yyyy-MM-dd hh:mm:ss.SSSS'
// - need to match the date format 'yyyy-MM-dd'
const formatDateRange = x => x.replace(/(\d{4}-\d{2}-\d{2}).*/, '$1');
const xrange = gd._fullLayout.xaxis.range.map(formatDateRange);
const ohlc = gd._fullData[0];
let i0 = ohlc.x.indexOf(xrange[0]);
let i1 = ohlc.x.indexOf(xrange[1]);
if (i0 === -1) i0 = 0;
if (i1 === -1) i1 = ohlc.x.length - 1;
const data = [...ohlc.open.slice(i0, i1), ...ohlc.close.slice(i0, i1)];
const ymin = Math.min(...data);
const ymax = Math.max(...data);
const room = Math.floor((ymax - ymin) / 20);
const yrange = [ymin - room, ymax + room];
Plotly.relayout(gd, {'yaxis.range': yrange});
});
});
Plotly.py:
# ... build `fig`
fig.update_layout(
xaxis_rangeslider_visible=True,
xaxis_rangeslider_yaxis_rangemode="auto" # so we can still see the big picture in the rangeslider plot
)
js = '''
const gd = document.getElementById('{plot_id}');
gd.on('plotly_relayout', (e) => {
if (e['yaxis.range'] || e['yaxis.autorange'] || e.autosize || e.width || e.height) {
return;
}
if (e['xaxis.autorange']) {
Plotly.relayout(gd, {'yaxis.autorange': true});
return;
}
const formatDateRange = x => x.replace(/(\d{4}-\d{2}-\d{2}).*/, '$1');
const xrange = gd._fullLayout.xaxis.range.map(formatDateRange);
const ohlc = gd._fullData[0];
let i0 = ohlc.x.indexOf(xrange[0]);
let i1 = ohlc.x.indexOf(xrange[1]);
if (i0 === -1) i0 = 0;
if (i1 === -1) i1 = ohlc.x.length - 1;
const data = [...ohlc.open.slice(i0, i1), ...ohlc.close.slice(i0, i1)];
const ymin = Math.min(...data);
const ymax = Math.max(...data);
const room = Math.floor((ymax - ymin) / 20);
const yrange = [ymin - room, ymax + room];
Plotly.relayout(gd, {'yaxis.range': yrange});
});
fig.show(post_script=[js])
'''