Reload Graph When Input in Textbox Changes - python

I have a situation at the moment where I am stuck on making my graph to change whenever the input in textbox changes. I also wanted to make sure that whenever changes made to the textbox, it will reflect to the output I wanted from the DB [the graph] and this should be done continuously ie the graph will flow continuously.
However after some tries in using a button to kickstart the n-intervals, I still failed in doing so.
It'd will be great if anyone can have a look at my code. Thank you so much.
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import random
import plotly.graph_objs as go
from collections import deque
import sqlite3
import pandas as pd
import time
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(
[ html.H2('Live Twitter Sentiment Trend'),
dcc.Input(id='sentiment_term', value='trump', type='text'),
dcc.Graph(id='live-graph', animate=False),
dcc.Interval(
id='graph-update',
interval=1*1000,
n_intervals = 0
),
]
)
#app.callback(
Output('live-graph', 'figure'),
[Input(component_id='sentiment_term', component_property='value'),
[Input(component_id='graph-update', component_property='n_intervals')])
def update_graph_scatter(sentiment_term):
try:
conn = sqlite3.connect('twitter.db')
conn.cursor()
df = pd.read_sql("SELECT * FROM sentiment WHERE tweet LIKE ? ORDER BY unix DESC LIMIT 1000", conn ,params=('%' + sentiment_term + '%',))
df.sort_values('unix', inplace=True)
df['sentiment_smoothed'] =
df['sentiment'].rolling(int(len(df)/2)).mean()
df['date'] = pd.to_datetime(df['unix'],unit='ms')
df.set_index('date', inplace=True)
df = df.resample('0.15min').mean()
df.dropna(inplace=True)
X = df.index
Y = df.sentiment_smoothed
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)]),
title='Term: {}'.format(sentiment_term))}
except Exception as e:
with open('errors.txt','a') as f:
f.write(str(e))
f.write('\n')
if __name__ == '__main__':
app.run_server(debug=True)

Your callback function update_graph_scatter is missing a second parameter. You can change it to:
def update_graph_scatter(sentiment_term, n_intervals):
Also your callback decorator should be:
#app.callback(
Output('live-graph', 'figure'),
[Input(component_id='sentiment_term', component_property='value'),
Input(component_id='graph-update', component_property='n_intervals')])
Because it had an extra "[" breaking it.
I'm not sure if this is causing your problem, but it might. I wish I had 50 reputation to post this as a mere comment because of its minor contribution.

Related

PlotlyDash embedding data into html.Details

im kinda new to Python and Dash.
As i'm interested in Data Analytics I started messing around with Dash.
The plan is to build a stock dashboard but i'm stuck with some things...
I want to have an dcc.Input box where you can put a Ticker and submit with html.button,
later on I want to display data from multiple stocks in different html.Details so my Question is
how to achieve that.
I'm not getting to far because I don't understand the callback.
When trying the provied code I get Error:Callback error updating table.children - AttributeError: 'NoneType' object has no attribute 'upper'
Whats the correct way to turn input into a datatable and put in a html.Details
import yfinance as yf
import pandas as pd
import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input, State
app = dash.Dash()
app.layout = html.Div([
dcc.Input(id='input', value='', type='text', placeholder='Enter Stock ticker'),
html.Button('Submit', id='button'),
html.Div(id='table')
])
#app.callback(
[Output('table', 'children')],
[Input('button', 'n_clicks')],
[State('input', 'value')]
)
def update_table(input_value, n_clicks):
ticker = yf.Ticker(input_value.upper())
history = ticker.history(period='max')
return html.Div([
html.Details(
dash_table.DataTable(
id='tickerdata',
columns = [{'name': i, 'id': i} for i in history.columns],
data = history,
)
)
])
if __name__ == '__main__':
app.run_server(debug=True)
The cause of the error was that the order in which the callback functions were received and the arguments of the update function were not in the correct order. n_clicks is trying to capitalize the number and the error occurred. The next error is that the data frame needs to be changed to a dictionary in order to be displayed in the table. Also, the data in the stocks is indexed by date, so the date column needs to be unindexed in order to display it.
import yfinance as yf
import pandas as pd
import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input, State
app = dash.Dash()
app.layout = html.Div([
dcc.Input(id='input', value='', type='text', placeholder='Enter Stock ticker'),
html.Button('Submit', id='submit_btn',n_clicks=0),
html.Div(id='table')
])
#app.callback(
Output('table', 'children'),
[Input('submit_btn', 'n_clicks')],
[State('input', 'value')]
)
def update_table(n_clicks, input_value):
ticker = yf.Ticker(input_value.upper())
history = ticker.history(period='max')
history.reset_index(inplace=True)
return html.Div([
html.Details(
dash_table.DataTable(
id='tickerdata',
columns = [{'name': i, 'id': i} for i in history.columns],
data = history.to_dict('records'),
fixed_rows={'headers':True}
)
)
])
if __name__ == '__main__':
app.run_server(debug=True)

