how can i make interactive network graph? - networkx

I have plot a figure to highlight the gas flow and direction in gas network like enter image description here
however, i wish make it interactive. I tried several ways but failed, is there idea to develop this?
many thanks!

You can use pyvis package to get interactive graph in a static HTML file.
import networkx as nx
from pyvis.network import Network
# Create directed graph with networkx
G = nx.DiGraph()
G.add_nodes_from([
('1', {'namespace': ['1n'], 'workload': {1: '1w'}}),
('2', {'namespace': ['2n'], 'workload': '2w'}),
('3', {'namespace': ['3n'], 'workload': '3w'}),
])
G.add_edges_from([
('1', '2', {'weight': 1}),
('2', '1', {'weight': 1}),
('2', '3', {'weight': 5}),
])
# Plot with pyvis
net = Network(
directed = True,
select_menu = True, # Show part 1 in the plot (optional)
filter_menu = True, # Show part 2 in the plot (optional)
)
net.show_buttons() # Show part 3 in the plot (optional)
net.from_nx(G) # Create directly from nx graph
net.show('test.html')
The output HTML file will show this:

Related

CRS for HS2 snakegrid plotting in wrong location on Folium and Leaflet

I've been trying to plot some geographical date which is the HS2 snakegrid co-ordinate system
According to ESPG HS2 ESPG Geodetic Parameters Page I should be using CRS 9300 (although have also tried 9306)
When used, the location is not plotting correctly, example below uses folium to generate a basemap in WGS 84 and geopandas to add a point with the CRS changed to the HS2 9300.
The co-ordinates used should place the point just outside Euston Station but instead is plotting in Hyde Park
I've used Jupyter notebooks to run the below code, although i've also run in geodjango so i could use leaflet with proj4js string for HS2 and same thing
import folium
from folium import plugins
import geopandas as gpd
from shapely.geometry import Point
f = folium.Figure()
m = folium.Map(
location=[51.5091, -0.16006],
max_zoom = 22,
zoom_start = 16,
tiles = None,
control_scale = True,
prefer_canvas = True,
crs = 'EPSG3857',
)
folium.TileLayer('cartodbpositron', max_zoom = 22, detect_retina = True, name = 'Carto',).add_to(m)
s = gpd.GeoSeries([Point(292001.0182, 288039.0657),],)
s = s.set_crs('epsg:9300')
s.explore(name = "S", m = m, highlight = True, show = False, style_kwds={'color': 'green','fill': False},)
example output
UPDATE
I need to use HS2TN15_NTv2.gsb transformation file within pyproj
Reading the pyproj docs i should use pyproj.datadir.get_data_dir() to identify the data directory which gives \Lib\site-packages\pyproj\proj_dir\share\proj
However when i then run TransformerGroup("epsg:4258","epsg:9300") I still get the following warning
\lib\site-packages\pyproj\transformer.py:184: UserWarning: Best
transformation is not available due to missing
Grid(short_name=HS2TN15_NTv2.gsb, full_name=, package_name=, url=,
direct_download=False, open_license=False, available=False)
super().init(

Editing the labels on a flow chart with DiagrammeR

I’m trying to make a flow chart with R. Attached is the chart I made in word (which is what I'm trying to get to). I don’t want to copy and paste it, I want to actually make it in R. I’ve been using DiagrammeR to try, and the code is below.
I'm having the main trouble with the labels, how to change some parts to bold and make them a nice distance away from the nodes. I've added in the blue and pink boxes in my code, which I like.
Code:
library(DiagrammeR)
graph <- "
digraph boxes_and_circles{
# Add node statements
# This states that all following nodes have a box shape
node[
shape=box,
style=rounded,
fontname=Helvetica,
penwidth=2,
fixedsize = true
width=4
]
# Connect the nodes with edge statements
edge[
arrowhead = normal,
arrowtail = none
]
# These are the main nodes at top of graph
'##1'->'##2'
[label=' Cleaning Function:
Text to lower case
Contractions expanded
Numbers replaced
Abbreviations expanded (Qdap)
NA’s ignored
Kerns replaced
White space removed', fontname=Helvetica, fontsize=20, fontweight=bold]
'##2'->'##3'
'##2'->'##4'
# Make subnodes with boxes around for tidy text grouping
# graph, node, and edge definitions
graph [
compound = true,
nodesep = 1,
ranksep = 0.25,
color = pink
]
# subgraph for tidy text, direct the flow
subgraph cluster0 {
'##3'->'##5'
[label=' -Tokenization
-Lemetisation
-Stop words removed', fontname=Helvetica, fontsize=20, fontweight=bold]
}
# Make subnodes with boxes around for Dictionary grouping
# graph, node, and edge definitions
graph [
compound = true,
nodesep = 1,
ranksep = .25,
color = blue
]
# subgraph for Dictionary direct the flow
subgraph cluster1 {
node [
fixedsize = true,
width = 3
]
'##4'->'##6' [label=' Scoring function (sentimentr)
Inner Join (dplyr)',fontname=Helvetica]
'##6'->'##7' [label=' Grouping
Summarise (dplyr)',fontname=Helvetica]
'##7'->'##8'
}
#Add a graph statement to change the properties of the graph
graph[nodesep=1] #this modifies distance between nodes
}
# Name the nodes
[1]: 'Response Data'
[2]: 'Clean Data'
[3]: 'Tidy Text'
[4]: 'Dictionary Creation'
[5]: 'Visualisation'
[6]: 'Sentiment Lexicon'
[7]: 'Summarised Text'
[8]: 'Visualisation and Statistics'
"

HierarchicalGraphMachine hiding nested states

I've been experimenting with the HierarchicalGraphMachine class to help visualise the machine structures as I edit them.
from transitions.extensions import HierarchicalGraphMachine as Machine
count_states = ['1', '2', '3', 'done']
count_trans = [
['increase', '1', '2'],
['increase', '2', '3'],
['decrease', '3', '2'],
['decrease', '2', '1'],
['done', '3', 'done'],
['reset', '*', '1']
]
counter = Machine(states=count_states, transitions=count_trans, initial='1')
states = ['waiting', 'collecting', {'name': 'counting', 'children': counter, 'initial': '1'}]
transitions = [
['collect', '*', 'collecting'],
['wait', '*', 'waiting'],
['count', 'collecting', 'counting']
]
collector = Machine(states=states, transitions=transitions, initial='waiting')
collector.get_graph(show_roi=False).draw('count1.png', prog='dot')
This generates the expected graphic showing both the parent and nested states in full (I'm not yet authorised to upload the graphics).
Is there a way to generate a the full parent state machine graphic without expanding the nested states? For example reducing the nested states to an empty box.
I've tried "show_roi=True", but this only shows the current transition event, and removes all other states.
Depending on whether you use the pygraphviz (default in 0.8.8 and prior) or graphviz backend, get_graph may return a pygraphiv.AGraph object or a custom transitions.Graph. An AGraph is easier to manipulate while the second is basically the pure graph notation in dot. However, you can manipulate both according to your needs. For instance, you could filter edges and nodes from an AGraph and rebuild a 'flat' version of it:
# your code here ...
collector.collect()
graph = collector.get_graph()
# iterate over all edges; We know that parent and child states are connected
# with an underscore. We just collect the root element of each source
# and target element of each edge. Furthermore, we collect the edge color,
# and the label which is stored either in 'label', 'taillabel' or 'headlabel'
new_edges = [(edge[0].split('_')[0],
edge[1].split('_')[0],
edge.attr['color'],
edge.attr['label']
or edge.attr['taillabel']
or edge.attr['headlabel']) for edge in graph.edges()]
# States with children are noted as subgraphs. We collect their name and their
# current color.
new_nodes = [(sgraph.graph_attr['label'], sgraph.graph_attr['color'])
for sgraph in graph.subgraphs()]
# We add all states that have no children and also do not contain an
# underscore in their name. An underscore would suggest that this node/state
# is a child/substate.
new_nodes += [(node.name, node.attr['color'])
for node in graph.nodes() if '_' not in node.name]
# remove everything from the graph obeject
graph.clear()
# and add nodes and edges again
for name, color in new_nodes:
graph.add_node(name, color=color)
for start, target, color, label in new_edges:
if label:
graph.add_edge(start, target, color=color, label=label)
graph.draw('agraph.png', prog='dot')
This results in the following graph:
You see that I also collected the edge and node color to visualize the last transition but graph.clear() removed all the 'default' styling attributes.
They could be copied and restored as well or we could only remove nodes, edges and subgraphs. This depends on how much you are willing to mess with (py)graphviz.

How to keep selected data persistent through callback in Dash/Plotly's clustered bar chart

I'm using Dash to plot some data. I currently have a clustered bar chart with two data sets (one for each bar in the clusters.) These data sets have their name and the corresponding color of the bars displayed in the top, left-hand corner of the figure. They can be clicked to be toggled on and off, which will remove their corresponding bars from the chart.
Separately, I have a checklist of items that can be displayed in the chart. I am using a callback to update the graph so that it only displays what the user has checked. This updates the graph as expected, however, it also resets the bars/datasets such that both are enabled. Ie. if you select only one of the bars, then select some new checklist items, it will display the new checklist items and both of the bars.
My thinking is that the logical way to do this is to pass some variable as a second input to the callback function, then set up the outputted figure within the function to only display the proper bars. However, I can't seem to find a variable that contains this data.
From what I can tell, the accessible properties of the Plotly graph object are 'id', 'clickData', 'clickAnnotationData', 'hoverData', 'clear_on_unhover', 'selectedData', 'relayoutData', 'figure', 'style', 'className', 'animate', 'animation_options', 'config', and 'loading_state'.
I've investigated all of these, and it seems that none hold the data that I am looking for. Does anyone know of an easy way to access this data?
This is how my callback is working right now:
#app.callback(
dash.dependencies.Output('theGraph', 'figure'),
[dash.dependencies.Input('theChecklist','values'),
dash.dependencies.Input('theGraph', 'clickData')
]
)
def updateGraph(checklistValues, figureInput):
#print to see what the variables hold
print(checklistValues)
print(figureInput)
figure=go.Figure(
data = [
go.Bar(
x = df[df['MyColumnName'].isin(checklistValues)].groupby('MyColumnName').size().index,
y = df[df['MyColumnName'].isin(checklistValues)].groupby('MyColumnName').size().values,
name = 'Bar 1'
),
go.Bar(
x = df[df['MyColumnName'].isin(checklistValues)].groupby('MyColumnName')['# cores'].sum().reset_index()['MyColumnName'],
y = df[df['MyColumnName'].isin(checklistValues)].groupby('MyColumnName')['# cores'].sum().reset_index()['MyOtherColumnName'],
name = 'Bar 2'
)
],
layout=go.Layout(
title='The Title',
showlegend=True,
legend=go.layout.Legend(
x=0,
y=1.0
),
margin=go.layout.Margin(l=40, r=40, t=40, b=110)
)
)
return figure

How to change color in networkx graph plotted with holoviews/bokeh?

How can I change the color of individual nodes in the following example?
%pylab inline
import pandas as pd
import networkx as nx
import holoviews as hv
hv.extension('bokeh')
G = nx.Graph()
ndxs = [1,2,3,4]
G.add_nodes_from(ndxs)
G.add_weighted_edges_from([(1,2,0), (1,3,1), (1,4,-1),
(2,4,1), (2,3,-1), (3,4,10)])
hv.extension('bokeh')
%opts Graph [width=400 height=400]
padding = dict(x=(-1.1, 1.1), y=(-1.1, 1.1))
hv.Graph.from_networkx(G, nx.layout.spring_layout).redim.range(**padding)
The graph as you currently define it does not define any attributes but you could still color by the node index. To color by a particular node attribute you can use the color_index option along with a cmap. Here's how we would color by the 'index'
graph = hv.Graph.from_networkx(G, nx.layout.spring_layout)
graph.options(color_index='index', cmap='Category10').redim.range(**padding)
If you do have attributes defined on the nodes the next version of HoloViews (1.10.5) due to be released this week will be able to extract them automatically and let you use the same approach to color by those variables.
If you want to manually add node attributes until the next release you can pass in a Dataset with a single key dimension defining the node indices and any attributes you want to add defined as value dimensions, e.g.:
nodes = hv.Dataset([(1, 'A'), (2, 'B'), (3, 'A'), (4, 'B')], 'index', 'some_attribute')
hv.Graph.from_networkx(G, nx.layout.spring_layout, nodes=nodes).options(color_index='some_attribute', cmap='Category10')
Thanks to Philippjfr, here is a nice solution (using the current development version of holoviews) that uses node attributes for coloring:
%pylab inline
import pandas as pd
import networkx as nx
import holoviews as hv
hv.extension('bokeh')
G = nx.Graph()
ndxs = [1,2,3,4]
G.add_nodes_from(ndxs)
G.add_weighted_edges_from([(1,2,0), (1,3,1), (1,4,-1),
(2,4,1), (2,3,-1), (3,4,10)])
attributes = {ndx: ndx%2 for ndx in ndxs}
nx.set_node_attributes(G, attributes, 'some_attribute')
%opts Graph [width=400 height=400]
padding = dict(x=(-1.1, 1.1), y=(-1.1, 1.1))
hv.Graph.from_networkx(G, nx.layout.spring_layout)\
.redim.range(**padding)\
.options(color_index='some_attribute', cmap='Category10')