Skip to content

Commit

Permalink
Merge pull request #891 from fabratu/20220222_plotlybridge
Browse files Browse the repository at this point in the history
Add new module vizbridges: Create visualization widgets in 2D and 3D with Cytoscape and Plotly.
  • Loading branch information
fabratu committed Mar 17, 2022
2 parents 581f3a6 + 8fffc8a commit f9fbdcf
Show file tree
Hide file tree
Showing 8 changed files with 488 additions and 113 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ jobs:
python -c 'import networkit'
pip install -r requirements.txt
python -m unittest discover -v networkit/test/
pip install ipycytoscape plotly seaborn
python notebooks/test_notebooks.py "notebooks"
env:
NATIVE: ${{ matrix.os == 'full (native)' }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/scripts/documentation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pip3 install cython
pip3 install 'sphinx<4.1' sphinx_bootstrap_theme numpydoc exhale nbsphinx breathe
pip3 install sphinx_copybutton sphinxcontrib.bibtex sphinx_gallery sphinx_last_updated_by_git
pip3 install ipykernel ipython matplotlib nbconvert jupyter-client networkx tabulate
pip3 install ipycytoscape plotly seaborn

# Build the C++ core library (no need for optimizations).
mkdir core_build && cd "$_"
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/scripts/full.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ pip3 install -r requirements.txt

python3 -m unittest discover -v networkit/test/

pip3 install ipycytoscape plotly seaborn

python3 notebooks/test_notebooks.py 'notebooks/'
1 change: 1 addition & 0 deletions docs/python_api/modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ Modules
structures
traversal
viz
vizbridges
viztasks
workflows
124 changes: 12 additions & 112 deletions networkit/csbridge.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# local imports
from .support import MissingDependencyError
from .structures import Partition
from .vizbridges import widgetFromGraph

# external imports
try:
Expand All @@ -21,11 +22,15 @@ def widget_from_graph(G, node_scores = None, node_partition = None, node_palette
"""
widget_from_graph(G, node_scores = None, node_partition = None, node_palette = None, show_ids = True)
Note
----
DEPRECATED. Use networkit.vizbridges.widgetFromGraph() instead.
Creates a ipycytoscape-widget from a given graph. The returned widget already contains
all nodes and edges from the graph. The graph is colored using an array of norm. rgb-values
based on seaborn perceptually uniform color map (rocket) or a user given custom color array.
See matplotlib color maps for the correct formatting:
https://matplotlib.org/api/_as_gen/matplotlib.colors.Colormap.html#matplotlib.colors.Colormap
https://matplotlib.org/api/_as_gen/matplotlib.colors.Colormap.html#matplotlib.colors.Colormap
Parameters
----------
Expand All @@ -34,119 +39,14 @@ def widget_from_graph(G, node_scores = None, node_partition = None, node_palette
node_scores : list of numbers, optional
List of scores for each nodes, for example scores from a centrality measure. This is
used for color-calculation of the nodes (continuous distribution). Provide either
node_scores or node_partition - not both.
node_scores or node_partition - not both. Default: None
node_partition : networkit.structures.Partition, optional
Partition object. This is used for color-calculation of the nodes (discrete distribution).
Provide either node_scores or node_partition - not both.
Provide either node_scores or node_partition - not both. Default: None
node_palette : list of tuples, optional
Array consisting of normalized rgb-values. If none is given, seaborn.color_palette.colors is used
Array consisting of normalized rgb-values. If none is given, seaborn.color_palette.colors is used. Default: None
show_ids : boolean, optional
Set whether node ids should be visible in plot-widget. Is set to True by default.
Set whether node ids should be visible in plot-widget. Is set to True by default. Default: None
"""
# Sanity checks
if not have_cyto:
raise MissingDependencyError("ipycytoscape")

if node_scores is not None:
if node_partition is not None:
raise Exception("Provide either node_scores or node_partition - not both.")
if len(node_scores) != G.upperNodeIdBound():
raise Exception("node_scores should include scores for every node.")

# Set color palettes (maybe overwritten below)
if node_palette is not None:
palette = node_palette
else:
if not have_seaborn:
raise MissingDependencyError("seaborn")
palette = seaborn.color_palette("rocket_r", as_cmap=True).colors

# Color calculation: score = continuous distribution, partition = discrete distribution
hc_colors = []

# Partition
if node_partition is not None:
if node_palette is None:
palette = seaborn.color_palette("hls", node_partition.numberOfSubsets())
else:
if len(node_palette) < node_partition.numberOfSubsets():
raise Exception("Number of partitions higher than number of colors in provided palette. Provide node_palette with enough colors.")

partitions = node_partition.getVector()

if len(palette) < node_partition.numberOfSubsets():
raise Exception("Number of partitions to high for default coloring. Provide node_palette with enough colors.")

for i in G.iterNodes():
hc_colors.append((palette[partitions[i]][0] * 255, palette[partitions[i]][1] * 255, palette[partitions[i]][2] * 255))

# Score
elif node_scores is not None:

minhc = min(node_scores)
maxhc = max(node_scores)

# calculate coloring of nodes
def get_rgb(minimum, maximum, value):
minimum, maximum, value = float(minimum), float(maximum), float(value)
ratio = int((len(palette) - 1) * (value-minimum) / (maximum - minimum) * (value-minimum) / (maximum - minimum))
r = int(palette[ratio][0] * 255)
g = int(palette[ratio][1] * 255)
b = int(palette[ratio][2] * 255)
return r, g, b

if abs(maxhc - minhc) > 0:
for score in node_scores:
hc_colors.append(get_rgb(minhc, maxhc, score))
else:
color = palette[int((len(palette) -1) / 2)];
for i in G.iterNodes():
hc_colors.append((color[0] * 255, color[1] * 255, color[2] * 255))

# No node values
else:
color = palette[0];
for i in G.iterNodes():
hc_colors.append((color[0] * 255, color[1] * 255, color[2] * 255))

# Set styling
if show_ids:
s = [{
'selector': 'node',
'css': {
'background-color': 'data(color)',
'content': 'data(id)'
}}]
else:
s = [{
'selector': 'node',
'css': {
'background-color': 'data(color)'
}}]

# Create widget
cytoWidget = ipycytoscape.CytoscapeWidget()

nodes = []
edges = []

if G.isDirected():
edge_class = "directed "
else:
edge_class = "undirected "

for i in G.iterNodes():
n = ipycytoscape.Node(data={"id": i, "color": hc_colors[i] })
nodes.append(n)

for u,v in G.iterEdges():
e = ipycytoscape.Edge(data={"source": u, "target": v, "classes": edge_class })
edges.append(e)

# It is much faster to add edges and nodes in bulk.
cytoWidget.graph.add_nodes(nodes)
cytoWidget.graph.add_edges(edges, G.isDirected())

cytoWidget.set_style(s)
cytoWidget.set_layout(name='cose')
return cytoWidget
print("WARNING: Module csbridge is deprecated and will be removed in future updates. Use networkit.vizbridges.widgetFromGraph() instead.")
return widgetFromGraph(G, nodeScores = node_scores, nodePartition = node_partition, nodePalette = node_palette, showIds = show_ids)
2 changes: 1 addition & 1 deletion networkit/test/test_graphtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ def generateGraph(directed):
self.assertLess(indexNode0, indexNode2)
self.assertLess(indexNode4, indexNode2)
self.assertLess(indexNode2, indexNode1)
self.assertLess(indexNode1, indexNode3)
self.assertLess(indexNode1, indexNode3)

if __name__ == "__main__":
unittest.main()
Loading

0 comments on commit f9fbdcf

Please sign in to comment.