Columns not displaying correctly using python dash and mongo db

I am working on a project and after finally getting around some Type errors, I am finally getting my chart to display but it is listing each character in each mongo db json document instead of in proper json form. I am a total noob at this and I cannot seem to get it right so any help that can be offered in getting my interactive chart displayed as well as my pie chart and location chart will be greatly appreciated. I think the problem is derived from how the data is being entered into the dataframe but I can not figure it out.
Here is the read functions that are being used to take the info from the mongo db and send it to the Python script:
def read(self, data):
if data !=None: # checks to make sure that the received data is not null
result = self.database.animals.find(data).limit(35)
return result
else:
raise Exception("Data entered is null!")
def readAll(self, data):
found = self.database.animals.find({}).limit(35)
return(found)
Here is the code for the python script that has imported dash:
from jupyter_plotly_dash import JupyterDash
import dash
import dash_leaflet as dl
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import dash_table
from dash.dependencies import Input, Output
from bson.json_util import dumps,loads
import bson
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pymongo import MongoClient
from AAC import AnimalShelter
###########################
# Data Manipulation / Model
###########################
# FIX ME update with your username and password and CRUD Python module name
username = "aacuser"
password = "Melvin1234!"
shelter = AnimalShelter()
shelter._init_(username,password)
# class read method must support return of cursor object and accept projection json input
df =pd.DataFrame.from_records(list(dumps(shelter.readAll({}))))
dff = pd.DataFrame.from_records(list(dumps(shelter.readAll({}))))
#########################
# Dashboard Layout / View
#########################
app = JupyterDash('SimpleExample')
import base64
app.layout = html.Div([
html.Div(id='hidden-div', style={'display':'none'}),
html.Center(html.B(html.H1('SNHU CS-340 Dashboard'))),
html.Div(className='header',
children=[html.Center(html.Img(src='/home/16514911_snhu/Downloads/Grazioso Salvare Logo.jpg',width="500", height="600")), #image logo
html.Center(html.B(html.H6('Jacqueline Woods')))]), #Unique Handle
html.Br(),
html.Div(className ='row',
children=[
html.Button(id='submit-button-one',n_clicks=0, children='Cats'),
html.Button(id='submit-button-two',n_clicks=0,children='Dogs') ]),
html.Div(dash_table.DataTable(
id='datatable-interactivity',
columns=[
{"name": i, "id": i,"age_upon_outcome":i, 'animal_id':i,'animal_type':i,'breed':i,"color":i,
'date_of_birth':i,'datetime':i,'monthyear':i,'outcome_subtype':i,'outcome_type':i,'sex_upon_outcome':i,
'location_lat':i,'location_long':i,'age_upon_outcome_in_weeks':i,
"deletable": False, "selectable": True} for i in df.columns]
,data=df.to_dict('records'),editable = False,filter_action='native')),
html.Br(),
html.Div(
dcc.Graph( #piechart
id ='graph_id',
figure=list(dumps(shelter.readAll({})))
),
title="Outcome_Type"),
html.Br(),
html.Div( #location map
id='map-id',
className='col s12 m6',
title="Location"
)])
##Interaction Between Components / Controller
#This callback will highlight a row on the data table when the user selects it
#app.callback(
Output('datatable-interactivity',"data"),
[Input('submit-button-one','n_clicks'),
Input('submit-button-two','n_clicks')
]
)
def on_click(bt1, bt2):
if(int(bt1) ==0 and int(bt2) ==0):
df =pd.DataFrame.from_records(shelter.readAll({}))
elif (int(bt1) > int(bt2)):
df = pd.DataFrame(dumps(shelter.read({"animal__type":"Cat"})))
df =pd.DataFrame(dumps(shelter.read({"animal_type":"Dog"})))
return df.to_dict('records')
#app.callback(
Output('datatable-interactivity', 'style_data_conditional'),
[Input('datatable-interactivity', 'selected_columns')]
)
def update_styles(selected_columns):
return [{
'if': { 'column_id': i },
'background_color': '#D2F3FF'
} for i in selected_columns]
#app.callback(
Output('graph_id','figure'),
[Input('datatable-interactivity',"derived_virtual_data")])
def update_Graph(allData):
dff= pd.DataFrame(allData)
pieChart = px.pie(
data_frame=dff,
names=dff['outcome_type'],
hole = 3,
)
return pieChart
#app.callback(
Output('map-id', "children"),
[Input('datatable-interactivity', 'derived_viewport_data'),
Input('datatable-interactivity', 'derived_virtual_selected_rows')
])
def update_map(viewData):
#FIXME Add in the code for your geolocation chart
dff = pd.DataFrame.from_dict(viewData)
dff = df if viewData is None else pd.DataFrame(viewData)
selected_animal = None
if not derived_virtual_selected_rows:
slected_animal = dff.iloc[0]
else:
slected_animal = dff.iloc[derived_vertual_selected_rows[0]]
latitude = selected_animal[12]
longitude =selected_animal[13]
breed = selected_animal[3]
name = selected_animal[0]
# Austin TX is at [30.75,-97.48]
return [
dl.Map(style={'width': '1000px', 'height': '500px'}, center=[30.75,-97.48], zoom=10, children=[
dl.TileLayer(id="base-layer-id"),
# Marker with tool tip and popup
dl.Marker(position=[latitude,longitude], children=[
dl.Tooltip(breed),
dl.Popup([
html.H1("Animal Name"),
html.P(name)
])
])
])
]
app
I was in the same boat as you searching for answers online. I happened to found the answer. Hopefully, this can help others who are experiencing this problem.
#Convert the pymongo cursor into a pandas DataFrame
df = pd.DataFrame(list(shelter.readAll({})))
#Drop the _id column generated by Mongo
df = df.iloc[:, 1:]
After that, you can access the data of the DataFrame by using
df.to_dict('records')

