using dash in python to upload files and plot a bar chart - python

I try to make an interactive dashboard using dash from plotly. I am a beginner in it therefore I used an example to do it.
The code should upload different data files from a certain folder and plot a histogram based on a certain column. The name of each file looks like "30092017ARB.csv" (date + ARB.csv). The code loops over all file names in the data-folder and print the name of files in a drop-down bottom. After selecting the name of file it should be uploaded and plot a histogram. I wrote the following code:
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
#
from os import listdir
from os.path import isfile, join
import numpy as np
#
mypath='/Users/Python/BeN_/data/'
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]
app = dash.Dash()
app.layout = html.Div([
html.H2("Ausfallsreport"),
html.Div(
[
dcc.Dropdown(
id="dataFH",
options=[{
'label': i,
'value': i
} for i in onlyfiles],
value=" "),
],
style={'width': '25%',
'display': 'inline-block'}),
dcc.Graph(id='Mygraph'),
])
#app.callback(
dash.dependencies.Output('Mygraph', 'figure'),
[dash.dependencies.Input('dataFH', 'value')])
def update_graph(dataFH):
df = pd.read_csv(mypath+dataFH, delimiter=';',encoding='cp1252')
# aggregate
dfagg = df.groupby('Bestand', as_index=False).agg({'MW': 'sum'})
# make strings for x-axis
x=["BE-"+s for s in[str(s) for s in [int(x) for x in dfagg.iloc[:,0].tolist()]]]
y=dfagg.ix[:,1].tolist()
trace = go.Bar(x=x, y=y, name='Declined')
return {
'data': [trace],
'layout':
go.Layout(
title='Customer Order Status for {}'.format(dataFH),
barmode='bar',
yaxis=dict(tickformat=".2%"))
}
if __name__ == '__main__':
app.run_server(debug=True)
I get the following under http://127.0.0.1:8050/ .
The problem is that I do not get any Bar chart. Could someone help me to figure out why.

In case you haven't figured it out yet, the issue is that when a user initially connects, it triggers your callback. And you're setting the initial value of dropdown to
dataFH=" "
which causes
df = pd.read_csv(mypath+dataFH ...
to throw an error because that file doesn't exist.
Different ways to fix this, but one way is to check if mypath+dataFH is a file within your callback and return an empty list for the trace if it's not a file:
def update_graph(dataFH):
if isfile(mypath+dataFH):
df = pd.read_csv(mypath+dataFH, delimiter=';',encoding='cp1252')
# aggregate
dfagg = df.groupby('Bestand', as_index=False).agg({'MW': 'sum'})
# make strings for x-axis
x=["BE-"+s for s in[str(s) for s in [int(x) for x in dfagg.iloc[:,0].tolist()]]]
y=dfagg.ix[:,1].tolist()
trace = go.Bar(x=x, y=y, name='Declined')
else:
trace=[]
return {
'data': [trace],
'layout':
go.Layout(
title='Customer Order Status for {}'.format(dataFH),
barmode='bar',
yaxis=dict(tickformat=".2%"))
}

Related

update DataFrame in Dash after text input

I want to update my DataFrame that feeds my graphs in realtime. In all the tutorials I have found, the DataFrame is created before creating the app layout. I want to take the input from the input component with the username_input ID and use that in the get_dataFrame function to create my initial DataFrame which will create the figure which will be used in the graph component.
app = Dash(__name__)
df = get_dataFrame(username_input)
fig = px.line(df, x='end', y="user",color="class")
app.layout = html.Div(children=[
dcc.Input(
id="username_input",
placeholder="username",
type='text'
),
dcc.Graph(
id='example-graph',
figure=fig
),
])
I don't fully understand how to structure the code so that this is possible. In essence I want a user input first and then after the user input all the dash data updates to reflect the new input. Any ideas?
Move your dataframe and figure creation into your callback, then return a new figure after the user's input comes in. Working example:
from dash import Dash, dcc, html, Input, Output, no_update
import pandas as pd
import plotly.express as px
import numpy as np
app = Dash(__name__)
def get_dataFrame(username_input):
data = np.random.rand(10) * len(username_input)
return pd.DataFrame({"end": [*range(10)], "user": data, "class": ["red"] * 10})
fig = px.line()
app.layout = html.Div(
children=[
dcc.Input(id="username_input", placeholder="username", type="text"),
dcc.Graph(id="example-graph", figure=fig),
]
)
#app.callback(
Output("example-graph", "figure"), Input("username_input", "value"),
)
def func(username_input: str):
if username_input:
df = get_dataFrame(username_input)
fig = px.line(df, x="end", y="user", color="class")
return fig
return no_update
if __name__ == "__main__":
app.run(debug=True)

