我是 Plotly/Dash 的新手。我正在尝试构建一个情绪分析仪表板。
我想用图表来表示“情绪”(实时数据:来自表格)到目前为止我已经达到了我的第一个目标:
我在 SQL 中选择:trump。
@app.callback(
Output("live-graph", "figure"),[Input('sentiment_term', 'value'), Input('graph-update', 'n_intervals')]
)
#Output("wind-speed", "figure")
#Input("wind-speed-update", "n_intervals")
#Input("sentiment_term", "value")
#Input("graph-update", "n_intervals")]
def gen_wind_speed(self, sentiment_term):
conn = sqlite3.connect('twitter.db')
c = conn.cursor()
df = pd.read_sql("SELECT * FROM sentiment WHERE tweet LIKE '%trump%' ORDER BY unix DESC LIMIT 1000", conn)
df.sort_values('unix', inplace=True)
df['sentiment_smoothed'] = df['sentiment'].rolling(int(len(df)/5)).mean()
df.dropna(inplace=True)
X = df.unix.values[-100:]
Y = df.sentiment_smoothed.values[-100:]
data = plotly.graph_objs.Scatter(
x=X,
y=Y,
name='Scatter',
mode= 'lines+markers'
)
return {'data': [data],'layout' : go.Layout(xaxis=dict(range=[min(X),max(X)]),
yaxis=dict(range=[min(Y),max(Y)]),)}
现在作为第二个目标,我想生成(就在图表旁边)一个表格,其中包含图表中使用的数据(在本例中为twits)。 但是当我添加表格时,我得到:
Dash 回调的
Input
中使用了不存在的对象。该对象的 id 是 recent-table-update
,属性是 n_intervals
。当前布局中的字符串 ID 为:[live-graph、sentiment_term、graph-update、bin-slider、bin-auto、bin-size、wind-histogram、recent-tweets-table]
完整的代码,如果看起来组织不够,请道歉
import os
import pathlib
import numpy as np
import pandas as pd
import datetime as dt
import dash
import dash_core_components as dcc
import dash_html_components as html
import sqlite3
import plotly
import plotly.graph_objs as go
from dash.exceptions import PreventUpdate
from dash.dependencies import Input, Output, State
from scipy.stats import rayleigh
conn = sqlite3.connect('twitter.db', check_same_thread=False)
app_colors = {
'pageBackground': '#272B30',
'background': '#0C0F0A',
'text': '#6699ff',
'sentiment-plot':'#41EAD4',
'volume-bar':'#6699ff',
'someothercolor':'#80aaff',
'papercolor':'#1E2022',
'plotcolor':'#262626',
'fillcolor':'#ff6666',
'gridcolor': '#737373',
'backgroundTableHeaders': '#001133',
'backgroundTableRows': '#002266'
}
POS_NEG_NEUT = 0.1
MAX_DF_LENGTH = 100
GRAPH_INTERVAL = os.environ.get("GRAPH_INTERVAL", 5000)
app = dash.Dash(
__name__,
meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}],
)
server = app.server
app_colorsor = {"graph_bg": "#082255", "graph_line": "#007ACE"}
app.layout = html.Div(
[
# header
html.Div(
[
html.Div(
[
html.H4("Twitter STREAMING", className="app__header__title"),
html.P(
"This app continually queries a SQL database and displays live charts of twitter sentiment analysis.",
className="app__header__title--grey",
),
],
className="app__header__desc",
),
html.Div(
[
html.Img(
src=app.get_asset_url("twitter_logo.png"),
className="app__menu__img",
)
],
className="app__header__logo",
),
],
className="app__header",
),
html.Div(
[
#Plot sentiment
html.Div(
[
html.Div(
[html.H6("Plot sentiment", className="graph__title")]
),
dcc.Graph(
id="live-graph",
animate=False
),
dcc.Input(
id="sentiment_term",
value="twitter",
type="text"
),
dcc.Interval(
id="graph-update",
interval=1*1000,
n_intervals=0
),
],
className="two-thirds column container",
),
html.Div(
[
# Recent tweets table
html.Div(
[
html.Div(
[
html.H6(
"Recent tweets table",
className="graph__title",
)
]
),
html.Div(
[
dcc.Slider(
id="bin-slider",
min=1,
max=60,
step=1,
value=20,
updatemode="drag",
marks={
20: {"label": "20"},
40: {"label": "40"},
60: {"label": "60"},
},
)
],
className="slider",
),
html.Div(
[
dcc.Checklist(
id="bin-auto",
options=[
{"label": "Auto", "value": "Auto"}
],
value=["Auto"],
inputClassName="auto__checkbox",
labelClassName="auto__label",
),
html.P(
"# of Bins: Auto",
id="bin-size",
className="auto__p",
),
],
className="auto__container",
),
#it says wind because I got it from a wind direction Dash template
dcc.Graph(
id="wind-histogram",
figure=dict(
layout=dict(
plot_bgcolor=app_colorsor["graph_bg"],
paper_bgcolor=app_colorsor["graph_bg"],
)
),
),
],
className="graph__container first",
),
# Table that I would like to add
html.Div(
[
html.H6(
"Recent tweets table",
className="graph__title",
)
]
),
html.Div(id="recent-tweets-table", children=[
html.Thead(html.Tr( children=[], style={'color': app_colorsors['text']})),
html.Tbody([html.Tr(children=[], style={'color': app_colorsors['text'],
'background-color': app_colors['backgroundTableRows'],
'border':'0.2px', 'font - size':'0.7rem'}
],
className='col s12 m6 l6', style={'width':'98%', 'margin-top':30, 'margin-left':15,
'margin-right':15,'max-width':500000}
),
],
className="one-third column histogram__direction",
),
],
className="app__content",
),
],
className="app__container",
)
dcc.Interval(
id='recent-table-update',
interval=2*1000,
n_intervals=0),
#def get_current_time():
#now = dt.datetime.now()
#total_time = (now.hour * 3600) + (now.minute * 60) + (now.second)
#return total_time
def df_resample_sizes(df, maxlen=MAX_DF_LENGTH):
df_len = len(df)
resample_amt = 100
vol_df = df.copy()
vol_df['volume'] = 1
ms_span = (df.index[-1] - df.index[0]).seconds * 1000
rs = int(ms_span / maxlen)
df = df.resample('{}ms'.format(int(rs))).mean()
df.dropna(inplace=True)
vol_df = vol_df.resample('{}ms'.format(int(rs))).sum()
vol_df.dropna(inplace=True)
df = df.join(vol_df['volume'])
return df
@app.callback(
Output("live-graph", "figure"),[Input('sentiment_term', 'value'), Input('graph-update', 'n_intervals')]
)
#Output("wind-speed", "figure")
#Input("wind-speed-update", "n_intervals")
#Input("sentiment_term", "value")
#Input("graph-update", "n_intervals")]
def gen_wind_speed(self, sentiment_term):
conn = sqlite3.connect('twitter.db')
c = conn.cursor()
df = pd.read_sql("SELECT * FROM sentiment WHERE tweet LIKE '%trump%' ORDER BY unix DESC LIMIT 1000", conn)
df.sort_values('unix', inplace=True)
df['sentiment_smoothed'] = df['sentiment'].rolling(int(len(df)/5)).mean()
df.dropna(inplace=True)
X = df.unix.values[-100:]
Y = df.sentiment_smoothed.values[-100:]
data = plotly.graph_objs.Scatter(
x=X,
y=Y,
name='Scatter',
mode= 'lines+markers'
)
return {'data': [data],'layout' : go.Layout(xaxis=dict(range=[min(X),max(X)]),
yaxis=dict(range=[min(Y),max(Y)]),)}
def generate_table(df, max_rows=20):
return html.Table(className="responsive-table",
children=[
html.Thead(
html.Tr(
children=[
html.Th(col.title()) for col in df.columns.values],
style={'color': app_colorsors['text'],
'background-color': app_colorsors['backgroundTableHeaders']}
)
),
html.Tbody(
[
html.Tr(
children=[
html.Td(data) for data in d
], style={'color': app_colorsors['text'],
'background-color': quick_color(d[2]),
'border':'0.2px', 'font - size':'0.7rem'}
)
for d in df.values.tolist()])
]
)
@app.callback(Output("recent-tweets-table", "children"),[Input('sentiment_term','value'), Input('recent-table-update', 'n_intervals')]
)
def update_recent_tweets(self, sentiment_term):
genTable = html.Table()
try:
conn = sqlite3.connect('twitter.db')
df = pd.read_sql("SELECT UnixTime, Tweet, Polarity FROM %s ORDER BY UnixTime DESC LIMIT 20" % (RunConfig.tableName), conn)
if len(df)>0:
df['Date'] = pd.to_datetime(df['UnixTime'], unit='ms')
df = df.drop(['UnixTime'], axis=1)
df = df[['Date', 'Tweet', 'Polarity']]
df.Polarity = df.Polarity.round(3)
genTable = generate_table(df, max_rows=10)
except Exception as e:
with open('errors.txt','a') as f:
f.write("update_recent_tweets: " + str(e))
f.write('\n')
return genTable
if __name__ == "__main__":
app.run_server(debug=True)
我认为问题在于您在回调中引入了一个不存在(尚未)的元素作为输入。该元素是“recent-table-update”,在更新表之前它不存在。为了避免这种情况,您应该将其作为状态引入回调中(它不会触发回调,代码也不会中断)。更多信息:链接
这是您的代码:
@app.callback(
Output("recent-tweets-table", "children"),
[Input('sentiment_term','value'),
Input('recent-table-update', 'n_intervals')]
)
这是我建议的代码:
@app.callback(
Output("recent-tweets-table", "children"),
Input('sentiment_term','value'),
State('recent-table-update', 'n_intervals')
)
如果它比上面的解决方案更能帮助其他人,请检查您是否在布局内调用有问题的 id。在某些情况下,例如当连接应用程序调用时(例如,当使用下拉列表的输出作为输入的输入时),您可能需要在布局中隐藏的某个位置进行显式调用。
要解决此问题,您可以在布局内的某个位置指定
html.Div(id='recent-table-update')
,这将不会输出任何内容,但会激活应用程序调用。在这里,id
与您有问题的id
相同。