Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
6
venv/Lib/site-packages/networkx/drawing/__init__.py
Normal file
6
venv/Lib/site-packages/networkx/drawing/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
# graph drawing and interface to graphviz
|
||||
|
||||
from .layout import *
|
||||
from .nx_pylab import *
|
||||
from . import nx_agraph
|
||||
from . import nx_pydot
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1211
venv/Lib/site-packages/networkx/drawing/layout.py
Normal file
1211
venv/Lib/site-packages/networkx/drawing/layout.py
Normal file
File diff suppressed because it is too large
Load diff
483
venv/Lib/site-packages/networkx/drawing/nx_agraph.py
Normal file
483
venv/Lib/site-packages/networkx/drawing/nx_agraph.py
Normal file
|
@ -0,0 +1,483 @@
|
|||
"""
|
||||
***************
|
||||
Graphviz AGraph
|
||||
***************
|
||||
|
||||
Interface to pygraphviz AGraph class.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.complete_graph(5)
|
||||
>>> A = nx.nx_agraph.to_agraph(G)
|
||||
>>> H = nx.nx_agraph.from_agraph(A)
|
||||
|
||||
See Also
|
||||
--------
|
||||
Pygraphviz: http://pygraphviz.github.io/
|
||||
"""
|
||||
import os
|
||||
import tempfile
|
||||
import networkx as nx
|
||||
|
||||
__all__ = [
|
||||
"from_agraph",
|
||||
"to_agraph",
|
||||
"write_dot",
|
||||
"read_dot",
|
||||
"graphviz_layout",
|
||||
"pygraphviz_layout",
|
||||
"view_pygraphviz",
|
||||
]
|
||||
|
||||
|
||||
def from_agraph(A, create_using=None):
|
||||
"""Returns a NetworkX Graph or DiGraph from a PyGraphviz graph.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
A : PyGraphviz AGraph
|
||||
A graph created with PyGraphviz
|
||||
|
||||
create_using : NetworkX graph constructor, optional (default=None)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
If `None`, then the appropriate Graph type is inferred from `A`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> K5 = nx.complete_graph(5)
|
||||
>>> A = nx.nx_agraph.to_agraph(K5)
|
||||
>>> G = nx.nx_agraph.from_agraph(A)
|
||||
|
||||
Notes
|
||||
-----
|
||||
The Graph G will have a dictionary G.graph_attr containing
|
||||
the default graphviz attributes for graphs, nodes and edges.
|
||||
|
||||
Default node attributes will be in the dictionary G.node_attr
|
||||
which is keyed by node.
|
||||
|
||||
Edge attributes will be returned as edge data in G. With
|
||||
edge_attr=False the edge data will be the Graphviz edge weight
|
||||
attribute or the value 1 if no edge weight attribute is found.
|
||||
|
||||
"""
|
||||
if create_using is None:
|
||||
if A.is_directed():
|
||||
if A.is_strict():
|
||||
create_using = nx.DiGraph
|
||||
else:
|
||||
create_using = nx.MultiDiGraph
|
||||
else:
|
||||
if A.is_strict():
|
||||
create_using = nx.Graph
|
||||
else:
|
||||
create_using = nx.MultiGraph
|
||||
|
||||
# assign defaults
|
||||
N = nx.empty_graph(0, create_using)
|
||||
if A.name is not None:
|
||||
N.name = A.name
|
||||
|
||||
# add graph attributes
|
||||
N.graph.update(A.graph_attr)
|
||||
|
||||
# add nodes, attributes to N.node_attr
|
||||
for n in A.nodes():
|
||||
str_attr = {str(k): v for k, v in n.attr.items()}
|
||||
N.add_node(str(n), **str_attr)
|
||||
|
||||
# add edges, assign edge data as dictionary of attributes
|
||||
for e in A.edges():
|
||||
u, v = str(e[0]), str(e[1])
|
||||
attr = dict(e.attr)
|
||||
str_attr = {str(k): v for k, v in attr.items()}
|
||||
if not N.is_multigraph():
|
||||
if e.name is not None:
|
||||
str_attr["key"] = e.name
|
||||
N.add_edge(u, v, **str_attr)
|
||||
else:
|
||||
N.add_edge(u, v, key=e.name, **str_attr)
|
||||
|
||||
# add default attributes for graph, nodes, and edges
|
||||
# hang them on N.graph_attr
|
||||
N.graph["graph"] = dict(A.graph_attr)
|
||||
N.graph["node"] = dict(A.node_attr)
|
||||
N.graph["edge"] = dict(A.edge_attr)
|
||||
return N
|
||||
|
||||
|
||||
def to_agraph(N):
|
||||
"""Returns a pygraphviz graph from a NetworkX graph N.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
N : NetworkX graph
|
||||
A graph created with NetworkX
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> K5 = nx.complete_graph(5)
|
||||
>>> A = nx.nx_agraph.to_agraph(K5)
|
||||
|
||||
Notes
|
||||
-----
|
||||
If N has an dict N.graph_attr an attempt will be made first
|
||||
to copy properties attached to the graph (see from_agraph)
|
||||
and then updated with the calling arguments if any.
|
||||
|
||||
"""
|
||||
try:
|
||||
import pygraphviz
|
||||
except ImportError as e:
|
||||
raise ImportError("requires pygraphviz " "http://pygraphviz.github.io/") from e
|
||||
directed = N.is_directed()
|
||||
strict = nx.number_of_selfloops(N) == 0 and not N.is_multigraph()
|
||||
A = pygraphviz.AGraph(name=N.name, strict=strict, directed=directed)
|
||||
|
||||
# default graph attributes
|
||||
A.graph_attr.update(N.graph.get("graph", {}))
|
||||
A.node_attr.update(N.graph.get("node", {}))
|
||||
A.edge_attr.update(N.graph.get("edge", {}))
|
||||
|
||||
A.graph_attr.update(
|
||||
(k, v) for k, v in N.graph.items() if k not in ("graph", "node", "edge")
|
||||
)
|
||||
|
||||
# add nodes
|
||||
for n, nodedata in N.nodes(data=True):
|
||||
A.add_node(n)
|
||||
# Add node data
|
||||
a = A.get_node(n)
|
||||
a.attr.update({k: str(v) for k, v in nodedata.items()})
|
||||
|
||||
# loop over edges
|
||||
if N.is_multigraph():
|
||||
for u, v, key, edgedata in N.edges(data=True, keys=True):
|
||||
str_edgedata = {k: str(v) for k, v in edgedata.items() if k != "key"}
|
||||
A.add_edge(u, v, key=str(key))
|
||||
# Add edge data
|
||||
a = A.get_edge(u, v)
|
||||
a.attr.update(str_edgedata)
|
||||
|
||||
else:
|
||||
for u, v, edgedata in N.edges(data=True):
|
||||
str_edgedata = {k: str(v) for k, v in edgedata.items()}
|
||||
A.add_edge(u, v)
|
||||
# Add edge data
|
||||
a = A.get_edge(u, v)
|
||||
a.attr.update(str_edgedata)
|
||||
|
||||
return A
|
||||
|
||||
|
||||
def write_dot(G, path):
|
||||
"""Write NetworkX graph G to Graphviz dot format on path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph
|
||||
path : filename
|
||||
Filename or file handle to write
|
||||
"""
|
||||
A = to_agraph(G)
|
||||
A.write(path)
|
||||
A.clear()
|
||||
return
|
||||
|
||||
|
||||
def read_dot(path):
|
||||
"""Returns a NetworkX graph from a dot file on path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File name or file handle to read.
|
||||
"""
|
||||
try:
|
||||
import pygraphviz
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"read_dot() requires pygraphviz " "http://pygraphviz.github.io/"
|
||||
) from e
|
||||
A = pygraphviz.AGraph(file=path)
|
||||
gr = from_agraph(A)
|
||||
A.clear()
|
||||
return gr
|
||||
|
||||
|
||||
def graphviz_layout(G, prog="neato", root=None, args=""):
|
||||
"""Create node positions for G using Graphviz.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
A graph created with NetworkX
|
||||
prog : string
|
||||
Name of Graphviz layout program
|
||||
root : string, optional
|
||||
Root node for twopi layout
|
||||
args : string, optional
|
||||
Extra arguments to Graphviz layout program
|
||||
|
||||
Returns
|
||||
-------
|
||||
Dictionary of x, y, positions keyed by node.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.petersen_graph()
|
||||
>>> pos = nx.nx_agraph.graphviz_layout(G)
|
||||
>>> pos = nx.nx_agraph.graphviz_layout(G, prog="dot")
|
||||
|
||||
Notes
|
||||
-----
|
||||
This is a wrapper for pygraphviz_layout.
|
||||
"""
|
||||
return pygraphviz_layout(G, prog=prog, root=root, args=args)
|
||||
|
||||
|
||||
def pygraphviz_layout(G, prog="neato", root=None, args=""):
|
||||
"""Create node positions for G using Graphviz.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
A graph created with NetworkX
|
||||
prog : string
|
||||
Name of Graphviz layout program
|
||||
root : string, optional
|
||||
Root node for twopi layout
|
||||
args : string, optional
|
||||
Extra arguments to Graphviz layout program
|
||||
|
||||
Returns
|
||||
-------
|
||||
node_pos : dict
|
||||
Dictionary of x, y, positions keyed by node.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.petersen_graph()
|
||||
>>> pos = nx.nx_agraph.graphviz_layout(G)
|
||||
>>> pos = nx.nx_agraph.graphviz_layout(G, prog="dot")
|
||||
|
||||
Notes
|
||||
-----
|
||||
If you use complex node objects, they may have the same string
|
||||
representation and GraphViz could treat them as the same node.
|
||||
The layout may assign both nodes a single location. See Issue #1568
|
||||
If this occurs in your case, consider relabeling the nodes just
|
||||
for the layout computation using something similar to::
|
||||
|
||||
>>> H = nx.convert_node_labels_to_integers(G, label_attribute="node_label")
|
||||
>>> H_layout = nx.nx_agraph.pygraphviz_layout(G, prog="dot")
|
||||
>>> G_layout = {H.nodes[n]["node_label"]: p for n, p in H_layout.items()}
|
||||
|
||||
"""
|
||||
try:
|
||||
import pygraphviz
|
||||
except ImportError as e:
|
||||
raise ImportError("requires pygraphviz " "http://pygraphviz.github.io/") from e
|
||||
if root is not None:
|
||||
args += f"-Groot={root}"
|
||||
A = to_agraph(G)
|
||||
A.layout(prog=prog, args=args)
|
||||
node_pos = {}
|
||||
for n in G:
|
||||
node = pygraphviz.Node(A, n)
|
||||
try:
|
||||
xs = node.attr["pos"].split(",")
|
||||
node_pos[n] = tuple(float(x) for x in xs)
|
||||
except:
|
||||
print("no position for node", n)
|
||||
node_pos[n] = (0.0, 0.0)
|
||||
return node_pos
|
||||
|
||||
|
||||
@nx.utils.open_file(5, "w+b")
|
||||
def view_pygraphviz(
|
||||
G, edgelabel=None, prog="dot", args="", suffix="", path=None, show=True
|
||||
):
|
||||
"""Views the graph G using the specified layout algorithm.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
The machine to draw.
|
||||
edgelabel : str, callable, None
|
||||
If a string, then it specifes the edge attribute to be displayed
|
||||
on the edge labels. If a callable, then it is called for each
|
||||
edge and it should return the string to be displayed on the edges.
|
||||
The function signature of `edgelabel` should be edgelabel(data),
|
||||
where `data` is the edge attribute dictionary.
|
||||
prog : string
|
||||
Name of Graphviz layout program.
|
||||
args : str
|
||||
Additional arguments to pass to the Graphviz layout program.
|
||||
suffix : str
|
||||
If `filename` is None, we save to a temporary file. The value of
|
||||
`suffix` will appear at the tail end of the temporary filename.
|
||||
path : str, None
|
||||
The filename used to save the image. If None, save to a temporary
|
||||
file. File formats are the same as those from pygraphviz.agraph.draw.
|
||||
show : bool, default = True
|
||||
Whether to display the graph with `networkx.utils.default_opener`,
|
||||
default is `True`. If `False`, the rendered graph is still available
|
||||
at `path`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
path : str
|
||||
The filename of the generated image.
|
||||
A : PyGraphviz graph
|
||||
The PyGraphviz graph instance used to generate the image.
|
||||
|
||||
Notes
|
||||
-----
|
||||
If this function is called in succession too quickly, sometimes the
|
||||
image is not displayed. So you might consider time.sleep(.5) between
|
||||
calls if you experience problems.
|
||||
|
||||
"""
|
||||
if not len(G):
|
||||
raise nx.NetworkXException("An empty graph cannot be drawn.")
|
||||
|
||||
# If we are providing default values for graphviz, these must be set
|
||||
# before any nodes or edges are added to the PyGraphviz graph object.
|
||||
# The reason for this is that default values only affect incoming objects.
|
||||
# If you change the default values after the objects have been added,
|
||||
# then they inherit no value and are set only if explicitly set.
|
||||
|
||||
# to_agraph() uses these values.
|
||||
attrs = ["edge", "node", "graph"]
|
||||
for attr in attrs:
|
||||
if attr not in G.graph:
|
||||
G.graph[attr] = {}
|
||||
|
||||
# These are the default values.
|
||||
edge_attrs = {"fontsize": "10"}
|
||||
node_attrs = {
|
||||
"style": "filled",
|
||||
"fillcolor": "#0000FF40",
|
||||
"height": "0.75",
|
||||
"width": "0.75",
|
||||
"shape": "circle",
|
||||
}
|
||||
graph_attrs = {}
|
||||
|
||||
def update_attrs(which, attrs):
|
||||
# Update graph attributes. Return list of those which were added.
|
||||
added = []
|
||||
for k, v in attrs.items():
|
||||
if k not in G.graph[which]:
|
||||
G.graph[which][k] = v
|
||||
added.append(k)
|
||||
|
||||
def clean_attrs(which, added):
|
||||
# Remove added attributes
|
||||
for attr in added:
|
||||
del G.graph[which][attr]
|
||||
if not G.graph[which]:
|
||||
del G.graph[which]
|
||||
|
||||
# Update all default values
|
||||
update_attrs("edge", edge_attrs)
|
||||
update_attrs("node", node_attrs)
|
||||
update_attrs("graph", graph_attrs)
|
||||
|
||||
# Convert to agraph, so we inherit default values
|
||||
A = to_agraph(G)
|
||||
|
||||
# Remove the default values we added to the original graph.
|
||||
clean_attrs("edge", edge_attrs)
|
||||
clean_attrs("node", node_attrs)
|
||||
clean_attrs("graph", graph_attrs)
|
||||
|
||||
# If the user passed in an edgelabel, we update the labels for all edges.
|
||||
if edgelabel is not None:
|
||||
if not hasattr(edgelabel, "__call__"):
|
||||
|
||||
def func(data):
|
||||
return "".join([" ", str(data[edgelabel]), " "])
|
||||
|
||||
else:
|
||||
func = edgelabel
|
||||
|
||||
# update all the edge labels
|
||||
if G.is_multigraph():
|
||||
for u, v, key, data in G.edges(keys=True, data=True):
|
||||
# PyGraphviz doesn't convert the key to a string. See #339
|
||||
edge = A.get_edge(u, v, str(key))
|
||||
edge.attr["label"] = str(func(data))
|
||||
else:
|
||||
for u, v, data in G.edges(data=True):
|
||||
edge = A.get_edge(u, v)
|
||||
edge.attr["label"] = str(func(data))
|
||||
|
||||
if path is None:
|
||||
ext = "png"
|
||||
if suffix:
|
||||
suffix = f"_{suffix}.{ext}"
|
||||
else:
|
||||
suffix = f".{ext}"
|
||||
path = tempfile.NamedTemporaryFile(suffix=suffix, delete=False)
|
||||
else:
|
||||
# Assume the decorator worked and it is a file-object.
|
||||
pass
|
||||
|
||||
# Write graph to file
|
||||
A.draw(path=path, format=None, prog=prog, args=args)
|
||||
path.close()
|
||||
|
||||
# Show graph in a new window (depends on platform configuration)
|
||||
if show:
|
||||
nx.utils.default_opener(path.name)
|
||||
|
||||
return path.name, A
|
||||
|
||||
|
||||
def display_pygraphviz(graph, path, format=None, prog=None, args=""):
|
||||
"""Internal function to display a graph in OS dependent manner.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
graph : PyGraphviz graph
|
||||
A PyGraphviz AGraph instance.
|
||||
path : file object
|
||||
An already opened file object that will be closed.
|
||||
format : str, None
|
||||
An attempt is made to guess the output format based on the extension
|
||||
of the filename. If that fails, the value of `format` is used.
|
||||
prog : string
|
||||
Name of Graphviz layout program.
|
||||
args : str
|
||||
Additional arguments to pass to the Graphviz layout program.
|
||||
|
||||
Notes
|
||||
-----
|
||||
If this function is called in succession too quickly, sometimes the
|
||||
image is not displayed. So you might consider time.sleep(.5) between
|
||||
calls if you experience problems.
|
||||
|
||||
"""
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"display_pygraphviz is deprecated and will be removed in NetworkX 3.0. "
|
||||
"To view a graph G using pygraphviz, use nx.nx_agraph.view_pygraphviz(G). "
|
||||
"To view a graph from file, consider nx.utils.default_opener(filename).",
|
||||
DeprecationWarning,
|
||||
)
|
||||
if format is None:
|
||||
filename = path.name
|
||||
format = os.path.splitext(filename)[1].lower()[1:]
|
||||
if not format:
|
||||
# Let the draw() function use its default
|
||||
format = None
|
||||
|
||||
# Save to a file and display in the default viewer.
|
||||
# We must close the file before viewing it.
|
||||
graph.draw(path, format, prog, args)
|
||||
path.close()
|
||||
nx.utils.default_opener(filename)
|
338
venv/Lib/site-packages/networkx/drawing/nx_pydot.py
Normal file
338
venv/Lib/site-packages/networkx/drawing/nx_pydot.py
Normal file
|
@ -0,0 +1,338 @@
|
|||
"""
|
||||
*****
|
||||
Pydot
|
||||
*****
|
||||
|
||||
Import and export NetworkX graphs in Graphviz dot format using pydot.
|
||||
|
||||
Either this module or nx_agraph can be used to interface with graphviz.
|
||||
|
||||
See Also
|
||||
--------
|
||||
pydot: https://github.com/erocarrera/pydot
|
||||
Graphviz: https://www.graphviz.org
|
||||
DOT Language: http://www.graphviz.org/doc/info/lang.html
|
||||
"""
|
||||
from locale import getpreferredencoding
|
||||
from networkx.utils import open_file
|
||||
import networkx as nx
|
||||
|
||||
__all__ = [
|
||||
"write_dot",
|
||||
"read_dot",
|
||||
"graphviz_layout",
|
||||
"pydot_layout",
|
||||
"to_pydot",
|
||||
"from_pydot",
|
||||
]
|
||||
|
||||
|
||||
@open_file(1, mode="w")
|
||||
def write_dot(G, path):
|
||||
"""Write NetworkX graph G to Graphviz dot format on path.
|
||||
|
||||
Path can be a string or a file handle.
|
||||
"""
|
||||
P = to_pydot(G)
|
||||
path.write(P.to_string())
|
||||
return
|
||||
|
||||
|
||||
@open_file(0, mode="r")
|
||||
def read_dot(path):
|
||||
"""Returns a NetworkX :class:`MultiGraph` or :class:`MultiDiGraph` from the
|
||||
dot file with the passed path.
|
||||
|
||||
If this file contains multiple graphs, only the first such graph is
|
||||
returned. All graphs _except_ the first are silently ignored.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : str or file
|
||||
Filename or file handle.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : MultiGraph or MultiDiGraph
|
||||
A :class:`MultiGraph` or :class:`MultiDiGraph`.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Use `G = nx.Graph(read_dot(path))` to return a :class:`Graph` instead of a
|
||||
:class:`MultiGraph`.
|
||||
"""
|
||||
import pydot
|
||||
|
||||
data = path.read()
|
||||
|
||||
# List of one or more "pydot.Dot" instances deserialized from this file.
|
||||
P_list = pydot.graph_from_dot_data(data)
|
||||
|
||||
# Convert only the first such instance into a NetworkX graph.
|
||||
return from_pydot(P_list[0])
|
||||
|
||||
|
||||
def from_pydot(P):
|
||||
"""Returns a NetworkX graph from a Pydot graph.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
P : Pydot graph
|
||||
A graph created with Pydot
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX multigraph
|
||||
A MultiGraph or MultiDiGraph.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> K5 = nx.complete_graph(5)
|
||||
>>> A = nx.nx_pydot.to_pydot(K5)
|
||||
>>> G = nx.nx_pydot.from_pydot(A) # return MultiGraph
|
||||
|
||||
# make a Graph instead of MultiGraph
|
||||
>>> G = nx.Graph(nx.nx_pydot.from_pydot(A))
|
||||
|
||||
"""
|
||||
if P.get_strict(None): # pydot bug: get_strict() shouldn't take argument
|
||||
multiedges = False
|
||||
else:
|
||||
multiedges = True
|
||||
|
||||
if P.get_type() == "graph": # undirected
|
||||
if multiedges:
|
||||
N = nx.MultiGraph()
|
||||
else:
|
||||
N = nx.Graph()
|
||||
else:
|
||||
if multiedges:
|
||||
N = nx.MultiDiGraph()
|
||||
else:
|
||||
N = nx.DiGraph()
|
||||
|
||||
# assign defaults
|
||||
name = P.get_name().strip('"')
|
||||
if name != "":
|
||||
N.name = name
|
||||
|
||||
# add nodes, attributes to N.node_attr
|
||||
for p in P.get_node_list():
|
||||
n = p.get_name().strip('"')
|
||||
if n in ("node", "graph", "edge"):
|
||||
continue
|
||||
N.add_node(n, **p.get_attributes())
|
||||
|
||||
# add edges
|
||||
for e in P.get_edge_list():
|
||||
u = e.get_source()
|
||||
v = e.get_destination()
|
||||
attr = e.get_attributes()
|
||||
s = []
|
||||
d = []
|
||||
|
||||
if isinstance(u, str):
|
||||
s.append(u.strip('"'))
|
||||
else:
|
||||
for unodes in u["nodes"]:
|
||||
s.append(unodes.strip('"'))
|
||||
|
||||
if isinstance(v, str):
|
||||
d.append(v.strip('"'))
|
||||
else:
|
||||
for vnodes in v["nodes"]:
|
||||
d.append(vnodes.strip('"'))
|
||||
|
||||
for source_node in s:
|
||||
for destination_node in d:
|
||||
N.add_edge(source_node, destination_node, **attr)
|
||||
|
||||
# add default attributes for graph, nodes, edges
|
||||
pattr = P.get_attributes()
|
||||
if pattr:
|
||||
N.graph["graph"] = pattr
|
||||
try:
|
||||
N.graph["node"] = P.get_node_defaults()[0]
|
||||
except (IndexError, TypeError):
|
||||
pass # N.graph['node']={}
|
||||
try:
|
||||
N.graph["edge"] = P.get_edge_defaults()[0]
|
||||
except (IndexError, TypeError):
|
||||
pass # N.graph['edge']={}
|
||||
return N
|
||||
|
||||
|
||||
def to_pydot(N):
|
||||
"""Returns a pydot graph from a NetworkX graph N.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
N : NetworkX graph
|
||||
A graph created with NetworkX
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> K5 = nx.complete_graph(5)
|
||||
>>> P = nx.nx_pydot.to_pydot(K5)
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
"""
|
||||
import pydot
|
||||
|
||||
# set Graphviz graph type
|
||||
if N.is_directed():
|
||||
graph_type = "digraph"
|
||||
else:
|
||||
graph_type = "graph"
|
||||
strict = nx.number_of_selfloops(N) == 0 and not N.is_multigraph()
|
||||
|
||||
name = N.name
|
||||
graph_defaults = N.graph.get("graph", {})
|
||||
if name == "":
|
||||
P = pydot.Dot("", graph_type=graph_type, strict=strict, **graph_defaults)
|
||||
else:
|
||||
P = pydot.Dot(
|
||||
f'"{name}"', graph_type=graph_type, strict=strict, **graph_defaults
|
||||
)
|
||||
try:
|
||||
P.set_node_defaults(**N.graph["node"])
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
P.set_edge_defaults(**N.graph["edge"])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
for n, nodedata in N.nodes(data=True):
|
||||
str_nodedata = {k: str(v) for k, v in nodedata.items()}
|
||||
p = pydot.Node(str(n), **str_nodedata)
|
||||
P.add_node(p)
|
||||
|
||||
if N.is_multigraph():
|
||||
for u, v, key, edgedata in N.edges(data=True, keys=True):
|
||||
str_edgedata = {k: str(v) for k, v in edgedata.items() if k != "key"}
|
||||
edge = pydot.Edge(str(u), str(v), key=str(key), **str_edgedata)
|
||||
P.add_edge(edge)
|
||||
|
||||
else:
|
||||
for u, v, edgedata in N.edges(data=True):
|
||||
str_edgedata = {k: str(v) for k, v in edgedata.items()}
|
||||
edge = pydot.Edge(str(u), str(v), **str_edgedata)
|
||||
P.add_edge(edge)
|
||||
return P
|
||||
|
||||
|
||||
def graphviz_layout(G, prog="neato", root=None):
|
||||
"""Create node positions using Pydot and Graphviz.
|
||||
|
||||
Returns a dictionary of positions keyed by node.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
The graph for which the layout is computed.
|
||||
prog : string (default: 'neato')
|
||||
The name of the GraphViz program to use for layout.
|
||||
Options depend on GraphViz version but may include:
|
||||
'dot', 'twopi', 'fdp', 'sfdp', 'circo'
|
||||
root : Node from G or None (default: None)
|
||||
The node of G from which to start some layout algorithms.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Dictionary of (x, y) positions keyed by node.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.complete_graph(4)
|
||||
>>> pos = nx.nx_pydot.graphviz_layout(G)
|
||||
>>> pos = nx.nx_pydot.graphviz_layout(G, prog="dot")
|
||||
|
||||
Notes
|
||||
-----
|
||||
This is a wrapper for pydot_layout.
|
||||
"""
|
||||
return pydot_layout(G=G, prog=prog, root=root)
|
||||
|
||||
|
||||
def pydot_layout(G, prog="neato", root=None):
|
||||
"""Create node positions using :mod:`pydot` and Graphviz.
|
||||
|
||||
Parameters
|
||||
--------
|
||||
G : Graph
|
||||
NetworkX graph to be laid out.
|
||||
prog : string (default: 'neato')
|
||||
Name of the GraphViz command to use for layout.
|
||||
Options depend on GraphViz version but may include:
|
||||
'dot', 'twopi', 'fdp', 'sfdp', 'circo'
|
||||
root : Node from G or None (default: None)
|
||||
The node of G from which to start some layout algorithms.
|
||||
|
||||
Returns
|
||||
--------
|
||||
dict
|
||||
Dictionary of positions keyed by node.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.complete_graph(4)
|
||||
>>> pos = nx.nx_pydot.pydot_layout(G)
|
||||
>>> pos = nx.nx_pydot.pydot_layout(G, prog="dot")
|
||||
|
||||
Notes
|
||||
-----
|
||||
If you use complex node objects, they may have the same string
|
||||
representation and GraphViz could treat them as the same node.
|
||||
The layout may assign both nodes a single location. See Issue #1568
|
||||
If this occurs in your case, consider relabeling the nodes just
|
||||
for the layout computation using something similar to:
|
||||
|
||||
H = nx.convert_node_labels_to_integers(G, label_attribute='node_label')
|
||||
H_layout = nx.nx_pydot.pydot_layout(G, prog='dot')
|
||||
G_layout = {H.nodes[n]['node_label']: p for n, p in H_layout.items()}
|
||||
|
||||
"""
|
||||
import pydot
|
||||
|
||||
P = to_pydot(G)
|
||||
if root is not None:
|
||||
P.set("root", str(root))
|
||||
|
||||
# List of low-level bytes comprising a string in the dot language converted
|
||||
# from the passed graph with the passed external GraphViz command.
|
||||
D_bytes = P.create_dot(prog=prog)
|
||||
|
||||
# Unique string decoded from these bytes with the preferred locale encoding
|
||||
D = str(D_bytes, encoding=getpreferredencoding())
|
||||
|
||||
if D == "": # no data returned
|
||||
print(f"Graphviz layout with {prog} failed")
|
||||
print()
|
||||
print("To debug what happened try:")
|
||||
print("P = nx.nx_pydot.to_pydot(G)")
|
||||
print('P.write_dot("file.dot")')
|
||||
print(f"And then run {prog} on file.dot")
|
||||
return
|
||||
|
||||
# List of one or more "pydot.Dot" instances deserialized from this string.
|
||||
Q_list = pydot.graph_from_dot_data(D)
|
||||
assert len(Q_list) == 1
|
||||
|
||||
# The first and only such instance, as guaranteed by the above assertion.
|
||||
Q = Q_list[0]
|
||||
|
||||
node_pos = {}
|
||||
for n in G.nodes():
|
||||
pydot_node = pydot.Node(str(n)).get_name()
|
||||
node = Q.get_node(pydot_node)
|
||||
|
||||
if isinstance(node, list):
|
||||
node = node[0]
|
||||
pos = node.get_pos()[1:-1] # strip leading and trailing double quotes
|
||||
if pos is not None:
|
||||
xx, yy = pos.split(",")
|
||||
node_pos[n] = (float(xx), float(yy))
|
||||
return node_pos
|
1279
venv/Lib/site-packages/networkx/drawing/nx_pylab.py
Normal file
1279
venv/Lib/site-packages/networkx/drawing/nx_pylab.py
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
249
venv/Lib/site-packages/networkx/drawing/tests/test_agraph.py
Normal file
249
venv/Lib/site-packages/networkx/drawing/tests/test_agraph.py
Normal file
|
@ -0,0 +1,249 @@
|
|||
"""Unit tests for PyGraphviz interface."""
|
||||
import os
|
||||
import tempfile
|
||||
import pytest
|
||||
|
||||
pygraphviz = pytest.importorskip("pygraphviz")
|
||||
|
||||
|
||||
from networkx.testing import assert_edges_equal, assert_nodes_equal, assert_graphs_equal
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestAGraph:
|
||||
def build_graph(self, G):
|
||||
edges = [("A", "B"), ("A", "C"), ("A", "C"), ("B", "C"), ("A", "D")]
|
||||
G.add_edges_from(edges)
|
||||
G.add_node("E")
|
||||
G.graph["metal"] = "bronze"
|
||||
return G
|
||||
|
||||
def assert_equal(self, G1, G2):
|
||||
assert_nodes_equal(G1.nodes(), G2.nodes())
|
||||
assert_edges_equal(G1.edges(), G2.edges())
|
||||
assert G1.graph["metal"] == G2.graph["metal"]
|
||||
|
||||
def agraph_checks(self, G):
|
||||
G = self.build_graph(G)
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
H = nx.nx_agraph.from_agraph(A)
|
||||
self.assert_equal(G, H)
|
||||
|
||||
fname = tempfile.mktemp()
|
||||
nx.drawing.nx_agraph.write_dot(H, fname)
|
||||
Hin = nx.nx_agraph.read_dot(fname)
|
||||
os.unlink(fname)
|
||||
self.assert_equal(H, Hin)
|
||||
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
with open(fname, "w") as fh:
|
||||
nx.drawing.nx_agraph.write_dot(H, fh)
|
||||
|
||||
with open(fname) as fh:
|
||||
Hin = nx.nx_agraph.read_dot(fh)
|
||||
os.unlink(fname)
|
||||
self.assert_equal(H, Hin)
|
||||
|
||||
def test_from_agraph_name(self):
|
||||
G = nx.Graph(name="test")
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
H = nx.nx_agraph.from_agraph(A)
|
||||
assert G.name == "test"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"graph_class", (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)
|
||||
)
|
||||
def test_from_agraph_create_using(self, graph_class):
|
||||
G = nx.path_graph(3)
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
H = nx.nx_agraph.from_agraph(A, create_using=graph_class)
|
||||
assert isinstance(H, graph_class)
|
||||
|
||||
def test_from_agraph_named_edges(self):
|
||||
# Create an AGraph from an existing (non-multi) Graph
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from([0, 1])
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
# Add edge (+ name, given by key) to the AGraph
|
||||
A.add_edge(0, 1, key="foo")
|
||||
# Verify a.name roundtrips out to 'key' in from_agraph
|
||||
H = nx.nx_agraph.from_agraph(A)
|
||||
assert isinstance(H, nx.Graph)
|
||||
assert ("0", "1", {"key": "foo"}) in H.edges(data=True)
|
||||
|
||||
def test_undirected(self):
|
||||
self.agraph_checks(nx.Graph())
|
||||
|
||||
def test_directed(self):
|
||||
self.agraph_checks(nx.DiGraph())
|
||||
|
||||
def test_multi_undirected(self):
|
||||
self.agraph_checks(nx.MultiGraph())
|
||||
|
||||
def test_multi_directed(self):
|
||||
self.agraph_checks(nx.MultiDiGraph())
|
||||
|
||||
def test_to_agraph_with_nodedata(self):
|
||||
G = nx.Graph()
|
||||
G.add_node(1, color="red")
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
assert dict(A.nodes()[0].attr) == {"color": "red"}
|
||||
|
||||
@pytest.mark.parametrize("graph_class", (nx.Graph, nx.MultiGraph))
|
||||
def test_to_agraph_with_edgedata(self, graph_class):
|
||||
G = graph_class()
|
||||
G.add_nodes_from([0, 1])
|
||||
G.add_edge(0, 1, color="yellow")
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
assert dict(A.edges()[0].attr) == {"color": "yellow"}
|
||||
|
||||
def test_view_pygraphviz_path(self, tmp_path):
|
||||
G = nx.complete_graph(3)
|
||||
input_path = str(tmp_path / "graph.png")
|
||||
out_path, A = nx.nx_agraph.view_pygraphviz(G, path=input_path, show=False)
|
||||
assert out_path == input_path
|
||||
# Ensure file is not empty
|
||||
with open(input_path, "rb") as fh:
|
||||
data = fh.read()
|
||||
assert len(data) > 0
|
||||
|
||||
def test_view_pygraphviz_file_suffix(self, tmp_path):
|
||||
G = nx.complete_graph(3)
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, suffix=1, show=False)
|
||||
assert path[-6:] == "_1.png"
|
||||
|
||||
def test_view_pygraphviz(self):
|
||||
G = nx.Graph() # "An empty graph cannot be drawn."
|
||||
pytest.raises(nx.NetworkXException, nx.nx_agraph.view_pygraphviz, G)
|
||||
G = nx.barbell_graph(4, 6)
|
||||
nx.nx_agraph.view_pygraphviz(G, show=False)
|
||||
|
||||
def test_view_pygraphviz_edgelabel(self):
|
||||
G = nx.Graph()
|
||||
G.add_edge(1, 2, weight=7)
|
||||
G.add_edge(2, 3, weight=8)
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, edgelabel="weight", show=False)
|
||||
for edge in A.edges():
|
||||
assert edge.attr["weight"] in ("7", "8")
|
||||
|
||||
def test_view_pygraphviz_callable_edgelabel(self):
|
||||
G = nx.complete_graph(3)
|
||||
|
||||
def foo_label(data):
|
||||
return "foo"
|
||||
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, edgelabel=foo_label, show=False)
|
||||
for edge in A.edges():
|
||||
assert edge.attr["label"] == "foo"
|
||||
|
||||
def test_view_pygraphviz_multigraph_edgelabels(self):
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(0, 1, key=0, name="left_fork")
|
||||
G.add_edge(0, 1, key=1, name="right_fork")
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, edgelabel="name", show=False)
|
||||
edges = A.edges()
|
||||
assert len(edges) == 2
|
||||
for edge in edges:
|
||||
assert edge.attr["label"].strip() in ("left_fork", "right_fork")
|
||||
|
||||
def test_graph_with_reserved_keywords(self):
|
||||
# test attribute/keyword clash case for #1582
|
||||
# node: n
|
||||
# edges: u,v
|
||||
G = nx.Graph()
|
||||
G = self.build_graph(G)
|
||||
G.nodes["E"]["n"] = "keyword"
|
||||
G.edges[("A", "B")]["u"] = "keyword"
|
||||
G.edges[("A", "B")]["v"] = "keyword"
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
|
||||
def test_view_pygraphviz_no_added_attrs_to_input(self):
|
||||
G = nx.complete_graph(2)
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, show=False)
|
||||
assert G.graph == {}
|
||||
|
||||
@pytest.mark.xfail(reason="known bug in clean_attrs")
|
||||
def test_view_pygraphviz_leaves_input_graph_unmodified(self):
|
||||
G = nx.complete_graph(2)
|
||||
# Add entries to graph dict that to_agraph handles specially
|
||||
G.graph["node"] = {"width": "0.80"}
|
||||
G.graph["edge"] = {"fontsize": "14"}
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, show=False)
|
||||
assert G.graph == {"node": {"width": "0.80"}, "edge": {"fontsize": "14"}}
|
||||
|
||||
def test_graph_with_AGraph_attrs(self):
|
||||
G = nx.complete_graph(2)
|
||||
# Add entries to graph dict that to_agraph handles specially
|
||||
G.graph["node"] = {"width": "0.80"}
|
||||
G.graph["edge"] = {"fontsize": "14"}
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, show=False)
|
||||
# Ensure user-specified values are not lost
|
||||
assert dict(A.node_attr)["width"] == "0.80"
|
||||
assert dict(A.edge_attr)["fontsize"] == "14"
|
||||
|
||||
def test_round_trip_empty_graph(self):
|
||||
G = nx.Graph()
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
H = nx.nx_agraph.from_agraph(A)
|
||||
# assert_graphs_equal(G, H)
|
||||
AA = nx.nx_agraph.to_agraph(H)
|
||||
HH = nx.nx_agraph.from_agraph(AA)
|
||||
assert_graphs_equal(H, HH)
|
||||
G.graph["graph"] = {}
|
||||
G.graph["node"] = {}
|
||||
G.graph["edge"] = {}
|
||||
assert_graphs_equal(G, HH)
|
||||
|
||||
@pytest.mark.xfail(reason="integer->string node conversion in round trip")
|
||||
def test_round_trip_integer_nodes(self):
|
||||
G = nx.complete_graph(3)
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
H = nx.nx_agraph.from_agraph(A)
|
||||
assert_graphs_equal(G, H)
|
||||
|
||||
def test_graphviz_alias(self):
|
||||
G = self.build_graph(nx.Graph())
|
||||
pos_graphviz = nx.nx_agraph.graphviz_layout(G)
|
||||
pos_pygraphviz = nx.nx_agraph.pygraphviz_layout(G)
|
||||
assert pos_graphviz == pos_pygraphviz
|
||||
|
||||
@pytest.mark.parametrize("root", range(5))
|
||||
def test_pygraphviz_layout_root(self, root):
|
||||
# NOTE: test depends on layout prog being deterministic
|
||||
G = nx.complete_graph(5)
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
# Get layout with root arg is not None
|
||||
pygv_layout = nx.nx_agraph.pygraphviz_layout(G, prog="circo", root=root)
|
||||
# Equivalent layout directly on AGraph
|
||||
A.layout(args=f"-Groot={root}", prog="circo")
|
||||
# Parse AGraph layout
|
||||
a1_pos = tuple(float(v) for v in dict(A.get_node("1").attr)["pos"].split(","))
|
||||
assert pygv_layout[1] == a1_pos
|
||||
|
||||
def test_2d_layout(self):
|
||||
G = nx.Graph()
|
||||
G = self.build_graph(G)
|
||||
G.graph["dimen"] = 2
|
||||
pos = nx.nx_agraph.pygraphviz_layout(G, prog="neato")
|
||||
pos = list(pos.values())
|
||||
assert len(pos) == 5
|
||||
assert len(pos[0]) == 2
|
||||
|
||||
def test_3d_layout(self):
|
||||
G = nx.Graph()
|
||||
G = self.build_graph(G)
|
||||
G.graph["dimen"] = 3
|
||||
pos = nx.nx_agraph.pygraphviz_layout(G, prog="neato")
|
||||
pos = list(pos.values())
|
||||
assert len(pos) == 5
|
||||
assert len(pos[0]) == 3
|
||||
|
||||
def test_display_pygraphviz_deprecation_warning(self):
|
||||
G = nx.complete_graph(2)
|
||||
path_name, A = nx.nx_agraph.view_pygraphviz(G, show=False)
|
||||
# Monkeypatch default_opener to prevent window opening
|
||||
nx.utils.default_opener = lambda x: None
|
||||
with pytest.warns(DeprecationWarning, match="display_pygraphviz is deprecated"):
|
||||
with open(path_name, "wb") as fh:
|
||||
nx.nx_agraph.display_pygraphviz(A, fh, prog="dot")
|
406
venv/Lib/site-packages/networkx/drawing/tests/test_layout.py
Normal file
406
venv/Lib/site-packages/networkx/drawing/tests/test_layout.py
Normal file
|
@ -0,0 +1,406 @@
|
|||
"""Unit tests for layout functions."""
|
||||
import networkx as nx
|
||||
from networkx.testing import almost_equal
|
||||
|
||||
import pytest
|
||||
|
||||
numpy = pytest.importorskip("numpy")
|
||||
test_smoke_empty_graphscipy = pytest.importorskip("scipy")
|
||||
|
||||
|
||||
class TestLayout:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.Gi = nx.grid_2d_graph(5, 5)
|
||||
cls.Gs = nx.Graph()
|
||||
nx.add_path(cls.Gs, "abcdef")
|
||||
cls.bigG = nx.grid_2d_graph(25, 25) # > 500 nodes for sparse
|
||||
|
||||
@staticmethod
|
||||
def collect_node_distances(positions):
|
||||
distances = []
|
||||
prev_val = None
|
||||
for k in positions:
|
||||
if prev_val is not None:
|
||||
diff = positions[k] - prev_val
|
||||
distances.append(numpy.dot(diff, diff) ** 0.5)
|
||||
prev_val = positions[k]
|
||||
return distances
|
||||
|
||||
def test_spring_fixed_without_pos(self):
|
||||
G = nx.path_graph(4)
|
||||
pytest.raises(ValueError, nx.spring_layout, G, fixed=[0])
|
||||
pos = {0: (1, 1), 2: (0, 0)}
|
||||
pytest.raises(ValueError, nx.spring_layout, G, fixed=[0, 1], pos=pos)
|
||||
nx.spring_layout(G, fixed=[0, 2], pos=pos) # No ValueError
|
||||
|
||||
def test_spring_init_pos(self):
|
||||
# Tests GH #2448
|
||||
import math
|
||||
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1), (1, 2), (2, 0), (2, 3)])
|
||||
|
||||
init_pos = {0: (0.0, 0.0)}
|
||||
fixed_pos = [0]
|
||||
pos = nx.fruchterman_reingold_layout(G, pos=init_pos, fixed=fixed_pos)
|
||||
has_nan = any(math.isnan(c) for coords in pos.values() for c in coords)
|
||||
assert not has_nan, "values should not be nan"
|
||||
|
||||
def test_smoke_empty_graph(self):
|
||||
G = []
|
||||
nx.random_layout(G)
|
||||
nx.circular_layout(G)
|
||||
nx.planar_layout(G)
|
||||
nx.spring_layout(G)
|
||||
nx.fruchterman_reingold_layout(G)
|
||||
nx.spectral_layout(G)
|
||||
nx.shell_layout(G)
|
||||
nx.bipartite_layout(G, G)
|
||||
nx.spiral_layout(G)
|
||||
nx.multipartite_layout(G)
|
||||
nx.kamada_kawai_layout(G)
|
||||
|
||||
def test_smoke_int(self):
|
||||
G = self.Gi
|
||||
nx.random_layout(G)
|
||||
nx.circular_layout(G)
|
||||
nx.planar_layout(G)
|
||||
nx.spring_layout(G)
|
||||
nx.fruchterman_reingold_layout(G)
|
||||
nx.fruchterman_reingold_layout(self.bigG)
|
||||
nx.spectral_layout(G)
|
||||
nx.spectral_layout(G.to_directed())
|
||||
nx.spectral_layout(self.bigG)
|
||||
nx.spectral_layout(self.bigG.to_directed())
|
||||
nx.shell_layout(G)
|
||||
nx.spiral_layout(G)
|
||||
nx.kamada_kawai_layout(G)
|
||||
nx.kamada_kawai_layout(G, dim=1)
|
||||
nx.kamada_kawai_layout(G, dim=3)
|
||||
|
||||
def test_smoke_string(self):
|
||||
G = self.Gs
|
||||
nx.random_layout(G)
|
||||
nx.circular_layout(G)
|
||||
nx.planar_layout(G)
|
||||
nx.spring_layout(G)
|
||||
nx.fruchterman_reingold_layout(G)
|
||||
nx.spectral_layout(G)
|
||||
nx.shell_layout(G)
|
||||
nx.spiral_layout(G)
|
||||
nx.kamada_kawai_layout(G)
|
||||
nx.kamada_kawai_layout(G, dim=1)
|
||||
nx.kamada_kawai_layout(G, dim=3)
|
||||
|
||||
def check_scale_and_center(self, pos, scale, center):
|
||||
center = numpy.array(center)
|
||||
low = center - scale
|
||||
hi = center + scale
|
||||
vpos = numpy.array(list(pos.values()))
|
||||
length = vpos.max(0) - vpos.min(0)
|
||||
assert (length <= 2 * scale).all()
|
||||
assert (vpos >= low).all()
|
||||
assert (vpos <= hi).all()
|
||||
|
||||
def test_scale_and_center_arg(self):
|
||||
sc = self.check_scale_and_center
|
||||
c = (4, 5)
|
||||
G = nx.complete_graph(9)
|
||||
G.add_node(9)
|
||||
sc(nx.random_layout(G, center=c), scale=0.5, center=(4.5, 5.5))
|
||||
# rest can have 2*scale length: [-scale, scale]
|
||||
sc(nx.spring_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
sc(nx.spectral_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
sc(nx.circular_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
sc(nx.shell_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
sc(nx.spiral_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
sc(nx.kamada_kawai_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
|
||||
c = (2, 3, 5)
|
||||
sc(nx.kamada_kawai_layout(G, dim=3, scale=2, center=c), scale=2, center=c)
|
||||
|
||||
def test_planar_layout_non_planar_input(self):
|
||||
G = nx.complete_graph(9)
|
||||
pytest.raises(nx.NetworkXException, nx.planar_layout, G)
|
||||
|
||||
def test_smoke_planar_layout_embedding_input(self):
|
||||
embedding = nx.PlanarEmbedding()
|
||||
embedding.set_data({0: [1, 2], 1: [0, 2], 2: [0, 1]})
|
||||
nx.planar_layout(embedding)
|
||||
|
||||
def test_default_scale_and_center(self):
|
||||
sc = self.check_scale_and_center
|
||||
c = (0, 0)
|
||||
G = nx.complete_graph(9)
|
||||
G.add_node(9)
|
||||
sc(nx.random_layout(G), scale=0.5, center=(0.5, 0.5))
|
||||
sc(nx.spring_layout(G), scale=1, center=c)
|
||||
sc(nx.spectral_layout(G), scale=1, center=c)
|
||||
sc(nx.circular_layout(G), scale=1, center=c)
|
||||
sc(nx.shell_layout(G), scale=1, center=c)
|
||||
sc(nx.spiral_layout(G), scale=1, center=c)
|
||||
sc(nx.kamada_kawai_layout(G), scale=1, center=c)
|
||||
|
||||
c = (0, 0, 0)
|
||||
sc(nx.kamada_kawai_layout(G, dim=3), scale=1, center=c)
|
||||
|
||||
def test_circular_planar_and_shell_dim_error(self):
|
||||
G = nx.path_graph(4)
|
||||
pytest.raises(ValueError, nx.circular_layout, G, dim=1)
|
||||
pytest.raises(ValueError, nx.shell_layout, G, dim=1)
|
||||
pytest.raises(ValueError, nx.shell_layout, G, dim=3)
|
||||
pytest.raises(ValueError, nx.planar_layout, G, dim=1)
|
||||
pytest.raises(ValueError, nx.planar_layout, G, dim=3)
|
||||
|
||||
def test_adjacency_interface_numpy(self):
|
||||
A = nx.to_numpy_array(self.Gs)
|
||||
pos = nx.drawing.layout._fruchterman_reingold(A)
|
||||
assert pos.shape == (6, 2)
|
||||
pos = nx.drawing.layout._fruchterman_reingold(A, dim=3)
|
||||
assert pos.shape == (6, 3)
|
||||
pos = nx.drawing.layout._sparse_fruchterman_reingold(A)
|
||||
assert pos.shape == (6, 2)
|
||||
|
||||
def test_adjacency_interface_scipy(self):
|
||||
A = nx.to_scipy_sparse_matrix(self.Gs, dtype="d")
|
||||
pos = nx.drawing.layout._sparse_fruchterman_reingold(A)
|
||||
assert pos.shape == (6, 2)
|
||||
pos = nx.drawing.layout._sparse_spectral(A)
|
||||
assert pos.shape == (6, 2)
|
||||
pos = nx.drawing.layout._sparse_fruchterman_reingold(A, dim=3)
|
||||
assert pos.shape == (6, 3)
|
||||
|
||||
def test_single_nodes(self):
|
||||
G = nx.path_graph(1)
|
||||
vpos = nx.shell_layout(G)
|
||||
assert not vpos[0].any()
|
||||
G = nx.path_graph(4)
|
||||
vpos = nx.shell_layout(G, [[0], [1, 2], [3]])
|
||||
assert not vpos[0].any()
|
||||
assert vpos[3].any() # ensure node 3 not at origin (#3188)
|
||||
assert numpy.linalg.norm(vpos[3]) <= 1 # ensure node 3 fits (#3753)
|
||||
vpos = nx.shell_layout(G, [[0], [1, 2], [3]], rotate=0)
|
||||
assert numpy.linalg.norm(vpos[3]) <= 1 # ensure node 3 fits (#3753)
|
||||
|
||||
def test_smoke_initial_pos_fruchterman_reingold(self):
|
||||
pos = nx.circular_layout(self.Gi)
|
||||
npos = nx.fruchterman_reingold_layout(self.Gi, pos=pos)
|
||||
|
||||
def test_fixed_node_fruchterman_reingold(self):
|
||||
# Dense version (numpy based)
|
||||
pos = nx.circular_layout(self.Gi)
|
||||
npos = nx.spring_layout(self.Gi, pos=pos, fixed=[(0, 0)])
|
||||
assert tuple(pos[(0, 0)]) == tuple(npos[(0, 0)])
|
||||
# Sparse version (scipy based)
|
||||
pos = nx.circular_layout(self.bigG)
|
||||
npos = nx.spring_layout(self.bigG, pos=pos, fixed=[(0, 0)])
|
||||
for axis in range(2):
|
||||
assert almost_equal(pos[(0, 0)][axis], npos[(0, 0)][axis])
|
||||
|
||||
def test_center_parameter(self):
|
||||
G = nx.path_graph(1)
|
||||
nx.random_layout(G, center=(1, 1))
|
||||
vpos = nx.circular_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.planar_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.spring_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.fruchterman_reingold_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.spectral_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.shell_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.spiral_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
|
||||
def test_center_wrong_dimensions(self):
|
||||
G = nx.path_graph(1)
|
||||
assert id(nx.spring_layout) == id(nx.fruchterman_reingold_layout)
|
||||
pytest.raises(ValueError, nx.random_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.circular_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.planar_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.spring_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.spring_layout, G, dim=3, center=(1, 1))
|
||||
pytest.raises(ValueError, nx.spectral_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.spectral_layout, G, dim=3, center=(1, 1))
|
||||
pytest.raises(ValueError, nx.shell_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.spiral_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.kamada_kawai_layout, G, center=(1, 1, 1))
|
||||
|
||||
def test_empty_graph(self):
|
||||
G = nx.empty_graph()
|
||||
vpos = nx.random_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.circular_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.planar_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.bipartite_layout(G, G)
|
||||
assert vpos == {}
|
||||
vpos = nx.spring_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.fruchterman_reingold_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.spectral_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.shell_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.spiral_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.multipartite_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.kamada_kawai_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
|
||||
def test_bipartite_layout(self):
|
||||
G = nx.complete_bipartite_graph(3, 5)
|
||||
top, bottom = nx.bipartite.sets(G)
|
||||
|
||||
vpos = nx.bipartite_layout(G, top)
|
||||
assert len(vpos) == len(G)
|
||||
|
||||
top_x = vpos[list(top)[0]][0]
|
||||
bottom_x = vpos[list(bottom)[0]][0]
|
||||
for node in top:
|
||||
assert vpos[node][0] == top_x
|
||||
for node in bottom:
|
||||
assert vpos[node][0] == bottom_x
|
||||
|
||||
vpos = nx.bipartite_layout(
|
||||
G, top, align="horizontal", center=(2, 2), scale=2, aspect_ratio=1
|
||||
)
|
||||
assert len(vpos) == len(G)
|
||||
|
||||
top_y = vpos[list(top)[0]][1]
|
||||
bottom_y = vpos[list(bottom)[0]][1]
|
||||
for node in top:
|
||||
assert vpos[node][1] == top_y
|
||||
for node in bottom:
|
||||
assert vpos[node][1] == bottom_y
|
||||
|
||||
pytest.raises(ValueError, nx.bipartite_layout, G, top, align="foo")
|
||||
|
||||
def test_multipartite_layout(self):
|
||||
sizes = (0, 5, 7, 2, 8)
|
||||
G = nx.complete_multipartite_graph(*sizes)
|
||||
|
||||
vpos = nx.multipartite_layout(G)
|
||||
assert len(vpos) == len(G)
|
||||
|
||||
start = 0
|
||||
for n in sizes:
|
||||
end = start + n
|
||||
assert all(vpos[start][0] == vpos[i][0] for i in range(start + 1, end))
|
||||
start += n
|
||||
|
||||
vpos = nx.multipartite_layout(G, align="horizontal", scale=2, center=(2, 2))
|
||||
assert len(vpos) == len(G)
|
||||
|
||||
start = 0
|
||||
for n in sizes:
|
||||
end = start + n
|
||||
assert all(vpos[start][1] == vpos[i][1] for i in range(start + 1, end))
|
||||
start += n
|
||||
|
||||
pytest.raises(ValueError, nx.multipartite_layout, G, align="foo")
|
||||
|
||||
def test_kamada_kawai_costfn_1d(self):
|
||||
costfn = nx.drawing.layout._kamada_kawai_costfn
|
||||
|
||||
pos = numpy.array([4.0, 7.0])
|
||||
invdist = 1 / numpy.array([[0.1, 2.0], [2.0, 0.3]])
|
||||
|
||||
cost, grad = costfn(pos, numpy, invdist, meanweight=0, dim=1)
|
||||
|
||||
assert almost_equal(cost, ((3 / 2.0 - 1) ** 2))
|
||||
assert almost_equal(grad[0], -0.5)
|
||||
assert almost_equal(grad[1], 0.5)
|
||||
|
||||
def check_kamada_kawai_costfn(self, pos, invdist, meanwt, dim):
|
||||
costfn = nx.drawing.layout._kamada_kawai_costfn
|
||||
|
||||
cost, grad = costfn(pos.ravel(), numpy, invdist, meanweight=meanwt, dim=dim)
|
||||
|
||||
expected_cost = 0.5 * meanwt * numpy.sum(numpy.sum(pos, axis=0) ** 2)
|
||||
for i in range(pos.shape[0]):
|
||||
for j in range(i + 1, pos.shape[0]):
|
||||
diff = numpy.linalg.norm(pos[i] - pos[j])
|
||||
expected_cost += (diff * invdist[i][j] - 1.0) ** 2
|
||||
|
||||
assert almost_equal(cost, expected_cost)
|
||||
|
||||
dx = 1e-4
|
||||
for nd in range(pos.shape[0]):
|
||||
for dm in range(pos.shape[1]):
|
||||
idx = nd * pos.shape[1] + dm
|
||||
pos0 = pos.flatten()
|
||||
|
||||
pos0[idx] += dx
|
||||
cplus = costfn(
|
||||
pos0, numpy, invdist, meanweight=meanwt, dim=pos.shape[1]
|
||||
)[0]
|
||||
|
||||
pos0[idx] -= 2 * dx
|
||||
cminus = costfn(
|
||||
pos0, numpy, invdist, meanweight=meanwt, dim=pos.shape[1]
|
||||
)[0]
|
||||
|
||||
assert almost_equal(grad[idx], (cplus - cminus) / (2 * dx), places=5)
|
||||
|
||||
def test_kamada_kawai_costfn(self):
|
||||
invdist = 1 / numpy.array([[0.1, 2.1, 1.7], [2.1, 0.2, 0.6], [1.7, 0.6, 0.3]])
|
||||
meanwt = 0.3
|
||||
|
||||
# 2d
|
||||
pos = numpy.array([[1.3, -3.2], [2.7, -0.3], [5.1, 2.5]])
|
||||
|
||||
self.check_kamada_kawai_costfn(pos, invdist, meanwt, 2)
|
||||
|
||||
# 3d
|
||||
pos = numpy.array([[0.9, 8.6, -8.7], [-10, -0.5, -7.1], [9.1, -8.1, 1.6]])
|
||||
|
||||
self.check_kamada_kawai_costfn(pos, invdist, meanwt, 3)
|
||||
|
||||
def test_spiral_layout(self):
|
||||
|
||||
G = self.Gs
|
||||
|
||||
# a lower value of resolution should result in a more compact layout
|
||||
# intuitively, the total distance from the start and end nodes
|
||||
# via each node in between (transiting through each) will be less,
|
||||
# assuming rescaling does not occur on the computed node positions
|
||||
pos_standard = nx.spiral_layout(G, resolution=0.35)
|
||||
pos_tighter = nx.spiral_layout(G, resolution=0.34)
|
||||
distances = self.collect_node_distances(pos_standard)
|
||||
distances_tighter = self.collect_node_distances(pos_tighter)
|
||||
assert sum(distances) > sum(distances_tighter)
|
||||
|
||||
# return near-equidistant points after the first value if set to true
|
||||
pos_equidistant = nx.spiral_layout(G, equidistant=True)
|
||||
distances_equidistant = self.collect_node_distances(pos_equidistant)
|
||||
for d in range(1, len(distances_equidistant) - 1):
|
||||
# test similarity to two decimal places
|
||||
assert almost_equal(
|
||||
distances_equidistant[d], distances_equidistant[d + 1], 2
|
||||
)
|
||||
|
||||
def test_rescale_layout_dict(self):
|
||||
G = nx.empty_graph()
|
||||
vpos = nx.random_layout(G, center=(1, 1))
|
||||
assert nx.rescale_layout_dict(vpos) == {}
|
||||
|
||||
G = nx.empty_graph(2)
|
||||
vpos = {0: (0.0, 0.0), 1: (1.0, 1.0)}
|
||||
s_vpos = nx.rescale_layout_dict(vpos)
|
||||
norm = numpy.linalg.norm
|
||||
assert norm([sum(x) for x in zip(*s_vpos.values())]) < 1e-6
|
||||
|
||||
G = nx.empty_graph(3)
|
||||
vpos = {0: (0, 0), 1: (1, 1), 2: (0.5, 0.5)}
|
||||
s_vpos = nx.rescale_layout_dict(vpos)
|
||||
assert s_vpos == {0: (-1, -1), 1: (1, 1), 2: (0, 0)}
|
||||
s_vpos = nx.rescale_layout_dict(vpos, scale=2)
|
||||
assert s_vpos == {0: (-2, -2), 1: (2, 2), 2: (0, 0)}
|
94
venv/Lib/site-packages/networkx/drawing/tests/test_pydot.py
Normal file
94
venv/Lib/site-packages/networkx/drawing/tests/test_pydot.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
"""Unit tests for pydot drawing functions."""
|
||||
from io import StringIO
|
||||
import tempfile
|
||||
import networkx as nx
|
||||
from networkx.testing import assert_graphs_equal
|
||||
|
||||
import pytest
|
||||
|
||||
pydot = pytest.importorskip("pydot")
|
||||
|
||||
|
||||
class TestPydot:
|
||||
def pydot_checks(self, G, prog):
|
||||
"""
|
||||
Validate :mod:`pydot`-based usage of the passed NetworkX graph with the
|
||||
passed basename of an external GraphViz command (e.g., `dot`, `neato`).
|
||||
"""
|
||||
|
||||
# Set the name of this graph to... "G". Failing to do so will
|
||||
# subsequently trip an assertion expecting this name.
|
||||
G.graph["name"] = "G"
|
||||
|
||||
# Add arbitrary nodes and edges to the passed empty graph.
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "C"), ("A", "D")])
|
||||
G.add_node("E")
|
||||
|
||||
# Validate layout of this graph with the passed GraphViz command.
|
||||
graph_layout = nx.nx_pydot.pydot_layout(G, prog=prog)
|
||||
assert isinstance(graph_layout, dict)
|
||||
|
||||
# Convert this graph into a "pydot.Dot" instance.
|
||||
P = nx.nx_pydot.to_pydot(G)
|
||||
|
||||
# Convert this "pydot.Dot" instance back into a graph of the same type.
|
||||
G2 = G.__class__(nx.nx_pydot.from_pydot(P))
|
||||
|
||||
# Validate the original and resulting graphs to be the same.
|
||||
assert_graphs_equal(G, G2)
|
||||
|
||||
# Serialize this "pydot.Dot" instance to a temporary file in dot format
|
||||
fname = tempfile.mktemp()
|
||||
P.write_raw(fname)
|
||||
|
||||
# Deserialize a list of new "pydot.Dot" instances back from this file.
|
||||
Pin_list = pydot.graph_from_dot_file(path=fname, encoding="utf-8")
|
||||
|
||||
# Validate this file to contain only one graph.
|
||||
assert len(Pin_list) == 1
|
||||
|
||||
# The single "pydot.Dot" instance deserialized from this file.
|
||||
Pin = Pin_list[0]
|
||||
|
||||
# Sorted list of all nodes in the original "pydot.Dot" instance.
|
||||
n1 = sorted([p.get_name() for p in P.get_node_list()])
|
||||
|
||||
# Sorted list of all nodes in the deserialized "pydot.Dot" instance.
|
||||
n2 = sorted([p.get_name() for p in Pin.get_node_list()])
|
||||
|
||||
# Validate these instances to contain the same nodes.
|
||||
assert n1 == n2
|
||||
|
||||
# Sorted list of all edges in the original "pydot.Dot" instance.
|
||||
e1 = sorted([(e.get_source(), e.get_destination()) for e in P.get_edge_list()])
|
||||
|
||||
# Sorted list of all edges in the original "pydot.Dot" instance.
|
||||
e2 = sorted(
|
||||
[(e.get_source(), e.get_destination()) for e in Pin.get_edge_list()]
|
||||
)
|
||||
|
||||
# Validate these instances to contain the same edges.
|
||||
assert e1 == e2
|
||||
|
||||
# Deserialize a new graph of the same type back from this file.
|
||||
Hin = nx.nx_pydot.read_dot(fname)
|
||||
Hin = G.__class__(Hin)
|
||||
|
||||
# Validate the original and resulting graphs to be the same.
|
||||
assert_graphs_equal(G, Hin)
|
||||
|
||||
def test_undirected(self):
|
||||
self.pydot_checks(nx.Graph(), prog="neato")
|
||||
|
||||
def test_directed(self):
|
||||
self.pydot_checks(nx.DiGraph(), prog="dot")
|
||||
|
||||
def test_read_write(self):
|
||||
G = nx.MultiGraph()
|
||||
G.graph["name"] = "G"
|
||||
G.add_edge("1", "2", key="0") # read assumes strings
|
||||
fh = StringIO()
|
||||
nx.nx_pydot.write_dot(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.nx_pydot.read_dot(fh)
|
||||
assert_graphs_equal(G, H)
|
257
venv/Lib/site-packages/networkx/drawing/tests/test_pylab.py
Normal file
257
venv/Lib/site-packages/networkx/drawing/tests/test_pylab.py
Normal file
|
@ -0,0 +1,257 @@
|
|||
"""Unit tests for matplotlib drawing functions."""
|
||||
import os
|
||||
import itertools
|
||||
import pytest
|
||||
|
||||
mpl = pytest.importorskip("matplotlib")
|
||||
mpl.use("PS")
|
||||
plt = pytest.importorskip("matplotlib.pyplot")
|
||||
plt.rcParams["text.usetex"] = False
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestPylab:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = nx.barbell_graph(4, 6)
|
||||
|
||||
def test_draw(self):
|
||||
try:
|
||||
functions = [
|
||||
nx.draw_circular,
|
||||
nx.draw_kamada_kawai,
|
||||
nx.draw_planar,
|
||||
nx.draw_random,
|
||||
nx.draw_spectral,
|
||||
nx.draw_spring,
|
||||
nx.draw_shell,
|
||||
]
|
||||
options = [{"node_color": "black", "node_size": 100, "width": 3}]
|
||||
for function, option in itertools.product(functions, options):
|
||||
function(self.G, **option)
|
||||
plt.savefig("test.ps")
|
||||
|
||||
finally:
|
||||
try:
|
||||
os.unlink("test.ps")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def test_draw_shell_nlist(self):
|
||||
try:
|
||||
nlist = [list(range(4)), list(range(4, 10)), list(range(10, 14))]
|
||||
nx.draw_shell(self.G, nlist=nlist)
|
||||
plt.savefig("test.ps")
|
||||
finally:
|
||||
try:
|
||||
os.unlink("test.ps")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def test_edge_colormap(self):
|
||||
colors = range(self.G.number_of_edges())
|
||||
nx.draw_spring(
|
||||
self.G, edge_color=colors, width=4, edge_cmap=plt.cm.Blues, with_labels=True
|
||||
)
|
||||
# plt.show()
|
||||
|
||||
def test_arrows(self):
|
||||
nx.draw_spring(self.G.to_directed())
|
||||
# plt.show()
|
||||
|
||||
def test_edge_colors_and_widths(self):
|
||||
pos = nx.circular_layout(self.G)
|
||||
for G in (self.G, self.G.to_directed()):
|
||||
nx.draw_networkx_nodes(G, pos, node_color=[(1.0, 1.0, 0.2, 0.5)])
|
||||
nx.draw_networkx_labels(G, pos)
|
||||
# edge with default color and width
|
||||
nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(0, 1)], width=None, edge_color=None
|
||||
)
|
||||
# edges with global color strings and widths in lists
|
||||
nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(0, 2), (0, 3)], width=[3], edge_color=["r"]
|
||||
)
|
||||
# edges with color strings and widths for each edge
|
||||
nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(0, 2), (0, 3)], width=[1, 3], edge_color=["r", "b"]
|
||||
)
|
||||
# edges with fewer color strings and widths than edges
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(1, 2), (1, 3), (2, 3), (3, 4)],
|
||||
width=[1, 3],
|
||||
edge_color=["g", "m", "c"],
|
||||
)
|
||||
# edges with more color strings and widths than edges
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(3, 4)],
|
||||
width=[1, 2, 3, 4],
|
||||
edge_color=["r", "b", "g", "k"],
|
||||
)
|
||||
# with rgb tuple and 3 edges - is interpreted with cmap
|
||||
nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(4, 5), (5, 6), (6, 7)], edge_color=(1.0, 0.4, 0.3)
|
||||
)
|
||||
# with rgb tuple in list
|
||||
nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(7, 8), (8, 9)], edge_color=[(0.4, 1.0, 0.0)]
|
||||
)
|
||||
# with rgba tuple and 4 edges - is interpretted with cmap
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(9, 10), (10, 11), (10, 12), (10, 13)],
|
||||
edge_color=(0.0, 1.0, 1.0, 0.5),
|
||||
)
|
||||
# with rgba tuple in list
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(9, 10), (10, 11), (10, 12), (10, 13)],
|
||||
edge_color=[(0.0, 1.0, 1.0, 0.5)],
|
||||
)
|
||||
# with color string and global alpha
|
||||
nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(11, 12), (11, 13)], edge_color="purple", alpha=0.2
|
||||
)
|
||||
# with color string in a list
|
||||
nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(11, 12), (11, 13)], edge_color=["purple"]
|
||||
)
|
||||
# with single edge and hex color string
|
||||
nx.draw_networkx_edges(G, pos, edgelist=[(12, 13)], edge_color="#1f78b4f0")
|
||||
|
||||
# edge_color as numeric using vmin, vmax
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(7, 8), (8, 9)],
|
||||
edge_color=[0.2, 0.5],
|
||||
edge_vmin=0.1,
|
||||
edge_vmax=0.6,
|
||||
)
|
||||
|
||||
# plt.show()
|
||||
|
||||
def test_labels_and_colors(self):
|
||||
G = nx.cubical_graph()
|
||||
pos = nx.spring_layout(G) # positions for all nodes
|
||||
# nodes
|
||||
nx.draw_networkx_nodes(
|
||||
G, pos, nodelist=[0, 1, 2, 3], node_color="r", node_size=500, alpha=0.75
|
||||
)
|
||||
nx.draw_networkx_nodes(
|
||||
G,
|
||||
pos,
|
||||
nodelist=[4, 5, 6, 7],
|
||||
node_color="b",
|
||||
node_size=500,
|
||||
alpha=[0.25, 0.5, 0.75, 1.0],
|
||||
)
|
||||
# edges
|
||||
nx.draw_networkx_edges(G, pos, width=1.0, alpha=0.5)
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(0, 1), (1, 2), (2, 3), (3, 0)],
|
||||
width=8,
|
||||
alpha=0.5,
|
||||
edge_color="r",
|
||||
)
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(4, 5), (5, 6), (6, 7), (7, 4)],
|
||||
width=8,
|
||||
alpha=0.5,
|
||||
edge_color="b",
|
||||
)
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(4, 5), (5, 6), (6, 7), (7, 4)],
|
||||
min_source_margin=0.5,
|
||||
min_target_margin=0.75,
|
||||
width=8,
|
||||
edge_color="b",
|
||||
)
|
||||
# some math labels
|
||||
labels = {}
|
||||
labels[0] = r"$a$"
|
||||
labels[1] = r"$b$"
|
||||
labels[2] = r"$c$"
|
||||
labels[3] = r"$d$"
|
||||
labels[4] = r"$\alpha$"
|
||||
labels[5] = r"$\beta$"
|
||||
labels[6] = r"$\gamma$"
|
||||
labels[7] = r"$\delta$"
|
||||
nx.draw_networkx_labels(G, pos, labels, font_size=16)
|
||||
nx.draw_networkx_edge_labels(G, pos, edge_labels=None, rotate=False)
|
||||
nx.draw_networkx_edge_labels(G, pos, edge_labels={(4, 5): "4-5"})
|
||||
# plt.show()
|
||||
|
||||
def test_axes(self):
|
||||
fig, ax = plt.subplots()
|
||||
nx.draw(self.G, ax=ax)
|
||||
|
||||
def test_empty_graph(self):
|
||||
G = nx.Graph()
|
||||
nx.draw(G)
|
||||
|
||||
def test_draw_empty_nodes_return_values(self):
|
||||
# See Issue #3833
|
||||
from matplotlib.collections import PathCollection, LineCollection
|
||||
|
||||
G = nx.Graph([(1, 2), (2, 3)])
|
||||
DG = nx.DiGraph([(1, 2), (2, 3)])
|
||||
pos = nx.circular_layout(G)
|
||||
assert isinstance(nx.draw_networkx_nodes(G, pos, nodelist=[]), PathCollection)
|
||||
assert isinstance(nx.draw_networkx_nodes(DG, pos, nodelist=[]), PathCollection)
|
||||
|
||||
# drawing empty edges either return an empty LineCollection or empty list.
|
||||
assert isinstance(
|
||||
nx.draw_networkx_edges(G, pos, edgelist=[], arrows=True), LineCollection
|
||||
)
|
||||
assert isinstance(
|
||||
nx.draw_networkx_edges(G, pos, edgelist=[], arrows=False), LineCollection
|
||||
)
|
||||
assert isinstance(
|
||||
nx.draw_networkx_edges(DG, pos, edgelist=[], arrows=False), LineCollection
|
||||
)
|
||||
assert nx.draw_networkx_edges(DG, pos, edgelist=[], arrows=True) == []
|
||||
|
||||
def test_multigraph_edgelist_tuples(self):
|
||||
# See Issue #3295
|
||||
G = nx.path_graph(3, create_using=nx.MultiDiGraph)
|
||||
nx.draw_networkx(G, edgelist=[(0, 1, 0)])
|
||||
nx.draw_networkx(G, edgelist=[(0, 1, 0)], node_size=[10, 20, 0])
|
||||
|
||||
def test_alpha_iter(self):
|
||||
pos = nx.random_layout(self.G)
|
||||
# with fewer alpha elements than nodes
|
||||
plt.subplot(131)
|
||||
nx.draw_networkx_nodes(self.G, pos, alpha=[0.1, 0.2])
|
||||
# with equal alpha elements and nodes
|
||||
num_nodes = len(self.G.nodes)
|
||||
alpha = [x / num_nodes for x in range(num_nodes)]
|
||||
colors = range(num_nodes)
|
||||
plt.subplot(132)
|
||||
nx.draw_networkx_nodes(self.G, pos, node_color=colors, alpha=alpha)
|
||||
# with more alpha elements than nodes
|
||||
alpha.append(1)
|
||||
plt.subplot(133)
|
||||
nx.draw_networkx_nodes(self.G, pos, alpha=alpha)
|
||||
|
||||
def test_error_invalid_kwds(self):
|
||||
with pytest.raises(ValueError, match="Received invalid argument"):
|
||||
nx.draw(self.G, foo="bar")
|
||||
|
||||
def test_np_edgelist(self):
|
||||
# see issue #4129
|
||||
np = pytest.importorskip("numpy")
|
||||
nx.draw_networkx(self.G, edgelist=np.array([(0, 2), (0, 3)]))
|
Loading…
Add table
Add a link
Reference in a new issue