Dash, merge two dashboards and add a button

I'm pretty new to Dash. I created two dashboards that read from two different dataframes. They work well individually. I would like to merge them in a single one, giving the possibility to the user to access them using a dropdown menu.
I would like to also add a button which (when clicked) returns a function.
Those are the two dashboards:
first one:
import pandas as pd
df1 = pd.read_csv('/df1.csv')
# Import libraries
from dash import Dash, html, dcc, Input, Output
import pandas as pd
import plotly.express as px
# Create the Dash app
app = Dash()
# Set up the app layout
options_dropdown = dcc.Dropdown(options=df1['options'].unique(),
value='wordcount')
app.layout = html.Div(children=[
html.H1(children='offensive/non offensive username activity dashboard'),
options_dropdown,
dcc.Graph(id='df1')
])
# Set up the callback function
#app.callback(
Output(component_id='df1', component_property='figure'),
Input(component_id=options_dropdown, component_property='value')
)
def update_graph(sel_option):
filtered_options = df1[df1['options'] == sel_option]
bar_fig = px.bar(filtered_options,
x= "user", y = "value",
color='user',
color_discrete_map={
'off': '#d62728',
'non_off': 'green'},
title=f' average {sel_option}',
width=500, height=500)
return bar_fig
print(df1)
# Run local server
if __name__ == '__main__':
app.run_server(debug=True)
second one:
import pandas as pd
df2 = pd.read_csv('/df2.csv')
# Import libraries
from dash import Dash, html, dcc, Input, Output
import pandas as pd
import plotly.express as px
# Create the Dash app
app = Dash()
# Set up the app layout
options_dropdown = dcc.Dropdown(options=df2['options'].unique(),
value='wordcount')
app.layout = html.Div(children=[
html.H1(children='offensive/non offensive username activity dashboard'),
options_dropdown,
dcc.Graph(id='df2')
])
# Set up the callback function
#app.callback(
Output(component_id='df2', component_property='figure'),
Input(component_id=options_dropdown, component_property='value')
)
def update_graph(sel_option):
filtered_options = df2[df2['options'] == sel_option]
line_fig = px.line(filtered_options,
x= "Week_Number", y = "value",
color='offensive',
color_discrete_map={
1: '#d62728',
0: 'green'},
title=f' average {sel_option}')
return line_fig
print(df2)
# Run local server
if __name__ == '__main__':
app.run_server(debug=True)
and this is the function I want to implement pressing a button:
sentences = []
df3 = pd.read_csv('/df3.csv')
def cool_function():
ext = rd.randint(0,len(df3.offensive))
return rd.choice(sentences).format(df3.author[ext], "", df3.text[ext])
How do I merge those three elements in a single dashboard?

python plolty dash - read .pkl files and display them in plotly dash

i changed the following code from using ".png" to ".pkl". but this doesn't work
import plotly.express as px
import joblib
#generate example pkl
fig = px.scatter(x=range(10), y=range(10))
joblib.dump(fig, "img_dash/figure.pkl")
#%%
import dash
import dash_core_components as dcc
import dash_html_components as html
import flask
import glob
import os
image_directory = 'img_dash/'
list_of_images = [os.path.basename(x) for x in glob.glob('{}*.pkl'.format(image_directory))]
static_image_route = '/static/'
app = dash.Dash()
app.layout = html.Div([
dcc.Dropdown(
id='image-dropdown',
options=[{'label': i, 'value': i} for i in list_of_images],
value=list_of_images[0]
),
html.Img(id='image')
])
#app.callback(
dash.dependencies.Output('image', 'src'),
[dash.dependencies.Input('image-dropdown', 'value')])
def update_image_src(value):
return static_image_route + value
#app.server.route('{}<image_path>.pkl'.format(static_image_route))
def serve_image(image_path):
image_name = '{}.pkl'.format(image_path)
if image_name not in list_of_images:
raise Exception('"{}" is excluded from the allowed static files'.format(image_path))
return flask.send_from_directory(image_directory, image_name)
if __name__ == '__main__':
app.run_server(debug=False)
i expect to show the .pkl files in the dashboard with hover-function. may other formats also possible! i was not able to check this.
I recommend you to use a Graph component here instead of an Img component since you want to display these figures dynamically.
Since each pkl file already stores a Figure you can just dynamically load these files using joblib in your callback based on the dropdown value.
Example based on your code:
# Example figures for demo purposes
fig1 = px.scatter(x=range(10), y=range(10))
fig2 = px.scatter(x=range(15), y=range(15))
fig3 = px.scatter(x=range(20), y=range(20))
joblib.dump(fig1, "img_dash/figure1.pkl")
joblib.dump(fig2, "img_dash/figure2.pkl")
joblib.dump(fig3, "img_dash/figure3.pkl")
# ...
image_directory = "img_dash/"
list_of_images = [
os.path.basename(x) for x in glob.glob("{}*.pkl".format(image_directory))
]
app = dash.Dash()
app.layout = html.Div(
[
dcc.Dropdown(
id="image-dropdown",
options=[{"label": i, "value": i} for i in list_of_images],
value=list_of_images[0],
),
dcc.Graph(id="graph"),
]
)
#app.callback(
dash.dependencies.Output("graph", "figure"),
[dash.dependencies.Input("image-dropdown", "value")],
)
def update_graph(file):
return joblib.load(os.path.join(image_directory, file))
if __name__ == "__main__":
app.run_server(debug=False)

