Correct callback for networkx dash connectivity - python

I am trying to create a simple networkx and dash dashboard with a dropdown to select either successors, predecessors, or connected then when I click on a node it will return that info.
For example, if I select predecessors and then click on Texas, it will provide US, but if I select successors and click on it, it will show Houston.
If I select connected and click on Texas, it will respond with US and Houston.
Would anyone know the correct callback function that I would need to create to accomplish this?
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_cytoscape as cyto
from dash.dependencies import Input, Output
import plotly.express as px
app = dash.Dash(__name__)
app.layout = html.Div([
html.P("Dash Cytoscape:"),
cyto.Cytoscape(
id='cytoscape',
elements=[
{'data': {'id': 'ca', 'label': 'Canada'}},
{'data': {'id': 'on', 'label': 'Ontario'}},
{'data': {'id': 'qc', 'label': 'Quebec'}},
{'data': {'id': 'us', 'label': 'US'}},
{'data': {'id': 'ny', 'label': 'New York'}},
{'data': {'id': 'tx', 'label': 'Texas'}},
{'data': {'id': 'fl', 'label': 'Florida'}},
{'data': {'id': 'mia', 'label': 'Miami'}},
{'data': {'id': 'hou', 'label': 'Houston'}},
{'data': {'source': 'ca', 'target': 'on'}},
{'data': {'source': 'ca', 'target': 'qc'}},
{'data': {'source': 'us', 'target': 'ny'}},
{'data': {'source': 'us', 'target': 'tx'}},
{'data': {'source': 'us', 'target': 'fl'}},
{'data': {'source': 'tx', 'target': 'hou'}},
{'data': {'source': 'fl', 'target': 'mia'}}
],
layout = {'name':'breadthfirst', 'directed':True},
style={'width': '400px', 'height': '500px'}
)
])
#app.callback(Output('cytoscape-tapNodeData-output', 'children'),
Input('cytoscape-event-callbacks-2', 'tapNodeData'))
def displayTapNodeData(data):
if data:
return
app.run_server(debug=True)

I add the necessary callback to make the dropdown menu working along with the clicking on the nodes. You need only to manipulate how to extract the data from the list edges and nodes in the callback function, and it will be pure python problem, and you can use directly the edges and nodes list inside the callback function without passing them as parameters.
import dash
import dash_cytoscape as cyto
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
app = dash.Dash(__name__)
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
nodes = [
{
'data': {'id': short, 'label': label},
}
for short, label in (
('la', 'Los Angeles'),
('nyc', 'New York'),
('to', 'Toronto'),
('mtl', 'Montreal'),
('van', 'Vancouver'),
('chi', 'Chicago'),
('bos', 'Boston'),
('hou', 'Houston')
)
]
edges = [
{'data': {'source': source, 'target': target}}
for source, target in (
('van', 'la'),
('la', 'chi'),
('hou', 'chi'),
('to', 'mtl'),
('mtl', 'bos'),
('nyc', 'bos'),
('to', 'hou'),
('to', 'nyc'),
('la', 'nyc'),
('nyc', 'bos')
)
]
default_stylesheet = [
{
'selector': 'node',
'style': {
'background-color': '#BFD7B5',
'label': 'data(label)'
}
}
]
app.layout = html.Div([
cyto.Cytoscape(
id='cytoscape',
layout={'name':'breadthfirst','directed':True},
elements=edges+nodes,
stylesheet=default_stylesheet,
style={'width': '100%', 'height': '450px'}
),
html.Div([
dcc.Dropdown(['predecessors', 'successors', 'connected'], 'predecessors', id='cyto-dropdown')
]),
html.Div(id='my-output'),
])
#app.callback(Output('my-output', 'children'),
[Input('cyto-dropdown', 'value'),
Input('cytoscape', 'tapNodeData')])
def displayTapNodeData(value, data):
if data:
if value == 'successors':
return "The successor node(s): " + data['id']
elif value == 'predecessors':
return "The predecessor node(s) " + data['id']
elif value == 'connected':
return "The connected node(s): " + data['id']
if __name__ == '__main__':
app.run_server(debug=True, use_reloader=False)

Related

python dash cyto layouts - how to use "depthfirstsearch"

