Plotly and Dash
plotly js python dash and flask
Plotly Express
High level api for common charts
Pie - Plotly - Python Pie Charts
fig = px.pie(df, values='id', names='response_type', title='Response Type',
hole=0.3, template=my_template)
Bar
fig = px.bar(df_graph, x='response_type', y = 'count', title = period, color='response_type',
template='none', width=1000, height=500)
Plotly
- poltly built on top of D3
- plotly python api uses plotly.js
Plotly Library:
- data - result of go.chartType(x=, y=, others=....)
- layout - title, axis, annotations
- has param
updatemenus
- There are four possible update methods:
- "restyle": modify data or data attributes
- "relayout": modify layout attributes
- "update": modify data and layout attributes
- "animate": start or pause an animation
- has param
- frames: used for animations
- we can add different frames to a chart
- this can be used to produce the animations
- Example can be found here.
- figure - final object combining data and layout.
Plotly and Flask
- Flask
dash()
route has a html form to show filters - JS on form change,
POST
s to acallback
url with form data and csrf. callback()
sends form data, filters-json toget_graph
and returns figure-json.get_graph()
reads data, filters, builds figure, returns json.
@bp.route('/dash', methods=["GET", "POST"])
def dash():
header = "Global Temperature"
subheader = "Global Temperature changes over the last few centuries"
description = """The graph shows the increase in temperature year on year.
The data spans the years 1881 to 2022 and includes temperature anomalies for periods of each year as indicated.
"""
menu_label = "Select a period"
params = {
'title': header,
'subtitle': subheader,
'content' : description,
'menu_label': menu_label,
'options' : [{'code':'50', 'desc':'Latest Week'},
{'code':'48','desc':'Week 48'},
{'code':'49','desc':'Week 49'},
{'code':'50','desc':'Week 50'},
{'code':'0','desc':'All'}],
'graph' : get_graph()
}
return render_template('viz/dash.html', params=params)
@bp.route('/callback', methods=['POST'])
def callback():
"""Ajax Request Handler for Charts
Reads filter json
Calls get_graph with filters
Returns:
json: figure json
"""
if request.is_json:
data = request.get_json()
# print(f"{list(data.keys())}")
# do something with the incoming data and return the appropiate data
return get_graph(filter=data['dropdown'])
else:
return jsonify({"error": "Invalid JSON data"}), 400
def get_graph(filter = '50'):
import pandas as pd
import plotly.express as px
df = pd.read_csv(...)
df_filtered = df[ df['dim1'] == filter ]
fig = px.bar(df_filtered, x='dim2', y='measure1')
graphJSON = fig.to_json()
return json.dumps(graphJSON)
HTML Template
<form>...</form>
<div id="graph"></div>
{% block scripts %}
{{ super() }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/plotly.js/2.27.1/plotly.min.js"></script>
<script>
function getFormValues(f) {
const form = document.forms.namedItem(f);
const formData = new FormData(form);
const value = Object.fromEntries(formData.entries());
postJSON(value);
}
async function postJSON(data) {
var csrf_token = "{{ csrf_token() }}";
try {
const response = await fetch("{{ url_for('viz.callback') }}", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": csrf_token
},
body: JSON.stringify(data),
});
const result = await response.json();
console.log("Success:");//, result);
drawGraph(result);
}
catch (error) {
console.error("Error:", error);
}
}
function drawGraph(graph) {
var figure = JSON.parse(graph);
// console.log(figure);
Plotly.newPlot(id='graph', figure, {});
}
getFormValues('form1');
</script>
{% endblock %}
Sub Plots
You can add graphs to one figure using subplots
.
More on Plotly - Python Api Reference Plotly
Multi Plots
Often the requirement is to plot more than one graphs. Use below logic for same:
- Build each fig in
get_graph()
. - Create a list,
graphsJSON
and appendfig.to_json()
for each figure. - Now you can return this list of json of figures to the callback. In JS you can iterate over this list and build graphs.
- In JS,
- Iterate over list
- Create
div
s for each figure and draw plot.
- Json handling remains the same
def get_graph(period = '50'):
...
fig1 = px.bar(...)
fig2 = px.pie(...)
fig3 = px.pie(...)
figures = [fig1, fig2, fig3]
graphsJSON = []
for fig in figures:
graphsJSON.append(fig.to_json())
return graphsJSON
function drawGraph(graphsJSON) {
const container = document.querySelector('#dash');
for (let i = 0; i < graphsJSON.length; i++) {
graphJSON=graphsJSON[i];
let element = document.createElement('div');
element.id='graph'+i;
container.appendChild(element);
var figure = JSON.parse(graphJSON);
Plotly.newPlot(id='graph'+i, figure, {});
}
}
Theme and Templates
- More on Plotly - Python Templates
- and on Geeksforgeeks - Python Plotly How To Set Up A Color Palette
Dash and Flask
Integration Philosophy - Things should be as decoupled as possible, that is, database models should work standalone without flask or dash, that means as python module. Flask should enter only when route has to render template. Dash should only enter when a route has to show dashboard. So, every thing is Python, then all pages are flask, then all dashboards are Dash.
AnnMarieW does eveything dash. even login pages, which is adding complexity.
Dash as main app prevents using flask capability.
So, build Flask app and withing that have Dash dashboard.
Best example is of okomarov.
Dash
Dash is putting and linking many plotly charts together.
- Dash is Python framework for building analytical web applications.
- It is built on Flask, Plotly.js and React.js
- Just like flask, we define
app = dash.Dash()
and then at endapp.run_server()
- We can create complete site with links.
- It has intractable story.
Adding controls
# Add controls to build the interaction
@callback(
Output(component_id='controls-and-graph', component_property='figure'),
Input(component_id='controls-and-radio-item', component_property='value')
) # decorator ends here and below is its function
def update_graph(col_chosen):
fig = px.histogram(df, x='continent', y=col_chosen, histfunc='avg')
return fig
Here, component_property
of the Input
is col_chosen
arg to update_graph()
function. So whenever the value
is changed in radio-button, update_graph()
is triggered, it builds the histogram with updated value and returns the figure.
Chart Studio
- is like Tableau web edit and public.
- Can create and host data, charts and dashboards.
- can explore other people's work.
- charts are interactable and linked together.
- can be reverse engineered.
- can host notebooks as well.
Links
- How and why I used Plotly (instead of D3)
- 4 interactive Sankey diagrams made in Python
- https://hackersandslackers.com/plotly-dash-with-flask/
- https://github.com/AnnMarieW/dash-multi-page-app-demos - moves everything to dash including core service features like login.