我们正在尝试以plotly dash的形式生成一个实时仪表板,它可以在生成时显示实时数据。我们通常遵循这里的指导(https://dash.plotly.com/live-updates)。
我们有一个回调函数,它大约每秒从数据源收集一大块新的数据点,然后将数据附加到图中。
当我们这样做时,图形的更新是不稳定的,因为我们每秒都会在回调上生成一个新的图形对象。我们希望图表流畅,即使这意味着我们落后实时数据一到两秒。
我们正在研究动画(https://plotly.com/python/animations/),但不清楚如何将动画应用于附加到图形的实时数据流。
发布于 2020-09-01 13:22:37
通过extendData属性可以在不生成新图形对象的情况下更新Graph组件的跟踪。下面是一个每秒追加数据的小示例,
import dash
import dash_html_components as html
import dash_core_components as dcc
import numpy as np
from dash.dependencies import Input, Output
# Example data (a circle).
resolution = 20
t = np.linspace(0, np.pi * 2, resolution)
x, y = np.cos(t), np.sin(t)
# Example app.
figure = dict(data=[{'x': [], 'y': []}], layout=dict(xaxis=dict(range=[-1, 1]), yaxis=dict(range=[-1, 1])))
app = dash.Dash(__name__, update_title=None) # remove "Updating..." from title
app.layout = html.Div([dcc.Graph(id='graph', figure=figure), dcc.Interval(id="interval")])
@app.callback(Output('graph', 'extendData'), [Input('interval', 'n_intervals')])
def update_data(n_intervals):
index = n_intervals % resolution
# tuple is (dict of new data, target trace index, number of points to keep)
return dict(x=[[x[index]]], y=[[y[index]]]), [0], 10
if __name__ == '__main__':
app.run_server()根据客户端和服务器之间的网络连接(每次更新时,客户端和服务器之间交换一个请求),此方法的刷新率约为1秒。
如果您需要更高的刷新率,我建议使用client side callback进行图形更新。采用前面的示例,代码将类似于
import dash
import dash_html_components as html
import dash_core_components as dcc
import numpy as np
from dash.dependencies import Input, Output, State
# Example data (a circle).
resolution = 1000
t = np.linspace(0, np.pi * 2, resolution)
x, y = np.cos(t), np.sin(t)
# Example app.
figure = dict(data=[{'x': [], 'y': []}], layout=dict(xaxis=dict(range=[-1, 1]), yaxis=dict(range=[-1, 1])))
app = dash.Dash(__name__, update_title=None) # remove "Updating..." from title
app.layout = html.Div([
dcc.Graph(id='graph', figure=dict(figure)), dcc.Interval(id="interval", interval=25),
dcc.Store(id='offset', data=0), dcc.Store(id='store', data=dict(x=x, y=y, resolution=resolution)),
])
app.clientside_callback(
"""
function (n_intervals, data, offset) {
offset = offset % data.x.length;
const end = Math.min((offset + 10), data.x.length);
return [[{x: [data.x.slice(offset, end)], y: [data.y.slice(offset, end)]}, [0], 500], end]
}
""",
[Output('graph', 'extendData'), Output('offset', 'data')],
[Input('interval', 'n_intervals')], [State('store', 'data'), State('offset', 'data')]
)
if __name__ == '__main__':
app.run_server()客户端更新应该足够快,以实现流畅的更新。下面的gif示出了以25ms的刷新率运行的上述示例,

请记住,只有当数据已经存在于客户端时,客户端更新才是可能的,即需要另一种机制来从服务器获取数据。一个可能的数据流可能是
Interval组件(例如2秒)来触发(正常)回调,该回调从源获取数据块并将其放置在快速Interval组件(例如25毫秒)中以触发客户端回调,该客户端回调将数据从Store组件流式传输到Graph组件发布于 2020-09-01 04:23:49
编辑:修订版1
在我的建议的末尾,您会发现一个完全可重现的、虽然很少的代码片段。但要注意,这是一个设计为在JupyterDash中启动的示例。
我只能假设您正在一个或多个pandas数据帧中收集数据流。要模拟我所理解的真实情况,我只能依赖于一些随机数据的生成。在对我最初答案的以下修订中,我将争辩说,要制作具有实时数据的平滑动画,您唯一需要的就是
1.熊猫绘图后端设置为plotly的 df.plot(),
2.一个dash组件,如下所示:
dcc.Interval(id='interval-component',
interval=1*1000, # in milliseconds
n_intervals=0
)3.和如下所示的回调函数:
@app.callback(
Output('graph', 'figure'),
[Input('interval-component', "n_intervals")]
)下面的代码片段所包含的代码完全符合您在问题中的描述:
1.它每秒在数据帧df2中收集一块随机数据,
2.将其添加到现有数据帧df1中,并且
3.绘制结果。
最初的数字如下所示:

几秒钟后,该图如下所示:

这听起来可能好得令人难以置信,但数字之间的转换开箱即用,看起来非常棒。新的点被优雅地添加到线条的末端,并且x轴和y轴的更新都非常顺利。
更新一开始可能看起来有点起伏不定,但在几千次运行之后,你只会看到行尾在移动:

在上图中,您可以看到几千次运行后包含了起始点。这可能是显而易见的,但如果您想在运行1000次之后保持窗口长度不变,只需包含用df3 = df3.cumsum().tail(1000)替换df3 = df3.cumsum()即可获得:

但你不必相信我的话。只需在JupyterLab中启动以下代码片段,您就可以自己查看:
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
# code and plot setup
# settings
pd.options.plotting.backend = "plotly"
countdown = 20
#global df
# sample dataframe of a wide format
np.random.seed(4); cols = list('abc')
X = np.random.randn(50,len(cols))
df=pd.DataFrame(X, columns=cols)
df.iloc[0]=0;
# plotly figure
fig = df.plot(template = 'plotly_dark')
app = JupyterDash(__name__)
app.layout = html.Div([
html.H1("Streaming of random data"),
dcc.Interval(
id='interval-component',
interval=1*1000, # in milliseconds
n_intervals=0
),
dcc.Graph(id='graph'),
])
# Define callback to update graph
@app.callback(
Output('graph', 'figure'),
[Input('interval-component', "n_intervals")]
)
def streamFig(value):
global df
Y = np.random.randn(1,len(cols))
df2 = pd.DataFrame(Y, columns = cols)
df = df.append(df2, ignore_index=True)#.reset_index()
df.tail()
df3=df.copy()
df3 = df3.cumsum()
fig = df3.plot(template = 'plotly_dark')
#fig.show()
return(fig)
app.run_server(mode='external', port = 8069, dev_tools_ui=True, #debug=True,
dev_tools_hot_reload =True, threaded=True)这个例子不是很优雅,还有很大的改进空间(甚至是一个全局变量……),但我希望它能对您有用。
编辑:修订版2:
运行大约6000次后,图表将如下所示:

现在,事情看起来不再那么有趣了,尽管事情运行得非常顺利。每一次更新都只是在端点显示一个微小的移动。因此,我在结尾处添加了一些注释,以更清楚地表明事情实际上仍在运行:
带注释的完整代码
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
# code and plot setup
# settings
pd.options.plotting.backend = "plotly"
countdown = 20
#global df
# sample dataframe of a wide format
np.random.seed(4); cols = list('abc')
X = np.random.randn(50,len(cols))
df=pd.DataFrame(X, columns=cols)
df.iloc[0]=0;
# plotly figure
fig = df.plot(template = 'plotly_dark')
app = JupyterDash(__name__)
app.layout = html.Div([
html.H1("Streaming of random data"),
dcc.Interval(
id='interval-component',
interval=1*1000, # in milliseconds
n_intervals=0
),
dcc.Graph(id='graph'),
])
# Define callback to update graph
@app.callback(
Output('graph', 'figure'),
[Input('interval-component', "n_intervals")]
)
def streamFig(value):
global df
Y = np.random.randn(1,len(cols))
df2 = pd.DataFrame(Y, columns = cols)
df = df.append(df2, ignore_index=True)#.reset_index()
#df.tail()
df3=df.copy()
df3 = df3.cumsum()#.tail(1000)
fig = df3.plot(template = 'plotly_dark')
#fig.show()
colors = px.colors.qualitative.Plotly
for i, col in enumerate(df3.columns):
fig.add_annotation(x=df3.index[-1], y=df3[col].iloc[-1],
text = str(df3[col].iloc[-1])[:4],
align="right",
arrowcolor = 'rgba(0,0,0,0)',
ax=25,
ay=0,
yanchor = 'middle',
font = dict(color = colors[i]))
return(fig)
app.run_server(mode='external', port = 8069, dev_tools_ui=True, #debug=True,
dev_tools_hot_reload =True, threaded=True)原创答案和建议
你没有提供任何示例代码,所以我只能提供一个一般性的建议,那就是仔细看看Dash库中的示例中的streams forex data是如何绘制的:

我将特别看看他们是如何设置其回调和932行in the source中的函数generate_figure_callback(pair)的
# Function to update Graph Figure
def generate_figure_callback(pair):
def chart_fig_callback(n_i, p, t, s, pairs, a, b, old_fig):
if pairs is None:
return {"layout": {}, "data": {}}
pairs = pairs.split(",")
if pair not in pairs:
return {"layout": {}, "data": []}
if old_fig is None or old_fig == {"layout": {}, "data": {}}:
return get_fig(pair, a, b, t, s, p)
fig = get_fig(pair, a, b, t, s, p)
return fig
return chart_fig_callback这就是我目前拥有的全部内容,但我希望您会发现它是有用的!
编辑:只是为了显示更新不限于5分钟。
21:16:29的屏幕截图

21:16:55的屏幕截图

您在出价/要价文本中看到的只是:出价和报价。而且它们一直在变化。如果我是100%正确的,这条线代表已完成的交易,这种情况只会时不时地发生。因此,我认为这只是您在这里显示的数据的问题。我希望您唯一需要做的事情就是用您的数据源替换这个示例的中心部分。您也可以查看Wind Streaming example。对于您的场景,这甚至可能更容易实现。

https://stackoverflow.com/questions/63589249
复制相似问题