Dav*_*rks 11 python plotly plotly-dash plotly-python
我们正在尝试在 plotly-dash 中生成一个实时仪表板,在生成时显示实时数据。我们通常遵循此处的指导(https://dash.plotly.com/live-updates)。
我们有一个回调,它大约每秒从源收集一大块新数据点,然后将数据附加到图形中。
当我们这样做时,对图形的更新是断断续续的,因为我们每秒都在回调上生成一个新的图形对象。我们希望图形流畅地流动,即使这意味着我们比实时数据落后一两秒。
我们正在研究动画(https://plotly.com/python/animations/),但尚不清楚我们如何将动画应用于附加到图形的实时数据流。
emh*_*her 25
Graph可以通过该extendData属性实现在不生成新图形对象的情况下更新组件的轨迹。这是一个每秒附加数据的小例子,
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()
Run Code Online (Sandbox Code Playgroud)
根据客户端和服务器之间的网络连接(在每次更新时,客户端和服务器之间交换一个请求),这种方法的刷新率约为 1 秒。
如果您需要更高的刷新率,我建议使用客户端回调进行图形更新。采用前面的例子,代码将沿着
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()
Run Code Online (Sandbox Code Playgroud)
客户端更新应该足够快以实现平滑更新。下面的 gif 显示了以 25 毫秒刷新率运行的上述示例,
请记住,只有当数据已经存在于客户端时才可能进行客户端更新,即需要另一种机制来从服务器获取数据。一个可能的数据流可能是
Interval组件(例如 2 秒)触发(正常)回调,该回调从源获取大量数据并将其放入Store组件中Interval组件(例如 25 毫秒)触发客户端回调,该回调将数据从Store组件流式传输到Graph组件You'll find a fully reproducible, albeit minimal code snippet at the end of my suggestion. But beware that it's an example designed to be fired up in JupyterDash.
I'll just have to assume that you're gathering your data stream in one or more pandas dataframes. To simulate what I understand to be your real-world situation, I'll just have to rely on the generation of some random data. In the following revision to my original answer, I will argue that the only thing you'll need to make a smooth animation with real-time data, is
1. df.plot() with pandas plotting backend set to plotly,
2. a dash component like this:
dcc.Interval(id='interval-component',
interval=1*1000, # in milliseconds
n_intervals=0
)
Run Code Online (Sandbox Code Playgroud)
3. and a callback function like this:
@app.callback(
Output('graph', 'figure'),
[Input('interval-component', "n_intervals")]
)
Run Code Online (Sandbox Code Playgroud)
The snippet below contains code that does exactly what you describe in your question:
1. It gathers a chunk of random data in a dataframe df2 every second,
2. adds that to an existing data frame df1, and
3. plots the result.
The initial figure looks like this:
After a few seconds the figure looks like this:
And this may sound too good to be true, but the transitions between the figures look pretty great right out of the box. New points are added gracefully at the end of the lines, and both the x- and y-axis update pretty smoothly.
The updating might seem a bit choppy at first, but after a few thousand runs you'll only see the end of the lines moving:
In the figure above you can see that the starting point is included after a few thousand runs. This is probably obvious, but if you'd like to keep a constant window length after for example 1000 runs, just include replace df3 = df3.cumsum() with df3 = df3.cumsum().tail(1000) to get:
But you don't have to take my word for it. Just fire up the following snippet in JupyterLab and see for yourself:
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)
Run Code Online (Sandbox Code Playgroud)
This example is not very elegant, and there's plenty of room for improvement (even a global variable....), but I hope it will be useful to you.
After about 6000 runs the chart will look like this:
And now things aren't that fun to look at anymore, although things are running very smoothly. Every update just reveals a tiny movement at the endpoints. So I added some annotations there at the end to make it more clear that things are in fact still running:
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)
Run Code Online (Sandbox Code Playgroud)
You haven't provided any sample code so I can only offer a general suggestion, and that is to take a closer look at how plotly streams forex data in an example in ther Dash gallery:
I would particularly take a look at how they've set up their callbacks and the function generate_figure_callback(pair) from line 932 in the source:
# 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
Run Code Online (Sandbox Code Playgroud)
This is all I have for now, but I hope you'll find it useful!
21:16:2921:16:55您在出价/询价文本中看到的只是:出价和出价。而且它们一直在变化。如果我 100% 正确,这条线代表已完成的交易,而且这种情况只是不时发生。所以我认为这只是你在这里显示什么数据的问题。我希望你唯一需要做的就是用你的数据源替换这个例子的中心部分。您还可以查看Wind Streaming 示例。对于您的场景,这甚至可能更容易实现。
| 归档时间: |
|
| 查看次数: |
8816 次 |
| 最近记录: |