I got breadthfirst working like:
app = dash.Dash()
cyto.load_extra_layouts()
app.layout = html.Div([
html.P("Dash Cytoscape:"),
cyto.Cytoscape(
id='cytoscape',
elements=[
{'data': {'id': 'ca', 'label': 'Canada'}},
{'data': {'id': 'on', 'label': 'Ontario'}},
{'data': {'id': 'qc', 'label': 'Quebec'}},
{'data': {'source': 'ca', 'target': 'on'}},
{'data': {'source': 'ca', 'target': 'qc'}}
],
layout={
'name': 'breadthfirst',
"fit": False,
},
style={'width': '100%', 'height': '700px'},
)
])
app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter
but how do I get this other method hacked in?
https://js.cytoscape.org/#eles.depthFirstSearch

Dash Callbacks: multiple outputs showing at different sections in the web app

I want to get from a call back a DF(DataFrame) and the figure for that same DF, but I want to show each one individually.
I will take this example posted by #Philippe in this link just to have something to work with and give some ideas:
import dash
import dash_core_components as dcc
import dash_table as dt
import dash_html_components as html
from dash.dependencies import Output, Input
from dash.exceptions import PreventUpdate
import plotly.graph_objs as go
sample_data = {
'series': {
'data': [
{'title': 'Game of Thrones', 'score': 9.5},
{'title': 'Stranger Things', 'score': 8.9},
{'title': 'Vikings', 'score': 8.6}
],
'style': {
'backgroundColor': '#ff998a'
}
},
'movies': {
'data': [
{'title': 'Rambo', 'score': 7.7},
{'title': 'The Terminator', 'score': 8.0},
{'title': 'Alien', 'score': 8.5}
],
'style': {
'backgroundColor': '#fff289'
}
}
}
app = dash.Dash(__name__)
app.layout = html.Div([
html.H1('Multi output example'),
dcc.Dropdown(id='data-dropdown', options=[
{'label': 'Movies', 'value': 'movies'},
{'label': 'Series', 'value': 'series'}
], value='movies'),
html.Div([
dcc.Graph(id='graph'),
dt.DataTable(id='data-table', columns=[
{'name': 'Title', 'id': 'title'},
{'name': 'Score', 'id': 'score'}
])
])
], id='container')
#app.callback([
Output('graph', 'figure'),
Output('data-table', 'data'),
Output('data-table', 'columns'),
Output('container', 'style')
], [Input('data-dropdown', 'value')])
def multi_output(value):
if value is None:
raise PreventUpdate
selected = sample_data[value]
data = selected['data']
columns = [
{'name': k.capitalize(), 'id': k}
for k in data[0].keys()
]
figure = go.Figure(
data=[
go.Bar(x=[x['score']], text=x['title'], name=x['title'])
for x in data
]
)
return figure, data, columns, selected['style']
if __name__ == '__main__':
app.run_server()
In the end a results showing the fig and data of the DataFrame is shown
like this.
However, I want to first show the figure. Then I want to use the data from the DF created in the callback to do more calculations and finally display those calculations using a submit button you click on it.
Is this possible?
What I have done so far is using two different callbacks with kind of the same script, but I was wondering if I can used just one callback a save some repetition in the code.
I hope my question is clear, and thank you in advance!
I found a solution!I used dcc.store here some references if someone need it in the future: https://dash.plotly.com/dash-core-components/store
My line code was this one:
dcc.Store(id='store-data', data=[], storage_type='memory'),
Taking from https://www.youtube.com/watch?v=dLykSQNIM1E. Great video created by Youtube Channel: Charming Data

Can I make a node clickable in dash as a hyperlink?