Plotly-Dash: How to code interactive callbacks for hover functions in plotly dash

Is it possible to have a text field at the bottom of a graph in dash that displays the text for the point they are on (showing hover data as plain text). So the text box will be able to make changes when users hover over a certain point. I have defined a dcc.Graph component and the app layout but am not sure how to define the callback function for the hoverdata.
I have used the below code to define dcc.Graph and app.layout
fig = go.Figure(data=plot_data, layout=plot_layout)
app.layout = html.Div([
dcc.Graph(figure=fig),
html.Div([
dcc.Markdown(id='mpg-metrics')
],style={'width':'20%','display':'inline-block'})
])
Any help with the callback will be great. thanks in advance
Yes, that's very possible! Since you haven't provided a complete description of your setup, I've put together a minimal example that draws on elements from dash.plotly.com/interactive-graphing and https://community.plotly.com/: Use Hover Trace As Input for Callback that among other things describes the use of hover data in callbacks. The code snippet below will produce the following app for JupyterDash. If you'd like to run a standard dash app, just rewrite it following these steps.
The solution I've put together should do exactly what you're aiming for. Every time you hover over a point on one of the lines in the figure in the dcc.Graph component, a set of details about the trace is displayed in the html.Pre component under it, such as x and y values. Try it out and let me know how it works out for you!
App 1:
If you'd like to retrieve only certain elements of the output, you can subset the output like this:
json.dumps({'Date:':hoverData['points'][0]['x'],
'Value:':hoverData['points'][0]['y']}, indent = 2)
App 2:
Complete code for JupyterDash, App1
import json
from textwrap import dedent as d
import pandas as pd
import plotly.graph_objects as go
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output
from jupyter_dash import JupyterDash
# app info
app = JupyterDash(__name__)
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
# data and basic figure
x = np.arange(20)+10
fig = go.Figure(data=go.Scatter(x=x, y=x**2, mode = 'lines+markers'))
fig.add_traces(go.Scatter(x=x, y=x**2.2, mode = 'lines+markers'))
app.layout = html.Div([
dcc.Graph(
id='basic-interactions',
figure=fig,
),
html.Div(className='row', children=[
html.Div([
dcc.Markdown(d("""
Click on points in the graph.
""")),
html.Pre(id='hover-data', style=styles['pre']),
], className='three columns'),
])
])
#app.callback(
Output('hover-data', 'children'),
[Input('basic-interactions', 'hoverData')])
def display_hover_data(hoverData):
return json.dumps(hoverData, indent=2)
app.run_server(mode='external', port = 8070, dev_tools_ui=True,
dev_tools_hot_reload =True, threaded=True)
Complete code for JupyterDash, App2
import json
from textwrap import dedent as d
import pandas as pd
import plotly.graph_objects as go
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output
from jupyter_dash import JupyterDash
# app info
app = JupyterDash(__name__)
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
# data and basic figure
y = np.arange(100)+10
x = pd.date_range(start='1/1/2021', periods=len(y))
fig = go.Figure(data=go.Scatter(x=x, y=y**2, mode = 'lines+markers'))
fig.add_traces(go.Scatter(x=x, y=y**2.2, mode = 'lines+markers'))
app.layout = html.Div([
dcc.Graph(
id='basic-interactions',
figure=fig,
),
html.Div(className='row', children=[
html.Div([
dcc.Markdown(d("""
Click on points in the graph.
""")),
html.Pre(id='hover-data', style=styles['pre']),
], className='three columns'),
])
])
#app.callback(
Output('hover-data', 'children'),
[Input('basic-interactions', 'hoverData')])
def display_hover_data(hoverData):
try:
return json.dumps({'Date:':hoverData['points'][0]['x'],
'Value:':hoverData['points'][0]['y']}, indent = 2)
except:
return None
app.run_server(mode='external', port = 8070, dev_tools_ui=True,
dev_tools_hot_reload =True, threaded=True)