Python Dash refresh page not updating source data

I have written a basic plotly dash app that pulls in data from a csv and displays it on a chart.
You can then toggle values on the app and the graph updates.
However, when I add new data to the csv (done once each day) the app doesn't update the data on refreshing the page.
The fix is normally that you define your app.layout as a function, as outlined here (scroll down to updates on page load). You'll see in my code below that I've done that.
Here's my code:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import numpy as np
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
path = 'https://raw.githubusercontent.com/tbuckworth/Public/master/CSVTest.csv'
df = pd.read_csv(path)
df2 = df[(df.Map==df.Map)]
def layout_function():
df = pd.read_csv(path)
df2 = df[(df.Map==df.Map)]
available_strats = np.append('ALL',pd.unique(df2.Map.sort_values()))
classes1 = pd.unique(df2["class"].sort_values())
metrics1 = pd.unique(df2.metric.sort_values())
return html.Div([
html.Div([
dcc.Dropdown(
id="Strategy",
options=[{"label":i,"value":i} for i in available_strats],
value=list(available_strats[0:1]),
multi=True
),
dcc.Dropdown(
id="Class1",
options=[{"label":i,"value":i} for i in classes1],
value=classes1[0]
),
dcc.Dropdown(
id="Metric",
options=[{"label":i,"value":i} for i in metrics1],
value=metrics1[0]
)],
style={"width":"20%","display":"block"}),
html.Hr(),
dcc.Graph(id='Risk-Report')
])
app.layout = layout_function
#app.callback(
Output("Risk-Report","figure"),
[Input("Strategy","value"),
Input("Class1","value"),
Input("Metric","value"),
])
def update_graph(selected_strat,selected_class,selected_metric):
if 'ALL' in selected_strat:
df3 = df2[(df2["class"]==selected_class)&(df2.metric==selected_metric)]
else:
df3 = df2[(df2.Map.isin(selected_strat))&(df2["class"]==selected_class)&(df2.metric==selected_metric)]
df4 = df3.pivot_table(index=["Fund","Date","metric","class"],values="value",aggfunc="sum").reset_index()
traces = []
for i in df4.Fund.unique():
df_by_fund = df4[df4["Fund"] == i]
traces.append(dict(
x=df_by_fund["Date"],
y=df_by_fund["value"],
mode="lines",
name=i
))
if selected_class=='USD':
tick_format=None
else:
tick_format='.2%'
return {
'data': traces,
'layout': dict(
xaxis={'type': 'date', 'title': 'Date'},
yaxis={'title': 'Values','tickformat':tick_format},
margin={'l': 40, 'b': 40, 't': 10, 'r': 10},
legend={'x': 0, 'y': 1},
hovermode='closest'
)
}
if __name__ == '__main__':
app.run_server(debug=True)
Things I've tried
Removing the initial df = pd.read_csv(path) before the def layout_function():. This results in an error.
Creating a callback button to refresh the data using this code:
#app.callback(
Output('Output-1','children'),
[Input('reload_button','n_clicks')]
)
def update_data(nclicks):
if nclicks == 0:
raise PreventUpdate
else:
df = pd.read_csv(path)
df2 = df[(df.Map==df.Map)]
return('Data refreshed. Click to refresh again')
This doesn't produce an error, but the button doesn't refresh the data either.
Defining df within the update_graph callback. This updates the data every time you toggle something, which is not practicable (my real data is > 10^6 rows, so i don't want to read it in every time the user changes a toggle value)
In short, i think that defining app.layout = layout_function should make this work, but it doesn't. What am I missing/not seeing?
Appreciate any help.
TLDR; I would suggest that you simply load the data from within the callback. If load time is too long, you could change the format (e.g. to feather) and/or reduce the data size via pre processing. If this is still not fast enough, the next step would be to store the data in a server-side in-memory cache such as Redis.
Since you are reassigning df and df2 in the layout_function, these variables are considered local in Python, and you are thus not modifying the df and df2 variables from the global scope. While you could achieve this behavior using the global keyword, the use of global variables is discouraged in Dash.
The standard approach in Dash would be to load the data in a callback (or in the the layout_function) and store it in a Store object (or equivalently, a hidden Div). The structure would be something like
import pandas as pd
import dash_core_components as dcc
from dash.dependencies import Output, Input
app.layout = html.Div([
...
dcc.Store(id="store"), html.Div(id="trigger")
])
#app.callback(Output('store','data'), [Input('trigger','children')], prevent_initial_call=False)
def update_data(children):
df = pd.read_csv(path)
return df.to_json()
#app.callback(Output("Risk-Report","figure"), [Input(...)], [State('store', 'data')])
def update_graph(..., data):
if data is None:
raise PreventUpdate
df = pd.read_json(data)
...
However, this approach will typically be much slower than just reading the data from disk inside the callback (which seems to be what you are trying to avoid) as it results in the data being transferred between the server and client.

Periodically updating figures in plotly

Below is a simple script which retrieves live population data. It updates periodically and updates the plotly figure:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
# Retrieve data
link = requests.get("https://countrymeters.info/en/World").text
table = pd.read_html(link)[0]
table = table
figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
# Dash app
app = dash.Dash(external_stylesheets=[dbc.themes.LUX])
# Bootstrap CSS
app.css.append_css({"external_url": "https://codepen.io/amyoshino/pen/jzXypZ.css"})
link = session.get("https://countrymeters.info/en/World").text
app.layout = html.Div(children=[
html.H1("Population Data Scrape"),
html.Div(children=
'''
A summary of the latest population data across the globe.
''' ),
# Graph 1
html.Div([
dcc.Tabs([ #Tab 1
dcc.Tab(label="Population Data", children=[
html.Div([
dcc.Graph(
id = "Data",
figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
),
dcc.Interval(
id="4secinterval",
interval="4000",
n_intervals=0
)], className = "six columns"),
]),
])
])
])
# Update graph
#app.callback(Output("Data", "figure"),
[Input("4secinterval", "n_intervals")])
def draw_figure(n):
test = session.get("https://countrymeters.info/en/World").text
table = pd.read_html(test)[0]
table = table
figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
return figure
if __name__ == "__main__":
app.run_server(debug=False)
In the "update graph" section of my code, for the graph to update I have to call the web scrape again to retrieve the latest data and define it in a whole function. I've tried defining the function before and using:
#app.callback(Output("Data", "figure"),
[Input("4secinterval", "n_intervals")])
draw_figure(n)
which I was hoping would just return the figure, however, this doesn't work. Is there a way in plotly/Dash to update the figure in a shorter way (i.e without having to scrape and format the data all over again)?
The catch here is in the dcc.Graph section. You are calling a global variable table.iplot() where table is defined as a global variable in your retrieve section.
Try to put all the functions in a separate file say `useful_functions.py'
def draw_graph():
link = requests.get("https://countrymeters.info/en/World").text
table = pd.read_html(link)[0]
table = table
figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
return figure
the_graph = draw_graph()
Now, in your main file as above, remove the global declaration of table and figure. To display the graph, in your graph section call the draw_graph() function as:
import useful_functions as uf
<rest of the html app code>
dcc.Graph(
id = "graph_id",
figure = uf.the_graph
),
This will call the graph for the first time on load. Now for the refresh bit, the callback would look like:
#app.callback(Output("Data", "figure"),
[Input("4secinterval", "n_intervals")])
def draw_figure(n):
fig = uf.draw_graph()
return fig

"I am trying get the values of the single row based on drop down value selected. getting error with output."

"I am trying to get the values of the single row based on the drop-down value selected.
I am confirming the output to ensure I can do a bar plot on the same late." need your expertise help fr the same.
Any otherr ways to call the same. Thanks for your help in advance.
code :
import dash
import pandas as pd
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import pandas as pd
df = pd.read_csv('E:\pylab\dshlab\my_dash_app\solar.csv')
app = dash.Dash()
dpdown = []
for i in df['state']:
str(dpdown.append({'label':i,'value':(i)}))
app.layout = html.Div([
html.H4('Select your State'),
dcc.Dropdown(id='dropdown', style={'height': '30px', 'width': '100px'}, options=dpdown,
value=df['state']),
#dcc.Graph(id='graph'),
html.Div(id='table-container')
])
#app.callback(
dash.dependencies.Output('table-container','children'),
[dash.dependencies.Input('dropdown', 'value')])
def display_table(dpdown):
return(df[df['state']==dpdown])
if __name__ == '__main__':
app.run_server(debug=True)
Error:
ash.exceptions.InvalidCallbackReturnValue:
The callback for <Outputtable-container.children>
returned a value having type DataFrame
which is not JSON serializable.
The value in question is either the only value returned,
or is in the top level of the returned list,
and has string representation
state Number of Solar Plants Installed Capacity (MW) Average MW Per Plant Generation (GWh)
1 Arizona 48 1078 22.5 2550
In general, Dash properties can only be
dash components, strings, dictionaries, numbers, None,
or lists of those.
I used dash_table for creation of the table. You can read more about it here.
Code is as follows:
import dash
import pandas as pd
import dash_core_components as dcc
import dash_html_components as html
import dash_table as dt
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import pandas as pd
df = pd.read_csv('E:\pylab\dshlab\my_dash_app\solar.csv')
app = dash.Dash()
dpdown = []
for i in df['state']:
str(dpdown.append({'label':i,'value':(i)}))
app.layout = html.Div([
html.H4('Select your State'),
dcc.Dropdown(id='dropdown', style={'height': '30px', 'width': '100px'}, options=dpdown),
#dcc.Graph(id='graph'),
html.Div(
id='table-container',
className='tableDiv'
)
])
#app.callback(
dash.dependencies.Output('table-container','children'),
[dash.dependencies.Input('dropdown', 'value')])
def display_table(dpdown):
df_temp = df[df['state']==dpdown]
return html.Div([
dt.DataTable(
id='main-table',
columns=[{'name': i, 'id': i} for i in df_temp.columns],
data=df_temp.to_dict('rows')
)
])
if __name__ == '__main__':
app.run_server(debug=True)
This will temporarily subset the dataframe and filter for your state every time a callback occurs, and output only the row corresponding to the state.

Categories