I have this code (from here) and it generates a network as expected:
import dash
import dash_core_components as dcc
from dash import html
import dash_cytoscape as cyto
from dash.dependencies import Input, Output
import plotly.express as px
app = dash.Dash(__name__)
app.layout = html.Div([
html.P("Dash Cytoscape:"),
cyto.Cytoscape(
id='cytoscape',
elements=[
{'data': {'id': 'ca', 'label': 'Canada'}},
{'data': {'id': 'on', 'label': 'Ontario'}},
{'data': {'id': 'qc', 'label': 'Quebec'}},
{'data': {'source': 'ca', 'target': 'on'}},
{'data': {'source': 'ca', 'target': 'qc'}}
],
layout={'name': 'breadthfirst'},
style={'width': '1000px', 'height': '1000px'}
)
])
if __name__ == '__main__':
app.run_server()
I'm just wondering, would someone know if I'm able to/how to edit this code so that I can click on a node, and it's a hyperlink to a webpage (e.g. in this case could just be the wikipedia page for each node).
You could create a callback which sets the href property of a dcc.Location component based on the clicked node:
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Location(id="location"),
cyto.Cytoscape(
id="cytoscape",
elements=[
{"data": {"id": "ca", "label": "Canada"}},
{"data": {"id": "on", "label": "Ontario"}},
{"data": {"id": "qc", "label": "Quebec"}},
{"data": {"source": "ca", "target": "on"}},
{"data": {"source": "ca", "target": "qc"}},
],
layout={"name": "breadthfirst"},
style={"width": "1000px", "height": "1000px"},
),
]
)
#app.callback(
Output("location", "href"),
Input("cytoscape", "tapNodeData"),
prevent_initial_call=True,
)
def navigate_to_url(node_data):
return f"https://en.wikipedia.org/wiki/{node_data['label']}"

Using dash_cytoscape line-gradient-stop-colors

I am attempting to create gradients within the edges of a graph in dash_cytoscape using line-gradient-stop-colors (from the js.cytoscape documentation). I am doing this with a stylesheet that describes a gradient layout for all of the edges in my graph. Example code is below, however it creates a graph with just grey edges.
import dash
import dash_cytoscape as cyto
from dash import html
app = dash.Dash(__name__)
style_1 = [
{
'selector': 'node',
'style': {
'label': 'data(id)',
'background-color': 'blue',
}
},
{
'selector': 'edge',
'style': {
'line-gradient-stop-colors': 'cyan magenta yellow', # these are the lines that I have issue with
'line-gradient-stop-positions': '25 50 75',
}
}
]
app.layout = html.Div([
cyto.Cytoscape(
id='cytoscape-elements-boolean',
layout={'name': 'preset'},
style={'width': '100%', 'height': '800px'},
stylesheet=style_1,
elements=[
{
'data': {'id': 'one'},
'position': {'x': 75, 'y': 75},
},
{
'data': {'id': 'two'},
'position': {'x': 75, 'y': 200},
},
{
'data': {'id': 'three'},
'position': {'x': 200, 'y': 75},
},
{
'data': {'id': 'four'},
'position': {'x': 200, 'y': 200}
},
{'data': {'source': 'one', 'target': 'two'}},
{'data': {'source': 'two', 'target': 'three'}},
{'data': {'source': 'three', 'target': 'four'}},
{'data': {'source': 'two', 'target': 'four'}},
]
)
])
if __name__ == '__main__':
app.run_server(debug=True)
I believe the issue is how I have the line-gradient-stop-colors string set up but I've tried multiple options and none of them have worked. Any help would be greatly appreciated!

How to make directed graph using Dash

I copied this code:
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_cytoscape as cyto
from dash.dependencies import Input, Output
import plotly.express as px
app = dash.Dash(__name__)
app.layout = html.Div([
html.P("Dash Cytoscape:"),
cyto.Cytoscape(
id='cytoscape',
elements=[
{'data': {'id': 'ca', 'label': 'Canada'}},
{'data': {'id': 'on', 'label': 'Ontario'}},
{'data': {'id': 'qc', 'label': 'Quebec'}},
{'data': {'source': 'ca', 'target': 'on'}},
{'data': {'source': 'ca', 'target': 'qc'}}
],
layout={'name': 'breadthfirst'},
style={'width': '400px', 'height': '500px'}
)
])
app.run_server(debug=True)
How do I add directed edges in this graph? I have seen examples online and they all add Matplotlib and Plotly to it. I want to change it with minimal changes. Is there any way for that?
I'm not sure if you are talking about making your layout (i) directed or to have (ii) edge arrows (or both) :
(i) You can add the parameter 'directed':True to your layout. The new layout would be:
layout = {'name':'breadthfirst', 'directed':True}
Other available parameters can be checked on the Cytoscape JavaSript documentation -> https://js.cytoscape.org/
(ii) You can check an example for Edge Arrows on Dash_cytoscape documentation -> https://dash.plotly.com/cytoscape/styling
You can add the source-arrow-shape or target-arrow-shape attribute inside the style object as the following example:
{'selector': '#BA',
'style': {
'source-arrow-color': 'red',
'source-arrow-shape': 'triangle',
'line-color': 'red'
}
},

Categories