How to subset and generate a large data frame with Python Dash?

I'm trying to create a Dash app that must:
Read a csv file and convert it to a dataframe
Subset this dataframe according to the user's choices (radio buttons,...)
Generate the subseted dataframe in a csv file
The issue is I can't subset the dataframe as I would like.
Let's imagine I got a data frame gas3 with a variable Flag. Here is a part of my code:
import dash
import dash_core_components as dcc # Graphs
import dash_html_components as html # Tags
from dash.dependencies import Input, Output, Event, State
from pandas_datareader import DataReader
import time
import pandas as pd
import plotly
import plotly.graph_objs as go
from collections import deque
import random
import os
import pandas as pd
import glob
import numpy as np
import statistics
from datetime import *
from pytz import *
import dash_table_experiments as dte
import io
from flask import send_file
import flask
import urllib.parse
# Import df
gas3 = pd.read_csv("D:/Path/gas3.csv", sep=';')
gas3['Date_Local_Time'] = pd.to_datetime(gas3['Date_Local_Time'], format='%Y/%m/%d %H:%M') # Conversion de la variable en type "date heure"
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets) # Starting application
app.layout = html.Div(children=[
# Radio buttons
html.Div([
html.Label("Sélectionnez la valeur du Flag que vous souhaitez :",
htmlFor='radioflag',
),
html.Br(),
html.Br(),
dcc.RadioItems(
id='radioflag',
options=[
{'label': 'Flag 0', 'value': 'Flag0'},
{'label': 'Flag 1', 'value': 'Flag1'},
{'label': 'Flag 3', 'value': 'Flag3'},
{'label': 'Chambre à flux', 'value': 'CAF'}
]
),
html.Br(),
]),
# Download button
html.Div(id='table'),
html.A(
'Download Data',
id='download-link',
download="rawdata.csv",
href="",
target="_blank"
)
])
### Subset function
def filter_data(flag):
gas_genere = gas3.copy()
if flag == 'Flag0':
gas_genere = gas_genere[gas_genere['Flag']==0]
return gas_genere
else:
return gas_genere[0:4]
# Callback
#app.callback(
Output('download-link', 'href'),
[Input('radioflag', 'value')])
def update_download_link(flag):
gas_genere = filter_data(flag)
csv_string = gas_genere.to_csv(index=False, encoding='utf-8', sep=';')
csv_string = "data:text/csv;charset=utf-8,%EF%BB%BF" + urllib.parse.quote(csv_string)
return csv_string
if __name__ == '__main__':
app.run_server(debug=True)
I found out the issue but I don't know how to fix it. If I change my filter_data function to (I just added [0:2] to the first return gas_genere) :
def filter_data(flag):
gas_genere = gas3.copy()
if flag == 'Flag0':
gas_genere = gas_genere[gas_genere['Flag']==0]
return gas_genere[0:2]
else:
return gas_genere[0:4]
The app works well! If I check "Flag 0", gas_genere[0:2] will be generated. Else, gas_genere[0:4] will be generated.
But if I keep it like in my original code, I can check "Flag 1", "Flag 3", "Chambre à flux" or don't check any button, it would work and generate gas_genere[0:4]. But if I check "Flag 0", instead of returning gas_genere, I would not get a csv file, but would get an error: "téléchargement Echec - Erreur réseau." which in English should be "Download Failed - Network Error".
I tryed to generate my dataframe gas_genere = gas3[gas3['Flag']==0] in Python and it got 14299 rows and 43 columns.
Is my dataframe too large? If so, what should I do?
Thank you!

Categories

Resources