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
18
venv/Lib/site-packages/networkx/readwrite/__init__.py
Normal file
18
venv/Lib/site-packages/networkx/readwrite/__init__.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
"""
|
||||
A package for reading and writing graphs in various formats.
|
||||
|
||||
"""
|
||||
from networkx.readwrite.adjlist import *
|
||||
from networkx.readwrite.multiline_adjlist import *
|
||||
from networkx.readwrite.edgelist import *
|
||||
from networkx.readwrite.gpickle import *
|
||||
from networkx.readwrite.pajek import *
|
||||
from networkx.readwrite.leda import *
|
||||
from networkx.readwrite.sparse6 import *
|
||||
from networkx.readwrite.graph6 import *
|
||||
from networkx.readwrite.nx_yaml import *
|
||||
from networkx.readwrite.gml import *
|
||||
from networkx.readwrite.graphml import *
|
||||
from networkx.readwrite.gexf import *
|
||||
from networkx.readwrite.nx_shp import *
|
||||
from networkx.readwrite.json_graph import *
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
296
venv/Lib/site-packages/networkx/readwrite/adjlist.py
Normal file
296
venv/Lib/site-packages/networkx/readwrite/adjlist.py
Normal file
|
@ -0,0 +1,296 @@
|
|||
"""
|
||||
**************
|
||||
Adjacency List
|
||||
**************
|
||||
Read and write NetworkX graphs as adjacency lists.
|
||||
|
||||
Adjacency list format is useful for graphs without data associated
|
||||
with nodes or edges and for nodes that can be meaningfully represented
|
||||
as strings.
|
||||
|
||||
Format
|
||||
------
|
||||
The adjacency list format consists of lines with node labels. The
|
||||
first label in a line is the source node. Further labels in the line
|
||||
are considered target nodes and are added to the graph along with an edge
|
||||
between the source node and target node.
|
||||
|
||||
The graph with edges a-b, a-c, d-e can be represented as the following
|
||||
adjacency list (anything following the # in a line is a comment)::
|
||||
|
||||
a b c # source target target
|
||||
d e
|
||||
"""
|
||||
|
||||
__all__ = ["generate_adjlist", "write_adjlist", "parse_adjlist", "read_adjlist"]
|
||||
|
||||
from networkx.utils import open_file
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def generate_adjlist(G, delimiter=" "):
|
||||
"""Generate a single line of the graph G in adjacency list format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
|
||||
delimiter : string, optional
|
||||
Separator for node labels
|
||||
|
||||
Returns
|
||||
-------
|
||||
lines : string
|
||||
Lines of data in adjlist format.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.lollipop_graph(4, 3)
|
||||
>>> for line in nx.generate_adjlist(G):
|
||||
... print(line)
|
||||
0 1 2 3
|
||||
1 2 3
|
||||
2 3
|
||||
3 4
|
||||
4 5
|
||||
5 6
|
||||
6
|
||||
|
||||
See Also
|
||||
--------
|
||||
write_adjlist, read_adjlist
|
||||
|
||||
"""
|
||||
directed = G.is_directed()
|
||||
seen = set()
|
||||
for s, nbrs in G.adjacency():
|
||||
line = str(s) + delimiter
|
||||
for t, data in nbrs.items():
|
||||
if not directed and t in seen:
|
||||
continue
|
||||
if G.is_multigraph():
|
||||
for d in data.values():
|
||||
line += str(t) + delimiter
|
||||
else:
|
||||
line += str(t) + delimiter
|
||||
if not directed:
|
||||
seen.add(s)
|
||||
yield line[: -len(delimiter)]
|
||||
|
||||
|
||||
@open_file(1, mode="wb")
|
||||
def write_adjlist(G, path, comments="#", delimiter=" ", encoding="utf-8"):
|
||||
"""Write graph G in single-line adjacency-list format to path.
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
|
||||
path : string or file
|
||||
Filename or file handle for data output.
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
|
||||
comments : string, optional
|
||||
Marker for comment lines
|
||||
|
||||
delimiter : string, optional
|
||||
Separator for node labels
|
||||
|
||||
encoding : string, optional
|
||||
Text encoding.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_adjlist(G, "test.adjlist")
|
||||
|
||||
The path can be a filehandle or a string with the name of the file. If a
|
||||
filehandle is provided, it has to be opened in 'wb' mode.
|
||||
|
||||
>>> fh = open("test.adjlist", "wb")
|
||||
>>> nx.write_adjlist(G, fh)
|
||||
|
||||
Notes
|
||||
-----
|
||||
This format does not store graph, node, or edge data.
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_adjlist, generate_adjlist
|
||||
"""
|
||||
import sys
|
||||
import time
|
||||
|
||||
pargs = comments + " ".join(sys.argv) + "\n"
|
||||
header = (
|
||||
pargs
|
||||
+ comments
|
||||
+ f" GMT {time.asctime(time.gmtime())}\n"
|
||||
+ comments
|
||||
+ f" {G.name}\n"
|
||||
)
|
||||
path.write(header.encode(encoding))
|
||||
|
||||
for line in generate_adjlist(G, delimiter):
|
||||
line += "\n"
|
||||
path.write(line.encode(encoding))
|
||||
|
||||
|
||||
def parse_adjlist(
|
||||
lines, comments="#", delimiter=None, create_using=None, nodetype=None
|
||||
):
|
||||
"""Parse lines of a graph adjacency list representation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lines : list or iterator of strings
|
||||
Input data in adjlist format
|
||||
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
nodetype : Python type, optional
|
||||
Convert nodes to this type.
|
||||
|
||||
comments : string, optional
|
||||
Marker for comment lines
|
||||
|
||||
delimiter : string, optional
|
||||
Separator for node labels. The default is whitespace.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G: NetworkX graph
|
||||
The graph corresponding to the lines in adjacency list format.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> lines = ["1 2 5", "2 3 4", "3 5", "4", "5"]
|
||||
>>> G = nx.parse_adjlist(lines, nodetype=int)
|
||||
>>> nodes = [1, 2, 3, 4, 5]
|
||||
>>> all(node in G for node in nodes)
|
||||
True
|
||||
>>> edges = [(1, 2), (1, 5), (2, 3), (2, 4), (3, 5)]
|
||||
>>> all((u, v) in G.edges() or (v, u) in G.edges() for (u, v) in edges)
|
||||
True
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_adjlist
|
||||
|
||||
"""
|
||||
G = nx.empty_graph(0, create_using)
|
||||
for line in lines:
|
||||
p = line.find(comments)
|
||||
if p >= 0:
|
||||
line = line[:p]
|
||||
if not len(line):
|
||||
continue
|
||||
vlist = line.strip().split(delimiter)
|
||||
u = vlist.pop(0)
|
||||
# convert types
|
||||
if nodetype is not None:
|
||||
try:
|
||||
u = nodetype(u)
|
||||
except BaseException as e:
|
||||
raise TypeError(
|
||||
f"Failed to convert node ({u}) to type " f"{nodetype}"
|
||||
) from e
|
||||
G.add_node(u)
|
||||
if nodetype is not None:
|
||||
try:
|
||||
vlist = list(map(nodetype, vlist))
|
||||
except BaseException as e:
|
||||
raise TypeError(
|
||||
f"Failed to convert nodes ({','.join(vlist)}) "
|
||||
f"to type {nodetype}"
|
||||
) from e
|
||||
G.add_edges_from([(u, v) for v in vlist])
|
||||
return G
|
||||
|
||||
|
||||
@open_file(0, mode="rb")
|
||||
def read_adjlist(
|
||||
path,
|
||||
comments="#",
|
||||
delimiter=None,
|
||||
create_using=None,
|
||||
nodetype=None,
|
||||
encoding="utf-8",
|
||||
):
|
||||
"""Read graph in adjacency list format from path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : string or file
|
||||
Filename or file handle to read.
|
||||
Filenames ending in .gz or .bz2 will be uncompressed.
|
||||
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
nodetype : Python type, optional
|
||||
Convert nodes to this type.
|
||||
|
||||
comments : string, optional
|
||||
Marker for comment lines
|
||||
|
||||
delimiter : string, optional
|
||||
Separator for node labels. The default is whitespace.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G: NetworkX graph
|
||||
The graph corresponding to the lines in adjacency list format.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_adjlist(G, "test.adjlist")
|
||||
>>> G = nx.read_adjlist("test.adjlist")
|
||||
|
||||
The path can be a filehandle or a string with the name of the file. If a
|
||||
filehandle is provided, it has to be opened in 'rb' mode.
|
||||
|
||||
>>> fh = open("test.adjlist", "rb")
|
||||
>>> G = nx.read_adjlist(fh)
|
||||
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
|
||||
>>> nx.write_adjlist(G, "test.adjlist.gz")
|
||||
>>> G = nx.read_adjlist("test.adjlist.gz")
|
||||
|
||||
The optional nodetype is a function to convert node strings to nodetype.
|
||||
|
||||
For example
|
||||
|
||||
>>> G = nx.read_adjlist("test.adjlist", nodetype=int)
|
||||
|
||||
will attempt to convert all nodes to integer type.
|
||||
|
||||
Since nodes must be hashable, the function nodetype must return hashable
|
||||
types (e.g. int, float, str, frozenset - or tuples of those, etc.)
|
||||
|
||||
The optional create_using parameter indicates the type of NetworkX graph
|
||||
created. The default is `nx.Graph`, an undirected graph.
|
||||
To read the data as a directed graph use
|
||||
|
||||
>>> G = nx.read_adjlist("test.adjlist", create_using=nx.DiGraph)
|
||||
|
||||
Notes
|
||||
-----
|
||||
This format does not store graph or node data.
|
||||
|
||||
See Also
|
||||
--------
|
||||
write_adjlist
|
||||
"""
|
||||
lines = (line.decode(encoding) for line in path)
|
||||
return parse_adjlist(
|
||||
lines,
|
||||
comments=comments,
|
||||
delimiter=delimiter,
|
||||
create_using=create_using,
|
||||
nodetype=nodetype,
|
||||
)
|
483
venv/Lib/site-packages/networkx/readwrite/edgelist.py
Normal file
483
venv/Lib/site-packages/networkx/readwrite/edgelist.py
Normal file
|
@ -0,0 +1,483 @@
|
|||
"""
|
||||
**********
|
||||
Edge Lists
|
||||
**********
|
||||
Read and write NetworkX graphs as edge lists.
|
||||
|
||||
The multi-line adjacency list format is useful for graphs with nodes
|
||||
that can be meaningfully represented as strings. With the edgelist
|
||||
format simple edge data can be stored but node or graph data is not.
|
||||
There is no way of representing isolated nodes unless the node has a
|
||||
self-loop edge.
|
||||
|
||||
Format
|
||||
------
|
||||
You can read or write three formats of edge lists with these functions.
|
||||
|
||||
Node pairs with no data::
|
||||
|
||||
1 2
|
||||
|
||||
Python dictionary as data::
|
||||
|
||||
1 2 {'weight':7, 'color':'green'}
|
||||
|
||||
Arbitrary data::
|
||||
|
||||
1 2 7 green
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
"generate_edgelist",
|
||||
"write_edgelist",
|
||||
"parse_edgelist",
|
||||
"read_edgelist",
|
||||
"read_weighted_edgelist",
|
||||
"write_weighted_edgelist",
|
||||
]
|
||||
|
||||
from networkx.utils import open_file
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def generate_edgelist(G, delimiter=" ", data=True):
|
||||
"""Generate a single line of the graph G in edge list format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
|
||||
delimiter : string, optional
|
||||
Separator for node labels
|
||||
|
||||
data : bool or list of keys
|
||||
If False generate no edge data. If True use a dictionary
|
||||
representation of edge data. If a list of keys use a list of data
|
||||
values corresponding to the keys.
|
||||
|
||||
Returns
|
||||
-------
|
||||
lines : string
|
||||
Lines of data in adjlist format.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.lollipop_graph(4, 3)
|
||||
>>> G[1][2]["weight"] = 3
|
||||
>>> G[3][4]["capacity"] = 12
|
||||
>>> for line in nx.generate_edgelist(G, data=False):
|
||||
... print(line)
|
||||
0 1
|
||||
0 2
|
||||
0 3
|
||||
1 2
|
||||
1 3
|
||||
2 3
|
||||
3 4
|
||||
4 5
|
||||
5 6
|
||||
|
||||
>>> for line in nx.generate_edgelist(G):
|
||||
... print(line)
|
||||
0 1 {}
|
||||
0 2 {}
|
||||
0 3 {}
|
||||
1 2 {'weight': 3}
|
||||
1 3 {}
|
||||
2 3 {}
|
||||
3 4 {'capacity': 12}
|
||||
4 5 {}
|
||||
5 6 {}
|
||||
|
||||
>>> for line in nx.generate_edgelist(G, data=["weight"]):
|
||||
... print(line)
|
||||
0 1
|
||||
0 2
|
||||
0 3
|
||||
1 2 3
|
||||
1 3
|
||||
2 3
|
||||
3 4
|
||||
4 5
|
||||
5 6
|
||||
|
||||
See Also
|
||||
--------
|
||||
write_adjlist, read_adjlist
|
||||
"""
|
||||
if data is True:
|
||||
for u, v, d in G.edges(data=True):
|
||||
e = u, v, dict(d)
|
||||
yield delimiter.join(map(str, e))
|
||||
elif data is False:
|
||||
for u, v in G.edges(data=False):
|
||||
e = u, v
|
||||
yield delimiter.join(map(str, e))
|
||||
else:
|
||||
for u, v, d in G.edges(data=True):
|
||||
e = [u, v]
|
||||
try:
|
||||
e.extend(d[k] for k in data)
|
||||
except KeyError:
|
||||
pass # missing data for this edge, should warn?
|
||||
yield delimiter.join(map(str, e))
|
||||
|
||||
|
||||
@open_file(1, mode="wb")
|
||||
def write_edgelist(G, path, comments="#", delimiter=" ", data=True, encoding="utf-8"):
|
||||
"""Write graph as a list of edges.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
path : file or string
|
||||
File or filename to write. If a file is provided, it must be
|
||||
opened in 'wb' mode. Filenames ending in .gz or .bz2 will be compressed.
|
||||
comments : string, optional
|
||||
The character used to indicate the start of a comment
|
||||
delimiter : string, optional
|
||||
The string used to separate values. The default is whitespace.
|
||||
data : bool or list, optional
|
||||
If False write no edge data.
|
||||
If True write a string representation of the edge data dictionary..
|
||||
If a list (or other iterable) is provided, write the keys specified
|
||||
in the list.
|
||||
encoding: string, optional
|
||||
Specify which encoding to use when writing file.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_edgelist(G, "test.edgelist")
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> fh = open("test.edgelist", "wb")
|
||||
>>> nx.write_edgelist(G, fh)
|
||||
>>> nx.write_edgelist(G, "test.edgelist.gz")
|
||||
>>> nx.write_edgelist(G, "test.edgelist.gz", data=False)
|
||||
|
||||
>>> G = nx.Graph()
|
||||
>>> G.add_edge(1, 2, weight=7, color="red")
|
||||
>>> nx.write_edgelist(G, "test.edgelist", data=False)
|
||||
>>> nx.write_edgelist(G, "test.edgelist", data=["color"])
|
||||
>>> nx.write_edgelist(G, "test.edgelist", data=["color", "weight"])
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_edgelist
|
||||
write_weighted_edgelist
|
||||
"""
|
||||
|
||||
for line in generate_edgelist(G, delimiter, data):
|
||||
line += "\n"
|
||||
path.write(line.encode(encoding))
|
||||
|
||||
|
||||
def parse_edgelist(
|
||||
lines, comments="#", delimiter=None, create_using=None, nodetype=None, data=True
|
||||
):
|
||||
"""Parse lines of an edge list representation of a graph.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lines : list or iterator of strings
|
||||
Input data in edgelist format
|
||||
comments : string, optional
|
||||
Marker for comment lines. Default is `'#'`
|
||||
delimiter : string, optional
|
||||
Separator for node labels. Default is `None`, meaning any whitespace.
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
nodetype : Python type, optional
|
||||
Convert nodes to this type. Default is `None`, meaning no conversion is
|
||||
performed.
|
||||
data : bool or list of (label,type) tuples
|
||||
If `False` generate no edge data or if `True` use a dictionary
|
||||
representation of edge data or a list tuples specifying dictionary
|
||||
key names and types for edge data.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G: NetworkX Graph
|
||||
The graph corresponding to lines
|
||||
|
||||
Examples
|
||||
--------
|
||||
Edgelist with no data:
|
||||
|
||||
>>> lines = ["1 2", "2 3", "3 4"]
|
||||
>>> G = nx.parse_edgelist(lines, nodetype=int)
|
||||
>>> list(G)
|
||||
[1, 2, 3, 4]
|
||||
>>> list(G.edges())
|
||||
[(1, 2), (2, 3), (3, 4)]
|
||||
|
||||
Edgelist with data in Python dictionary representation:
|
||||
|
||||
>>> lines = ["1 2 {'weight': 3}", "2 3 {'weight': 27}", "3 4 {'weight': 3.0}"]
|
||||
>>> G = nx.parse_edgelist(lines, nodetype=int)
|
||||
>>> list(G)
|
||||
[1, 2, 3, 4]
|
||||
>>> list(G.edges(data=True))
|
||||
[(1, 2, {'weight': 3}), (2, 3, {'weight': 27}), (3, 4, {'weight': 3.0})]
|
||||
|
||||
Edgelist with data in a list:
|
||||
|
||||
>>> lines = ["1 2 3", "2 3 27", "3 4 3.0"]
|
||||
>>> G = nx.parse_edgelist(lines, nodetype=int, data=(("weight", float),))
|
||||
>>> list(G)
|
||||
[1, 2, 3, 4]
|
||||
>>> list(G.edges(data=True))
|
||||
[(1, 2, {'weight': 3.0}), (2, 3, {'weight': 27.0}), (3, 4, {'weight': 3.0})]
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_weighted_edgelist
|
||||
"""
|
||||
from ast import literal_eval
|
||||
|
||||
G = nx.empty_graph(0, create_using)
|
||||
for line in lines:
|
||||
p = line.find(comments)
|
||||
if p >= 0:
|
||||
line = line[:p]
|
||||
if not line:
|
||||
continue
|
||||
# split line, should have 2 or more
|
||||
s = line.strip().split(delimiter)
|
||||
if len(s) < 2:
|
||||
continue
|
||||
u = s.pop(0)
|
||||
v = s.pop(0)
|
||||
d = s
|
||||
if nodetype is not None:
|
||||
try:
|
||||
u = nodetype(u)
|
||||
v = nodetype(v)
|
||||
except Exception as e:
|
||||
raise TypeError(
|
||||
f"Failed to convert nodes {u},{v} to type {nodetype}."
|
||||
) from e
|
||||
|
||||
if len(d) == 0 or data is False:
|
||||
# no data or data type specified
|
||||
edgedata = {}
|
||||
elif data is True:
|
||||
# no edge types specified
|
||||
try: # try to evaluate as dictionary
|
||||
if delimiter == ",":
|
||||
edgedata_str = ",".join(d)
|
||||
else:
|
||||
edgedata_str = " ".join(d)
|
||||
edgedata = dict(literal_eval(edgedata_str.strip()))
|
||||
except Exception as e:
|
||||
raise TypeError(
|
||||
f"Failed to convert edge data ({d}) to dictionary."
|
||||
) from e
|
||||
else:
|
||||
# convert edge data to dictionary with specified keys and type
|
||||
if len(d) != len(data):
|
||||
raise IndexError(
|
||||
f"Edge data {d} and data_keys {data} are not the same length"
|
||||
)
|
||||
edgedata = {}
|
||||
for (edge_key, edge_type), edge_value in zip(data, d):
|
||||
try:
|
||||
edge_value = edge_type(edge_value)
|
||||
except Exception as e:
|
||||
raise TypeError(
|
||||
f"Failed to convert {edge_key} data {edge_value} "
|
||||
f"to type {edge_type}."
|
||||
) from e
|
||||
edgedata.update({edge_key: edge_value})
|
||||
G.add_edge(u, v, **edgedata)
|
||||
return G
|
||||
|
||||
|
||||
@open_file(0, mode="rb")
|
||||
def read_edgelist(
|
||||
path,
|
||||
comments="#",
|
||||
delimiter=None,
|
||||
create_using=None,
|
||||
nodetype=None,
|
||||
data=True,
|
||||
edgetype=None,
|
||||
encoding="utf-8",
|
||||
):
|
||||
"""Read a graph from a list of edges.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File or filename to read. If a file is provided, it must be
|
||||
opened in 'rb' mode.
|
||||
Filenames ending in .gz or .bz2 will be uncompressed.
|
||||
comments : string, optional
|
||||
The character used to indicate the start of a comment.
|
||||
delimiter : string, optional
|
||||
The string used to separate values. The default is whitespace.
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
nodetype : int, float, str, Python type, optional
|
||||
Convert node data from strings to specified type
|
||||
data : bool or list of (label,type) tuples
|
||||
Tuples specifying dictionary key names and types for edge data
|
||||
edgetype : int, float, str, Python type, optional OBSOLETE
|
||||
Convert edge data from strings to specified type and use as 'weight'
|
||||
encoding: string, optional
|
||||
Specify which encoding to use when reading file.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : graph
|
||||
A networkx Graph or other type specified with create_using
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> nx.write_edgelist(nx.path_graph(4), "test.edgelist")
|
||||
>>> G = nx.read_edgelist("test.edgelist")
|
||||
|
||||
>>> fh = open("test.edgelist", "rb")
|
||||
>>> G = nx.read_edgelist(fh)
|
||||
>>> fh.close()
|
||||
|
||||
>>> G = nx.read_edgelist("test.edgelist", nodetype=int)
|
||||
>>> G = nx.read_edgelist("test.edgelist", create_using=nx.DiGraph)
|
||||
|
||||
Edgelist with data in a list:
|
||||
|
||||
>>> textline = "1 2 3"
|
||||
>>> fh = open("test.edgelist", "w")
|
||||
>>> d = fh.write(textline)
|
||||
>>> fh.close()
|
||||
>>> G = nx.read_edgelist("test.edgelist", nodetype=int, data=(("weight", float),))
|
||||
>>> list(G)
|
||||
[1, 2]
|
||||
>>> list(G.edges(data=True))
|
||||
[(1, 2, {'weight': 3.0})]
|
||||
|
||||
See parse_edgelist() for more examples of formatting.
|
||||
|
||||
See Also
|
||||
--------
|
||||
parse_edgelist
|
||||
write_edgelist
|
||||
|
||||
Notes
|
||||
-----
|
||||
Since nodes must be hashable, the function nodetype must return hashable
|
||||
types (e.g. int, float, str, frozenset - or tuples of those, etc.)
|
||||
"""
|
||||
lines = (line if isinstance(line, str) else line.decode(encoding) for line in path)
|
||||
return parse_edgelist(
|
||||
lines,
|
||||
comments=comments,
|
||||
delimiter=delimiter,
|
||||
create_using=create_using,
|
||||
nodetype=nodetype,
|
||||
data=data,
|
||||
)
|
||||
|
||||
|
||||
def write_weighted_edgelist(G, path, comments="#", delimiter=" ", encoding="utf-8"):
|
||||
"""Write graph G as a list of edges with numeric weights.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
path : file or string
|
||||
File or filename to write. If a file is provided, it must be
|
||||
opened in 'wb' mode.
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
comments : string, optional
|
||||
The character used to indicate the start of a comment
|
||||
delimiter : string, optional
|
||||
The string used to separate values. The default is whitespace.
|
||||
encoding: string, optional
|
||||
Specify which encoding to use when writing file.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph()
|
||||
>>> G.add_edge(1, 2, weight=7)
|
||||
>>> nx.write_weighted_edgelist(G, "test.weighted.edgelist")
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_edgelist
|
||||
write_edgelist
|
||||
read_weighted_edgelist
|
||||
"""
|
||||
write_edgelist(
|
||||
G,
|
||||
path,
|
||||
comments=comments,
|
||||
delimiter=delimiter,
|
||||
data=("weight",),
|
||||
encoding=encoding,
|
||||
)
|
||||
|
||||
|
||||
def read_weighted_edgelist(
|
||||
path,
|
||||
comments="#",
|
||||
delimiter=None,
|
||||
create_using=None,
|
||||
nodetype=None,
|
||||
encoding="utf-8",
|
||||
):
|
||||
"""Read a graph as list of edges with numeric weights.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File or filename to read. If a file is provided, it must be
|
||||
opened in 'rb' mode.
|
||||
Filenames ending in .gz or .bz2 will be uncompressed.
|
||||
comments : string, optional
|
||||
The character used to indicate the start of a comment.
|
||||
delimiter : string, optional
|
||||
The string used to separate values. The default is whitespace.
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
nodetype : int, float, str, Python type, optional
|
||||
Convert node data from strings to specified type
|
||||
encoding: string, optional
|
||||
Specify which encoding to use when reading file.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : graph
|
||||
A networkx Graph or other type specified with create_using
|
||||
|
||||
Notes
|
||||
-----
|
||||
Since nodes must be hashable, the function nodetype must return hashable
|
||||
types (e.g. int, float, str, frozenset - or tuples of those, etc.)
|
||||
|
||||
Example edgelist file format.
|
||||
|
||||
With numeric edge data::
|
||||
|
||||
# read with
|
||||
# >>> G=nx.read_weighted_edgelist(fh)
|
||||
# source target data
|
||||
a b 1
|
||||
a c 3.14159
|
||||
d e 42
|
||||
|
||||
See Also
|
||||
--------
|
||||
write_weighted_edgelist
|
||||
"""
|
||||
return read_edgelist(
|
||||
path,
|
||||
comments=comments,
|
||||
delimiter=delimiter,
|
||||
create_using=create_using,
|
||||
nodetype=nodetype,
|
||||
data=(("weight", float),),
|
||||
encoding=encoding,
|
||||
)
|
1051
venv/Lib/site-packages/networkx/readwrite/gexf.py
Normal file
1051
venv/Lib/site-packages/networkx/readwrite/gexf.py
Normal file
File diff suppressed because it is too large
Load diff
824
venv/Lib/site-packages/networkx/readwrite/gml.py
Normal file
824
venv/Lib/site-packages/networkx/readwrite/gml.py
Normal file
|
@ -0,0 +1,824 @@
|
|||
"""
|
||||
Read graphs in GML format.
|
||||
|
||||
"GML, the Graph Modelling Language, is our proposal for a portable
|
||||
file format for graphs. GML's key features are portability, simple
|
||||
syntax, extensibility and flexibility. A GML file consists of a
|
||||
hierarchical key-value lists. Graphs can be annotated with arbitrary
|
||||
data structures. The idea for a common file format was born at the
|
||||
GD'95; this proposal is the outcome of many discussions. GML is the
|
||||
standard file format in the Graphlet graph editor system. It has been
|
||||
overtaken and adapted by several other systems for drawing graphs."
|
||||
|
||||
GML files are stored using a 7-bit ASCII encoding with any extended
|
||||
ASCII characters (iso8859-1) appearing as HTML character entities.
|
||||
You will need to give some thought into how the exported data should
|
||||
interact with different languages and even different Python versions.
|
||||
Re-importing from gml is also a concern.
|
||||
|
||||
Without specifying a `stringizer`/`destringizer`, the code is capable of
|
||||
handling `int`/`float`/`str`/`dict`/`list` data as required by the GML
|
||||
specification. For other data types, you need to explicitly supply a
|
||||
`stringizer`/`destringizer`.
|
||||
|
||||
For additional documentation on the GML file format, please see the
|
||||
`GML website <http://www.infosun.fim.uni-passau.de/Graphlet/GML/gml-tr.html>`_.
|
||||
|
||||
Several example graphs in GML format may be found on Mark Newman's
|
||||
`Network data page <http://www-personal.umich.edu/~mejn/netdata/>`_.
|
||||
"""
|
||||
from io import StringIO
|
||||
from ast import literal_eval
|
||||
from collections import defaultdict
|
||||
from enum import Enum
|
||||
from typing import Any, NamedTuple
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError
|
||||
from networkx.utils import open_file
|
||||
|
||||
import warnings
|
||||
import re
|
||||
import html.entities as htmlentitydefs
|
||||
|
||||
__all__ = ["read_gml", "parse_gml", "generate_gml", "write_gml"]
|
||||
|
||||
|
||||
def escape(text):
|
||||
"""Use XML character references to escape characters.
|
||||
|
||||
Use XML character references for unprintable or non-ASCII
|
||||
characters, double quotes and ampersands in a string
|
||||
"""
|
||||
|
||||
def fixup(m):
|
||||
ch = m.group(0)
|
||||
return "&#" + str(ord(ch)) + ";"
|
||||
|
||||
text = re.sub('[^ -~]|[&"]', fixup, text)
|
||||
return text if isinstance(text, str) else str(text)
|
||||
|
||||
|
||||
def unescape(text):
|
||||
"""Replace XML character references with the referenced characters"""
|
||||
|
||||
def fixup(m):
|
||||
text = m.group(0)
|
||||
if text[1] == "#":
|
||||
# Character reference
|
||||
if text[2] == "x":
|
||||
code = int(text[3:-1], 16)
|
||||
else:
|
||||
code = int(text[2:-1])
|
||||
else:
|
||||
# Named entity
|
||||
try:
|
||||
code = htmlentitydefs.name2codepoint[text[1:-1]]
|
||||
except KeyError:
|
||||
return text # leave unchanged
|
||||
try:
|
||||
return chr(code)
|
||||
except (ValueError, OverflowError):
|
||||
return text # leave unchanged
|
||||
|
||||
return re.sub("&(?:[0-9A-Za-z]+|#(?:[0-9]+|x[0-9A-Fa-f]+));", fixup, text)
|
||||
|
||||
|
||||
def literal_destringizer(rep):
|
||||
"""Convert a Python literal to the value it represents.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
rep : string
|
||||
A Python literal.
|
||||
|
||||
Returns
|
||||
-------
|
||||
value : object
|
||||
The value of the Python literal.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `rep` is not a Python literal.
|
||||
"""
|
||||
msg = "literal_destringizer is deprecated and will be removed in 3.0."
|
||||
warnings.warn(msg, DeprecationWarning)
|
||||
if isinstance(rep, str):
|
||||
orig_rep = rep
|
||||
try:
|
||||
return literal_eval(rep)
|
||||
except SyntaxError as e:
|
||||
raise ValueError(f"{orig_rep!r} is not a valid Python literal") from e
|
||||
else:
|
||||
raise ValueError(f"{rep!r} is not a string")
|
||||
|
||||
|
||||
@open_file(0, mode="rb")
|
||||
def read_gml(path, label="label", destringizer=None):
|
||||
"""Read graph in GML format from `path`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : filename or filehandle
|
||||
The filename or filehandle to read from.
|
||||
|
||||
label : string, optional
|
||||
If not None, the parsed nodes will be renamed according to node
|
||||
attributes indicated by `label`. Default value: 'label'.
|
||||
|
||||
destringizer : callable, optional
|
||||
A `destringizer` that recovers values stored as strings in GML. If it
|
||||
cannot convert a string to a value, a `ValueError` is raised. Default
|
||||
value : None.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX graph
|
||||
The parsed graph.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the input cannot be parsed.
|
||||
|
||||
See Also
|
||||
--------
|
||||
write_gml, parse_gml
|
||||
|
||||
Notes
|
||||
-----
|
||||
GML files are stored using a 7-bit ASCII encoding with any extended
|
||||
ASCII characters (iso8859-1) appearing as HTML character entities.
|
||||
Without specifying a `stringizer`/`destringizer`, the code is capable of
|
||||
handling `int`/`float`/`str`/`dict`/`list` data as required by the GML
|
||||
specification. For other data types, you need to explicitly supply a
|
||||
`stringizer`/`destringizer`.
|
||||
|
||||
For additional documentation on the GML file format, please see the
|
||||
`GML url <http://www.infosun.fim.uni-passau.de/Graphlet/GML/gml-tr.html>`_.
|
||||
|
||||
See the module docstring :mod:`networkx.readwrite.gml` for more details.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_gml(G, "test.gml")
|
||||
>>> H = nx.read_gml("test.gml")
|
||||
"""
|
||||
|
||||
def filter_lines(lines):
|
||||
for line in lines:
|
||||
try:
|
||||
line = line.decode("ascii")
|
||||
except UnicodeDecodeError as e:
|
||||
raise NetworkXError("input is not ASCII-encoded") from e
|
||||
if not isinstance(line, str):
|
||||
lines = str(lines)
|
||||
if line and line[-1] == "\n":
|
||||
line = line[:-1]
|
||||
yield line
|
||||
|
||||
G = parse_gml_lines(filter_lines(path), label, destringizer)
|
||||
return G
|
||||
|
||||
|
||||
def parse_gml(lines, label="label", destringizer=None):
|
||||
"""Parse GML graph from a string or iterable.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lines : string or iterable of strings
|
||||
Data in GML format.
|
||||
|
||||
label : string, optional
|
||||
If not None, the parsed nodes will be renamed according to node
|
||||
attributes indicated by `label`. Default value: 'label'.
|
||||
|
||||
destringizer : callable, optional
|
||||
A `destringizer` that recovers values stored as strings in GML. If it
|
||||
cannot convert a string to a value, a `ValueError` is raised. Default
|
||||
value : None.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX graph
|
||||
The parsed graph.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the input cannot be parsed.
|
||||
|
||||
See Also
|
||||
--------
|
||||
write_gml, read_gml
|
||||
|
||||
Notes
|
||||
-----
|
||||
This stores nested GML attributes as dictionaries in the NetworkX graph,
|
||||
node, and edge attribute structures.
|
||||
|
||||
GML files are stored using a 7-bit ASCII encoding with any extended
|
||||
ASCII characters (iso8859-1) appearing as HTML character entities.
|
||||
Without specifying a `stringizer`/`destringizer`, the code is capable of
|
||||
handling `int`/`float`/`str`/`dict`/`list` data as required by the GML
|
||||
specification. For other data types, you need to explicitly supply a
|
||||
`stringizer`/`destringizer`.
|
||||
|
||||
For additional documentation on the GML file format, please see the
|
||||
`GML url <http://www.infosun.fim.uni-passau.de/Graphlet/GML/gml-tr.html>`_.
|
||||
|
||||
See the module docstring :mod:`networkx.readwrite.gml` for more details.
|
||||
"""
|
||||
|
||||
def decode_line(line):
|
||||
if isinstance(line, bytes):
|
||||
try:
|
||||
line.decode("ascii")
|
||||
except UnicodeDecodeError as e:
|
||||
raise NetworkXError("input is not ASCII-encoded") from e
|
||||
if not isinstance(line, str):
|
||||
line = str(line)
|
||||
return line
|
||||
|
||||
def filter_lines(lines):
|
||||
if isinstance(lines, str):
|
||||
lines = decode_line(lines)
|
||||
lines = lines.splitlines()
|
||||
yield from lines
|
||||
else:
|
||||
for line in lines:
|
||||
line = decode_line(line)
|
||||
if line and line[-1] == "\n":
|
||||
line = line[:-1]
|
||||
if line.find("\n") != -1:
|
||||
raise NetworkXError("input line contains newline")
|
||||
yield line
|
||||
|
||||
G = parse_gml_lines(filter_lines(lines), label, destringizer)
|
||||
return G
|
||||
|
||||
|
||||
class Pattern(Enum):
|
||||
""" encodes the index of each token-matching pattern in `tokenize`. """
|
||||
|
||||
KEYS = 0
|
||||
REALS = 1
|
||||
INTS = 2
|
||||
STRINGS = 3
|
||||
DICT_START = 4
|
||||
DICT_END = 5
|
||||
COMMENT_WHITESPACE = 6
|
||||
|
||||
|
||||
class Token(NamedTuple):
|
||||
category: Pattern
|
||||
value: Any
|
||||
line: int
|
||||
position: int
|
||||
|
||||
|
||||
LIST_START_VALUE = "_networkx_list_start"
|
||||
|
||||
|
||||
def parse_gml_lines(lines, label, destringizer):
|
||||
"""Parse GML `lines` into a graph.
|
||||
"""
|
||||
|
||||
def tokenize():
|
||||
patterns = [
|
||||
r"[A-Za-z][0-9A-Za-z_]*\b", # keys
|
||||
# reals
|
||||
r"[+-]?(?:[0-9]*\.[0-9]+|[0-9]+\.[0-9]*)(?:[Ee][+-]?[0-9]+)?",
|
||||
r"[+-]?[0-9]+", # ints
|
||||
r'".*?"', # strings
|
||||
r"\[", # dict start
|
||||
r"\]", # dict end
|
||||
r"#.*$|\s+", # comments and whitespaces
|
||||
]
|
||||
tokens = re.compile("|".join(f"({pattern})" for pattern in patterns))
|
||||
lineno = 0
|
||||
for line in lines:
|
||||
length = len(line)
|
||||
pos = 0
|
||||
while pos < length:
|
||||
match = tokens.match(line, pos)
|
||||
if match is None:
|
||||
m = f"cannot tokenize {line[pos:]} at ({lineno + 1}, {pos + 1})"
|
||||
raise NetworkXError(m)
|
||||
for i in range(len(patterns)):
|
||||
group = match.group(i + 1)
|
||||
if group is not None:
|
||||
if i == 0: # keys
|
||||
value = group.rstrip()
|
||||
elif i == 1: # reals
|
||||
value = float(group)
|
||||
elif i == 2: # ints
|
||||
value = int(group)
|
||||
else:
|
||||
value = group
|
||||
if i != 6: # comments and whitespaces
|
||||
yield Token(Pattern(i), value, lineno + 1, pos + 1)
|
||||
pos += len(group)
|
||||
break
|
||||
lineno += 1
|
||||
yield Token(None, None, lineno + 1, 1) # EOF
|
||||
|
||||
def unexpected(curr_token, expected):
|
||||
category, value, lineno, pos = curr_token
|
||||
value = repr(value) if value is not None else "EOF"
|
||||
raise NetworkXError(f"expected {expected}, found {value} at ({lineno}, {pos})")
|
||||
|
||||
def consume(curr_token, category, expected):
|
||||
if curr_token.category == category:
|
||||
return next(tokens)
|
||||
unexpected(curr_token, expected)
|
||||
|
||||
def parse_kv(curr_token):
|
||||
dct = defaultdict(list)
|
||||
while curr_token.category == Pattern.KEYS:
|
||||
key = curr_token.value
|
||||
curr_token = next(tokens)
|
||||
category = curr_token.category
|
||||
if category == Pattern.REALS or category == Pattern.INTS:
|
||||
value = curr_token.value
|
||||
curr_token = next(tokens)
|
||||
elif category == Pattern.STRINGS:
|
||||
value = unescape(curr_token.value[1:-1])
|
||||
if destringizer:
|
||||
try:
|
||||
value = destringizer(value)
|
||||
except ValueError:
|
||||
pass
|
||||
curr_token = next(tokens)
|
||||
elif category == Pattern.DICT_START:
|
||||
curr_token, value = parse_dict(curr_token)
|
||||
else:
|
||||
# Allow for string convertible id and label values
|
||||
if key in ("id", "label", "source", "target"):
|
||||
try:
|
||||
# String convert the token value
|
||||
value = unescape(str(curr_token.value))
|
||||
if destringizer:
|
||||
try:
|
||||
value = destringizer(value)
|
||||
except ValueError:
|
||||
pass
|
||||
curr_token = next(tokens)
|
||||
except Exception:
|
||||
msg = (
|
||||
"an int, float, string, '[' or string"
|
||||
+ " convertable ASCII value for node id or label"
|
||||
)
|
||||
unexpected(curr_token, msg)
|
||||
else: # Otherwise error out
|
||||
unexpected(curr_token, "an int, float, string or '['")
|
||||
dct[key].append(value)
|
||||
|
||||
def clean_dict_value(value):
|
||||
if not isinstance(value, list):
|
||||
return value
|
||||
if len(value) == 1:
|
||||
return value[0]
|
||||
if value[0] == LIST_START_VALUE:
|
||||
return value[1:]
|
||||
return value
|
||||
|
||||
dct = {key: clean_dict_value(value) for key, value in dct.items()}
|
||||
return curr_token, dct
|
||||
|
||||
def parse_dict(curr_token):
|
||||
# dict start
|
||||
curr_token = consume(curr_token, Pattern.DICT_START, "'['")
|
||||
# dict contents
|
||||
curr_token, dct = parse_kv(curr_token)
|
||||
# dict end
|
||||
curr_token = consume(curr_token, Pattern.DICT_END, "']'")
|
||||
return curr_token, dct
|
||||
|
||||
def parse_graph():
|
||||
curr_token, dct = parse_kv(next(tokens))
|
||||
if curr_token.category is not None: # EOF
|
||||
unexpected(curr_token, "EOF")
|
||||
if "graph" not in dct:
|
||||
raise NetworkXError("input contains no graph")
|
||||
graph = dct["graph"]
|
||||
if isinstance(graph, list):
|
||||
raise NetworkXError("input contains more than one graph")
|
||||
return graph
|
||||
|
||||
tokens = tokenize()
|
||||
graph = parse_graph()
|
||||
|
||||
directed = graph.pop("directed", False)
|
||||
multigraph = graph.pop("multigraph", False)
|
||||
if not multigraph:
|
||||
G = nx.DiGraph() if directed else nx.Graph()
|
||||
else:
|
||||
G = nx.MultiDiGraph() if directed else nx.MultiGraph()
|
||||
graph_attr = {k: v for k, v in graph.items() if k not in ("node", "edge")}
|
||||
G.graph.update(graph_attr)
|
||||
|
||||
def pop_attr(dct, category, attr, i):
|
||||
try:
|
||||
return dct.pop(attr)
|
||||
except KeyError as e:
|
||||
raise NetworkXError(f"{category} #{i} has no '{attr}' attribute") from e
|
||||
|
||||
nodes = graph.get("node", [])
|
||||
mapping = {}
|
||||
node_labels = set()
|
||||
for i, node in enumerate(nodes if isinstance(nodes, list) else [nodes]):
|
||||
id = pop_attr(node, "node", "id", i)
|
||||
if id in G:
|
||||
raise NetworkXError(f"node id {id!r} is duplicated")
|
||||
if label is not None and label != "id":
|
||||
node_label = pop_attr(node, "node", label, i)
|
||||
if node_label in node_labels:
|
||||
raise NetworkXError(f"node label {node_label!r} is duplicated")
|
||||
node_labels.add(node_label)
|
||||
mapping[id] = node_label
|
||||
G.add_node(id, **node)
|
||||
|
||||
edges = graph.get("edge", [])
|
||||
for i, edge in enumerate(edges if isinstance(edges, list) else [edges]):
|
||||
source = pop_attr(edge, "edge", "source", i)
|
||||
target = pop_attr(edge, "edge", "target", i)
|
||||
if source not in G:
|
||||
raise NetworkXError(f"edge #{i} has undefined source {source!r}")
|
||||
if target not in G:
|
||||
raise NetworkXError(f"edge #{i} has undefined target {target!r}")
|
||||
if not multigraph:
|
||||
if not G.has_edge(source, target):
|
||||
G.add_edge(source, target, **edge)
|
||||
else:
|
||||
arrow = "->" if directed else "--"
|
||||
msg = f"edge #{i} ({source!r}{arrow}{target!r}) is duplicated"
|
||||
raise nx.NetworkXError(msg)
|
||||
else:
|
||||
key = edge.pop("key", None)
|
||||
if key is not None and G.has_edge(source, target, key):
|
||||
arrow = "->" if directed else "--"
|
||||
msg = f"edge #{i} ({source!r}{arrow}{target!r}, {key!r})"
|
||||
msg2 = 'Hint: If multigraph add "multigraph 1" to file header.'
|
||||
raise nx.NetworkXError(msg + " is duplicated\n" + msg2)
|
||||
G.add_edge(source, target, key, **edge)
|
||||
|
||||
if label is not None and label != "id":
|
||||
G = nx.relabel_nodes(G, mapping)
|
||||
return G
|
||||
|
||||
|
||||
def literal_stringizer(value):
|
||||
"""Convert a `value` to a Python literal in GML representation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : object
|
||||
The `value` to be converted to GML representation.
|
||||
|
||||
Returns
|
||||
-------
|
||||
rep : string
|
||||
A double-quoted Python literal representing value. Unprintable
|
||||
characters are replaced by XML character references.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `value` cannot be converted to GML.
|
||||
|
||||
Notes
|
||||
-----
|
||||
`literal_stringizer` is largely the same as `repr` in terms of
|
||||
functionality but attempts prefix `unicode` and `bytes` literals with
|
||||
`u` and `b` to provide better interoperability of data generated by
|
||||
Python 2 and Python 3.
|
||||
|
||||
The original value can be recovered using the
|
||||
:func:`networkx.readwrite.gml.literal_destringizer` function.
|
||||
"""
|
||||
msg = "literal_stringizer is deprecated and will be removed in 3.0."
|
||||
warnings.warn(msg, DeprecationWarning)
|
||||
|
||||
def stringize(value):
|
||||
if isinstance(value, (int, bool)) or value is None:
|
||||
if value is True: # GML uses 1/0 for boolean values.
|
||||
buf.write(str(1))
|
||||
elif value is False:
|
||||
buf.write(str(0))
|
||||
else:
|
||||
buf.write(str(value))
|
||||
elif isinstance(value, str):
|
||||
text = repr(value)
|
||||
if text[0] != "u":
|
||||
try:
|
||||
value.encode("latin1")
|
||||
except UnicodeEncodeError:
|
||||
text = "u" + text
|
||||
buf.write(text)
|
||||
elif isinstance(value, (float, complex, str, bytes)):
|
||||
buf.write(repr(value))
|
||||
elif isinstance(value, list):
|
||||
buf.write("[")
|
||||
first = True
|
||||
for item in value:
|
||||
if not first:
|
||||
buf.write(",")
|
||||
else:
|
||||
first = False
|
||||
stringize(item)
|
||||
buf.write("]")
|
||||
elif isinstance(value, tuple):
|
||||
if len(value) > 1:
|
||||
buf.write("(")
|
||||
first = True
|
||||
for item in value:
|
||||
if not first:
|
||||
buf.write(",")
|
||||
else:
|
||||
first = False
|
||||
stringize(item)
|
||||
buf.write(")")
|
||||
elif value:
|
||||
buf.write("(")
|
||||
stringize(value[0])
|
||||
buf.write(",)")
|
||||
else:
|
||||
buf.write("()")
|
||||
elif isinstance(value, dict):
|
||||
buf.write("{")
|
||||
first = True
|
||||
for key, value in value.items():
|
||||
if not first:
|
||||
buf.write(",")
|
||||
else:
|
||||
first = False
|
||||
stringize(key)
|
||||
buf.write(":")
|
||||
stringize(value)
|
||||
buf.write("}")
|
||||
elif isinstance(value, set):
|
||||
buf.write("{")
|
||||
first = True
|
||||
for item in value:
|
||||
if not first:
|
||||
buf.write(",")
|
||||
else:
|
||||
first = False
|
||||
stringize(item)
|
||||
buf.write("}")
|
||||
else:
|
||||
msg = "{value!r} cannot be converted into a Python literal"
|
||||
raise ValueError(msg)
|
||||
|
||||
buf = StringIO()
|
||||
stringize(value)
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def generate_gml(G, stringizer=None):
|
||||
r"""Generate a single entry of the graph `G` in GML format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
The graph to be converted to GML.
|
||||
|
||||
stringizer : callable, optional
|
||||
A `stringizer` which converts non-int/non-float/non-dict values into
|
||||
strings. If it cannot convert a value into a string, it should raise a
|
||||
`ValueError` to indicate that. Default value: None.
|
||||
|
||||
Returns
|
||||
-------
|
||||
lines: generator of strings
|
||||
Lines of GML data. Newlines are not appended.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If `stringizer` cannot convert a value into a string, or the value to
|
||||
convert is not a string while `stringizer` is None.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Graph attributes named 'directed', 'multigraph', 'node' or
|
||||
'edge', node attributes named 'id' or 'label', edge attributes
|
||||
named 'source' or 'target' (or 'key' if `G` is a multigraph)
|
||||
are ignored because these attribute names are used to encode the graph
|
||||
structure.
|
||||
|
||||
GML files are stored using a 7-bit ASCII encoding with any extended
|
||||
ASCII characters (iso8859-1) appearing as HTML character entities.
|
||||
Without specifying a `stringizer`/`destringizer`, the code is capable of
|
||||
handling `int`/`float`/`str`/`dict`/`list` data as required by the GML
|
||||
specification. For other data types, you need to explicitly supply a
|
||||
`stringizer`/`destringizer`.
|
||||
|
||||
For additional documentation on the GML file format, please see the
|
||||
`GML url <http://www.infosun.fim.uni-passau.de/Graphlet/GML/gml-tr.html>`_.
|
||||
|
||||
See the module docstring :mod:`networkx.readwrite.gml` for more details.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph()
|
||||
>>> G.add_node("1")
|
||||
>>> print("\n".join(nx.generate_gml(G)))
|
||||
graph [
|
||||
node [
|
||||
id 0
|
||||
label "1"
|
||||
]
|
||||
]
|
||||
>>> G = nx.OrderedMultiGraph([("a", "b"), ("a", "b")])
|
||||
>>> print("\n".join(nx.generate_gml(G)))
|
||||
graph [
|
||||
multigraph 1
|
||||
node [
|
||||
id 0
|
||||
label "a"
|
||||
]
|
||||
node [
|
||||
id 1
|
||||
label "b"
|
||||
]
|
||||
edge [
|
||||
source 0
|
||||
target 1
|
||||
key 0
|
||||
]
|
||||
edge [
|
||||
source 0
|
||||
target 1
|
||||
key 1
|
||||
]
|
||||
]
|
||||
"""
|
||||
valid_keys = re.compile("^[A-Za-z][0-9A-Za-z_]*$")
|
||||
|
||||
def stringize(key, value, ignored_keys, indent, in_list=False):
|
||||
if not isinstance(key, str):
|
||||
raise NetworkXError(f"{key!r} is not a string")
|
||||
if not valid_keys.match(key):
|
||||
raise NetworkXError(f"{key!r} is not a valid key")
|
||||
if not isinstance(key, str):
|
||||
key = str(key)
|
||||
if key not in ignored_keys:
|
||||
if isinstance(value, (int, bool)):
|
||||
if key == "label":
|
||||
yield indent + key + ' "' + str(value) + '"'
|
||||
elif value is True:
|
||||
# python bool is an instance of int
|
||||
yield indent + key + " 1"
|
||||
elif value is False:
|
||||
yield indent + key + " 0"
|
||||
# GML only supports signed 32-bit integers
|
||||
elif value < -(2 ** 31) or value >= 2 ** 31:
|
||||
yield indent + key + ' "' + str(value) + '"'
|
||||
else:
|
||||
yield indent + key + " " + str(value)
|
||||
elif isinstance(value, float):
|
||||
text = repr(value).upper()
|
||||
# GML requires that a real literal contain a decimal point, but
|
||||
# repr may not output a decimal point when the mantissa is
|
||||
# integral and hence needs fixing.
|
||||
epos = text.rfind("E")
|
||||
if epos != -1 and text.find(".", 0, epos) == -1:
|
||||
text = text[:epos] + "." + text[epos:]
|
||||
if key == "label":
|
||||
yield indent + key + ' "' + text + '"'
|
||||
else:
|
||||
yield indent + key + " " + text
|
||||
elif isinstance(value, dict):
|
||||
yield indent + key + " ["
|
||||
next_indent = indent + " "
|
||||
for key, value in value.items():
|
||||
yield from stringize(key, value, (), next_indent)
|
||||
yield indent + "]"
|
||||
elif (
|
||||
isinstance(value, (list, tuple))
|
||||
and key != "label"
|
||||
and value
|
||||
and not in_list
|
||||
):
|
||||
if len(value) == 1:
|
||||
yield indent + key + " " + f'"{LIST_START_VALUE}"'
|
||||
for val in value:
|
||||
yield from stringize(key, val, (), indent, True)
|
||||
else:
|
||||
if stringizer:
|
||||
try:
|
||||
value = stringizer(value)
|
||||
except ValueError as e:
|
||||
raise NetworkXError(
|
||||
f"{value!r} cannot be converted into a string"
|
||||
) from e
|
||||
if not isinstance(value, str):
|
||||
raise NetworkXError(f"{value!r} is not a string")
|
||||
yield indent + key + ' "' + escape(value) + '"'
|
||||
|
||||
multigraph = G.is_multigraph()
|
||||
yield "graph ["
|
||||
|
||||
# Output graph attributes
|
||||
if G.is_directed():
|
||||
yield " directed 1"
|
||||
if multigraph:
|
||||
yield " multigraph 1"
|
||||
ignored_keys = {"directed", "multigraph", "node", "edge"}
|
||||
for attr, value in G.graph.items():
|
||||
yield from stringize(attr, value, ignored_keys, " ")
|
||||
|
||||
# Output node data
|
||||
node_id = dict(zip(G, range(len(G))))
|
||||
ignored_keys = {"id", "label"}
|
||||
for node, attrs in G.nodes.items():
|
||||
yield " node ["
|
||||
yield " id " + str(node_id[node])
|
||||
yield from stringize("label", node, (), " ")
|
||||
for attr, value in attrs.items():
|
||||
yield from stringize(attr, value, ignored_keys, " ")
|
||||
yield " ]"
|
||||
|
||||
# Output edge data
|
||||
ignored_keys = {"source", "target"}
|
||||
kwargs = {"data": True}
|
||||
if multigraph:
|
||||
ignored_keys.add("key")
|
||||
kwargs["keys"] = True
|
||||
for e in G.edges(**kwargs):
|
||||
yield " edge ["
|
||||
yield " source " + str(node_id[e[0]])
|
||||
yield " target " + str(node_id[e[1]])
|
||||
if multigraph:
|
||||
yield from stringize("key", e[2], (), " ")
|
||||
for attr, value in e[-1].items():
|
||||
yield from stringize(attr, value, ignored_keys, " ")
|
||||
yield " ]"
|
||||
yield "]"
|
||||
|
||||
|
||||
@open_file(1, mode="wb")
|
||||
def write_gml(G, path, stringizer=None):
|
||||
"""Write a graph `G` in GML format to the file or file handle `path`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
The graph to be converted to GML.
|
||||
|
||||
path : filename or filehandle
|
||||
The filename or filehandle to write. Files whose names end with .gz or
|
||||
.bz2 will be compressed.
|
||||
|
||||
stringizer : callable, optional
|
||||
A `stringizer` which converts non-int/non-float/non-dict values into
|
||||
strings. If it cannot convert a value into a string, it should raise a
|
||||
`ValueError` to indicate that. Default value: None.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If `stringizer` cannot convert a value into a string, or the value to
|
||||
convert is not a string while `stringizer` is None.
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_gml, generate_gml
|
||||
|
||||
Notes
|
||||
-----
|
||||
Graph attributes named 'directed', 'multigraph', 'node' or
|
||||
'edge', node attributes named 'id' or 'label', edge attributes
|
||||
named 'source' or 'target' (or 'key' if `G` is a multigraph)
|
||||
are ignored because these attribute names are used to encode the graph
|
||||
structure.
|
||||
|
||||
GML files are stored using a 7-bit ASCII encoding with any extended
|
||||
ASCII characters (iso8859-1) appearing as HTML character entities.
|
||||
Without specifying a `stringizer`/`destringizer`, the code is capable of
|
||||
handling `int`/`float`/`str`/`dict`/`list` data as required by the GML
|
||||
specification. For other data types, you need to explicitly supply a
|
||||
`stringizer`/`destringizer`.
|
||||
|
||||
Note that while we allow non-standard GML to be read from a file, we make
|
||||
sure to write GML format. In particular, underscores are not allowed in
|
||||
attribute names.
|
||||
For additional documentation on the GML file format, please see the
|
||||
`GML url <http://www.infosun.fim.uni-passau.de/Graphlet/GML/gml-tr.html>`_.
|
||||
|
||||
See the module docstring :mod:`networkx.readwrite.gml` for more details.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_gml(G, "test.gml")
|
||||
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
|
||||
>>> nx.write_gml(G, "test.gml.gz")
|
||||
"""
|
||||
for line in generate_gml(G, stringizer):
|
||||
path.write((line + "\n").encode("ascii"))
|
90
venv/Lib/site-packages/networkx/readwrite/gpickle.py
Normal file
90
venv/Lib/site-packages/networkx/readwrite/gpickle.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
"""
|
||||
**************
|
||||
Pickled Graphs
|
||||
**************
|
||||
Read and write NetworkX graphs as Python pickles.
|
||||
|
||||
"The pickle module implements a fundamental, but powerful algorithm
|
||||
for serializing and de-serializing a Python object
|
||||
structure. "Pickling" is the process whereby a Python object hierarchy
|
||||
is converted into a byte stream, and "unpickling" is the inverse
|
||||
operation, whereby a byte stream is converted back into an object
|
||||
hierarchy."
|
||||
|
||||
Note that NetworkX graphs can contain any hashable Python object as
|
||||
node (not just integers and strings). For arbitrary data types it may
|
||||
be difficult to represent the data as text. In that case using Python
|
||||
pickles to store the graph data can be used.
|
||||
|
||||
Format
|
||||
------
|
||||
See https://docs.python.org/3/library/pickle.html
|
||||
"""
|
||||
|
||||
__all__ = ["read_gpickle", "write_gpickle"]
|
||||
|
||||
from networkx.utils import open_file
|
||||
|
||||
import pickle
|
||||
|
||||
|
||||
@open_file(1, mode="wb")
|
||||
def write_gpickle(G, path, protocol=pickle.HIGHEST_PROTOCOL):
|
||||
"""Write graph in Python pickle format.
|
||||
|
||||
Pickles are a serialized byte stream of a Python object [1]_.
|
||||
This format will preserve Python objects used as nodes or edges.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
path : file or string
|
||||
File or filename to write.
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
|
||||
protocol : integer
|
||||
Pickling protocol to use. Default value: ``pickle.HIGHEST_PROTOCOL``.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_gpickle(G, "test.gpickle")
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://docs.python.org/3/library/pickle.html
|
||||
"""
|
||||
pickle.dump(G, path, protocol)
|
||||
|
||||
|
||||
@open_file(0, mode="rb")
|
||||
def read_gpickle(path):
|
||||
"""Read graph object in Python pickle format.
|
||||
|
||||
Pickles are a serialized byte stream of a Python object [1]_.
|
||||
This format will preserve Python objects used as nodes or edges.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File or filename to write.
|
||||
Filenames ending in .gz or .bz2 will be uncompressed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_gpickle(G, "test.gpickle")
|
||||
>>> G = nx.read_gpickle("test.gpickle")
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://docs.python.org/3/library/pickle.html
|
||||
"""
|
||||
return pickle.load(path)
|
412
venv/Lib/site-packages/networkx/readwrite/graph6.py
Normal file
412
venv/Lib/site-packages/networkx/readwrite/graph6.py
Normal file
|
@ -0,0 +1,412 @@
|
|||
# Original author: D. Eppstein, UC Irvine, August 12, 2003.
|
||||
# The original code at http://www.ics.uci.edu/~eppstein/PADS/ is public domain.
|
||||
"""Functions for reading and writing graphs in the *graph6* format.
|
||||
|
||||
The *graph6* file format is suitable for small graphs or large dense
|
||||
graphs. For large sparse graphs, use the *sparse6* format.
|
||||
|
||||
For more information, see the `graph6`_ homepage.
|
||||
|
||||
.. _graph6: http://users.cecs.anu.edu.au/~bdm/data/formats.html
|
||||
|
||||
"""
|
||||
from itertools import islice
|
||||
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError
|
||||
from networkx.utils import open_file, not_implemented_for
|
||||
|
||||
__all__ = ["from_graph6_bytes", "read_graph6", "to_graph6_bytes", "write_graph6"]
|
||||
|
||||
|
||||
def _generate_graph6_bytes(G, nodes, header):
|
||||
"""Yield bytes in the graph6 encoding of a graph.
|
||||
|
||||
`G` is an undirected simple graph. `nodes` is the list of nodes for
|
||||
which the node-induced subgraph will be encoded; if `nodes` is the
|
||||
list of all nodes in the graph, the entire graph will be
|
||||
encoded. `header` is a Boolean that specifies whether to generate
|
||||
the header ``b'>>graph6<<'`` before the remaining data.
|
||||
|
||||
This function generates `bytes` objects in the following order:
|
||||
|
||||
1. the header (if requested),
|
||||
2. the encoding of the number of nodes,
|
||||
3. each character, one-at-a-time, in the encoding of the requested
|
||||
node-induced subgraph,
|
||||
4. a newline character.
|
||||
|
||||
This function raises :exc:`ValueError` if the graph is too large for
|
||||
the graph6 format (that is, greater than ``2 ** 36`` nodes).
|
||||
|
||||
"""
|
||||
n = len(G)
|
||||
if n >= 2 ** 36:
|
||||
raise ValueError(
|
||||
"graph6 is only defined if number of nodes is less " "than 2 ** 36"
|
||||
)
|
||||
if header:
|
||||
yield b">>graph6<<"
|
||||
for d in n_to_data(n):
|
||||
yield str.encode(chr(d + 63))
|
||||
# This generates the same as `(v in G[u] for u, v in combinations(G, 2))`,
|
||||
# but in "column-major" order instead of "row-major" order.
|
||||
bits = (nodes[j] in G[nodes[i]] for j in range(1, n) for i in range(j))
|
||||
chunk = list(islice(bits, 6))
|
||||
while chunk:
|
||||
d = sum(b << 5 - i for i, b in enumerate(chunk))
|
||||
yield str.encode(chr(d + 63))
|
||||
chunk = list(islice(bits, 6))
|
||||
yield b"\n"
|
||||
|
||||
|
||||
def from_graph6_bytes(bytes_in):
|
||||
"""Read a simple undirected graph in graph6 format from bytes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bytes_in : bytes
|
||||
Data in graph6 format, without a trailing newline.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : Graph
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If bytes_in is unable to be parsed in graph6 format
|
||||
|
||||
ValueError
|
||||
If any character ``c`` in bytes_in does not satisfy
|
||||
``63 <= ord(c) < 127``.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.from_graph6_bytes(b"A_")
|
||||
>>> sorted(G.edges())
|
||||
[(0, 1)]
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_graph6, write_graph6
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Graph6 specification
|
||||
<http://users.cecs.anu.edu.au/~bdm/data/formats.html>
|
||||
|
||||
"""
|
||||
|
||||
def bits():
|
||||
"""Returns sequence of individual bits from 6-bit-per-value
|
||||
list of data values."""
|
||||
for d in data:
|
||||
for i in [5, 4, 3, 2, 1, 0]:
|
||||
yield (d >> i) & 1
|
||||
|
||||
if bytes_in.startswith(b">>graph6<<"):
|
||||
bytes_in = bytes_in[10:]
|
||||
|
||||
data = [c - 63 for c in bytes_in]
|
||||
if any(c > 63 for c in data):
|
||||
raise ValueError("each input character must be in range(63, 127)")
|
||||
|
||||
n, data = data_to_n(data)
|
||||
nd = (n * (n - 1) // 2 + 5) // 6
|
||||
if len(data) != nd:
|
||||
raise NetworkXError(
|
||||
f"Expected {n * (n - 1) // 2} bits but got {len(data) * 6} in graph6"
|
||||
)
|
||||
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from(range(n))
|
||||
for (i, j), b in zip([(i, j) for j in range(1, n) for i in range(j)], bits()):
|
||||
if b:
|
||||
G.add_edge(i, j)
|
||||
|
||||
return G
|
||||
|
||||
|
||||
def to_graph6_bytes(G, nodes=None, header=True):
|
||||
"""Convert a simple undirected graph to bytes in graph6 format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : Graph (undirected)
|
||||
|
||||
nodes: list or iterable
|
||||
Nodes are labeled 0...n-1 in the order provided. If None the ordering
|
||||
given by ``G.nodes()`` is used.
|
||||
|
||||
header: bool
|
||||
If True add '>>graph6<<' bytes to head of data.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the graph is directed or is a multigraph.
|
||||
|
||||
ValueError
|
||||
If the graph has at least ``2 ** 36`` nodes; the graph6 format
|
||||
is only defined for graphs of order less than ``2 ** 36``.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> nx.to_graph6_bytes(nx.path_graph(2))
|
||||
b'>>graph6<<A_\\n'
|
||||
|
||||
See Also
|
||||
--------
|
||||
from_graph6_bytes, read_graph6, write_graph6_bytes
|
||||
|
||||
Notes
|
||||
-----
|
||||
The returned bytes end with a newline character.
|
||||
|
||||
The format does not support edge or node labels, parallel edges or
|
||||
self loops. If self loops are present they are silently ignored.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Graph6 specification
|
||||
<http://users.cecs.anu.edu.au/~bdm/data/formats.html>
|
||||
|
||||
"""
|
||||
if nodes is not None:
|
||||
G = G.subgraph(nodes)
|
||||
H = nx.convert_node_labels_to_integers(G)
|
||||
nodes = sorted(H.nodes())
|
||||
return b"".join(_generate_graph6_bytes(H, nodes, header))
|
||||
|
||||
|
||||
@open_file(0, mode="rb")
|
||||
def read_graph6(path):
|
||||
"""Read simple undirected graphs in graph6 format from path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File or filename to write.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : Graph or list of Graphs
|
||||
If the file contains multiple lines then a list of graphs is returned
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the string is unable to be parsed in graph6 format
|
||||
|
||||
Examples
|
||||
--------
|
||||
You can read a graph6 file by giving the path to the file::
|
||||
|
||||
>>> import tempfile
|
||||
>>> with tempfile.NamedTemporaryFile() as f:
|
||||
... _ = f.write(b">>graph6<<A_\\n")
|
||||
... _ = f.seek(0)
|
||||
... G = nx.read_graph6(f.name)
|
||||
>>> list(G.edges())
|
||||
[(0, 1)]
|
||||
|
||||
You can also read a graph6 file by giving an open file-like object::
|
||||
|
||||
>>> import tempfile
|
||||
>>> with tempfile.NamedTemporaryFile() as f:
|
||||
... _ = f.write(b">>graph6<<A_\\n")
|
||||
... _ = f.seek(0)
|
||||
... G = nx.read_graph6(f)
|
||||
>>> list(G.edges())
|
||||
[(0, 1)]
|
||||
|
||||
See Also
|
||||
--------
|
||||
from_graph6_bytes, write_graph6
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Graph6 specification
|
||||
<http://users.cecs.anu.edu.au/~bdm/data/formats.html>
|
||||
|
||||
"""
|
||||
glist = []
|
||||
for line in path:
|
||||
line = line.strip()
|
||||
if not len(line):
|
||||
continue
|
||||
glist.append(from_graph6_bytes(line))
|
||||
if len(glist) == 1:
|
||||
return glist[0]
|
||||
else:
|
||||
return glist
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@not_implemented_for("multigraph")
|
||||
@open_file(1, mode="wb")
|
||||
def write_graph6(G, path, nodes=None, header=True):
|
||||
"""Write a simple undirected graph to a path in graph6 format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : Graph (undirected)
|
||||
|
||||
path : str
|
||||
The path naming the file to which to write the graph.
|
||||
|
||||
nodes: list or iterable
|
||||
Nodes are labeled 0...n-1 in the order provided. If None the ordering
|
||||
given by ``G.nodes()`` is used.
|
||||
|
||||
header: bool
|
||||
If True add '>>graph6<<' string to head of data
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the graph is directed or is a multigraph.
|
||||
|
||||
ValueError
|
||||
If the graph has at least ``2 ** 36`` nodes; the graph6 format
|
||||
is only defined for graphs of order less than ``2 ** 36``.
|
||||
|
||||
Examples
|
||||
--------
|
||||
You can write a graph6 file by giving the path to a file::
|
||||
|
||||
>>> import tempfile
|
||||
>>> with tempfile.NamedTemporaryFile() as f:
|
||||
... nx.write_graph6(nx.path_graph(2), f.name)
|
||||
... _ = f.seek(0)
|
||||
... print(f.read())
|
||||
b'>>graph6<<A_\\n'
|
||||
|
||||
See Also
|
||||
--------
|
||||
from_graph6_bytes, read_graph6
|
||||
|
||||
Notes
|
||||
-----
|
||||
The function writes a newline character after writing the encoding
|
||||
of the graph.
|
||||
|
||||
The format does not support edge or node labels, parallel edges or
|
||||
self loops. If self loops are present they are silently ignored.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Graph6 specification
|
||||
<http://users.cecs.anu.edu.au/~bdm/data/formats.html>
|
||||
|
||||
"""
|
||||
return write_graph6_file(G, path, nodes=nodes, header=header)
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@not_implemented_for("multigraph")
|
||||
def write_graph6_file(G, f, nodes=None, header=True):
|
||||
"""Write a simple undirected graph to a file-like object in graph6 format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : Graph (undirected)
|
||||
|
||||
f : file-like object
|
||||
The file to write.
|
||||
|
||||
nodes: list or iterable
|
||||
Nodes are labeled 0...n-1 in the order provided. If None the ordering
|
||||
given by ``G.nodes()`` is used.
|
||||
|
||||
header: bool
|
||||
If True add '>>graph6<<' string to head of data
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the graph is directed or is a multigraph.
|
||||
|
||||
ValueError
|
||||
If the graph has at least ``2 ** 36`` nodes; the graph6 format
|
||||
is only defined for graphs of order less than ``2 ** 36``.
|
||||
|
||||
Examples
|
||||
--------
|
||||
You can write a graph6 file by giving an open file-like object::
|
||||
|
||||
>>> import tempfile
|
||||
>>> with tempfile.NamedTemporaryFile() as f:
|
||||
... nx.write_graph6(nx.path_graph(2), f)
|
||||
... _ = f.seek(0)
|
||||
... print(f.read())
|
||||
b'>>graph6<<A_\\n'
|
||||
|
||||
See Also
|
||||
--------
|
||||
from_graph6_bytes, read_graph6
|
||||
|
||||
Notes
|
||||
-----
|
||||
The function writes a newline character after writing the encoding
|
||||
of the graph.
|
||||
|
||||
The format does not support edge or node labels, parallel edges or
|
||||
self loops. If self loops are present they are silently ignored.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Graph6 specification
|
||||
<http://users.cecs.anu.edu.au/~bdm/data/formats.html>
|
||||
|
||||
"""
|
||||
if nodes is not None:
|
||||
G = G.subgraph(nodes)
|
||||
H = nx.convert_node_labels_to_integers(G)
|
||||
nodes = sorted(H.nodes())
|
||||
for b in _generate_graph6_bytes(H, nodes, header):
|
||||
f.write(b)
|
||||
|
||||
|
||||
def data_to_n(data):
|
||||
"""Read initial one-, four- or eight-unit value from graph6
|
||||
integer sequence.
|
||||
|
||||
Return (value, rest of seq.)"""
|
||||
if data[0] <= 62:
|
||||
return data[0], data[1:]
|
||||
if data[1] <= 62:
|
||||
return (data[1] << 12) + (data[2] << 6) + data[3], data[4:]
|
||||
return (
|
||||
(data[2] << 30)
|
||||
+ (data[3] << 24)
|
||||
+ (data[4] << 18)
|
||||
+ (data[5] << 12)
|
||||
+ (data[6] << 6)
|
||||
+ data[7],
|
||||
data[8:],
|
||||
)
|
||||
|
||||
|
||||
def n_to_data(n):
|
||||
"""Convert an integer to one-, four- or eight-unit graph6 sequence.
|
||||
|
||||
This function is undefined if `n` is not in ``range(2 ** 36)``.
|
||||
|
||||
"""
|
||||
if n <= 62:
|
||||
return [n]
|
||||
elif n <= 258047:
|
||||
return [63, (n >> 12) & 0x3F, (n >> 6) & 0x3F, n & 0x3F]
|
||||
else: # if n <= 68719476735:
|
||||
return [
|
||||
63,
|
||||
63,
|
||||
(n >> 30) & 0x3F,
|
||||
(n >> 24) & 0x3F,
|
||||
(n >> 18) & 0x3F,
|
||||
(n >> 12) & 0x3F,
|
||||
(n >> 6) & 0x3F,
|
||||
n & 0x3F,
|
||||
]
|
958
venv/Lib/site-packages/networkx/readwrite/graphml.py
Normal file
958
venv/Lib/site-packages/networkx/readwrite/graphml.py
Normal file
|
@ -0,0 +1,958 @@
|
|||
"""
|
||||
*******
|
||||
GraphML
|
||||
*******
|
||||
Read and write graphs in GraphML format.
|
||||
|
||||
This implementation does not support mixed graphs (directed and unidirected
|
||||
edges together), hyperedges, nested graphs, or ports.
|
||||
|
||||
"GraphML is a comprehensive and easy-to-use file format for graphs. It
|
||||
consists of a language core to describe the structural properties of a
|
||||
graph and a flexible extension mechanism to add application-specific
|
||||
data. Its main features include support of
|
||||
|
||||
* directed, undirected, and mixed graphs,
|
||||
* hypergraphs,
|
||||
* hierarchical graphs,
|
||||
* graphical representations,
|
||||
* references to external data,
|
||||
* application-specific attribute data, and
|
||||
* light-weight parsers.
|
||||
|
||||
Unlike many other file formats for graphs, GraphML does not use a
|
||||
custom syntax. Instead, it is based on XML and hence ideally suited as
|
||||
a common denominator for all kinds of services generating, archiving,
|
||||
or processing graphs."
|
||||
|
||||
http://graphml.graphdrawing.org/
|
||||
|
||||
Format
|
||||
------
|
||||
GraphML is an XML format. See
|
||||
http://graphml.graphdrawing.org/specification.html for the specification and
|
||||
http://graphml.graphdrawing.org/primer/graphml-primer.html
|
||||
for examples.
|
||||
"""
|
||||
import warnings
|
||||
from collections import defaultdict
|
||||
|
||||
from xml.etree.ElementTree import Element, ElementTree, tostring, fromstring
|
||||
|
||||
try:
|
||||
import lxml.etree as lxmletree
|
||||
except ImportError:
|
||||
lxmletree = None
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import open_file
|
||||
|
||||
__all__ = [
|
||||
"write_graphml",
|
||||
"read_graphml",
|
||||
"generate_graphml",
|
||||
"write_graphml_xml",
|
||||
"write_graphml_lxml",
|
||||
"parse_graphml",
|
||||
"GraphMLWriter",
|
||||
"GraphMLReader",
|
||||
]
|
||||
|
||||
|
||||
@open_file(1, mode="wb")
|
||||
def write_graphml_xml(
|
||||
G,
|
||||
path,
|
||||
encoding="utf-8",
|
||||
prettyprint=True,
|
||||
infer_numeric_types=False,
|
||||
named_key_ids=False,
|
||||
):
|
||||
"""Write G in GraphML XML format to path
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph
|
||||
path : file or string
|
||||
File or filename to write.
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
encoding : string (optional)
|
||||
Encoding for text data.
|
||||
prettyprint : bool (optional)
|
||||
If True use line breaks and indenting in output XML.
|
||||
infer_numeric_types : boolean
|
||||
Determine if numeric types should be generalized.
|
||||
For example, if edges have both int and float 'weight' attributes,
|
||||
we infer in GraphML that both are floats.
|
||||
named_key_ids : bool (optional)
|
||||
If True use attr.name as value for key elements' id attribute.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_graphml(G, "test.graphml")
|
||||
|
||||
Notes
|
||||
-----
|
||||
This implementation does not support mixed graphs (directed
|
||||
and unidirected edges together) hyperedges, nested graphs, or ports.
|
||||
"""
|
||||
writer = GraphMLWriter(
|
||||
encoding=encoding,
|
||||
prettyprint=prettyprint,
|
||||
infer_numeric_types=infer_numeric_types,
|
||||
named_key_ids=named_key_ids,
|
||||
)
|
||||
writer.add_graph_element(G)
|
||||
writer.dump(path)
|
||||
|
||||
|
||||
@open_file(1, mode="wb")
|
||||
def write_graphml_lxml(
|
||||
G,
|
||||
path,
|
||||
encoding="utf-8",
|
||||
prettyprint=True,
|
||||
infer_numeric_types=False,
|
||||
named_key_ids=False,
|
||||
):
|
||||
"""Write G in GraphML XML format to path
|
||||
|
||||
This function uses the LXML framework and should be faster than
|
||||
the version using the xml library.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph
|
||||
path : file or string
|
||||
File or filename to write.
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
encoding : string (optional)
|
||||
Encoding for text data.
|
||||
prettyprint : bool (optional)
|
||||
If True use line breaks and indenting in output XML.
|
||||
infer_numeric_types : boolean
|
||||
Determine if numeric types should be generalized.
|
||||
For example, if edges have both int and float 'weight' attributes,
|
||||
we infer in GraphML that both are floats.
|
||||
named_key_ids : bool (optional)
|
||||
If True use attr.name as value for key elements' id attribute.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_graphml_lxml(G, "fourpath.graphml") # doctest: +SKIP
|
||||
|
||||
Notes
|
||||
-----
|
||||
This implementation does not support mixed graphs (directed
|
||||
and unidirected edges together) hyperedges, nested graphs, or ports.
|
||||
"""
|
||||
writer = GraphMLWriterLxml(
|
||||
path,
|
||||
graph=G,
|
||||
encoding=encoding,
|
||||
prettyprint=prettyprint,
|
||||
infer_numeric_types=infer_numeric_types,
|
||||
named_key_ids=named_key_ids,
|
||||
)
|
||||
writer.dump()
|
||||
|
||||
|
||||
def generate_graphml(G, encoding="utf-8", prettyprint=True, named_key_ids=False):
|
||||
"""Generate GraphML lines for G
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A networkx graph
|
||||
encoding : string (optional)
|
||||
Encoding for text data.
|
||||
prettyprint : bool (optional)
|
||||
If True use line breaks and indenting in output XML.
|
||||
named_key_ids : bool (optional)
|
||||
If True use attr.name as value for key elements' id attribute.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> linefeed = chr(10) # linefeed = \n
|
||||
>>> s = linefeed.join(nx.generate_graphml(G)) # doctest: +SKIP
|
||||
>>> for line in nx.generate_graphml(G): # doctest: +SKIP
|
||||
... print(line)
|
||||
|
||||
Notes
|
||||
-----
|
||||
This implementation does not support mixed graphs (directed and unidirected
|
||||
edges together) hyperedges, nested graphs, or ports.
|
||||
"""
|
||||
writer = GraphMLWriter(
|
||||
encoding=encoding, prettyprint=prettyprint, named_key_ids=named_key_ids
|
||||
)
|
||||
writer.add_graph_element(G)
|
||||
yield from str(writer).splitlines()
|
||||
|
||||
|
||||
@open_file(0, mode="rb")
|
||||
def read_graphml(path, node_type=str, edge_key_type=int, force_multigraph=False):
|
||||
"""Read graph in GraphML format from path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File or filename to write.
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
|
||||
node_type: Python type (default: str)
|
||||
Convert node ids to this type
|
||||
|
||||
edge_key_type: Python type (default: int)
|
||||
Convert graphml edge ids to this type. Multigraphs use id as edge key.
|
||||
Non-multigraphs add to edge attribute dict with name "id".
|
||||
|
||||
force_multigraph : bool (default: False)
|
||||
If True, return a multigraph with edge keys. If False (the default)
|
||||
return a multigraph when multiedges are in the graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
graph: NetworkX graph
|
||||
If parallel edges are present or `force_multigraph=True` then
|
||||
a MultiGraph or MultiDiGraph is returned. Otherwise a Graph/DiGraph.
|
||||
The returned graph is directed if the file indicates it should be.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Default node and edge attributes are not propagated to each node and edge.
|
||||
They can be obtained from `G.graph` and applied to node and edge attributes
|
||||
if desired using something like this:
|
||||
|
||||
>>> default_color = G.graph["node_default"]["color"] # doctest: +SKIP
|
||||
>>> for node, data in G.nodes(data=True): # doctest: +SKIP
|
||||
... if "color" not in data:
|
||||
... data["color"] = default_color
|
||||
>>> default_color = G.graph["edge_default"]["color"] # doctest: +SKIP
|
||||
>>> for u, v, data in G.edges(data=True): # doctest: +SKIP
|
||||
... if "color" not in data:
|
||||
... data["color"] = default_color
|
||||
|
||||
This implementation does not support mixed graphs (directed and unidirected
|
||||
edges together), hypergraphs, nested graphs, or ports.
|
||||
|
||||
For multigraphs the GraphML edge "id" will be used as the edge
|
||||
key. If not specified then they "key" attribute will be used. If
|
||||
there is no "key" attribute a default NetworkX multigraph edge key
|
||||
will be provided.
|
||||
|
||||
Files with the yEd "yfiles" extension will can be read but the graphics
|
||||
information is discarded.
|
||||
|
||||
yEd compressed files ("file.graphmlz" extension) can be read by renaming
|
||||
the file to "file.graphml.gz".
|
||||
|
||||
"""
|
||||
reader = GraphMLReader(node_type, edge_key_type, force_multigraph)
|
||||
# need to check for multiple graphs
|
||||
glist = list(reader(path=path))
|
||||
if len(glist) == 0:
|
||||
# If no graph comes back, try looking for an incomplete header
|
||||
header = b'<graphml xmlns="http://graphml.graphdrawing.org/xmlns">'
|
||||
path.seek(0)
|
||||
old_bytes = path.read()
|
||||
new_bytes = old_bytes.replace(b"<graphml>", header)
|
||||
glist = list(reader(string=new_bytes))
|
||||
if len(glist) == 0:
|
||||
raise nx.NetworkXError("file not successfully read as graphml")
|
||||
return glist[0]
|
||||
|
||||
|
||||
def parse_graphml(
|
||||
graphml_string, node_type=str, edge_key_type=int, force_multigraph=False
|
||||
):
|
||||
"""Read graph in GraphML format from string.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
graphml_string : string
|
||||
String containing graphml information
|
||||
(e.g., contents of a graphml file).
|
||||
|
||||
node_type: Python type (default: str)
|
||||
Convert node ids to this type
|
||||
|
||||
edge_key_type: Python type (default: int)
|
||||
Convert graphml edge ids to this type. Multigraphs use id as edge key.
|
||||
Non-multigraphs add to edge attribute dict with name "id".
|
||||
|
||||
force_multigraph : bool (default: False)
|
||||
If True, return a multigraph with edge keys. If False (the default)
|
||||
return a multigraph when multiedges are in the graph.
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
graph: NetworkX graph
|
||||
If no parallel edges are found a Graph or DiGraph is returned.
|
||||
Otherwise a MultiGraph or MultiDiGraph is returned.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> linefeed = chr(10) # linefeed = \n
|
||||
>>> s = linefeed.join(nx.generate_graphml(G))
|
||||
>>> H = nx.parse_graphml(s)
|
||||
|
||||
Notes
|
||||
-----
|
||||
Default node and edge attributes are not propagated to each node and edge.
|
||||
They can be obtained from `G.graph` and applied to node and edge attributes
|
||||
if desired using something like this:
|
||||
|
||||
>>> default_color = G.graph["node_default"]["color"] # doctest: +SKIP
|
||||
>>> for node, data in G.nodes(data=True): # doctest: +SKIP
|
||||
... if "color" not in data:
|
||||
... data["color"] = default_color
|
||||
>>> default_color = G.graph["edge_default"]["color"] # doctest: +SKIP
|
||||
>>> for u, v, data in G.edges(data=True): # doctest: +SKIP
|
||||
... if "color" not in data:
|
||||
... data["color"] = default_color
|
||||
|
||||
This implementation does not support mixed graphs (directed and unidirected
|
||||
edges together), hypergraphs, nested graphs, or ports.
|
||||
|
||||
For multigraphs the GraphML edge "id" will be used as the edge
|
||||
key. If not specified then they "key" attribute will be used. If
|
||||
there is no "key" attribute a default NetworkX multigraph edge key
|
||||
will be provided.
|
||||
|
||||
"""
|
||||
reader = GraphMLReader(node_type, edge_key_type, force_multigraph)
|
||||
# need to check for multiple graphs
|
||||
glist = list(reader(string=graphml_string))
|
||||
if len(glist) == 0:
|
||||
# If no graph comes back, try looking for an incomplete header
|
||||
header = '<graphml xmlns="http://graphml.graphdrawing.org/xmlns">'
|
||||
new_string = graphml_string.replace("<graphml>", header)
|
||||
glist = list(reader(string=new_string))
|
||||
if len(glist) == 0:
|
||||
raise nx.NetworkXError("file not successfully read as graphml")
|
||||
return glist[0]
|
||||
|
||||
|
||||
class GraphML:
|
||||
NS_GRAPHML = "http://graphml.graphdrawing.org/xmlns"
|
||||
NS_XSI = "http://www.w3.org/2001/XMLSchema-instance"
|
||||
# xmlns:y="http://www.yworks.com/xml/graphml"
|
||||
NS_Y = "http://www.yworks.com/xml/graphml"
|
||||
SCHEMALOCATION = " ".join(
|
||||
[
|
||||
"http://graphml.graphdrawing.org/xmlns",
|
||||
"http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd",
|
||||
]
|
||||
)
|
||||
|
||||
types = [
|
||||
(int, "integer"), # for Gephi GraphML bug
|
||||
(str, "yfiles"),
|
||||
(str, "string"),
|
||||
(int, "int"),
|
||||
(float, "float"),
|
||||
(float, "double"),
|
||||
(bool, "boolean"),
|
||||
]
|
||||
|
||||
# These additions to types allow writing numpy types
|
||||
try:
|
||||
import numpy as np
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
# prepend so that python types are created upon read (last entry wins)
|
||||
types = [
|
||||
(np.float64, "float"),
|
||||
(np.float32, "float"),
|
||||
(np.float16, "float"),
|
||||
(np.float_, "float"),
|
||||
(np.int_, "int"),
|
||||
(np.int8, "int"),
|
||||
(np.int16, "int"),
|
||||
(np.int32, "int"),
|
||||
(np.int64, "int"),
|
||||
(np.uint8, "int"),
|
||||
(np.uint16, "int"),
|
||||
(np.uint32, "int"),
|
||||
(np.uint64, "int"),
|
||||
(np.int_, "int"),
|
||||
(np.intc, "int"),
|
||||
(np.intp, "int"),
|
||||
] + types
|
||||
|
||||
xml_type = dict(types)
|
||||
python_type = dict(reversed(a) for a in types)
|
||||
|
||||
# This page says that data types in GraphML follow Java(TM).
|
||||
# http://graphml.graphdrawing.org/primer/graphml-primer.html#AttributesDefinition
|
||||
# true and false are the only boolean literals:
|
||||
# http://en.wikibooks.org/wiki/Java_Programming/Literals#Boolean_Literals
|
||||
convert_bool = {
|
||||
# We use data.lower() in actual use.
|
||||
"true": True,
|
||||
"false": False,
|
||||
# Include integer strings for convenience.
|
||||
"0": False,
|
||||
0: False,
|
||||
"1": True,
|
||||
1: True,
|
||||
}
|
||||
|
||||
|
||||
class GraphMLWriter(GraphML):
|
||||
def __init__(
|
||||
self,
|
||||
graph=None,
|
||||
encoding="utf-8",
|
||||
prettyprint=True,
|
||||
infer_numeric_types=False,
|
||||
named_key_ids=False,
|
||||
):
|
||||
self.myElement = Element
|
||||
|
||||
self.infer_numeric_types = infer_numeric_types
|
||||
self.prettyprint = prettyprint
|
||||
self.named_key_ids = named_key_ids
|
||||
self.encoding = encoding
|
||||
self.xml = self.myElement(
|
||||
"graphml",
|
||||
{
|
||||
"xmlns": self.NS_GRAPHML,
|
||||
"xmlns:xsi": self.NS_XSI,
|
||||
"xsi:schemaLocation": self.SCHEMALOCATION,
|
||||
},
|
||||
)
|
||||
self.keys = {}
|
||||
self.attributes = defaultdict(list)
|
||||
self.attribute_types = defaultdict(set)
|
||||
|
||||
if graph is not None:
|
||||
self.add_graph_element(graph)
|
||||
|
||||
def __str__(self):
|
||||
if self.prettyprint:
|
||||
self.indent(self.xml)
|
||||
s = tostring(self.xml).decode(self.encoding)
|
||||
return s
|
||||
|
||||
def attr_type(self, name, scope, value):
|
||||
"""Infer the attribute type of data named name. Currently this only
|
||||
supports inference of numeric types.
|
||||
|
||||
If self.infer_numeric_types is false, type is used. Otherwise, pick the
|
||||
most general of types found across all values with name and scope. This
|
||||
means edges with data named 'weight' are treated separately from nodes
|
||||
with data named 'weight'.
|
||||
"""
|
||||
if self.infer_numeric_types:
|
||||
types = self.attribute_types[(name, scope)]
|
||||
|
||||
if len(types) > 1:
|
||||
types = {self.xml_type[t] for t in types}
|
||||
if "string" in types:
|
||||
return str
|
||||
elif "float" in types or "double" in types:
|
||||
return float
|
||||
else:
|
||||
return int
|
||||
else:
|
||||
return list(types)[0]
|
||||
else:
|
||||
return type(value)
|
||||
|
||||
def get_key(self, name, attr_type, scope, default):
|
||||
keys_key = (name, attr_type, scope)
|
||||
try:
|
||||
return self.keys[keys_key]
|
||||
except KeyError:
|
||||
if self.named_key_ids:
|
||||
new_id = name
|
||||
else:
|
||||
new_id = f"d{len(list(self.keys))}"
|
||||
|
||||
self.keys[keys_key] = new_id
|
||||
key_kwargs = {
|
||||
"id": new_id,
|
||||
"for": scope,
|
||||
"attr.name": name,
|
||||
"attr.type": attr_type,
|
||||
}
|
||||
key_element = self.myElement("key", **key_kwargs)
|
||||
# add subelement for data default value if present
|
||||
if default is not None:
|
||||
default_element = self.myElement("default")
|
||||
default_element.text = str(default)
|
||||
key_element.append(default_element)
|
||||
self.xml.insert(0, key_element)
|
||||
return new_id
|
||||
|
||||
def add_data(self, name, element_type, value, scope="all", default=None):
|
||||
"""
|
||||
Make a data element for an edge or a node. Keep a log of the
|
||||
type in the keys table.
|
||||
"""
|
||||
if element_type not in self.xml_type:
|
||||
msg = f"GraphML writer does not support {element_type} as data values."
|
||||
raise nx.NetworkXError(msg)
|
||||
keyid = self.get_key(name, self.xml_type[element_type], scope, default)
|
||||
data_element = self.myElement("data", key=keyid)
|
||||
data_element.text = str(value)
|
||||
return data_element
|
||||
|
||||
def add_attributes(self, scope, xml_obj, data, default):
|
||||
"""Appends attribute data to edges or nodes, and stores type information
|
||||
to be added later. See add_graph_element.
|
||||
"""
|
||||
for k, v in data.items():
|
||||
self.attribute_types[(str(k), scope)].add(type(v))
|
||||
self.attributes[xml_obj].append([k, v, scope, default.get(k)])
|
||||
|
||||
def add_nodes(self, G, graph_element):
|
||||
default = G.graph.get("node_default", {})
|
||||
for node, data in G.nodes(data=True):
|
||||
node_element = self.myElement("node", id=str(node))
|
||||
self.add_attributes("node", node_element, data, default)
|
||||
graph_element.append(node_element)
|
||||
|
||||
def add_edges(self, G, graph_element):
|
||||
if G.is_multigraph():
|
||||
for u, v, key, data in G.edges(data=True, keys=True):
|
||||
edge_element = self.myElement(
|
||||
"edge", source=str(u), target=str(v), id=str(key)
|
||||
)
|
||||
default = G.graph.get("edge_default", {})
|
||||
self.add_attributes("edge", edge_element, data, default)
|
||||
graph_element.append(edge_element)
|
||||
else:
|
||||
for u, v, data in G.edges(data=True):
|
||||
edge_element = self.myElement("edge", source=str(u), target=str(v))
|
||||
default = G.graph.get("edge_default", {})
|
||||
self.add_attributes("edge", edge_element, data, default)
|
||||
graph_element.append(edge_element)
|
||||
|
||||
def add_graph_element(self, G):
|
||||
"""
|
||||
Serialize graph G in GraphML to the stream.
|
||||
"""
|
||||
if G.is_directed():
|
||||
default_edge_type = "directed"
|
||||
else:
|
||||
default_edge_type = "undirected"
|
||||
|
||||
graphid = G.graph.pop("id", None)
|
||||
if graphid is None:
|
||||
graph_element = self.myElement("graph", edgedefault=default_edge_type)
|
||||
else:
|
||||
graph_element = self.myElement(
|
||||
"graph", edgedefault=default_edge_type, id=graphid
|
||||
)
|
||||
default = {}
|
||||
data = {
|
||||
k: v
|
||||
for (k, v) in G.graph.items()
|
||||
if k not in ["node_default", "edge_default"]
|
||||
}
|
||||
self.add_attributes("graph", graph_element, data, default)
|
||||
self.add_nodes(G, graph_element)
|
||||
self.add_edges(G, graph_element)
|
||||
|
||||
# self.attributes contains a mapping from XML Objects to a list of
|
||||
# data that needs to be added to them.
|
||||
# We postpone processing in order to do type inference/generalization.
|
||||
# See self.attr_type
|
||||
for (xml_obj, data) in self.attributes.items():
|
||||
for (k, v, scope, default) in data:
|
||||
xml_obj.append(
|
||||
self.add_data(
|
||||
str(k), self.attr_type(k, scope, v), str(v), scope, default
|
||||
)
|
||||
)
|
||||
self.xml.append(graph_element)
|
||||
|
||||
def add_graphs(self, graph_list):
|
||||
""" Add many graphs to this GraphML document. """
|
||||
for G in graph_list:
|
||||
self.add_graph_element(G)
|
||||
|
||||
def dump(self, stream):
|
||||
if self.prettyprint:
|
||||
self.indent(self.xml)
|
||||
document = ElementTree(self.xml)
|
||||
document.write(stream, encoding=self.encoding, xml_declaration=True)
|
||||
|
||||
def indent(self, elem, level=0):
|
||||
# in-place prettyprint formatter
|
||||
i = "\n" + level * " "
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + " "
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
self.indent(elem, level + 1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
|
||||
|
||||
class IncrementalElement:
|
||||
"""Wrapper for _IncrementalWriter providing an Element like interface.
|
||||
|
||||
This wrapper does not intend to be a complete implementation but rather to
|
||||
deal with those calls used in GraphMLWriter.
|
||||
"""
|
||||
|
||||
def __init__(self, xml, prettyprint):
|
||||
self.xml = xml
|
||||
self.prettyprint = prettyprint
|
||||
|
||||
def append(self, element):
|
||||
self.xml.write(element, pretty_print=self.prettyprint)
|
||||
|
||||
|
||||
class GraphMLWriterLxml(GraphMLWriter):
|
||||
def __init__(
|
||||
self,
|
||||
path,
|
||||
graph=None,
|
||||
encoding="utf-8",
|
||||
prettyprint=True,
|
||||
infer_numeric_types=False,
|
||||
named_key_ids=False,
|
||||
):
|
||||
self.myElement = lxmletree.Element
|
||||
|
||||
self._encoding = encoding
|
||||
self._prettyprint = prettyprint
|
||||
self.named_key_ids = named_key_ids
|
||||
self.infer_numeric_types = infer_numeric_types
|
||||
|
||||
self._xml_base = lxmletree.xmlfile(path, encoding=encoding)
|
||||
self._xml = self._xml_base.__enter__()
|
||||
self._xml.write_declaration()
|
||||
|
||||
# We need to have a xml variable that support insertion. This call is
|
||||
# used for adding the keys to the document.
|
||||
# We will store those keys in a plain list, and then after the graph
|
||||
# element is closed we will add them to the main graphml element.
|
||||
self.xml = []
|
||||
self._keys = self.xml
|
||||
self._graphml = self._xml.element(
|
||||
"graphml",
|
||||
{
|
||||
"xmlns": self.NS_GRAPHML,
|
||||
"xmlns:xsi": self.NS_XSI,
|
||||
"xsi:schemaLocation": self.SCHEMALOCATION,
|
||||
},
|
||||
)
|
||||
self._graphml.__enter__()
|
||||
self.keys = {}
|
||||
self.attribute_types = defaultdict(set)
|
||||
|
||||
if graph is not None:
|
||||
self.add_graph_element(graph)
|
||||
|
||||
def add_graph_element(self, G):
|
||||
"""
|
||||
Serialize graph G in GraphML to the stream.
|
||||
"""
|
||||
if G.is_directed():
|
||||
default_edge_type = "directed"
|
||||
else:
|
||||
default_edge_type = "undirected"
|
||||
|
||||
graphid = G.graph.pop("id", None)
|
||||
if graphid is None:
|
||||
graph_element = self._xml.element("graph", edgedefault=default_edge_type)
|
||||
else:
|
||||
graph_element = self._xml.element(
|
||||
"graph", edgedefault=default_edge_type, id=graphid
|
||||
)
|
||||
|
||||
# gather attributes types for the whole graph
|
||||
# to find the most general numeric format needed.
|
||||
# Then pass through attributes to create key_id for each.
|
||||
graphdata = {
|
||||
k: v
|
||||
for k, v in G.graph.items()
|
||||
if k not in ("node_default", "edge_default")
|
||||
}
|
||||
node_default = G.graph.get("node_default", {})
|
||||
edge_default = G.graph.get("edge_default", {})
|
||||
# Graph attributes
|
||||
for k, v in graphdata.items():
|
||||
self.attribute_types[(str(k), "graph")].add(type(v))
|
||||
for k, v in graphdata.items():
|
||||
element_type = self.xml_type[self.attr_type(k, "graph", v)]
|
||||
self.get_key(str(k), element_type, "graph", None)
|
||||
# Nodes and data
|
||||
for node, d in G.nodes(data=True):
|
||||
for k, v in d.items():
|
||||
self.attribute_types[(str(k), "node")].add(type(v))
|
||||
for node, d in G.nodes(data=True):
|
||||
for k, v in d.items():
|
||||
T = self.xml_type[self.attr_type(k, "node", v)]
|
||||
self.get_key(str(k), T, "node", node_default.get(k))
|
||||
# Edges and data
|
||||
if G.is_multigraph():
|
||||
for u, v, ekey, d in G.edges(keys=True, data=True):
|
||||
for k, v in d.items():
|
||||
self.attribute_types[(str(k), "edge")].add(type(v))
|
||||
for u, v, ekey, d in G.edges(keys=True, data=True):
|
||||
for k, v in d.items():
|
||||
T = self.xml_type[self.attr_type(k, "edge", v)]
|
||||
self.get_key(str(k), T, "edge", edge_default.get(k))
|
||||
else:
|
||||
for u, v, d in G.edges(data=True):
|
||||
for k, v in d.items():
|
||||
self.attribute_types[(str(k), "edge")].add(type(v))
|
||||
for u, v, d in G.edges(data=True):
|
||||
for k, v in d.items():
|
||||
T = self.xml_type[self.attr_type(k, "edge", v)]
|
||||
self.get_key(str(k), T, "edge", edge_default.get(k))
|
||||
|
||||
# Now add attribute keys to the xml file
|
||||
for key in self.xml:
|
||||
self._xml.write(key, pretty_print=self._prettyprint)
|
||||
|
||||
# The incremental_writer writes each node/edge as it is created
|
||||
incremental_writer = IncrementalElement(self._xml, self._prettyprint)
|
||||
with graph_element:
|
||||
self.add_attributes("graph", incremental_writer, graphdata, {})
|
||||
self.add_nodes(G, incremental_writer) # adds attributes too
|
||||
self.add_edges(G, incremental_writer) # adds attributes too
|
||||
|
||||
def add_attributes(self, scope, xml_obj, data, default):
|
||||
"""Appends attribute data."""
|
||||
for k, v in data.items():
|
||||
data_element = self.add_data(
|
||||
str(k), self.attr_type(str(k), scope, v), str(v), scope, default.get(k)
|
||||
)
|
||||
xml_obj.append(data_element)
|
||||
|
||||
def __str__(self):
|
||||
return object.__str__(self)
|
||||
|
||||
def dump(self):
|
||||
self._graphml.__exit__(None, None, None)
|
||||
self._xml_base.__exit__(None, None, None)
|
||||
|
||||
|
||||
# Choose a writer function for default
|
||||
if lxmletree is None:
|
||||
write_graphml = write_graphml_xml
|
||||
else:
|
||||
write_graphml = write_graphml_lxml
|
||||
|
||||
|
||||
class GraphMLReader(GraphML):
|
||||
"""Read a GraphML document. Produces NetworkX graph objects."""
|
||||
|
||||
def __init__(self, node_type=str, edge_key_type=int, force_multigraph=False):
|
||||
self.node_type = node_type
|
||||
self.edge_key_type = edge_key_type
|
||||
self.multigraph = force_multigraph # If False, test for multiedges
|
||||
self.edge_ids = {} # dict mapping (u,v) tuples to edge id attributes
|
||||
|
||||
def __call__(self, path=None, string=None):
|
||||
if path is not None:
|
||||
self.xml = ElementTree(file=path)
|
||||
elif string is not None:
|
||||
self.xml = fromstring(string)
|
||||
else:
|
||||
raise ValueError("Must specify either 'path' or 'string' as kwarg")
|
||||
(keys, defaults) = self.find_graphml_keys(self.xml)
|
||||
for g in self.xml.findall(f"{{{self.NS_GRAPHML}}}graph"):
|
||||
yield self.make_graph(g, keys, defaults)
|
||||
|
||||
def make_graph(self, graph_xml, graphml_keys, defaults, G=None):
|
||||
# set default graph type
|
||||
edgedefault = graph_xml.get("edgedefault", None)
|
||||
if G is None:
|
||||
if edgedefault == "directed":
|
||||
G = nx.MultiDiGraph()
|
||||
else:
|
||||
G = nx.MultiGraph()
|
||||
# set defaults for graph attributes
|
||||
G.graph["node_default"] = {}
|
||||
G.graph["edge_default"] = {}
|
||||
for key_id, value in defaults.items():
|
||||
key_for = graphml_keys[key_id]["for"]
|
||||
name = graphml_keys[key_id]["name"]
|
||||
python_type = graphml_keys[key_id]["type"]
|
||||
if key_for == "node":
|
||||
G.graph["node_default"].update({name: python_type(value)})
|
||||
if key_for == "edge":
|
||||
G.graph["edge_default"].update({name: python_type(value)})
|
||||
# hyperedges are not supported
|
||||
hyperedge = graph_xml.find(f"{{{self.NS_GRAPHML}}}hyperedge")
|
||||
if hyperedge is not None:
|
||||
raise nx.NetworkXError("GraphML reader doesn't support hyperedges")
|
||||
# add nodes
|
||||
for node_xml in graph_xml.findall(f"{{{self.NS_GRAPHML}}}node"):
|
||||
self.add_node(G, node_xml, graphml_keys, defaults)
|
||||
# add edges
|
||||
for edge_xml in graph_xml.findall(f"{{{self.NS_GRAPHML}}}edge"):
|
||||
self.add_edge(G, edge_xml, graphml_keys)
|
||||
# add graph data
|
||||
data = self.decode_data_elements(graphml_keys, graph_xml)
|
||||
G.graph.update(data)
|
||||
|
||||
# switch to Graph or DiGraph if no parallel edges were found
|
||||
if self.multigraph:
|
||||
return G
|
||||
|
||||
G = nx.DiGraph(G) if G.is_directed() else nx.Graph(G)
|
||||
# add explicit edge "id" from file as attribute in NX graph.
|
||||
nx.set_edge_attributes(G, values=self.edge_ids, name="id")
|
||||
return G
|
||||
|
||||
def add_node(self, G, node_xml, graphml_keys, defaults):
|
||||
"""Add a node to the graph.
|
||||
"""
|
||||
# warn on finding unsupported ports tag
|
||||
ports = node_xml.find(f"{{{self.NS_GRAPHML}}}port")
|
||||
if ports is not None:
|
||||
warnings.warn("GraphML port tag not supported.")
|
||||
# find the node by id and cast it to the appropriate type
|
||||
node_id = self.node_type(node_xml.get("id"))
|
||||
# get data/attributes for node
|
||||
data = self.decode_data_elements(graphml_keys, node_xml)
|
||||
G.add_node(node_id, **data)
|
||||
# get child nodes
|
||||
if node_xml.attrib.get("yfiles.foldertype") == "group":
|
||||
graph_xml = node_xml.find(f"{{{self.NS_GRAPHML}}}graph")
|
||||
self.make_graph(graph_xml, graphml_keys, defaults, G)
|
||||
|
||||
def add_edge(self, G, edge_element, graphml_keys):
|
||||
"""Add an edge to the graph.
|
||||
"""
|
||||
# warn on finding unsupported ports tag
|
||||
ports = edge_element.find(f"{{{self.NS_GRAPHML}}}port")
|
||||
if ports is not None:
|
||||
warnings.warn("GraphML port tag not supported.")
|
||||
|
||||
# raise error if we find mixed directed and undirected edges
|
||||
directed = edge_element.get("directed")
|
||||
if G.is_directed() and directed == "false":
|
||||
msg = "directed=false edge found in directed graph."
|
||||
raise nx.NetworkXError(msg)
|
||||
if (not G.is_directed()) and directed == "true":
|
||||
msg = "directed=true edge found in undirected graph."
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
source = self.node_type(edge_element.get("source"))
|
||||
target = self.node_type(edge_element.get("target"))
|
||||
data = self.decode_data_elements(graphml_keys, edge_element)
|
||||
# GraphML stores edge ids as an attribute
|
||||
# NetworkX uses them as keys in multigraphs too if no key
|
||||
# attribute is specified
|
||||
edge_id = edge_element.get("id")
|
||||
if edge_id:
|
||||
# self.edge_ids is used by `make_graph` method for non-multigraphs
|
||||
self.edge_ids[source, target] = edge_id
|
||||
try:
|
||||
edge_id = self.edge_key_type(edge_id)
|
||||
except ValueError: # Could not convert.
|
||||
pass
|
||||
else:
|
||||
edge_id = data.get("key")
|
||||
|
||||
if G.has_edge(source, target):
|
||||
# mark this as a multigraph
|
||||
self.multigraph = True
|
||||
|
||||
# Use add_edges_from to avoid error with add_edge when `'key' in data`
|
||||
# Note there is only one edge here...
|
||||
G.add_edges_from([(source, target, edge_id, data)])
|
||||
|
||||
def decode_data_elements(self, graphml_keys, obj_xml):
|
||||
"""Use the key information to decode the data XML if present."""
|
||||
data = {}
|
||||
for data_element in obj_xml.findall(f"{{{self.NS_GRAPHML}}}data"):
|
||||
key = data_element.get("key")
|
||||
try:
|
||||
data_name = graphml_keys[key]["name"]
|
||||
data_type = graphml_keys[key]["type"]
|
||||
except KeyError as e:
|
||||
raise nx.NetworkXError(f"Bad GraphML data: no key {key}") from e
|
||||
text = data_element.text
|
||||
# assume anything with subelements is a yfiles extension
|
||||
if text is not None and len(list(data_element)) == 0:
|
||||
if data_type == bool:
|
||||
# Ignore cases.
|
||||
# http://docs.oracle.com/javase/6/docs/api/java/lang/
|
||||
# Boolean.html#parseBoolean%28java.lang.String%29
|
||||
data[data_name] = self.convert_bool[text.lower()]
|
||||
else:
|
||||
data[data_name] = data_type(text)
|
||||
elif len(list(data_element)) > 0:
|
||||
# Assume yfiles as subelements, try to extract node_label
|
||||
node_label = None
|
||||
for node_type in ["ShapeNode", "SVGNode", "ImageNode"]:
|
||||
pref = f"{{{self.NS_Y}}}{node_type}/{{{self.NS_Y}}}"
|
||||
geometry = data_element.find(f"{pref}Geometry")
|
||||
if geometry is not None:
|
||||
data["x"] = geometry.get("x")
|
||||
data["y"] = geometry.get("y")
|
||||
if node_label is None:
|
||||
node_label = data_element.find(f"{pref}NodeLabel")
|
||||
if node_label is not None:
|
||||
data["label"] = node_label.text
|
||||
|
||||
# check all the different types of edges avaivable in yEd.
|
||||
for e in [
|
||||
"PolyLineEdge",
|
||||
"SplineEdge",
|
||||
"QuadCurveEdge",
|
||||
"BezierEdge",
|
||||
"ArcEdge",
|
||||
]:
|
||||
pref = f"{{{self.NS_Y}}}{e}/{{{self.NS_Y}}}"
|
||||
edge_label = data_element.find(f"{pref}EdgeLabel")
|
||||
if edge_label is not None:
|
||||
break
|
||||
|
||||
if edge_label is not None:
|
||||
data["label"] = edge_label.text
|
||||
return data
|
||||
|
||||
def find_graphml_keys(self, graph_element):
|
||||
"""Extracts all the keys and key defaults from the xml.
|
||||
"""
|
||||
graphml_keys = {}
|
||||
graphml_key_defaults = {}
|
||||
for k in graph_element.findall(f"{{{self.NS_GRAPHML}}}key"):
|
||||
attr_id = k.get("id")
|
||||
attr_type = k.get("attr.type")
|
||||
attr_name = k.get("attr.name")
|
||||
yfiles_type = k.get("yfiles.type")
|
||||
if yfiles_type is not None:
|
||||
attr_name = yfiles_type
|
||||
attr_type = "yfiles"
|
||||
if attr_type is None:
|
||||
attr_type = "string"
|
||||
warnings.warn(f"No key type for id {attr_id}. Using string")
|
||||
if attr_name is None:
|
||||
raise nx.NetworkXError(f"Unknown key for id {attr_id}.")
|
||||
graphml_keys[attr_id] = {
|
||||
"name": attr_name,
|
||||
"type": self.python_type[attr_type],
|
||||
"for": k.get("for"),
|
||||
}
|
||||
# check for "default" subelement of key element
|
||||
default = k.find(f"{{{self.NS_GRAPHML}}}default")
|
||||
if default is not None:
|
||||
graphml_key_defaults[attr_id] = default.text
|
||||
return graphml_keys, graphml_key_defaults
|
|
@ -0,0 +1,19 @@
|
|||
"""
|
||||
*********
|
||||
JSON data
|
||||
*********
|
||||
Generate and parse JSON serializable data for NetworkX graphs.
|
||||
|
||||
These formats are suitable for use with the d3.js examples https://d3js.org/
|
||||
|
||||
The three formats that you can generate with NetworkX are:
|
||||
|
||||
- node-link like in the d3.js example https://bl.ocks.org/mbostock/4062045
|
||||
- tree like in the d3.js example https://bl.ocks.org/mbostock/4063550
|
||||
- adjacency like in the d3.js example https://bost.ocks.org/mike/miserables/
|
||||
"""
|
||||
from networkx.readwrite.json_graph.node_link import *
|
||||
from networkx.readwrite.json_graph.adjacency import *
|
||||
from networkx.readwrite.json_graph.tree import *
|
||||
from networkx.readwrite.json_graph.jit import *
|
||||
from networkx.readwrite.json_graph.cytoscape import *
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,156 @@
|
|||
from itertools import chain
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["adjacency_data", "adjacency_graph"]
|
||||
|
||||
_attrs = dict(id="id", key="key")
|
||||
|
||||
|
||||
def adjacency_data(G, attrs=_attrs):
|
||||
"""Returns data in adjacency format that is suitable for JSON serialization
|
||||
and use in Javascript documents.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
|
||||
attrs : dict
|
||||
A dictionary that contains two keys 'id' and 'key'. The corresponding
|
||||
values provide the attribute names for storing NetworkX-internal graph
|
||||
data. The values should be unique. Default value:
|
||||
:samp:`dict(id='id', key='key')`.
|
||||
|
||||
If some user-defined graph data use these attribute names as data keys,
|
||||
they may be silently dropped.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data : dict
|
||||
A dictionary with adjacency formatted data.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If values in attrs are not unique.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from networkx.readwrite import json_graph
|
||||
>>> G = nx.Graph([(1, 2)])
|
||||
>>> data = json_graph.adjacency_data(G)
|
||||
|
||||
To serialize with json
|
||||
|
||||
>>> import json
|
||||
>>> s = json.dumps(data)
|
||||
|
||||
Notes
|
||||
-----
|
||||
Graph, node, and link attributes will be written when using this format
|
||||
but attribute keys must be strings if you want to serialize the resulting
|
||||
data with JSON.
|
||||
|
||||
The default value of attrs will be changed in a future release of NetworkX.
|
||||
|
||||
See Also
|
||||
--------
|
||||
adjacency_graph, node_link_data, tree_data
|
||||
"""
|
||||
multigraph = G.is_multigraph()
|
||||
id_ = attrs["id"]
|
||||
# Allow 'key' to be omitted from attrs if the graph is not a multigraph.
|
||||
key = None if not multigraph else attrs["key"]
|
||||
if id_ == key:
|
||||
raise nx.NetworkXError("Attribute names are not unique.")
|
||||
data = {}
|
||||
data["directed"] = G.is_directed()
|
||||
data["multigraph"] = multigraph
|
||||
data["graph"] = list(G.graph.items())
|
||||
data["nodes"] = []
|
||||
data["adjacency"] = []
|
||||
for n, nbrdict in G.adjacency():
|
||||
data["nodes"].append(dict(chain(G.nodes[n].items(), [(id_, n)])))
|
||||
adj = []
|
||||
if multigraph:
|
||||
for nbr, keys in nbrdict.items():
|
||||
for k, d in keys.items():
|
||||
adj.append(dict(chain(d.items(), [(id_, nbr), (key, k)])))
|
||||
else:
|
||||
for nbr, d in nbrdict.items():
|
||||
adj.append(dict(chain(d.items(), [(id_, nbr)])))
|
||||
data["adjacency"].append(adj)
|
||||
return data
|
||||
|
||||
|
||||
def adjacency_graph(data, directed=False, multigraph=True, attrs=_attrs):
|
||||
"""Returns graph from adjacency data format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : dict
|
||||
Adjacency list formatted graph data
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX graph
|
||||
A NetworkX graph object
|
||||
|
||||
directed : bool
|
||||
If True, and direction not specified in data, return a directed graph.
|
||||
|
||||
multigraph : bool
|
||||
If True, and multigraph not specified in data, return a multigraph.
|
||||
|
||||
attrs : dict
|
||||
A dictionary that contains two keys 'id' and 'key'. The corresponding
|
||||
values provide the attribute names for storing NetworkX-internal graph
|
||||
data. The values should be unique. Default value:
|
||||
:samp:`dict(id='id', key='key')`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from networkx.readwrite import json_graph
|
||||
>>> G = nx.Graph([(1, 2)])
|
||||
>>> data = json_graph.adjacency_data(G)
|
||||
>>> H = json_graph.adjacency_graph(data)
|
||||
|
||||
Notes
|
||||
-----
|
||||
The default value of attrs will be changed in a future release of NetworkX.
|
||||
|
||||
See Also
|
||||
--------
|
||||
adjacency_graph, node_link_data, tree_data
|
||||
"""
|
||||
multigraph = data.get("multigraph", multigraph)
|
||||
directed = data.get("directed", directed)
|
||||
if multigraph:
|
||||
graph = nx.MultiGraph()
|
||||
else:
|
||||
graph = nx.Graph()
|
||||
if directed:
|
||||
graph = graph.to_directed()
|
||||
id_ = attrs["id"]
|
||||
# Allow 'key' to be omitted from attrs if the graph is not a multigraph.
|
||||
key = None if not multigraph else attrs["key"]
|
||||
graph.graph = dict(data.get("graph", []))
|
||||
mapping = []
|
||||
for d in data["nodes"]:
|
||||
node_data = d.copy()
|
||||
node = node_data.pop(id_)
|
||||
mapping.append(node)
|
||||
graph.add_node(node)
|
||||
graph.nodes[node].update(node_data)
|
||||
for i, d in enumerate(data["adjacency"]):
|
||||
source = mapping[i]
|
||||
for tdata in d:
|
||||
target_data = tdata.copy()
|
||||
target = target_data.pop(id_)
|
||||
if not multigraph:
|
||||
graph.add_edge(source, target)
|
||||
graph[source][target].update(tdata)
|
||||
else:
|
||||
ky = target_data.pop(key, None)
|
||||
graph.add_edge(source, target, key=ky)
|
||||
graph[source][target][ky].update(tdata)
|
||||
return graph
|
|
@ -0,0 +1,110 @@
|
|||
import networkx as nx
|
||||
|
||||
__all__ = ["cytoscape_data", "cytoscape_graph"]
|
||||
|
||||
_attrs = dict(name="name", ident="id")
|
||||
|
||||
|
||||
def cytoscape_data(G, attrs=None):
|
||||
"""Returns data in Cytoscape JSON format (cyjs).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
data: dict
|
||||
A dictionary with cyjs formatted data.
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If values in attrs are not unique.
|
||||
"""
|
||||
if not attrs:
|
||||
attrs = _attrs
|
||||
else:
|
||||
attrs.update({k: v for (k, v) in _attrs.items() if k not in attrs})
|
||||
|
||||
name = attrs["name"]
|
||||
ident = attrs["ident"]
|
||||
|
||||
if len({name, ident}) < 2:
|
||||
raise nx.NetworkXError("Attribute names are not unique.")
|
||||
|
||||
jsondata = {"data": list(G.graph.items())}
|
||||
jsondata["directed"] = G.is_directed()
|
||||
jsondata["multigraph"] = G.is_multigraph()
|
||||
jsondata["elements"] = {"nodes": [], "edges": []}
|
||||
nodes = jsondata["elements"]["nodes"]
|
||||
edges = jsondata["elements"]["edges"]
|
||||
|
||||
for i, j in G.nodes.items():
|
||||
n = {"data": j.copy()}
|
||||
n["data"]["id"] = j.get(ident) or str(i)
|
||||
n["data"]["value"] = i
|
||||
n["data"]["name"] = j.get(name) or str(i)
|
||||
nodes.append(n)
|
||||
|
||||
if G.is_multigraph():
|
||||
for e in G.edges(keys=True):
|
||||
n = {"data": G.adj[e[0]][e[1]][e[2]].copy()}
|
||||
n["data"]["source"] = e[0]
|
||||
n["data"]["target"] = e[1]
|
||||
n["data"]["key"] = e[2]
|
||||
edges.append(n)
|
||||
else:
|
||||
for e in G.edges():
|
||||
n = {"data": G.adj[e[0]][e[1]].copy()}
|
||||
n["data"]["source"] = e[0]
|
||||
n["data"]["target"] = e[1]
|
||||
edges.append(n)
|
||||
return jsondata
|
||||
|
||||
|
||||
def cytoscape_graph(data, attrs=None):
|
||||
if not attrs:
|
||||
attrs = _attrs
|
||||
else:
|
||||
attrs.update({k: v for (k, v) in _attrs.items() if k not in attrs})
|
||||
|
||||
name = attrs["name"]
|
||||
ident = attrs["ident"]
|
||||
|
||||
if len({ident, name}) < 2:
|
||||
raise nx.NetworkXError("Attribute names are not unique.")
|
||||
|
||||
multigraph = data.get("multigraph")
|
||||
directed = data.get("directed")
|
||||
if multigraph:
|
||||
graph = nx.MultiGraph()
|
||||
else:
|
||||
graph = nx.Graph()
|
||||
if directed:
|
||||
graph = graph.to_directed()
|
||||
graph.graph = dict(data.get("data"))
|
||||
for d in data["elements"]["nodes"]:
|
||||
node_data = d["data"].copy()
|
||||
node = d["data"]["value"]
|
||||
|
||||
if d["data"].get(name):
|
||||
node_data[name] = d["data"].get(name)
|
||||
if d["data"].get(ident):
|
||||
node_data[ident] = d["data"].get(ident)
|
||||
|
||||
graph.add_node(node)
|
||||
graph.nodes[node].update(node_data)
|
||||
|
||||
for d in data["elements"]["edges"]:
|
||||
edge_data = d["data"].copy()
|
||||
sour = d["data"].pop("source")
|
||||
targ = d["data"].pop("target")
|
||||
if multigraph:
|
||||
key = d["data"].get("key", 0)
|
||||
graph.add_edge(sour, targ, key=key)
|
||||
graph.edges[sour, targ, key].update(edge_data)
|
||||
else:
|
||||
graph.add_edge(sour, targ)
|
||||
graph.edges[sour, targ].update(edge_data)
|
||||
return graph
|
103
venv/Lib/site-packages/networkx/readwrite/json_graph/jit.py
Normal file
103
venv/Lib/site-packages/networkx/readwrite/json_graph/jit.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
"""
|
||||
Read and write NetworkX graphs as JavaScript InfoVis Toolkit (JIT) format JSON.
|
||||
|
||||
See the `JIT documentation`_ for more examples.
|
||||
|
||||
Format
|
||||
------
|
||||
var json = [
|
||||
{
|
||||
"id": "aUniqueIdentifier",
|
||||
"name": "usually a nodes name",
|
||||
"data": {
|
||||
"some key": "some value",
|
||||
"some other key": "some other value"
|
||||
},
|
||||
"adjacencies": [
|
||||
{
|
||||
nodeTo:"aNodeId",
|
||||
data: {} //put whatever you want here
|
||||
},
|
||||
'other adjacencies go here...'
|
||||
},
|
||||
|
||||
'other nodes go here...'
|
||||
];
|
||||
.. _JIT documentation: http://thejit.org
|
||||
"""
|
||||
|
||||
import json
|
||||
import networkx as nx
|
||||
from networkx.utils.decorators import not_implemented_for
|
||||
|
||||
__all__ = ["jit_graph", "jit_data"]
|
||||
|
||||
|
||||
def jit_graph(data, create_using=None):
|
||||
"""Read a graph from JIT JSON.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : JSON Graph Object
|
||||
|
||||
create_using : Networkx Graph, optional (default: Graph())
|
||||
Return graph of this type. The provided instance will be cleared.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX Graph built from create_using if provided.
|
||||
"""
|
||||
if create_using is None:
|
||||
G = nx.Graph()
|
||||
else:
|
||||
G = create_using
|
||||
G.clear()
|
||||
|
||||
if isinstance(data, str):
|
||||
data = json.loads(data)
|
||||
|
||||
for node in data:
|
||||
G.add_node(node["id"], **node["data"])
|
||||
if node.get("adjacencies") is not None:
|
||||
for adj in node["adjacencies"]:
|
||||
G.add_edge(node["id"], adj["nodeTo"], **adj["data"])
|
||||
return G
|
||||
|
||||
|
||||
@not_implemented_for("multigraph")
|
||||
def jit_data(G, indent=None, default=None):
|
||||
"""Returns data in JIT JSON format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
|
||||
indent: optional, default=None
|
||||
If indent is a non-negative integer, then JSON array elements and
|
||||
object members will be pretty-printed with that indent level.
|
||||
An indent level of 0, or negative, will only insert newlines.
|
||||
None (the default) selects the most compact representation.
|
||||
|
||||
default: optional, default=None
|
||||
It will pass the value to the json.dumps function in order to
|
||||
be able to serialize custom objects used as nodes.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data: JIT JSON string
|
||||
"""
|
||||
json_graph = []
|
||||
for node in G.nodes():
|
||||
json_node = {"id": node, "name": node}
|
||||
# node data
|
||||
json_node["data"] = G.nodes[node]
|
||||
# adjacencies
|
||||
if G[node]:
|
||||
json_node["adjacencies"] = []
|
||||
for neighbour in G[node]:
|
||||
adjacency = {"nodeTo": neighbour}
|
||||
# adjacency data
|
||||
adjacency["data"] = G.edges[node, neighbour]
|
||||
json_node["adjacencies"].append(adjacency)
|
||||
json_graph.append(json_node)
|
||||
return json.dumps(json_graph, indent=indent, default=default)
|
|
@ -0,0 +1,184 @@
|
|||
from itertools import chain, count
|
||||
import networkx as nx
|
||||
from networkx.utils import to_tuple
|
||||
|
||||
__all__ = ["node_link_data", "node_link_graph"]
|
||||
|
||||
|
||||
_attrs = dict(source="source", target="target", name="id", key="key", link="links")
|
||||
|
||||
|
||||
def node_link_data(G, attrs=None):
|
||||
"""Returns data in node-link format that is suitable for JSON serialization
|
||||
and use in Javascript documents.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
|
||||
attrs : dict
|
||||
A dictionary that contains five keys 'source', 'target', 'name',
|
||||
'key' and 'link'. The corresponding values provide the attribute
|
||||
names for storing NetworkX-internal graph data. The values should
|
||||
be unique. Default value::
|
||||
|
||||
dict(source='source', target='target', name='id',
|
||||
key='key', link='links')
|
||||
|
||||
If some user-defined graph data use these attribute names as data keys,
|
||||
they may be silently dropped.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data : dict
|
||||
A dictionary with node-link formatted data.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If values in attrs are not unique.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from networkx.readwrite import json_graph
|
||||
>>> G = nx.Graph([("A", "B")])
|
||||
>>> data1 = json_graph.node_link_data(G)
|
||||
>>> H = nx.gn_graph(2)
|
||||
>>> data2 = json_graph.node_link_data(
|
||||
... H, {"link": "edges", "source": "from", "target": "to"}
|
||||
... )
|
||||
|
||||
To serialize with json
|
||||
|
||||
>>> import json
|
||||
>>> s1 = json.dumps(data1)
|
||||
>>> s2 = json.dumps(
|
||||
... data2, default={"link": "edges", "source": "from", "target": "to"}
|
||||
... )
|
||||
|
||||
Notes
|
||||
-----
|
||||
Graph, node, and link attributes are stored in this format. Note that
|
||||
attribute keys will be converted to strings in order to comply with JSON.
|
||||
|
||||
Attribute 'key' is only used for multigraphs.
|
||||
|
||||
See Also
|
||||
--------
|
||||
node_link_graph, adjacency_data, tree_data
|
||||
"""
|
||||
multigraph = G.is_multigraph()
|
||||
# Allow 'attrs' to keep default values.
|
||||
if attrs is None:
|
||||
attrs = _attrs
|
||||
else:
|
||||
attrs.update({k: v for (k, v) in _attrs.items() if k not in attrs})
|
||||
name = attrs["name"]
|
||||
source = attrs["source"]
|
||||
target = attrs["target"]
|
||||
links = attrs["link"]
|
||||
# Allow 'key' to be omitted from attrs if the graph is not a multigraph.
|
||||
key = None if not multigraph else attrs["key"]
|
||||
if len({source, target, key}) < 3:
|
||||
raise nx.NetworkXError("Attribute names are not unique.")
|
||||
data = {
|
||||
"directed": G.is_directed(),
|
||||
"multigraph": multigraph,
|
||||
"graph": G.graph,
|
||||
"nodes": [dict(chain(G.nodes[n].items(), [(name, n)])) for n in G],
|
||||
}
|
||||
if multigraph:
|
||||
data[links] = [
|
||||
dict(chain(d.items(), [(source, u), (target, v), (key, k)]))
|
||||
for u, v, k, d in G.edges(keys=True, data=True)
|
||||
]
|
||||
else:
|
||||
data[links] = [
|
||||
dict(chain(d.items(), [(source, u), (target, v)]))
|
||||
for u, v, d in G.edges(data=True)
|
||||
]
|
||||
return data
|
||||
|
||||
|
||||
def node_link_graph(data, directed=False, multigraph=True, attrs=None):
|
||||
"""Returns graph from node-link data format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : dict
|
||||
node-link formatted graph data
|
||||
|
||||
directed : bool
|
||||
If True, and direction not specified in data, return a directed graph.
|
||||
|
||||
multigraph : bool
|
||||
If True, and multigraph not specified in data, return a multigraph.
|
||||
|
||||
attrs : dict
|
||||
A dictionary that contains five keys 'source', 'target', 'name',
|
||||
'key' and 'link'. The corresponding values provide the attribute
|
||||
names for storing NetworkX-internal graph data. Default value:
|
||||
|
||||
dict(source='source', target='target', name='id',
|
||||
key='key', link='links')
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX graph
|
||||
A NetworkX graph object
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from networkx.readwrite import json_graph
|
||||
>>> G = nx.Graph([("A", "B")])
|
||||
>>> data = json_graph.node_link_data(G)
|
||||
>>> H = json_graph.node_link_graph(data)
|
||||
|
||||
Notes
|
||||
-----
|
||||
Attribute 'key' is only used for multigraphs.
|
||||
|
||||
See Also
|
||||
--------
|
||||
node_link_data, adjacency_data, tree_data
|
||||
"""
|
||||
# Allow 'attrs' to keep default values.
|
||||
if attrs is None:
|
||||
attrs = _attrs
|
||||
else:
|
||||
attrs.update({k: v for k, v in _attrs.items() if k not in attrs})
|
||||
multigraph = data.get("multigraph", multigraph)
|
||||
directed = data.get("directed", directed)
|
||||
if multigraph:
|
||||
graph = nx.MultiGraph()
|
||||
else:
|
||||
graph = nx.Graph()
|
||||
if directed:
|
||||
graph = graph.to_directed()
|
||||
name = attrs["name"]
|
||||
source = attrs["source"]
|
||||
target = attrs["target"]
|
||||
links = attrs["link"]
|
||||
# Allow 'key' to be omitted from attrs if the graph is not a multigraph.
|
||||
key = None if not multigraph else attrs["key"]
|
||||
graph.graph = data.get("graph", {})
|
||||
c = count()
|
||||
for d in data["nodes"]:
|
||||
node = to_tuple(d.get(name, next(c)))
|
||||
nodedata = {str(k): v for k, v in d.items() if k != name}
|
||||
graph.add_node(node, **nodedata)
|
||||
for d in data[links]:
|
||||
src = tuple(d[source]) if isinstance(d[source], list) else d[source]
|
||||
tgt = tuple(d[target]) if isinstance(d[target], list) else d[target]
|
||||
if not multigraph:
|
||||
edgedata = {str(k): v for k, v in d.items() if k != source and k != target}
|
||||
graph.add_edge(src, tgt, **edgedata)
|
||||
else:
|
||||
ky = d.get(key, None)
|
||||
edgedata = {
|
||||
str(k): v
|
||||
for k, v in d.items()
|
||||
if k != source and k != target and k != key
|
||||
}
|
||||
graph.add_edge(src, tgt, ky, **edgedata)
|
||||
return graph
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,58 @@
|
|||
import json
|
||||
import pytest
|
||||
import networkx as nx
|
||||
from networkx.readwrite.json_graph import adjacency_data, adjacency_graph
|
||||
|
||||
|
||||
class TestAdjacency:
|
||||
def test_graph(self):
|
||||
G = nx.path_graph(4)
|
||||
H = adjacency_graph(adjacency_data(G))
|
||||
nx.is_isomorphic(G, H)
|
||||
|
||||
def test_graph_attributes(self):
|
||||
G = nx.path_graph(4)
|
||||
G.add_node(1, color="red")
|
||||
G.add_edge(1, 2, width=7)
|
||||
G.graph["foo"] = "bar"
|
||||
G.graph[1] = "one"
|
||||
|
||||
H = adjacency_graph(adjacency_data(G))
|
||||
assert H.graph["foo"] == "bar"
|
||||
assert H.nodes[1]["color"] == "red"
|
||||
assert H[1][2]["width"] == 7
|
||||
|
||||
d = json.dumps(adjacency_data(G))
|
||||
H = adjacency_graph(json.loads(d))
|
||||
assert H.graph["foo"] == "bar"
|
||||
assert H.graph[1] == "one"
|
||||
assert H.nodes[1]["color"] == "red"
|
||||
assert H[1][2]["width"] == 7
|
||||
|
||||
def test_digraph(self):
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, [1, 2, 3])
|
||||
H = adjacency_graph(adjacency_data(G))
|
||||
assert H.is_directed()
|
||||
nx.is_isomorphic(G, H)
|
||||
|
||||
def test_multidigraph(self):
|
||||
G = nx.MultiDiGraph()
|
||||
nx.add_path(G, [1, 2, 3])
|
||||
H = adjacency_graph(adjacency_data(G))
|
||||
assert H.is_directed()
|
||||
assert H.is_multigraph()
|
||||
|
||||
def test_multigraph(self):
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(1, 2, key="first")
|
||||
G.add_edge(1, 2, key="second", color="blue")
|
||||
H = adjacency_graph(adjacency_data(G))
|
||||
nx.is_isomorphic(G, H)
|
||||
assert H[1][2]["second"]["color"] == "blue"
|
||||
|
||||
def test_exception(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.MultiDiGraph()
|
||||
attrs = dict(id="node", key="node")
|
||||
adjacency_data(G, attrs)
|
|
@ -0,0 +1,63 @@
|
|||
import json
|
||||
import pytest
|
||||
import networkx as nx
|
||||
from networkx.readwrite.json_graph import cytoscape_data, cytoscape_graph
|
||||
|
||||
|
||||
class TestCytoscape:
|
||||
def test_graph(self):
|
||||
G = nx.path_graph(4)
|
||||
H = cytoscape_graph(cytoscape_data(G))
|
||||
nx.is_isomorphic(G, H)
|
||||
|
||||
def test_graph_attributes(self):
|
||||
G = nx.path_graph(4)
|
||||
G.add_node(1, color="red")
|
||||
G.add_edge(1, 2, width=7)
|
||||
G.graph["foo"] = "bar"
|
||||
G.graph[1] = "one"
|
||||
G.add_node(3, name="node", id="123")
|
||||
|
||||
H = cytoscape_graph(cytoscape_data(G))
|
||||
assert H.graph["foo"] == "bar"
|
||||
assert H.nodes[1]["color"] == "red"
|
||||
assert H[1][2]["width"] == 7
|
||||
assert H.nodes[3]["name"] == "node"
|
||||
assert H.nodes[3]["id"] == "123"
|
||||
|
||||
d = json.dumps(cytoscape_data(G))
|
||||
H = cytoscape_graph(json.loads(d))
|
||||
assert H.graph["foo"] == "bar"
|
||||
assert H.graph[1] == "one"
|
||||
assert H.nodes[1]["color"] == "red"
|
||||
assert H[1][2]["width"] == 7
|
||||
assert H.nodes[3]["name"] == "node"
|
||||
assert H.nodes[3]["id"] == "123"
|
||||
|
||||
def test_digraph(self):
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, [1, 2, 3])
|
||||
H = cytoscape_graph(cytoscape_data(G))
|
||||
assert H.is_directed()
|
||||
nx.is_isomorphic(G, H)
|
||||
|
||||
def test_multidigraph(self):
|
||||
G = nx.MultiDiGraph()
|
||||
nx.add_path(G, [1, 2, 3])
|
||||
H = cytoscape_graph(cytoscape_data(G))
|
||||
assert H.is_directed()
|
||||
assert H.is_multigraph()
|
||||
|
||||
def test_multigraph(self):
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(1, 2, key="first")
|
||||
G.add_edge(1, 2, key="second", color="blue")
|
||||
H = cytoscape_graph(cytoscape_data(G))
|
||||
assert nx.is_isomorphic(G, H)
|
||||
assert H[1][2]["second"]["color"] == "blue"
|
||||
|
||||
def test_exception(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.MultiDiGraph()
|
||||
attrs = dict(name="node", ident="node")
|
||||
cytoscape_data(G, attrs)
|
|
@ -0,0 +1,64 @@
|
|||
import json
|
||||
import pytest
|
||||
import networkx as nx
|
||||
from networkx.readwrite.json_graph import jit_data, jit_graph
|
||||
|
||||
|
||||
class TestJIT:
|
||||
def test_jit(self):
|
||||
G = nx.Graph()
|
||||
G.add_node("Node1", node_data="foobar")
|
||||
G.add_node("Node3", node_data="bar")
|
||||
G.add_node("Node4")
|
||||
G.add_edge("Node1", "Node2", weight=9, something="isSomething")
|
||||
G.add_edge("Node2", "Node3", weight=4, something="isNotSomething")
|
||||
G.add_edge("Node1", "Node2")
|
||||
d = jit_data(G)
|
||||
K = jit_graph(json.loads(d))
|
||||
assert nx.is_isomorphic(G, K)
|
||||
|
||||
def test_jit_2(self):
|
||||
G = nx.Graph()
|
||||
G.add_node(1, node_data=3)
|
||||
G.add_node(3, node_data=0)
|
||||
G.add_edge(1, 2, weight=9, something=0)
|
||||
G.add_edge(2, 3, weight=4, something=3)
|
||||
G.add_edge(1, 2)
|
||||
d = jit_data(G)
|
||||
K = jit_graph(json.loads(d))
|
||||
assert nx.is_isomorphic(G, K)
|
||||
|
||||
def test_jit_directed(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_node(1, node_data=3)
|
||||
G.add_node(3, node_data=0)
|
||||
G.add_edge(1, 2, weight=9, something=0)
|
||||
G.add_edge(2, 3, weight=4, something=3)
|
||||
G.add_edge(1, 2)
|
||||
d = jit_data(G)
|
||||
K = jit_graph(json.loads(d), create_using=nx.DiGraph())
|
||||
assert nx.is_isomorphic(G, K)
|
||||
|
||||
def test_jit_multi_directed(self):
|
||||
G = nx.MultiDiGraph()
|
||||
G.add_node(1, node_data=3)
|
||||
G.add_node(3, node_data=0)
|
||||
G.add_edge(1, 2, weight=9, something=0)
|
||||
G.add_edge(2, 3, weight=4, something=3)
|
||||
G.add_edge(1, 2)
|
||||
pytest.raises(nx.NetworkXNotImplemented, jit_data, G)
|
||||
|
||||
H = nx.DiGraph(G)
|
||||
d = jit_data(H)
|
||||
K = jit_graph(json.loads(d), create_using=nx.MultiDiGraph())
|
||||
assert nx.is_isomorphic(H, K)
|
||||
K.add_edge(1, 2)
|
||||
assert not nx.is_isomorphic(H, K)
|
||||
assert nx.is_isomorphic(G, K)
|
||||
|
||||
def test_jit_round_trip(self):
|
||||
G = nx.Graph()
|
||||
d = nx.jit_data(G)
|
||||
H = jit_graph(json.loads(d))
|
||||
K = jit_graph(d)
|
||||
assert nx.is_isomorphic(H, K)
|
|
@ -0,0 +1,104 @@
|
|||
import json
|
||||
import pytest
|
||||
import networkx as nx
|
||||
from networkx.readwrite.json_graph import node_link_data, node_link_graph
|
||||
|
||||
|
||||
class TestNodeLink:
|
||||
def test_graph(self):
|
||||
G = nx.path_graph(4)
|
||||
H = node_link_graph(node_link_data(G))
|
||||
assert nx.is_isomorphic(G, H)
|
||||
|
||||
def test_graph_attributes(self):
|
||||
G = nx.path_graph(4)
|
||||
G.add_node(1, color="red")
|
||||
G.add_edge(1, 2, width=7)
|
||||
G.graph[1] = "one"
|
||||
G.graph["foo"] = "bar"
|
||||
|
||||
H = node_link_graph(node_link_data(G))
|
||||
assert H.graph["foo"] == "bar"
|
||||
assert H.nodes[1]["color"] == "red"
|
||||
assert H[1][2]["width"] == 7
|
||||
|
||||
d = json.dumps(node_link_data(G))
|
||||
H = node_link_graph(json.loads(d))
|
||||
assert H.graph["foo"] == "bar"
|
||||
assert H.graph["1"] == "one"
|
||||
assert H.nodes[1]["color"] == "red"
|
||||
assert H[1][2]["width"] == 7
|
||||
|
||||
def test_digraph(self):
|
||||
G = nx.DiGraph()
|
||||
H = node_link_graph(node_link_data(G))
|
||||
assert H.is_directed()
|
||||
|
||||
def test_multigraph(self):
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(1, 2, key="first")
|
||||
G.add_edge(1, 2, key="second", color="blue")
|
||||
H = node_link_graph(node_link_data(G))
|
||||
nx.is_isomorphic(G, H)
|
||||
assert H[1][2]["second"]["color"] == "blue"
|
||||
|
||||
def test_graph_with_tuple_nodes(self):
|
||||
G = nx.Graph()
|
||||
G.add_edge((0, 0), (1, 0), color=[255, 255, 0])
|
||||
d = node_link_data(G)
|
||||
dumped_d = json.dumps(d)
|
||||
dd = json.loads(dumped_d)
|
||||
H = node_link_graph(dd)
|
||||
assert H.nodes[(0, 0)] == G.nodes[(0, 0)]
|
||||
assert H[(0, 0)][(1, 0)]["color"] == [255, 255, 0]
|
||||
|
||||
def test_unicode_keys(self):
|
||||
q = "qualité"
|
||||
G = nx.Graph()
|
||||
G.add_node(1, **{q: q})
|
||||
s = node_link_data(G)
|
||||
output = json.dumps(s, ensure_ascii=False)
|
||||
data = json.loads(output)
|
||||
H = node_link_graph(data)
|
||||
assert H.nodes[1][q] == q
|
||||
|
||||
def test_exception(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.MultiDiGraph()
|
||||
attrs = dict(name="node", source="node", target="node", key="node")
|
||||
node_link_data(G, attrs)
|
||||
|
||||
def test_string_ids(self):
|
||||
q = "qualité"
|
||||
G = nx.DiGraph()
|
||||
G.add_node("A")
|
||||
G.add_node(q)
|
||||
G.add_edge("A", q)
|
||||
data = node_link_data(G)
|
||||
assert data["links"][0]["source"] == "A"
|
||||
assert data["links"][0]["target"] == q
|
||||
H = node_link_graph(data)
|
||||
assert nx.is_isomorphic(G, H)
|
||||
|
||||
def test_custom_attrs(self):
|
||||
G = nx.path_graph(4)
|
||||
G.add_node(1, color="red")
|
||||
G.add_edge(1, 2, width=7)
|
||||
G.graph[1] = "one"
|
||||
G.graph["foo"] = "bar"
|
||||
|
||||
attrs = dict(
|
||||
source="c_source",
|
||||
target="c_target",
|
||||
name="c_id",
|
||||
key="c_key",
|
||||
link="c_links",
|
||||
)
|
||||
|
||||
H = node_link_graph(
|
||||
node_link_data(G, attrs=attrs), multigraph=False, attrs=attrs
|
||||
)
|
||||
assert nx.is_isomorphic(G, H)
|
||||
assert H.graph["foo"] == "bar"
|
||||
assert H.nodes[1]["color"] == "red"
|
||||
assert H[1][2]["width"] == 7
|
|
@ -0,0 +1,35 @@
|
|||
import json
|
||||
import pytest
|
||||
import networkx as nx
|
||||
from networkx.readwrite.json_graph import tree_data, tree_graph
|
||||
|
||||
|
||||
class TestTree:
|
||||
def test_graph(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_nodes_from([1, 2, 3], color="red")
|
||||
G.add_edge(1, 2, foo=7)
|
||||
G.add_edge(1, 3, foo=10)
|
||||
G.add_edge(3, 4, foo=10)
|
||||
H = tree_graph(tree_data(G, 1))
|
||||
nx.is_isomorphic(G, H)
|
||||
|
||||
def test_graph_attributes(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_nodes_from([1, 2, 3], color="red")
|
||||
G.add_edge(1, 2, foo=7)
|
||||
G.add_edge(1, 3, foo=10)
|
||||
G.add_edge(3, 4, foo=10)
|
||||
H = tree_graph(tree_data(G, 1))
|
||||
assert H.nodes[1]["color"] == "red"
|
||||
|
||||
d = json.dumps(tree_data(G, 1))
|
||||
H = tree_graph(json.loads(d))
|
||||
assert H.nodes[1]["color"] == "red"
|
||||
|
||||
def test_exception(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.MultiDiGraph()
|
||||
G.add_node(0)
|
||||
attrs = dict(id="node", children="node")
|
||||
tree_data(G, 0, attrs)
|
146
venv/Lib/site-packages/networkx/readwrite/json_graph/tree.py
Normal file
146
venv/Lib/site-packages/networkx/readwrite/json_graph/tree.py
Normal file
|
@ -0,0 +1,146 @@
|
|||
from itertools import chain
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["tree_data", "tree_graph"]
|
||||
|
||||
_attrs = dict(id="id", children="children")
|
||||
|
||||
|
||||
def tree_data(G, root, attrs=_attrs):
|
||||
"""Returns data in tree format that is suitable for JSON serialization
|
||||
and use in Javascript documents.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
G must be an oriented tree
|
||||
|
||||
root : node
|
||||
The root of the tree
|
||||
|
||||
attrs : dict
|
||||
A dictionary that contains two keys 'id' and 'children'. The
|
||||
corresponding values provide the attribute names for storing
|
||||
NetworkX-internal graph data. The values should be unique. Default
|
||||
value: :samp:`dict(id='id', children='children')`.
|
||||
|
||||
If some user-defined graph data use these attribute names as data keys,
|
||||
they may be silently dropped.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data : dict
|
||||
A dictionary with node-link formatted data.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If values in attrs are not unique.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from networkx.readwrite import json_graph
|
||||
>>> G = nx.DiGraph([(1, 2)])
|
||||
>>> data = json_graph.tree_data(G, root=1)
|
||||
|
||||
To serialize with json
|
||||
|
||||
>>> import json
|
||||
>>> s = json.dumps(data)
|
||||
|
||||
Notes
|
||||
-----
|
||||
Node attributes are stored in this format but keys
|
||||
for attributes must be strings if you want to serialize with JSON.
|
||||
|
||||
Graph and edge attributes are not stored.
|
||||
|
||||
The default value of attrs will be changed in a future release of NetworkX.
|
||||
|
||||
See Also
|
||||
--------
|
||||
tree_graph, node_link_data, node_link_data
|
||||
"""
|
||||
if G.number_of_nodes() != G.number_of_edges() + 1:
|
||||
raise TypeError("G is not a tree.")
|
||||
if not G.is_directed():
|
||||
raise TypeError("G is not directed.")
|
||||
|
||||
id_ = attrs["id"]
|
||||
children = attrs["children"]
|
||||
if id_ == children:
|
||||
raise nx.NetworkXError("Attribute names are not unique.")
|
||||
|
||||
def add_children(n, G):
|
||||
nbrs = G[n]
|
||||
if len(nbrs) == 0:
|
||||
return []
|
||||
children_ = []
|
||||
for child in nbrs:
|
||||
d = dict(chain(G.nodes[child].items(), [(id_, child)]))
|
||||
c = add_children(child, G)
|
||||
if c:
|
||||
d[children] = c
|
||||
children_.append(d)
|
||||
return children_
|
||||
|
||||
data = dict(chain(G.nodes[root].items(), [(id_, root)]))
|
||||
data[children] = add_children(root, G)
|
||||
return data
|
||||
|
||||
|
||||
def tree_graph(data, attrs=_attrs):
|
||||
"""Returns graph from tree data format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : dict
|
||||
Tree formatted graph data
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX DiGraph
|
||||
|
||||
attrs : dict
|
||||
A dictionary that contains two keys 'id' and 'children'. The
|
||||
corresponding values provide the attribute names for storing
|
||||
NetworkX-internal graph data. The values should be unique. Default
|
||||
value: :samp:`dict(id='id', children='children')`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from networkx.readwrite import json_graph
|
||||
>>> G = nx.DiGraph([(1, 2)])
|
||||
>>> data = json_graph.tree_data(G, root=1)
|
||||
>>> H = json_graph.tree_graph(data)
|
||||
|
||||
Notes
|
||||
-----
|
||||
The default value of attrs will be changed in a future release of NetworkX.
|
||||
|
||||
See Also
|
||||
--------
|
||||
tree_graph, node_link_data, adjacency_data
|
||||
"""
|
||||
graph = nx.DiGraph()
|
||||
id_ = attrs["id"]
|
||||
children = attrs["children"]
|
||||
|
||||
def add_children(parent, children_):
|
||||
for data in children_:
|
||||
child = data[id_]
|
||||
graph.add_edge(parent, child)
|
||||
grandchildren = data.get(children, [])
|
||||
if grandchildren:
|
||||
add_children(child, grandchildren)
|
||||
nodedata = {
|
||||
str(k): v for k, v in data.items() if k != id_ and k != children
|
||||
}
|
||||
graph.add_node(child, **nodedata)
|
||||
|
||||
root = data[id_]
|
||||
children_ = data.get(children, [])
|
||||
nodedata = {str(k): v for k, v in data.items() if k != id_ and k != children}
|
||||
graph.add_node(root, **nodedata)
|
||||
add_children(root, children_)
|
||||
return graph
|
106
venv/Lib/site-packages/networkx/readwrite/leda.py
Normal file
106
venv/Lib/site-packages/networkx/readwrite/leda.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
"""
|
||||
Read graphs in LEDA format.
|
||||
|
||||
LEDA is a C++ class library for efficient data types and algorithms.
|
||||
|
||||
Format
|
||||
------
|
||||
See http://www.algorithmic-solutions.info/leda_guide/graphs/leda_native_graph_fileformat.html
|
||||
|
||||
"""
|
||||
# Original author: D. Eppstein, UC Irvine, August 12, 2003.
|
||||
# The original code at http://www.ics.uci.edu/~eppstein/PADS/ is public domain.
|
||||
|
||||
__all__ = ["read_leda", "parse_leda"]
|
||||
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError
|
||||
from networkx.utils import open_file
|
||||
|
||||
|
||||
@open_file(0, mode="rb")
|
||||
def read_leda(path, encoding="UTF-8"):
|
||||
"""Read graph in LEDA format from path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File or filename to read. Filenames ending in .gz or .bz2 will be
|
||||
uncompressed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX graph
|
||||
|
||||
Examples
|
||||
--------
|
||||
G=nx.read_leda('file.leda')
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.algorithmic-solutions.info/leda_guide/graphs/leda_native_graph_fileformat.html
|
||||
"""
|
||||
lines = (line.decode(encoding) for line in path)
|
||||
G = parse_leda(lines)
|
||||
return G
|
||||
|
||||
|
||||
def parse_leda(lines):
|
||||
"""Read graph in LEDA format from string or iterable.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lines : string or iterable
|
||||
Data in LEDA format.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX graph
|
||||
|
||||
Examples
|
||||
--------
|
||||
G=nx.parse_leda(string)
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.algorithmic-solutions.info/leda_guide/graphs/leda_native_graph_fileformat.html
|
||||
"""
|
||||
if isinstance(lines, str):
|
||||
lines = iter(lines.split("\n"))
|
||||
lines = iter(
|
||||
[
|
||||
line.rstrip("\n")
|
||||
for line in lines
|
||||
if not (line.startswith("#") or line.startswith("\n") or line == "")
|
||||
]
|
||||
)
|
||||
for i in range(3):
|
||||
next(lines)
|
||||
# Graph
|
||||
du = int(next(lines)) # -1=directed, -2=undirected
|
||||
if du == -1:
|
||||
G = nx.DiGraph()
|
||||
else:
|
||||
G = nx.Graph()
|
||||
|
||||
# Nodes
|
||||
n = int(next(lines)) # number of nodes
|
||||
node = {}
|
||||
for i in range(1, n + 1): # LEDA counts from 1 to n
|
||||
symbol = next(lines).rstrip().strip("|{}| ")
|
||||
if symbol == "":
|
||||
symbol = str(i) # use int if no label - could be trouble
|
||||
node[i] = symbol
|
||||
|
||||
G.add_nodes_from([s for i, s in node.items()])
|
||||
|
||||
# Edges
|
||||
m = int(next(lines)) # number of edges
|
||||
for i in range(m):
|
||||
try:
|
||||
s, t, reversal, label = next(lines).split()
|
||||
except BaseException as e:
|
||||
raise NetworkXError(f"Too few fields in LEDA.GRAPH edge {i+1}") from e
|
||||
# BEWARE: no handling of reversal edges
|
||||
G.add_edge(node[int(s)], node[int(t)], label=label[2:-2])
|
||||
return G
|
384
venv/Lib/site-packages/networkx/readwrite/multiline_adjlist.py
Normal file
384
venv/Lib/site-packages/networkx/readwrite/multiline_adjlist.py
Normal file
|
@ -0,0 +1,384 @@
|
|||
"""
|
||||
*************************
|
||||
Multi-line Adjacency List
|
||||
*************************
|
||||
Read and write NetworkX graphs as multi-line adjacency lists.
|
||||
|
||||
The multi-line adjacency list format is useful for graphs with
|
||||
nodes that can be meaningfully represented as strings. With this format
|
||||
simple edge data can be stored but node or graph data is not.
|
||||
|
||||
Format
|
||||
------
|
||||
The first label in a line is the source node label followed by the node degree
|
||||
d. The next d lines are target node labels and optional edge data.
|
||||
That pattern repeats for all nodes in the graph.
|
||||
|
||||
The graph with edges a-b, a-c, d-e can be represented as the following
|
||||
adjacency list (anything following the # in a line is a comment)::
|
||||
|
||||
# example.multiline-adjlist
|
||||
a 2
|
||||
b
|
||||
c
|
||||
d 1
|
||||
e
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
"generate_multiline_adjlist",
|
||||
"write_multiline_adjlist",
|
||||
"parse_multiline_adjlist",
|
||||
"read_multiline_adjlist",
|
||||
]
|
||||
|
||||
from networkx.utils import open_file
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def generate_multiline_adjlist(G, delimiter=" "):
|
||||
"""Generate a single line of the graph G in multiline adjacency list format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
|
||||
delimiter : string, optional
|
||||
Separator for node labels
|
||||
|
||||
Returns
|
||||
-------
|
||||
lines : string
|
||||
Lines of data in multiline adjlist format.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.lollipop_graph(4, 3)
|
||||
>>> for line in nx.generate_multiline_adjlist(G):
|
||||
... print(line)
|
||||
0 3
|
||||
1 {}
|
||||
2 {}
|
||||
3 {}
|
||||
1 2
|
||||
2 {}
|
||||
3 {}
|
||||
2 1
|
||||
3 {}
|
||||
3 1
|
||||
4 {}
|
||||
4 1
|
||||
5 {}
|
||||
5 1
|
||||
6 {}
|
||||
6 0
|
||||
|
||||
See Also
|
||||
--------
|
||||
write_multiline_adjlist, read_multiline_adjlist
|
||||
"""
|
||||
if G.is_directed():
|
||||
if G.is_multigraph():
|
||||
for s, nbrs in G.adjacency():
|
||||
nbr_edges = [
|
||||
(u, data)
|
||||
for u, datadict in nbrs.items()
|
||||
for key, data in datadict.items()
|
||||
]
|
||||
deg = len(nbr_edges)
|
||||
yield str(s) + delimiter + str(deg)
|
||||
for u, d in nbr_edges:
|
||||
if d is None:
|
||||
yield str(u)
|
||||
else:
|
||||
yield str(u) + delimiter + str(d)
|
||||
else: # directed single edges
|
||||
for s, nbrs in G.adjacency():
|
||||
deg = len(nbrs)
|
||||
yield str(s) + delimiter + str(deg)
|
||||
for u, d in nbrs.items():
|
||||
if d is None:
|
||||
yield str(u)
|
||||
else:
|
||||
yield str(u) + delimiter + str(d)
|
||||
else: # undirected
|
||||
if G.is_multigraph():
|
||||
seen = set() # helper dict used to avoid duplicate edges
|
||||
for s, nbrs in G.adjacency():
|
||||
nbr_edges = [
|
||||
(u, data)
|
||||
for u, datadict in nbrs.items()
|
||||
if u not in seen
|
||||
for key, data in datadict.items()
|
||||
]
|
||||
deg = len(nbr_edges)
|
||||
yield str(s) + delimiter + str(deg)
|
||||
for u, d in nbr_edges:
|
||||
if d is None:
|
||||
yield str(u)
|
||||
else:
|
||||
yield str(u) + delimiter + str(d)
|
||||
seen.add(s)
|
||||
else: # undirected single edges
|
||||
seen = set() # helper dict used to avoid duplicate edges
|
||||
for s, nbrs in G.adjacency():
|
||||
nbr_edges = [(u, d) for u, d in nbrs.items() if u not in seen]
|
||||
deg = len(nbr_edges)
|
||||
yield str(s) + delimiter + str(deg)
|
||||
for u, d in nbr_edges:
|
||||
if d is None:
|
||||
yield str(u)
|
||||
else:
|
||||
yield str(u) + delimiter + str(d)
|
||||
seen.add(s)
|
||||
|
||||
|
||||
@open_file(1, mode="wb")
|
||||
def write_multiline_adjlist(G, path, delimiter=" ", comments="#", encoding="utf-8"):
|
||||
""" Write the graph G in multiline adjacency list format to path
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
|
||||
comments : string, optional
|
||||
Marker for comment lines
|
||||
|
||||
delimiter : string, optional
|
||||
Separator for node labels
|
||||
|
||||
encoding : string, optional
|
||||
Text encoding.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_multiline_adjlist(G, "test.adjlist")
|
||||
|
||||
The path can be a file handle or a string with the name of the file. If a
|
||||
file handle is provided, it has to be opened in 'wb' mode.
|
||||
|
||||
>>> fh = open("test.adjlist", "wb")
|
||||
>>> nx.write_multiline_adjlist(G, fh)
|
||||
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
|
||||
>>> nx.write_multiline_adjlist(G, "test.adjlist.gz")
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_multiline_adjlist
|
||||
"""
|
||||
import sys
|
||||
import time
|
||||
|
||||
pargs = comments + " ".join(sys.argv)
|
||||
header = (
|
||||
f"{pargs}\n"
|
||||
+ comments
|
||||
+ f" GMT {time.asctime(time.gmtime())}\n"
|
||||
+ comments
|
||||
+ f" {G.name}\n"
|
||||
)
|
||||
path.write(header.encode(encoding))
|
||||
|
||||
for multiline in generate_multiline_adjlist(G, delimiter):
|
||||
multiline += "\n"
|
||||
path.write(multiline.encode(encoding))
|
||||
|
||||
|
||||
def parse_multiline_adjlist(
|
||||
lines, comments="#", delimiter=None, create_using=None, nodetype=None, edgetype=None
|
||||
):
|
||||
"""Parse lines of a multiline adjacency list representation of a graph.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lines : list or iterator of strings
|
||||
Input data in multiline adjlist format
|
||||
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
nodetype : Python type, optional
|
||||
Convert nodes to this type.
|
||||
|
||||
comments : string, optional
|
||||
Marker for comment lines
|
||||
|
||||
delimiter : string, optional
|
||||
Separator for node labels. The default is whitespace.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G: NetworkX graph
|
||||
The graph corresponding to the lines in multiline adjacency list format.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> lines = [
|
||||
... "1 2",
|
||||
... "2 {'weight':3, 'name': 'Frodo'}",
|
||||
... "3 {}",
|
||||
... "2 1",
|
||||
... "5 {'weight':6, 'name': 'Saruman'}",
|
||||
... ]
|
||||
>>> G = nx.parse_multiline_adjlist(iter(lines), nodetype=int)
|
||||
>>> list(G)
|
||||
[1, 2, 3, 5]
|
||||
|
||||
"""
|
||||
from ast import literal_eval
|
||||
|
||||
G = nx.empty_graph(0, create_using)
|
||||
for line in lines:
|
||||
p = line.find(comments)
|
||||
if p >= 0:
|
||||
line = line[:p]
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
(u, deg) = line.strip().split(delimiter)
|
||||
deg = int(deg)
|
||||
except BaseException as e:
|
||||
raise TypeError(f"Failed to read node and degree on line ({line})") from e
|
||||
if nodetype is not None:
|
||||
try:
|
||||
u = nodetype(u)
|
||||
except BaseException as e:
|
||||
raise TypeError(
|
||||
f"Failed to convert node ({u}) to " f"type {nodetype}"
|
||||
) from e
|
||||
G.add_node(u)
|
||||
for i in range(deg):
|
||||
while True:
|
||||
try:
|
||||
line = next(lines)
|
||||
except StopIteration as e:
|
||||
msg = f"Failed to find neighbor for node ({u})"
|
||||
raise TypeError(msg) from e
|
||||
p = line.find(comments)
|
||||
if p >= 0:
|
||||
line = line[:p]
|
||||
if line:
|
||||
break
|
||||
vlist = line.strip().split(delimiter)
|
||||
numb = len(vlist)
|
||||
if numb < 1:
|
||||
continue # isolated node
|
||||
v = vlist.pop(0)
|
||||
data = "".join(vlist)
|
||||
if nodetype is not None:
|
||||
try:
|
||||
v = nodetype(v)
|
||||
except BaseException as e:
|
||||
raise TypeError(
|
||||
f"Failed to convert node ({v}) " f"to type {nodetype}"
|
||||
) from e
|
||||
if edgetype is not None:
|
||||
try:
|
||||
edgedata = {"weight": edgetype(data)}
|
||||
except BaseException as e:
|
||||
raise TypeError(
|
||||
f"Failed to convert edge data ({data}) " f"to type {edgetype}"
|
||||
) from e
|
||||
else:
|
||||
try: # try to evaluate
|
||||
edgedata = literal_eval(data)
|
||||
except:
|
||||
edgedata = {}
|
||||
G.add_edge(u, v, **edgedata)
|
||||
|
||||
return G
|
||||
|
||||
|
||||
@open_file(0, mode="rb")
|
||||
def read_multiline_adjlist(
|
||||
path,
|
||||
comments="#",
|
||||
delimiter=None,
|
||||
create_using=None,
|
||||
nodetype=None,
|
||||
edgetype=None,
|
||||
encoding="utf-8",
|
||||
):
|
||||
"""Read graph in multi-line adjacency list format from path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : string or file
|
||||
Filename or file handle to read.
|
||||
Filenames ending in .gz or .bz2 will be uncompressed.
|
||||
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
nodetype : Python type, optional
|
||||
Convert nodes to this type.
|
||||
|
||||
edgetype : Python type, optional
|
||||
Convert edge data to this type.
|
||||
|
||||
comments : string, optional
|
||||
Marker for comment lines
|
||||
|
||||
delimiter : string, optional
|
||||
Separator for node labels. The default is whitespace.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G: NetworkX graph
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_multiline_adjlist(G, "test.adjlist")
|
||||
>>> G = nx.read_multiline_adjlist("test.adjlist")
|
||||
|
||||
The path can be a file or a string with the name of the file. If a
|
||||
file s provided, it has to be opened in 'rb' mode.
|
||||
|
||||
>>> fh = open("test.adjlist", "rb")
|
||||
>>> G = nx.read_multiline_adjlist(fh)
|
||||
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
|
||||
>>> nx.write_multiline_adjlist(G, "test.adjlist.gz")
|
||||
>>> G = nx.read_multiline_adjlist("test.adjlist.gz")
|
||||
|
||||
The optional nodetype is a function to convert node strings to nodetype.
|
||||
|
||||
For example
|
||||
|
||||
>>> G = nx.read_multiline_adjlist("test.adjlist", nodetype=int)
|
||||
|
||||
will attempt to convert all nodes to integer type.
|
||||
|
||||
The optional edgetype is a function to convert edge data strings to
|
||||
edgetype.
|
||||
|
||||
>>> G = nx.read_multiline_adjlist("test.adjlist")
|
||||
|
||||
The optional create_using parameter is a NetworkX graph container.
|
||||
The default is Graph(), an undirected graph. To read the data as
|
||||
a directed graph use
|
||||
|
||||
>>> G = nx.read_multiline_adjlist("test.adjlist", create_using=nx.DiGraph)
|
||||
|
||||
Notes
|
||||
-----
|
||||
This format does not store graph, node, or edge data.
|
||||
|
||||
See Also
|
||||
--------
|
||||
write_multiline_adjlist
|
||||
"""
|
||||
lines = (line.decode(encoding) for line in path)
|
||||
return parse_multiline_adjlist(
|
||||
lines,
|
||||
comments=comments,
|
||||
delimiter=delimiter,
|
||||
create_using=create_using,
|
||||
nodetype=nodetype,
|
||||
edgetype=edgetype,
|
||||
)
|
316
venv/Lib/site-packages/networkx/readwrite/nx_shp.py
Normal file
316
venv/Lib/site-packages/networkx/readwrite/nx_shp.py
Normal file
|
@ -0,0 +1,316 @@
|
|||
"""
|
||||
*********
|
||||
Shapefile
|
||||
*********
|
||||
|
||||
Generates a networkx.DiGraph from point and line shapefiles.
|
||||
|
||||
"The Esri Shapefile or simply a shapefile is a popular geospatial vector
|
||||
data format for geographic information systems software. It is developed
|
||||
and regulated by Esri as a (mostly) open specification for data
|
||||
interoperability among Esri and other software products."
|
||||
See https://en.wikipedia.org/wiki/Shapefile for additional information.
|
||||
"""
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["read_shp", "write_shp"]
|
||||
|
||||
|
||||
def read_shp(path, simplify=True, geom_attrs=True, strict=True):
|
||||
"""Generates a networkx.DiGraph from shapefiles. Point geometries are
|
||||
translated into nodes, lines into edges. Coordinate tuples are used as
|
||||
keys. Attributes are preserved, line geometries are simplified into start
|
||||
and end coordinates. Accepts a single shapefile or directory of many
|
||||
shapefiles.
|
||||
|
||||
"The Esri Shapefile or simply a shapefile is a popular geospatial vector
|
||||
data format for geographic information systems software [1]_."
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File, directory, or filename to read.
|
||||
|
||||
simplify: bool
|
||||
If True, simplify line geometries to start and end coordinates.
|
||||
If False, and line feature geometry has multiple segments, the
|
||||
non-geometric attributes for that feature will be repeated for each
|
||||
edge comprising that feature.
|
||||
|
||||
geom_attrs: bool
|
||||
If True, include the Wkb, Wkt and Json geometry attributes with
|
||||
each edge.
|
||||
|
||||
NOTE: if these attributes are available, write_shp will use them
|
||||
to write the geometry. If nodes store the underlying coordinates for
|
||||
the edge geometry as well (as they do when they are read via
|
||||
this method) and they change, your geomety will be out of sync.
|
||||
|
||||
strict: bool
|
||||
If True, raise NetworkXError when feature geometry is missing or
|
||||
GeometryType is not supported.
|
||||
If False, silently ignore missing or unsupported geometry in features.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX graph
|
||||
|
||||
Raises
|
||||
------
|
||||
ImportError
|
||||
If ogr module is not available.
|
||||
|
||||
RuntimeError
|
||||
If file cannot be open or read.
|
||||
|
||||
NetworkXError
|
||||
If strict=True and feature is missing geometry or GeometryType is
|
||||
not supported.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.read_shp("test.shp") # doctest: +SKIP
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Shapefile
|
||||
"""
|
||||
try:
|
||||
from osgeo import ogr
|
||||
except ImportError as e:
|
||||
raise ImportError("read_shp requires OGR: http://www.gdal.org/") from e
|
||||
|
||||
if not isinstance(path, str):
|
||||
return
|
||||
|
||||
net = nx.DiGraph()
|
||||
shp = ogr.Open(path)
|
||||
if shp is None:
|
||||
raise RuntimeError(f"Unable to open {path}")
|
||||
for lyr in shp:
|
||||
fields = [x.GetName() for x in lyr.schema]
|
||||
for f in lyr:
|
||||
g = f.geometry()
|
||||
if g is None:
|
||||
if strict:
|
||||
raise nx.NetworkXError("Bad data: feature missing geometry")
|
||||
else:
|
||||
continue
|
||||
flddata = [f.GetField(f.GetFieldIndex(x)) for x in fields]
|
||||
attributes = dict(zip(fields, flddata))
|
||||
attributes["ShpName"] = lyr.GetName()
|
||||
# Note: Using layer level geometry type
|
||||
if g.GetGeometryType() == ogr.wkbPoint:
|
||||
net.add_node((g.GetPoint_2D(0)), **attributes)
|
||||
elif g.GetGeometryType() in (ogr.wkbLineString, ogr.wkbMultiLineString):
|
||||
for edge in edges_from_line(g, attributes, simplify, geom_attrs):
|
||||
e1, e2, attr = edge
|
||||
net.add_edge(e1, e2)
|
||||
net[e1][e2].update(attr)
|
||||
else:
|
||||
if strict:
|
||||
raise nx.NetworkXError(
|
||||
"GeometryType {} not supported".format(g.GetGeometryType())
|
||||
)
|
||||
|
||||
return net
|
||||
|
||||
|
||||
def edges_from_line(geom, attrs, simplify=True, geom_attrs=True):
|
||||
"""
|
||||
Generate edges for each line in geom
|
||||
Written as a helper for read_shp
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
geom: ogr line geometry
|
||||
To be converted into an edge or edges
|
||||
|
||||
attrs: dict
|
||||
Attributes to be associated with all geoms
|
||||
|
||||
simplify: bool
|
||||
If True, simplify the line as in read_shp
|
||||
|
||||
geom_attrs: bool
|
||||
If True, add geom attributes to edge as in read_shp
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
edges: generator of edges
|
||||
each edge is a tuple of form
|
||||
(node1_coord, node2_coord, attribute_dict)
|
||||
suitable for expanding into a networkx Graph add_edge call
|
||||
"""
|
||||
try:
|
||||
from osgeo import ogr
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"edges_from_line requires OGR: " "http://www.gdal.org/"
|
||||
) from e
|
||||
|
||||
if geom.GetGeometryType() == ogr.wkbLineString:
|
||||
if simplify:
|
||||
edge_attrs = attrs.copy()
|
||||
last = geom.GetPointCount() - 1
|
||||
if geom_attrs:
|
||||
edge_attrs["Wkb"] = geom.ExportToWkb()
|
||||
edge_attrs["Wkt"] = geom.ExportToWkt()
|
||||
edge_attrs["Json"] = geom.ExportToJson()
|
||||
yield (geom.GetPoint_2D(0), geom.GetPoint_2D(last), edge_attrs)
|
||||
else:
|
||||
for i in range(0, geom.GetPointCount() - 1):
|
||||
pt1 = geom.GetPoint_2D(i)
|
||||
pt2 = geom.GetPoint_2D(i + 1)
|
||||
edge_attrs = attrs.copy()
|
||||
if geom_attrs:
|
||||
segment = ogr.Geometry(ogr.wkbLineString)
|
||||
segment.AddPoint_2D(pt1[0], pt1[1])
|
||||
segment.AddPoint_2D(pt2[0], pt2[1])
|
||||
edge_attrs["Wkb"] = segment.ExportToWkb()
|
||||
edge_attrs["Wkt"] = segment.ExportToWkt()
|
||||
edge_attrs["Json"] = segment.ExportToJson()
|
||||
del segment
|
||||
yield (pt1, pt2, edge_attrs)
|
||||
|
||||
elif geom.GetGeometryType() == ogr.wkbMultiLineString:
|
||||
for i in range(geom.GetGeometryCount()):
|
||||
geom_i = geom.GetGeometryRef(i)
|
||||
yield from edges_from_line(geom_i, attrs, simplify, geom_attrs)
|
||||
|
||||
|
||||
def write_shp(G, outdir):
|
||||
"""Writes a networkx.DiGraph to two shapefiles, edges and nodes.
|
||||
Nodes and edges are expected to have a Well Known Binary (Wkb) or
|
||||
Well Known Text (Wkt) key in order to generate geometries. Also
|
||||
acceptable are nodes with a numeric tuple key (x,y).
|
||||
|
||||
"The Esri Shapefile or simply a shapefile is a popular geospatial vector
|
||||
data format for geographic information systems software [1]_."
|
||||
|
||||
Parameters
|
||||
----------
|
||||
outdir : directory path
|
||||
Output directory for the two shapefiles.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
Examples
|
||||
--------
|
||||
nx.write_shp(digraph, '/shapefiles') # doctest +SKIP
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Shapefile
|
||||
"""
|
||||
try:
|
||||
from osgeo import ogr
|
||||
except ImportError as e:
|
||||
raise ImportError("write_shp requires OGR: http://www.gdal.org/") from e
|
||||
# easier to debug in python if ogr throws exceptions
|
||||
ogr.UseExceptions()
|
||||
|
||||
def netgeometry(key, data):
|
||||
if "Wkb" in data:
|
||||
geom = ogr.CreateGeometryFromWkb(data["Wkb"])
|
||||
elif "Wkt" in data:
|
||||
geom = ogr.CreateGeometryFromWkt(data["Wkt"])
|
||||
elif type(key[0]).__name__ == "tuple": # edge keys are packed tuples
|
||||
geom = ogr.Geometry(ogr.wkbLineString)
|
||||
_from, _to = key[0], key[1]
|
||||
try:
|
||||
geom.SetPoint(0, *_from)
|
||||
geom.SetPoint(1, *_to)
|
||||
except TypeError:
|
||||
# assume user used tuple of int and choked ogr
|
||||
_ffrom = [float(x) for x in _from]
|
||||
_fto = [float(x) for x in _to]
|
||||
geom.SetPoint(0, *_ffrom)
|
||||
geom.SetPoint(1, *_fto)
|
||||
else:
|
||||
geom = ogr.Geometry(ogr.wkbPoint)
|
||||
try:
|
||||
geom.SetPoint(0, *key)
|
||||
except TypeError:
|
||||
# assume user used tuple of int and choked ogr
|
||||
fkey = [float(x) for x in key]
|
||||
geom.SetPoint(0, *fkey)
|
||||
|
||||
return geom
|
||||
|
||||
# Create_feature with new optional attributes arg (should be dict type)
|
||||
def create_feature(geometry, lyr, attributes=None):
|
||||
feature = ogr.Feature(lyr.GetLayerDefn())
|
||||
feature.SetGeometry(g)
|
||||
if attributes is not None:
|
||||
# Loop through attributes, assigning data to each field
|
||||
for field, data in attributes.items():
|
||||
feature.SetField(field, data)
|
||||
lyr.CreateFeature(feature)
|
||||
feature.Destroy()
|
||||
|
||||
# Conversion dict between python and ogr types
|
||||
OGRTypes = {int: ogr.OFTInteger, str: ogr.OFTString, float: ogr.OFTReal}
|
||||
|
||||
# Check/add fields from attribute data to Shapefile layers
|
||||
def add_fields_to_layer(key, value, fields, layer):
|
||||
# Field not in previous edges so add to dict
|
||||
if type(value) in OGRTypes:
|
||||
fields[key] = OGRTypes[type(value)]
|
||||
else:
|
||||
# Data type not supported, default to string (char 80)
|
||||
fields[key] = ogr.OFTString
|
||||
# Create the new field
|
||||
newfield = ogr.FieldDefn(key, fields[key])
|
||||
layer.CreateField(newfield)
|
||||
|
||||
drv = ogr.GetDriverByName("ESRI Shapefile")
|
||||
shpdir = drv.CreateDataSource(outdir)
|
||||
# delete pre-existing output first otherwise ogr chokes
|
||||
try:
|
||||
shpdir.DeleteLayer("nodes")
|
||||
except:
|
||||
pass
|
||||
nodes = shpdir.CreateLayer("nodes", None, ogr.wkbPoint)
|
||||
|
||||
# Storage for node field names and their data types
|
||||
node_fields = {}
|
||||
|
||||
def create_attributes(data, fields, layer):
|
||||
attributes = {} # storage for attribute data (indexed by field names)
|
||||
for key, value in data.items():
|
||||
# Reject spatial data not required for attribute table
|
||||
if key != "Json" and key != "Wkt" and key != "Wkb" and key != "ShpName":
|
||||
# Check/add field and data type to fields dict
|
||||
if key not in fields:
|
||||
add_fields_to_layer(key, value, fields, layer)
|
||||
# Store the data from new field to dict for CreateLayer()
|
||||
attributes[key] = value
|
||||
return attributes, layer
|
||||
|
||||
for n in G:
|
||||
data = G.nodes[n]
|
||||
g = netgeometry(n, data)
|
||||
attributes, nodes = create_attributes(data, node_fields, nodes)
|
||||
create_feature(g, nodes, attributes)
|
||||
|
||||
try:
|
||||
shpdir.DeleteLayer("edges")
|
||||
except:
|
||||
pass
|
||||
edges = shpdir.CreateLayer("edges", None, ogr.wkbLineString)
|
||||
|
||||
# New edge attribute write support merged into edge loop
|
||||
edge_fields = {} # storage for field names and their data types
|
||||
|
||||
for e in G.edges(data=True):
|
||||
data = G.get_edge_data(*e)
|
||||
g = netgeometry(e, data)
|
||||
attributes, edges = create_attributes(e[2], edge_fields, edges)
|
||||
create_feature(g, edges, attributes)
|
||||
|
||||
nodes, edges = None, None
|
92
venv/Lib/site-packages/networkx/readwrite/nx_yaml.py
Normal file
92
venv/Lib/site-packages/networkx/readwrite/nx_yaml.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
"""
|
||||
****
|
||||
YAML
|
||||
****
|
||||
Read and write NetworkX graphs in YAML format.
|
||||
|
||||
"YAML is a data serialization format designed for human readability
|
||||
and interaction with scripting languages."
|
||||
See http://www.yaml.org for documentation.
|
||||
|
||||
Format
|
||||
------
|
||||
http://pyyaml.org/wiki/PyYAML
|
||||
|
||||
"""
|
||||
|
||||
__all__ = ["read_yaml", "write_yaml"]
|
||||
|
||||
from networkx.utils import open_file
|
||||
|
||||
|
||||
@open_file(1, mode="w")
|
||||
def write_yaml(G_to_be_yaml, path_for_yaml_output, **kwds):
|
||||
"""Write graph G in YAML format to path.
|
||||
|
||||
YAML is a data serialization format designed for human readability
|
||||
and interaction with scripting languages [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
path : file or string
|
||||
File or filename to write.
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
|
||||
Notes
|
||||
-----
|
||||
To use encoding on the output file include e.g. `encoding='utf-8'`
|
||||
in the keyword arguments.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_yaml(G, "test.yaml")
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.yaml.org
|
||||
"""
|
||||
try:
|
||||
import yaml
|
||||
except ImportError as e:
|
||||
raise ImportError("write_yaml() requires PyYAML: http://pyyaml.org/") from e
|
||||
yaml.dump(G_to_be_yaml, path_for_yaml_output, **kwds)
|
||||
|
||||
|
||||
@open_file(0, mode="r")
|
||||
def read_yaml(path):
|
||||
"""Read graph in YAML format from path.
|
||||
|
||||
YAML is a data serialization format designed for human readability
|
||||
and interaction with scripting languages [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File or filename to read. Filenames ending in .gz or .bz2
|
||||
will be uncompressed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX graph
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_yaml(G, "test.yaml")
|
||||
>>> G = nx.read_yaml("test.yaml")
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.yaml.org
|
||||
|
||||
"""
|
||||
try:
|
||||
import yaml
|
||||
except ImportError as e:
|
||||
raise ImportError("read_yaml() requires PyYAML: http://pyyaml.org/") from e
|
||||
|
||||
G = yaml.load(path, Loader=yaml.FullLoader)
|
||||
return G
|
102
venv/Lib/site-packages/networkx/readwrite/p2g.py
Normal file
102
venv/Lib/site-packages/networkx/readwrite/p2g.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
"""
|
||||
This module provides the following: read and write of p2g format
|
||||
used in metabolic pathway studies.
|
||||
|
||||
See https://web.archive.org/web/20080626113807/http://www.cs.purdue.edu/homes/koyuturk/pathway/ for a description.
|
||||
|
||||
The summary is included here:
|
||||
|
||||
A file that describes a uniquely labeled graph (with extension ".gr")
|
||||
format looks like the following:
|
||||
|
||||
|
||||
name
|
||||
3 4
|
||||
a
|
||||
1 2
|
||||
b
|
||||
|
||||
c
|
||||
0 2
|
||||
|
||||
"name" is simply a description of what the graph corresponds to. The
|
||||
second line displays the number of nodes and number of edges,
|
||||
respectively. This sample graph contains three nodes labeled "a", "b",
|
||||
and "c". The rest of the graph contains two lines for each node. The
|
||||
first line for a node contains the node label. After the declaration
|
||||
of the node label, the out-edges of that node in the graph are
|
||||
provided. For instance, "a" is linked to nodes 1 and 2, which are
|
||||
labeled "b" and "c", while the node labeled "b" has no outgoing
|
||||
edges. Observe that node labeled "c" has an outgoing edge to
|
||||
itself. Indeed, self-loops are allowed. Node index starts from 0.
|
||||
|
||||
"""
|
||||
import networkx
|
||||
from networkx.utils import open_file
|
||||
|
||||
|
||||
@open_file(1, mode="w")
|
||||
def write_p2g(G, path, encoding="utf-8"):
|
||||
"""Write NetworkX graph in p2g format.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This format is meant to be used with directed graphs with
|
||||
possible self loops.
|
||||
"""
|
||||
path.write((f"{G.name}\n").encode(encoding))
|
||||
path.write((f"{G.order()} {G.size()}\n").encode(encoding))
|
||||
nodes = list(G)
|
||||
# make dictionary mapping nodes to integers
|
||||
nodenumber = dict(zip(nodes, range(len(nodes))))
|
||||
for n in nodes:
|
||||
path.write((f"{n}\n").encode(encoding))
|
||||
for nbr in G.neighbors(n):
|
||||
path.write((f"{nodenumber[nbr]} ").encode(encoding))
|
||||
path.write("\n".encode(encoding))
|
||||
|
||||
|
||||
@open_file(0, mode="r")
|
||||
def read_p2g(path, encoding="utf-8"):
|
||||
"""Read graph in p2g format from path.
|
||||
|
||||
Returns
|
||||
-------
|
||||
MultiDiGraph
|
||||
|
||||
Notes
|
||||
-----
|
||||
If you want a DiGraph (with no self loops allowed and no edge data)
|
||||
use D=networkx.DiGraph(read_p2g(path))
|
||||
"""
|
||||
lines = (line.decode(encoding) for line in path)
|
||||
G = parse_p2g(lines)
|
||||
return G
|
||||
|
||||
|
||||
def parse_p2g(lines):
|
||||
"""Parse p2g format graph from string or iterable.
|
||||
|
||||
Returns
|
||||
-------
|
||||
MultiDiGraph
|
||||
"""
|
||||
description = next(lines).strip()
|
||||
# are multiedges (parallel edges) allowed?
|
||||
G = networkx.MultiDiGraph(name=description, selfloops=True)
|
||||
nnodes, nedges = map(int, next(lines).split())
|
||||
nodelabel = {}
|
||||
nbrs = {}
|
||||
# loop over the nodes keeping track of node labels and out neighbors
|
||||
# defer adding edges until all node labels are known
|
||||
for i in range(nnodes):
|
||||
n = next(lines).strip()
|
||||
nodelabel[i] = n
|
||||
G.add_node(n)
|
||||
nbrs[n] = map(int, next(lines).split())
|
||||
# now we know all of the node labels so we can add the edges
|
||||
# with the correct labels
|
||||
for n in G:
|
||||
for nbr in nbrs[n]:
|
||||
G.add_edge(n, nodelabel[nbr])
|
||||
return G
|
284
venv/Lib/site-packages/networkx/readwrite/pajek.py
Normal file
284
venv/Lib/site-packages/networkx/readwrite/pajek.py
Normal file
|
@ -0,0 +1,284 @@
|
|||
"""
|
||||
*****
|
||||
Pajek
|
||||
*****
|
||||
Read graphs in Pajek format.
|
||||
|
||||
This implementation handles directed and undirected graphs including
|
||||
those with self loops and parallel edges.
|
||||
|
||||
Format
|
||||
------
|
||||
See http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/draweps.htm
|
||||
for format information.
|
||||
|
||||
"""
|
||||
|
||||
import warnings
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import open_file
|
||||
|
||||
__all__ = ["read_pajek", "parse_pajek", "generate_pajek", "write_pajek"]
|
||||
|
||||
|
||||
def generate_pajek(G):
|
||||
"""Generate lines in Pajek graph format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A Networkx graph
|
||||
|
||||
References
|
||||
----------
|
||||
See http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/draweps.htm
|
||||
for format information.
|
||||
"""
|
||||
if G.name == "":
|
||||
name = "NetworkX"
|
||||
else:
|
||||
name = G.name
|
||||
# Apparently many Pajek format readers can't process this line
|
||||
# So we'll leave it out for now.
|
||||
# yield '*network %s'%name
|
||||
|
||||
# write nodes with attributes
|
||||
yield f"*vertices {G.order()}"
|
||||
nodes = list(G)
|
||||
# make dictionary mapping nodes to integers
|
||||
nodenumber = dict(zip(nodes, range(1, len(nodes) + 1)))
|
||||
for n in nodes:
|
||||
# copy node attributes and pop mandatory attributes
|
||||
# to avoid duplication.
|
||||
na = G.nodes.get(n, {}).copy()
|
||||
x = na.pop("x", 0.0)
|
||||
y = na.pop("y", 0.0)
|
||||
try:
|
||||
id = int(na.pop("id", nodenumber[n]))
|
||||
except ValueError as e:
|
||||
e.args += (
|
||||
(
|
||||
"Pajek format requires 'id' to be an int()."
|
||||
" Refer to the 'Relabeling nodes' section."
|
||||
),
|
||||
)
|
||||
raise
|
||||
nodenumber[n] = id
|
||||
shape = na.pop("shape", "ellipse")
|
||||
s = " ".join(map(make_qstr, (id, n, x, y, shape)))
|
||||
# only optional attributes are left in na.
|
||||
for k, v in na.items():
|
||||
if isinstance(v, str) and v.strip() != "":
|
||||
s += f" {make_qstr(k)} {make_qstr(v)}"
|
||||
else:
|
||||
warnings.warn(
|
||||
f"Node attribute {k} is not processed. {('Empty attribute' if isinstance(v, str) else 'Non-string attribute')}."
|
||||
)
|
||||
yield s
|
||||
|
||||
# write edges with attributes
|
||||
if G.is_directed():
|
||||
yield "*arcs"
|
||||
else:
|
||||
yield "*edges"
|
||||
for u, v, edgedata in G.edges(data=True):
|
||||
d = edgedata.copy()
|
||||
value = d.pop("weight", 1.0) # use 1 as default edge value
|
||||
s = " ".join(map(make_qstr, (nodenumber[u], nodenumber[v], value)))
|
||||
for k, v in d.items():
|
||||
if isinstance(v, str) and v.strip() != "":
|
||||
s += f" {make_qstr(k)} {make_qstr(v)}"
|
||||
else:
|
||||
warnings.warn(
|
||||
f"Edge attribute {k} is not processed. {('Empty attribute' if isinstance(v, str) else 'Non-string attribute')}."
|
||||
)
|
||||
yield s
|
||||
|
||||
|
||||
@open_file(1, mode="wb")
|
||||
def write_pajek(G, path, encoding="UTF-8"):
|
||||
"""Write graph in Pajek format to path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A Networkx graph
|
||||
path : file or string
|
||||
File or filename to write.
|
||||
Filenames ending in .gz or .bz2 will be compressed.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_pajek(G, "test.net")
|
||||
|
||||
Warnings
|
||||
--------
|
||||
Optional node attributes and edge attributes must be non-empty strings.
|
||||
Otherwise it will not be written into the file. You will need to
|
||||
convert those attributes to strings if you want to keep them.
|
||||
|
||||
References
|
||||
----------
|
||||
See http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/draweps.htm
|
||||
for format information.
|
||||
"""
|
||||
for line in generate_pajek(G):
|
||||
line += "\n"
|
||||
path.write(line.encode(encoding))
|
||||
|
||||
|
||||
@open_file(0, mode="rb")
|
||||
def read_pajek(path, encoding="UTF-8"):
|
||||
"""Read graph in Pajek format from path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File or filename to write.
|
||||
Filenames ending in .gz or .bz2 will be uncompressed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX MultiGraph or MultiDiGraph.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.write_pajek(G, "test.net")
|
||||
>>> G = nx.read_pajek("test.net")
|
||||
|
||||
To create a Graph instead of a MultiGraph use
|
||||
|
||||
>>> G1 = nx.Graph(G)
|
||||
|
||||
References
|
||||
----------
|
||||
See http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/draweps.htm
|
||||
for format information.
|
||||
"""
|
||||
lines = (line.decode(encoding) for line in path)
|
||||
return parse_pajek(lines)
|
||||
|
||||
|
||||
def parse_pajek(lines):
|
||||
"""Parse Pajek format graph from string or iterable.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lines : string or iterable
|
||||
Data in Pajek format.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : NetworkX graph
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_pajek()
|
||||
|
||||
"""
|
||||
import shlex
|
||||
|
||||
# multigraph=False
|
||||
if isinstance(lines, str):
|
||||
lines = iter(lines.split("\n"))
|
||||
lines = iter([line.rstrip("\n") for line in lines])
|
||||
G = nx.MultiDiGraph() # are multiedges allowed in Pajek? assume yes
|
||||
labels = [] # in the order of the file, needed for matrix
|
||||
while lines:
|
||||
try:
|
||||
l = next(lines)
|
||||
except: # EOF
|
||||
break
|
||||
if l.lower().startswith("*network"):
|
||||
try:
|
||||
label, name = l.split(None, 1)
|
||||
except ValueError:
|
||||
# Line was not of the form: *network NAME
|
||||
pass
|
||||
else:
|
||||
G.graph["name"] = name
|
||||
elif l.lower().startswith("*vertices"):
|
||||
nodelabels = {}
|
||||
l, nnodes = l.split()
|
||||
for i in range(int(nnodes)):
|
||||
l = next(lines)
|
||||
try:
|
||||
splitline = [
|
||||
x.decode("utf-8") for x in shlex.split(str(l).encode("utf-8"))
|
||||
]
|
||||
except AttributeError:
|
||||
splitline = shlex.split(str(l))
|
||||
id, label = splitline[0:2]
|
||||
labels.append(label)
|
||||
G.add_node(label)
|
||||
nodelabels[id] = label
|
||||
G.nodes[label]["id"] = id
|
||||
try:
|
||||
x, y, shape = splitline[2:5]
|
||||
G.nodes[label].update(
|
||||
{"x": float(x), "y": float(y), "shape": shape}
|
||||
)
|
||||
except:
|
||||
pass
|
||||
extra_attr = zip(splitline[5::2], splitline[6::2])
|
||||
G.nodes[label].update(extra_attr)
|
||||
elif l.lower().startswith("*edges") or l.lower().startswith("*arcs"):
|
||||
if l.lower().startswith("*edge"):
|
||||
# switch from multidigraph to multigraph
|
||||
G = nx.MultiGraph(G)
|
||||
if l.lower().startswith("*arcs"):
|
||||
# switch to directed with multiple arcs for each existing edge
|
||||
G = G.to_directed()
|
||||
for l in lines:
|
||||
try:
|
||||
splitline = [
|
||||
x.decode("utf-8") for x in shlex.split(str(l).encode("utf-8"))
|
||||
]
|
||||
except AttributeError:
|
||||
splitline = shlex.split(str(l))
|
||||
|
||||
if len(splitline) < 2:
|
||||
continue
|
||||
ui, vi = splitline[0:2]
|
||||
u = nodelabels.get(ui, ui)
|
||||
v = nodelabels.get(vi, vi)
|
||||
# parse the data attached to this edge and put in a dictionary
|
||||
edge_data = {}
|
||||
try:
|
||||
# there should always be a single value on the edge?
|
||||
w = splitline[2:3]
|
||||
edge_data.update({"weight": float(w[0])})
|
||||
except:
|
||||
pass
|
||||
# if there isn't, just assign a 1
|
||||
# edge_data.update({'value':1})
|
||||
extra_attr = zip(splitline[3::2], splitline[4::2])
|
||||
edge_data.update(extra_attr)
|
||||
# if G.has_edge(u,v):
|
||||
# multigraph=True
|
||||
G.add_edge(u, v, **edge_data)
|
||||
elif l.lower().startswith("*matrix"):
|
||||
G = nx.DiGraph(G)
|
||||
adj_list = (
|
||||
(labels[row], labels[col], {"weight": int(data)})
|
||||
for (row, line) in enumerate(lines)
|
||||
for (col, data) in enumerate(line.split())
|
||||
if int(data) != 0
|
||||
)
|
||||
G.add_edges_from(adj_list)
|
||||
|
||||
return G
|
||||
|
||||
|
||||
def make_qstr(t):
|
||||
"""Returns the string representation of t.
|
||||
Add outer double-quotes if the string has a space.
|
||||
"""
|
||||
if not isinstance(t, str):
|
||||
t = str(t)
|
||||
if " " in t:
|
||||
t = f'"{t}"'
|
||||
return t
|
374
venv/Lib/site-packages/networkx/readwrite/sparse6.py
Normal file
374
venv/Lib/site-packages/networkx/readwrite/sparse6.py
Normal file
|
@ -0,0 +1,374 @@
|
|||
# Original author: D. Eppstein, UC Irvine, August 12, 2003.
|
||||
# The original code at http://www.ics.uci.edu/~eppstein/PADS/ is public domain.
|
||||
"""Functions for reading and writing graphs in the *sparse6* format.
|
||||
|
||||
The *sparse6* file format is a space-efficient format for large sparse
|
||||
graphs. For small graphs or large dense graphs, use the *graph6* file
|
||||
format.
|
||||
|
||||
For more information, see the `sparse6`_ homepage.
|
||||
|
||||
.. _sparse6: http://users.cecs.anu.edu.au/~bdm/data/formats.html
|
||||
|
||||
"""
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError
|
||||
from networkx.utils import open_file, not_implemented_for
|
||||
from networkx.readwrite.graph6 import data_to_n, n_to_data
|
||||
|
||||
__all__ = ["from_sparse6_bytes", "read_sparse6", "to_sparse6_bytes", "write_sparse6"]
|
||||
|
||||
|
||||
def _generate_sparse6_bytes(G, nodes, header):
|
||||
"""Yield bytes in the sparse6 encoding of a graph.
|
||||
|
||||
`G` is an undirected simple graph. `nodes` is the list of nodes for
|
||||
which the node-induced subgraph will be encoded; if `nodes` is the
|
||||
list of all nodes in the graph, the entire graph will be
|
||||
encoded. `header` is a Boolean that specifies whether to generate
|
||||
the header ``b'>>sparse6<<'`` before the remaining data.
|
||||
|
||||
This function generates `bytes` objects in the following order:
|
||||
|
||||
1. the header (if requested),
|
||||
2. the encoding of the number of nodes,
|
||||
3. each character, one-at-a-time, in the encoding of the requested
|
||||
node-induced subgraph,
|
||||
4. a newline character.
|
||||
|
||||
This function raises :exc:`ValueError` if the graph is too large for
|
||||
the graph6 format (that is, greater than ``2 ** 36`` nodes).
|
||||
|
||||
"""
|
||||
n = len(G)
|
||||
if n >= 2 ** 36:
|
||||
raise ValueError(
|
||||
"sparse6 is only defined if number of nodes is less " "than 2 ** 36"
|
||||
)
|
||||
if header:
|
||||
yield b">>sparse6<<"
|
||||
yield b":"
|
||||
for d in n_to_data(n):
|
||||
yield str.encode(chr(d + 63))
|
||||
|
||||
k = 1
|
||||
while 1 << k < n:
|
||||
k += 1
|
||||
|
||||
def enc(x):
|
||||
"""Big endian k-bit encoding of x"""
|
||||
return [1 if (x & 1 << (k - 1 - i)) else 0 for i in range(k)]
|
||||
|
||||
edges = sorted((max(u, v), min(u, v)) for u, v in G.edges())
|
||||
bits = []
|
||||
curv = 0
|
||||
for (v, u) in edges:
|
||||
if v == curv: # current vertex edge
|
||||
bits.append(0)
|
||||
bits.extend(enc(u))
|
||||
elif v == curv + 1: # next vertex edge
|
||||
curv += 1
|
||||
bits.append(1)
|
||||
bits.extend(enc(u))
|
||||
else: # skip to vertex v and then add edge to u
|
||||
curv = v
|
||||
bits.append(1)
|
||||
bits.extend(enc(v))
|
||||
bits.append(0)
|
||||
bits.extend(enc(u))
|
||||
if k < 6 and n == (1 << k) and ((-len(bits)) % 6) >= k and curv < (n - 1):
|
||||
# Padding special case: small k, n=2^k,
|
||||
# more than k bits of padding needed,
|
||||
# current vertex is not (n-1) --
|
||||
# appending 1111... would add a loop on (n-1)
|
||||
bits.append(0)
|
||||
bits.extend([1] * ((-len(bits)) % 6))
|
||||
else:
|
||||
bits.extend([1] * ((-len(bits)) % 6))
|
||||
|
||||
data = [
|
||||
(bits[i + 0] << 5)
|
||||
+ (bits[i + 1] << 4)
|
||||
+ (bits[i + 2] << 3)
|
||||
+ (bits[i + 3] << 2)
|
||||
+ (bits[i + 4] << 1)
|
||||
+ (bits[i + 5] << 0)
|
||||
for i in range(0, len(bits), 6)
|
||||
]
|
||||
|
||||
for d in data:
|
||||
yield str.encode(chr(d + 63))
|
||||
yield b"\n"
|
||||
|
||||
|
||||
def from_sparse6_bytes(string):
|
||||
"""Read an undirected graph in sparse6 format from string.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
string : string
|
||||
Data in sparse6 format
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : Graph
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the string is unable to be parsed in sparse6 format
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.from_sparse6_bytes(b":A_")
|
||||
>>> sorted(G.edges())
|
||||
[(0, 1), (0, 1), (0, 1)]
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_sparse6, write_sparse6
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Sparse6 specification
|
||||
<http://users.cecs.anu.edu.au/~bdm/data/formats.html>
|
||||
|
||||
"""
|
||||
if string.startswith(b">>sparse6<<"):
|
||||
string = string[11:]
|
||||
if not string.startswith(b":"):
|
||||
raise NetworkXError("Expected leading colon in sparse6")
|
||||
|
||||
chars = [c - 63 for c in string[1:]]
|
||||
n, data = data_to_n(chars)
|
||||
k = 1
|
||||
while 1 << k < n:
|
||||
k += 1
|
||||
|
||||
def parseData():
|
||||
"""Returns stream of pairs b[i], x[i] for sparse6 format."""
|
||||
chunks = iter(data)
|
||||
d = None # partial data word
|
||||
dLen = 0 # how many unparsed bits are left in d
|
||||
|
||||
while 1:
|
||||
if dLen < 1:
|
||||
try:
|
||||
d = next(chunks)
|
||||
except StopIteration:
|
||||
return
|
||||
dLen = 6
|
||||
dLen -= 1
|
||||
b = (d >> dLen) & 1 # grab top remaining bit
|
||||
|
||||
x = d & ((1 << dLen) - 1) # partially built up value of x
|
||||
xLen = dLen # how many bits included so far in x
|
||||
while xLen < k: # now grab full chunks until we have enough
|
||||
try:
|
||||
d = next(chunks)
|
||||
except StopIteration:
|
||||
return
|
||||
dLen = 6
|
||||
x = (x << 6) + d
|
||||
xLen += 6
|
||||
x = x >> (xLen - k) # shift back the extra bits
|
||||
dLen = xLen - k
|
||||
yield b, x
|
||||
|
||||
v = 0
|
||||
|
||||
G = nx.MultiGraph()
|
||||
G.add_nodes_from(range(n))
|
||||
|
||||
multigraph = False
|
||||
for b, x in parseData():
|
||||
if b == 1:
|
||||
v += 1
|
||||
# padding with ones can cause overlarge number here
|
||||
if x >= n or v >= n:
|
||||
break
|
||||
elif x > v:
|
||||
v = x
|
||||
else:
|
||||
if G.has_edge(x, v):
|
||||
multigraph = True
|
||||
G.add_edge(x, v)
|
||||
if not multigraph:
|
||||
G = nx.Graph(G)
|
||||
return G
|
||||
|
||||
|
||||
def to_sparse6_bytes(G, nodes=None, header=True):
|
||||
"""Convert an undirected graph to bytes in sparse6 format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : Graph (undirected)
|
||||
|
||||
nodes: list or iterable
|
||||
Nodes are labeled 0...n-1 in the order provided. If None the ordering
|
||||
given by ``G.nodes()`` is used.
|
||||
|
||||
header: bool
|
||||
If True add '>>sparse6<<' bytes to head of data.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the graph is directed.
|
||||
|
||||
ValueError
|
||||
If the graph has at least ``2 ** 36`` nodes; the sparse6 format
|
||||
is only defined for graphs of order less than ``2 ** 36``.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> nx.to_sparse6_bytes(nx.path_graph(2))
|
||||
b'>>sparse6<<:An\\n'
|
||||
|
||||
See Also
|
||||
--------
|
||||
to_sparse6_bytes, read_sparse6, write_sparse6_bytes
|
||||
|
||||
Notes
|
||||
-----
|
||||
The returned bytes end with a newline character.
|
||||
|
||||
The format does not support edge or node labels.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Graph6 specification
|
||||
<http://users.cecs.anu.edu.au/~bdm/data/formats.html>
|
||||
|
||||
"""
|
||||
if nodes is not None:
|
||||
G = G.subgraph(nodes)
|
||||
G = nx.convert_node_labels_to_integers(G, ordering="sorted")
|
||||
return b"".join(_generate_sparse6_bytes(G, nodes, header))
|
||||
|
||||
|
||||
@open_file(0, mode="rb")
|
||||
def read_sparse6(path):
|
||||
"""Read an undirected graph in sparse6 format from path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : file or string
|
||||
File or filename to write.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : Graph/Multigraph or list of Graphs/MultiGraphs
|
||||
If the file contains multiple lines then a list of graphs is returned
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the string is unable to be parsed in sparse6 format
|
||||
|
||||
Examples
|
||||
--------
|
||||
You can read a sparse6 file by giving the path to the file::
|
||||
|
||||
>>> import tempfile
|
||||
>>> with tempfile.NamedTemporaryFile() as f:
|
||||
... _ = f.write(b">>sparse6<<:An\\n")
|
||||
... _ = f.seek(0)
|
||||
... G = nx.read_sparse6(f.name)
|
||||
>>> list(G.edges())
|
||||
[(0, 1)]
|
||||
|
||||
You can also read a sparse6 file by giving an open file-like object::
|
||||
|
||||
>>> import tempfile
|
||||
>>> with tempfile.NamedTemporaryFile() as f:
|
||||
... _ = f.write(b">>sparse6<<:An\\n")
|
||||
... _ = f.seek(0)
|
||||
... G = nx.read_sparse6(f)
|
||||
>>> list(G.edges())
|
||||
[(0, 1)]
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_sparse6, from_sparse6_bytes
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Sparse6 specification
|
||||
<http://users.cecs.anu.edu.au/~bdm/data/formats.html>
|
||||
|
||||
"""
|
||||
glist = []
|
||||
for line in path:
|
||||
line = line.strip()
|
||||
if not len(line):
|
||||
continue
|
||||
glist.append(from_sparse6_bytes(line))
|
||||
if len(glist) == 1:
|
||||
return glist[0]
|
||||
else:
|
||||
return glist
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@open_file(1, mode="wb")
|
||||
def write_sparse6(G, path, nodes=None, header=True):
|
||||
"""Write graph G to given path in sparse6 format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : Graph (undirected)
|
||||
|
||||
path : file or string
|
||||
File or filename to write
|
||||
|
||||
nodes: list or iterable
|
||||
Nodes are labeled 0...n-1 in the order provided. If None the ordering
|
||||
given by G.nodes() is used.
|
||||
|
||||
header: bool
|
||||
If True add '>>sparse6<<' string to head of data
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the graph is directed
|
||||
|
||||
Examples
|
||||
--------
|
||||
You can write a sparse6 file by giving the path to the file::
|
||||
|
||||
>>> import tempfile
|
||||
>>> with tempfile.NamedTemporaryFile() as f:
|
||||
... nx.write_sparse6(nx.path_graph(2), f.name)
|
||||
... print(f.read())
|
||||
b'>>sparse6<<:An\\n'
|
||||
|
||||
You can also write a sparse6 file by giving an open file-like object::
|
||||
|
||||
>>> with tempfile.NamedTemporaryFile() as f:
|
||||
... nx.write_sparse6(nx.path_graph(2), f)
|
||||
... _ = f.seek(0)
|
||||
... print(f.read())
|
||||
b'>>sparse6<<:An\\n'
|
||||
|
||||
See Also
|
||||
--------
|
||||
read_sparse6, from_sparse6_bytes
|
||||
|
||||
Notes
|
||||
-----
|
||||
The format does not support edge or node labels.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Sparse6 specification
|
||||
<http://users.cecs.anu.edu.au/~bdm/data/formats.html>
|
||||
|
||||
"""
|
||||
if nodes is not None:
|
||||
G = G.subgraph(nodes)
|
||||
G = nx.convert_node_labels_to_integers(G, ordering="sorted")
|
||||
for b in _generate_sparse6_bytes(G, nodes, header):
|
||||
path.write(b)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
266
venv/Lib/site-packages/networkx/readwrite/tests/test_adjlist.py
Normal file
266
venv/Lib/site-packages/networkx/readwrite/tests/test_adjlist.py
Normal file
|
@ -0,0 +1,266 @@
|
|||
"""
|
||||
Unit tests for adjlist.
|
||||
"""
|
||||
import io
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
import networkx as nx
|
||||
from networkx.testing import assert_nodes_equal, assert_edges_equal, assert_graphs_equal
|
||||
|
||||
|
||||
class TestAdjlist:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = nx.Graph(name="test")
|
||||
e = [("a", "b"), ("b", "c"), ("c", "d"), ("d", "e"), ("e", "f"), ("a", "f")]
|
||||
cls.G.add_edges_from(e)
|
||||
cls.G.add_node("g")
|
||||
cls.DG = nx.DiGraph(cls.G)
|
||||
cls.XG = nx.MultiGraph()
|
||||
cls.XG.add_weighted_edges_from([(1, 2, 5), (1, 2, 5), (1, 2, 1), (3, 3, 42)])
|
||||
cls.XDG = nx.MultiDiGraph(cls.XG)
|
||||
|
||||
def test_read_multiline_adjlist_1(self):
|
||||
# Unit test for https://networkx.lanl.gov/trac/ticket/252
|
||||
s = b"""# comment line
|
||||
1 2
|
||||
# comment line
|
||||
2
|
||||
3
|
||||
"""
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_multiline_adjlist(bytesIO)
|
||||
adj = {"1": {"3": {}, "2": {}}, "3": {"1": {}}, "2": {"1": {}}}
|
||||
assert_graphs_equal(G, nx.Graph(adj))
|
||||
|
||||
def test_unicode(self):
|
||||
G = nx.Graph()
|
||||
name1 = chr(2344) + chr(123) + chr(6543)
|
||||
name2 = chr(5543) + chr(1543) + chr(324)
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
fd, fname = tempfile.mkstemp()
|
||||
nx.write_multiline_adjlist(G, fname)
|
||||
H = nx.read_multiline_adjlist(fname)
|
||||
assert_graphs_equal(G, H)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_latin1_err(self):
|
||||
G = nx.Graph()
|
||||
name1 = chr(2344) + chr(123) + chr(6543)
|
||||
name2 = chr(5543) + chr(1543) + chr(324)
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
fd, fname = tempfile.mkstemp()
|
||||
pytest.raises(
|
||||
UnicodeEncodeError, nx.write_multiline_adjlist, G, fname, encoding="latin-1"
|
||||
)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_latin1(self):
|
||||
G = nx.Graph()
|
||||
name1 = "Bj" + chr(246) + "rk"
|
||||
name2 = chr(220) + "ber"
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
fd, fname = tempfile.mkstemp()
|
||||
nx.write_multiline_adjlist(G, fname, encoding="latin-1")
|
||||
H = nx.read_multiline_adjlist(fname, encoding="latin-1")
|
||||
assert_graphs_equal(G, H)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_parse_adjlist(self):
|
||||
lines = ["1 2 5", "2 3 4", "3 5", "4", "5"]
|
||||
nx.parse_adjlist(lines, nodetype=int) # smoke test
|
||||
with pytest.raises(TypeError):
|
||||
nx.parse_adjlist(lines, nodetype="int")
|
||||
lines = ["1 2 5", "2 b", "c"]
|
||||
with pytest.raises(TypeError):
|
||||
nx.parse_adjlist(lines, nodetype=int)
|
||||
|
||||
def test_adjlist_graph(self):
|
||||
G = self.G
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_adjlist(G, fname)
|
||||
H = nx.read_adjlist(fname)
|
||||
H2 = nx.read_adjlist(fname)
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_adjlist_digraph(self):
|
||||
G = self.DG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_adjlist(G, fname)
|
||||
H = nx.read_adjlist(fname, create_using=nx.DiGraph())
|
||||
H2 = nx.read_adjlist(fname, create_using=nx.DiGraph())
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_adjlist_integers(self):
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
G = nx.convert_node_labels_to_integers(self.G)
|
||||
nx.write_adjlist(G, fname)
|
||||
H = nx.read_adjlist(fname, nodetype=int)
|
||||
H2 = nx.read_adjlist(fname, nodetype=int)
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_adjlist_multigraph(self):
|
||||
G = self.XG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_adjlist(G, fname)
|
||||
H = nx.read_adjlist(fname, nodetype=int, create_using=nx.MultiGraph())
|
||||
H2 = nx.read_adjlist(fname, nodetype=int, create_using=nx.MultiGraph())
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_adjlist_multidigraph(self):
|
||||
G = self.XDG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_adjlist(G, fname)
|
||||
H = nx.read_adjlist(fname, nodetype=int, create_using=nx.MultiDiGraph())
|
||||
H2 = nx.read_adjlist(fname, nodetype=int, create_using=nx.MultiDiGraph())
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_adjlist_delimiter(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.path_graph(3)
|
||||
nx.write_adjlist(G, fh, delimiter=":")
|
||||
fh.seek(0)
|
||||
H = nx.read_adjlist(fh, nodetype=int, delimiter=":")
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
|
||||
|
||||
class TestMultilineAdjlist:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = nx.Graph(name="test")
|
||||
e = [("a", "b"), ("b", "c"), ("c", "d"), ("d", "e"), ("e", "f"), ("a", "f")]
|
||||
cls.G.add_edges_from(e)
|
||||
cls.G.add_node("g")
|
||||
cls.DG = nx.DiGraph(cls.G)
|
||||
cls.DG.remove_edge("b", "a")
|
||||
cls.DG.remove_edge("b", "c")
|
||||
cls.XG = nx.MultiGraph()
|
||||
cls.XG.add_weighted_edges_from([(1, 2, 5), (1, 2, 5), (1, 2, 1), (3, 3, 42)])
|
||||
cls.XDG = nx.MultiDiGraph(cls.XG)
|
||||
|
||||
def test_parse_multiline_adjlist(self):
|
||||
lines = [
|
||||
"1 2",
|
||||
"b {'weight':3, 'name': 'Frodo'}",
|
||||
"c {}",
|
||||
"d 1",
|
||||
"e {'weight':6, 'name': 'Saruman'}",
|
||||
]
|
||||
nx.parse_multiline_adjlist(iter(lines)) # smoke test
|
||||
with pytest.raises(TypeError):
|
||||
nx.parse_multiline_adjlist(iter(lines), nodetype=int)
|
||||
nx.parse_multiline_adjlist(iter(lines), edgetype=str) # smoke test
|
||||
with pytest.raises(TypeError):
|
||||
nx.parse_multiline_adjlist(iter(lines), nodetype=int)
|
||||
lines = ["1 a"]
|
||||
with pytest.raises(TypeError):
|
||||
nx.parse_multiline_adjlist(iter(lines))
|
||||
lines = ["a 2"]
|
||||
with pytest.raises(TypeError):
|
||||
nx.parse_multiline_adjlist(iter(lines), nodetype=int)
|
||||
lines = ["1 2"]
|
||||
with pytest.raises(TypeError):
|
||||
nx.parse_multiline_adjlist(iter(lines))
|
||||
lines = ["1 2", "2 {}"]
|
||||
with pytest.raises(TypeError):
|
||||
nx.parse_multiline_adjlist(iter(lines))
|
||||
|
||||
def test_multiline_adjlist_graph(self):
|
||||
G = self.G
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_multiline_adjlist(G, fname)
|
||||
H = nx.read_multiline_adjlist(fname)
|
||||
H2 = nx.read_multiline_adjlist(fname)
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_multiline_adjlist_digraph(self):
|
||||
G = self.DG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_multiline_adjlist(G, fname)
|
||||
H = nx.read_multiline_adjlist(fname, create_using=nx.DiGraph())
|
||||
H2 = nx.read_multiline_adjlist(fname, create_using=nx.DiGraph())
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_multiline_adjlist_integers(self):
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
G = nx.convert_node_labels_to_integers(self.G)
|
||||
nx.write_multiline_adjlist(G, fname)
|
||||
H = nx.read_multiline_adjlist(fname, nodetype=int)
|
||||
H2 = nx.read_multiline_adjlist(fname, nodetype=int)
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_multiline_adjlist_multigraph(self):
|
||||
G = self.XG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_multiline_adjlist(G, fname)
|
||||
H = nx.read_multiline_adjlist(fname, nodetype=int, create_using=nx.MultiGraph())
|
||||
H2 = nx.read_multiline_adjlist(
|
||||
fname, nodetype=int, create_using=nx.MultiGraph()
|
||||
)
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_multiline_adjlist_multidigraph(self):
|
||||
G = self.XDG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_multiline_adjlist(G, fname)
|
||||
H = nx.read_multiline_adjlist(
|
||||
fname, nodetype=int, create_using=nx.MultiDiGraph()
|
||||
)
|
||||
H2 = nx.read_multiline_adjlist(
|
||||
fname, nodetype=int, create_using=nx.MultiDiGraph()
|
||||
)
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_multiline_adjlist_delimiter(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.path_graph(3)
|
||||
nx.write_multiline_adjlist(G, fh, delimiter=":")
|
||||
fh.seek(0)
|
||||
H = nx.read_multiline_adjlist(fh, nodetype=int, delimiter=":")
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
275
venv/Lib/site-packages/networkx/readwrite/tests/test_edgelist.py
Normal file
275
venv/Lib/site-packages/networkx/readwrite/tests/test_edgelist.py
Normal file
|
@ -0,0 +1,275 @@
|
|||
"""
|
||||
Unit tests for edgelists.
|
||||
"""
|
||||
import pytest
|
||||
import io
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
import networkx as nx
|
||||
from networkx.testing import assert_edges_equal, assert_nodes_equal, assert_graphs_equal
|
||||
|
||||
|
||||
class TestEdgelist:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = nx.Graph(name="test")
|
||||
e = [("a", "b"), ("b", "c"), ("c", "d"), ("d", "e"), ("e", "f"), ("a", "f")]
|
||||
cls.G.add_edges_from(e)
|
||||
cls.G.add_node("g")
|
||||
cls.DG = nx.DiGraph(cls.G)
|
||||
cls.XG = nx.MultiGraph()
|
||||
cls.XG.add_weighted_edges_from([(1, 2, 5), (1, 2, 5), (1, 2, 1), (3, 3, 42)])
|
||||
cls.XDG = nx.MultiDiGraph(cls.XG)
|
||||
|
||||
def test_read_edgelist_1(self):
|
||||
s = b"""\
|
||||
# comment line
|
||||
1 2
|
||||
# comment line
|
||||
2 3
|
||||
"""
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_edgelist(bytesIO, nodetype=int)
|
||||
assert_edges_equal(G.edges(), [(1, 2), (2, 3)])
|
||||
|
||||
def test_read_edgelist_2(self):
|
||||
s = b"""\
|
||||
# comment line
|
||||
1 2 2.0
|
||||
# comment line
|
||||
2 3 3.0
|
||||
"""
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_edgelist(bytesIO, nodetype=int, data=False)
|
||||
assert_edges_equal(G.edges(), [(1, 2), (2, 3)])
|
||||
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_weighted_edgelist(bytesIO, nodetype=int)
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"weight": 2.0}), (2, 3, {"weight": 3.0})]
|
||||
)
|
||||
|
||||
def test_read_edgelist_3(self):
|
||||
s = b"""\
|
||||
# comment line
|
||||
1 2 {'weight':2.0}
|
||||
# comment line
|
||||
2 3 {'weight':3.0}
|
||||
"""
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_edgelist(bytesIO, nodetype=int, data=False)
|
||||
assert_edges_equal(G.edges(), [(1, 2), (2, 3)])
|
||||
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_edgelist(bytesIO, nodetype=int, data=True)
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"weight": 2.0}), (2, 3, {"weight": 3.0})]
|
||||
)
|
||||
|
||||
def test_read_edgelist_4(self):
|
||||
s = b"""\
|
||||
# comment line
|
||||
1 2 {'weight':2.0}
|
||||
# comment line
|
||||
2 3 {'weight':3.0}
|
||||
"""
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_edgelist(bytesIO, nodetype=int, data=False)
|
||||
assert_edges_equal(G.edges(), [(1, 2), (2, 3)])
|
||||
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_edgelist(bytesIO, nodetype=int, data=True)
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"weight": 2.0}), (2, 3, {"weight": 3.0})]
|
||||
)
|
||||
|
||||
s = """\
|
||||
# comment line
|
||||
1 2 {'weight':2.0}
|
||||
# comment line
|
||||
2 3 {'weight':3.0}
|
||||
"""
|
||||
StringIO = io.StringIO(s)
|
||||
G = nx.read_edgelist(StringIO, nodetype=int, data=False)
|
||||
assert_edges_equal(G.edges(), [(1, 2), (2, 3)])
|
||||
|
||||
StringIO = io.StringIO(s)
|
||||
G = nx.read_edgelist(StringIO, nodetype=int, data=True)
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"weight": 2.0}), (2, 3, {"weight": 3.0})]
|
||||
)
|
||||
|
||||
def test_read_edgelist_5(self):
|
||||
s = b"""\
|
||||
# comment line
|
||||
1 2 {'weight':2.0, 'color':'green'}
|
||||
# comment line
|
||||
2 3 {'weight':3.0, 'color':'red'}
|
||||
"""
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_edgelist(bytesIO, nodetype=int, data=False)
|
||||
assert_edges_equal(G.edges(), [(1, 2), (2, 3)])
|
||||
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_edgelist(bytesIO, nodetype=int, data=True)
|
||||
assert_edges_equal(
|
||||
G.edges(data=True),
|
||||
[
|
||||
(1, 2, {"weight": 2.0, "color": "green"}),
|
||||
(2, 3, {"weight": 3.0, "color": "red"}),
|
||||
],
|
||||
)
|
||||
|
||||
def test_read_edgelist_6(self):
|
||||
s = b"""\
|
||||
# comment line
|
||||
1, 2, {'weight':2.0, 'color':'green'}
|
||||
# comment line
|
||||
2, 3, {'weight':3.0, 'color':'red'}
|
||||
"""
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_edgelist(bytesIO, nodetype=int, data=False, delimiter=",")
|
||||
assert_edges_equal(G.edges(), [(1, 2), (2, 3)])
|
||||
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = nx.read_edgelist(bytesIO, nodetype=int, data=True, delimiter=",")
|
||||
assert_edges_equal(
|
||||
G.edges(data=True),
|
||||
[
|
||||
(1, 2, {"weight": 2.0, "color": "green"}),
|
||||
(2, 3, {"weight": 3.0, "color": "red"}),
|
||||
],
|
||||
)
|
||||
|
||||
def test_write_edgelist_1(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.OrderedGraph()
|
||||
G.add_edges_from([(1, 2), (2, 3)])
|
||||
nx.write_edgelist(G, fh, data=False)
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2\n2 3\n"
|
||||
|
||||
def test_write_edgelist_2(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.OrderedGraph()
|
||||
G.add_edges_from([(1, 2), (2, 3)])
|
||||
nx.write_edgelist(G, fh, data=True)
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2 {}\n2 3 {}\n"
|
||||
|
||||
def test_write_edgelist_3(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.OrderedGraph()
|
||||
G.add_edge(1, 2, weight=2.0)
|
||||
G.add_edge(2, 3, weight=3.0)
|
||||
nx.write_edgelist(G, fh, data=True)
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2 {'weight': 2.0}\n2 3 {'weight': 3.0}\n"
|
||||
|
||||
def test_write_edgelist_4(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.OrderedGraph()
|
||||
G.add_edge(1, 2, weight=2.0)
|
||||
G.add_edge(2, 3, weight=3.0)
|
||||
nx.write_edgelist(G, fh, data=[("weight")])
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2 2.0\n2 3 3.0\n"
|
||||
|
||||
def test_unicode(self):
|
||||
G = nx.Graph()
|
||||
name1 = chr(2344) + chr(123) + chr(6543)
|
||||
name2 = chr(5543) + chr(1543) + chr(324)
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
fd, fname = tempfile.mkstemp()
|
||||
nx.write_edgelist(G, fname)
|
||||
H = nx.read_edgelist(fname)
|
||||
assert_graphs_equal(G, H)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_latin1_issue(self):
|
||||
G = nx.Graph()
|
||||
name1 = chr(2344) + chr(123) + chr(6543)
|
||||
name2 = chr(5543) + chr(1543) + chr(324)
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
fd, fname = tempfile.mkstemp()
|
||||
pytest.raises(
|
||||
UnicodeEncodeError, nx.write_edgelist, G, fname, encoding="latin-1"
|
||||
)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_latin1(self):
|
||||
G = nx.Graph()
|
||||
name1 = "Bj" + chr(246) + "rk"
|
||||
name2 = chr(220) + "ber"
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
fd, fname = tempfile.mkstemp()
|
||||
nx.write_edgelist(G, fname, encoding="latin-1")
|
||||
H = nx.read_edgelist(fname, encoding="latin-1")
|
||||
assert_graphs_equal(G, H)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_graph(self):
|
||||
G = self.G
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_edgelist(G, fname)
|
||||
H = nx.read_edgelist(fname)
|
||||
H2 = nx.read_edgelist(fname)
|
||||
assert H != H2 # they should be different graphs
|
||||
G.remove_node("g") # isolated nodes are not written in edgelist
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_digraph(self):
|
||||
G = self.DG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_edgelist(G, fname)
|
||||
H = nx.read_edgelist(fname, create_using=nx.DiGraph())
|
||||
H2 = nx.read_edgelist(fname, create_using=nx.DiGraph())
|
||||
assert H != H2 # they should be different graphs
|
||||
G.remove_node("g") # isolated nodes are not written in edgelist
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_integers(self):
|
||||
G = nx.convert_node_labels_to_integers(self.G)
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_edgelist(G, fname)
|
||||
H = nx.read_edgelist(fname, nodetype=int)
|
||||
# isolated nodes are not written in edgelist
|
||||
G.remove_nodes_from(list(nx.isolates(G)))
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_multigraph(self):
|
||||
G = self.XG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_edgelist(G, fname)
|
||||
H = nx.read_edgelist(fname, nodetype=int, create_using=nx.MultiGraph())
|
||||
H2 = nx.read_edgelist(fname, nodetype=int, create_using=nx.MultiGraph())
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_multidigraph(self):
|
||||
G = self.XDG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_edgelist(G, fname)
|
||||
H = nx.read_edgelist(fname, nodetype=int, create_using=nx.MultiDiGraph())
|
||||
H2 = nx.read_edgelist(fname, nodetype=int, create_using=nx.MultiDiGraph())
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
658
venv/Lib/site-packages/networkx/readwrite/tests/test_gexf.py
Normal file
658
venv/Lib/site-packages/networkx/readwrite/tests/test_gexf.py
Normal file
|
@ -0,0 +1,658 @@
|
|||
import io
|
||||
import sys
|
||||
import time
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestGEXF:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
_ = pytest.importorskip("xml.etree.ElementTree")
|
||||
|
||||
cls.simple_directed_data = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version="1.2">
|
||||
<graph mode="static" defaultedgetype="directed">
|
||||
<nodes>
|
||||
<node id="0" label="Hello" />
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
cls.simple_directed_graph = nx.DiGraph()
|
||||
cls.simple_directed_graph.add_node("0", label="Hello")
|
||||
cls.simple_directed_graph.add_node("1", label="World")
|
||||
cls.simple_directed_graph.add_edge("0", "1", id="0")
|
||||
|
||||
cls.simple_directed_fh = io.BytesIO(cls.simple_directed_data.encode("UTF-8"))
|
||||
|
||||
cls.attribute_data = """<?xml version="1.0" encoding="UTF-8"?>\
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi="http://www.w3.\
|
||||
org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gexf.net/\
|
||||
1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2">
|
||||
<meta lastmodifieddate="2009-03-20">
|
||||
<creator>Gephi.org</creator>
|
||||
<description>A Web network</description>
|
||||
</meta>
|
||||
<graph defaultedgetype="directed">
|
||||
<attributes class="node">
|
||||
<attribute id="0" title="url" type="string"/>
|
||||
<attribute id="1" title="indegree" type="integer"/>
|
||||
<attribute id="2" title="frog" type="boolean">
|
||||
<default>true</default>
|
||||
</attribute>
|
||||
</attributes>
|
||||
<nodes>
|
||||
<node id="0" label="Gephi">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="https://gephi.org"/>
|
||||
<attvalue for="1" value="1"/>
|
||||
<attvalue for="2" value="false"/>
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="1" label="Webatlas">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="http://webatlas.fr"/>
|
||||
<attvalue for="1" value="2"/>
|
||||
<attvalue for="2" value="false"/>
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="2" label="RTGI">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="http://rtgi.fr"/>
|
||||
<attvalue for="1" value="1"/>
|
||||
<attvalue for="2" value="true"/>
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="3" label="BarabasiLab">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="http://barabasilab.com"/>
|
||||
<attvalue for="1" value="1"/>
|
||||
<attvalue for="2" value="true"/>
|
||||
</attvalues>
|
||||
</node>
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" label="foo"/>
|
||||
<edge id="1" source="0" target="2"/>
|
||||
<edge id="2" source="1" target="0"/>
|
||||
<edge id="3" source="2" target="1"/>
|
||||
<edge id="4" source="0" target="3"/>
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
cls.attribute_graph = nx.DiGraph()
|
||||
cls.attribute_graph.graph["node_default"] = {"frog": True}
|
||||
cls.attribute_graph.add_node(
|
||||
"0", label="Gephi", url="https://gephi.org", indegree=1, frog=False
|
||||
)
|
||||
cls.attribute_graph.add_node(
|
||||
"1", label="Webatlas", url="http://webatlas.fr", indegree=2, frog=False
|
||||
)
|
||||
cls.attribute_graph.add_node(
|
||||
"2", label="RTGI", url="http://rtgi.fr", indegree=1, frog=True
|
||||
)
|
||||
cls.attribute_graph.add_node(
|
||||
"3",
|
||||
label="BarabasiLab",
|
||||
url="http://barabasilab.com",
|
||||
indegree=1,
|
||||
frog=True,
|
||||
)
|
||||
cls.attribute_graph.add_edge("0", "1", id="0", label="foo")
|
||||
cls.attribute_graph.add_edge("0", "2", id="1")
|
||||
cls.attribute_graph.add_edge("1", "0", id="2")
|
||||
cls.attribute_graph.add_edge("2", "1", id="3")
|
||||
cls.attribute_graph.add_edge("0", "3", id="4")
|
||||
cls.attribute_fh = io.BytesIO(cls.attribute_data.encode("UTF-8"))
|
||||
|
||||
cls.simple_undirected_data = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version="1.2">
|
||||
<graph mode="static" defaultedgetype="undirected">
|
||||
<nodes>
|
||||
<node id="0" label="Hello" />
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
cls.simple_undirected_graph = nx.Graph()
|
||||
cls.simple_undirected_graph.add_node("0", label="Hello")
|
||||
cls.simple_undirected_graph.add_node("1", label="World")
|
||||
cls.simple_undirected_graph.add_edge("0", "1", id="0")
|
||||
|
||||
cls.simple_undirected_fh = io.BytesIO(
|
||||
cls.simple_undirected_data.encode("UTF-8")
|
||||
)
|
||||
|
||||
def test_read_simple_directed_graphml(self):
|
||||
G = self.simple_directed_graph
|
||||
H = nx.read_gexf(self.simple_directed_fh)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(G.edges()) == sorted(H.edges())
|
||||
assert sorted(G.edges(data=True)) == sorted(H.edges(data=True))
|
||||
self.simple_directed_fh.seek(0)
|
||||
|
||||
def test_write_read_simple_directed_graphml(self):
|
||||
G = self.simple_directed_graph
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(G.edges()) == sorted(H.edges())
|
||||
assert sorted(G.edges(data=True)) == sorted(H.edges(data=True))
|
||||
self.simple_directed_fh.seek(0)
|
||||
|
||||
def test_read_simple_undirected_graphml(self):
|
||||
G = self.simple_undirected_graph
|
||||
H = nx.read_gexf(self.simple_undirected_fh)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(sorted(e) for e in G.edges()) == sorted(
|
||||
sorted(e) for e in H.edges()
|
||||
)
|
||||
self.simple_undirected_fh.seek(0)
|
||||
|
||||
def test_read_attribute_graphml(self):
|
||||
G = self.attribute_graph
|
||||
H = nx.read_gexf(self.attribute_fh)
|
||||
assert sorted(G.nodes(True)) == sorted(H.nodes(data=True))
|
||||
ge = sorted(G.edges(data=True))
|
||||
he = sorted(H.edges(data=True))
|
||||
for a, b in zip(ge, he):
|
||||
assert a == b
|
||||
self.attribute_fh.seek(0)
|
||||
|
||||
def test_directed_edge_in_undirected(self):
|
||||
s = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
|
||||
<graph mode="static" defaultedgetype="undirected" name="">
|
||||
<nodes>
|
||||
<node id="0" label="Hello" />
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" type="directed"/>
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
fh = io.BytesIO(s.encode("UTF-8"))
|
||||
pytest.raises(nx.NetworkXError, nx.read_gexf, fh)
|
||||
|
||||
def test_undirected_edge_in_directed(self):
|
||||
s = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
|
||||
<graph mode="static" defaultedgetype="directed" name="">
|
||||
<nodes>
|
||||
<node id="0" label="Hello" />
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" type="undirected"/>
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
fh = io.BytesIO(s.encode("UTF-8"))
|
||||
pytest.raises(nx.NetworkXError, nx.read_gexf, fh)
|
||||
|
||||
def test_key_raises(self):
|
||||
s = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
|
||||
<graph mode="static" defaultedgetype="directed" name="">
|
||||
<nodes>
|
||||
<node id="0" label="Hello">
|
||||
<attvalues>
|
||||
<attvalue for='0' value='1'/>
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" type="undirected"/>
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
fh = io.BytesIO(s.encode("UTF-8"))
|
||||
pytest.raises(nx.NetworkXError, nx.read_gexf, fh)
|
||||
|
||||
def test_relabel(self):
|
||||
s = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
|
||||
<graph mode="static" defaultedgetype="directed" name="">
|
||||
<nodes>
|
||||
<node id="0" label="Hello" />
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1"/>
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
fh = io.BytesIO(s.encode("UTF-8"))
|
||||
G = nx.read_gexf(fh, relabel=True)
|
||||
assert sorted(G.nodes()) == ["Hello", "Word"]
|
||||
|
||||
def test_default_attribute(self):
|
||||
G = nx.Graph()
|
||||
G.add_node(1, label="1", color="green")
|
||||
nx.add_path(G, [0, 1, 2, 3])
|
||||
G.add_edge(1, 2, foo=3)
|
||||
G.graph["node_default"] = {"color": "yellow"}
|
||||
G.graph["edge_default"] = {"foo": 7}
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(sorted(e) for e in G.edges()) == sorted(
|
||||
sorted(e) for e in H.edges()
|
||||
)
|
||||
# Reading a gexf graph always sets mode attribute to either
|
||||
# 'static' or 'dynamic'. Remove the mode attribute from the
|
||||
# read graph for the sake of comparing remaining attributes.
|
||||
del H.graph["mode"]
|
||||
assert G.graph == H.graph
|
||||
|
||||
def test_serialize_ints_to_strings(self):
|
||||
G = nx.Graph()
|
||||
G.add_node(1, id=7, label=77)
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert list(H) == [7]
|
||||
assert H.nodes[7]["label"] == "77"
|
||||
|
||||
# FIXME: We should test xml without caring about their order This is causing a
|
||||
# problem b/c of a change in Python 3.8
|
||||
#
|
||||
# "Prior to Python 3.8, the serialisation order of the XML attributes of
|
||||
# elements was artificially made predictable by sorting the attributes by their
|
||||
# name. Based on the now guaranteed ordering of dicts, this arbitrary
|
||||
# reordering was removed in Python 3.8 to preserve the order in which
|
||||
# attributes were originally parsed or created by user code."
|
||||
#
|
||||
# https://docs.python.org/3.8/library/xml.etree.elementtree.html
|
||||
# https://bugs.python.org/issue34160
|
||||
|
||||
def test_write_with_node_attributes(self):
|
||||
# Addresses #673.
|
||||
G = nx.OrderedGraph()
|
||||
G.add_edges_from([(0, 1), (1, 2), (2, 3)])
|
||||
for i in range(4):
|
||||
G.nodes[i]["id"] = i
|
||||
G.nodes[i]["label"] = i
|
||||
G.nodes[i]["pid"] = i
|
||||
G.nodes[i]["start"] = i
|
||||
G.nodes[i]["end"] = i + 1
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
expected = f"""<gexf version="1.2" xmlns="http://www.gexf.net/1.2\
|
||||
draft" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:\
|
||||
schemaLocation="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/\
|
||||
gexf.xsd">
|
||||
<meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
|
||||
<creator>NetworkX {nx.__version__}</creator>
|
||||
</meta>
|
||||
<graph defaultedgetype="undirected" mode="dynamic" name="" timeformat="long">
|
||||
<nodes>
|
||||
<node end="1" id="0" label="0" pid="0" start="0" />
|
||||
<node end="2" id="1" label="1" pid="1" start="1" />
|
||||
<node end="3" id="2" label="2" pid="2" start="2" />
|
||||
<node end="4" id="3" label="3" pid="3" start="3" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" />
|
||||
<edge id="1" source="1" target="2" />
|
||||
<edge id="2" source="2" target="3" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>"""
|
||||
else:
|
||||
expected = f"""<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi\
|
||||
="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=\
|
||||
"http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/\
|
||||
gexf.xsd" version="1.2">
|
||||
<meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
|
||||
<creator>NetworkX {nx.__version__}</creator>
|
||||
</meta>
|
||||
<graph defaultedgetype="undirected" mode="dynamic" name="" timeformat="long">
|
||||
<nodes>
|
||||
<node id="0" label="0" pid="0" start="0" end="1" />
|
||||
<node id="1" label="1" pid="1" start="1" end="2" />
|
||||
<node id="2" label="2" pid="2" start="2" end="3" />
|
||||
<node id="3" label="3" pid="3" start="3" end="4" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge source="0" target="1" id="0" />
|
||||
<edge source="1" target="2" id="1" />
|
||||
<edge source="2" target="3" id="2" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>"""
|
||||
obtained = "\n".join(nx.generate_gexf(G))
|
||||
assert expected == obtained
|
||||
|
||||
def test_edge_id_construct(self):
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1, {"id": 0}), (1, 2, {"id": 2}), (2, 3)])
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
expected = f"""<gexf version="1.2" xmlns="http://www.gexf.net/\
|
||||
1.2draft" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:\
|
||||
schemaLocation="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/\
|
||||
gexf.xsd">
|
||||
<meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
|
||||
<creator>NetworkX {nx.__version__}</creator>
|
||||
</meta>
|
||||
<graph defaultedgetype="undirected" mode="static" name="">
|
||||
<nodes>
|
||||
<node id="0" label="0" />
|
||||
<node id="1" label="1" />
|
||||
<node id="2" label="2" />
|
||||
<node id="3" label="3" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" />
|
||||
<edge id="2" source="1" target="2" />
|
||||
<edge id="1" source="2" target="3" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>"""
|
||||
else:
|
||||
expected = f"""<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi\
|
||||
="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.\
|
||||
gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2">
|
||||
<meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
|
||||
<creator>NetworkX {nx.__version__}</creator>
|
||||
</meta>
|
||||
<graph defaultedgetype="undirected" mode="static" name="">
|
||||
<nodes>
|
||||
<node id="0" label="0" />
|
||||
<node id="1" label="1" />
|
||||
<node id="2" label="2" />
|
||||
<node id="3" label="3" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge source="0" target="1" id="0" />
|
||||
<edge source="1" target="2" id="2" />
|
||||
<edge source="2" target="3" id="1" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>"""
|
||||
|
||||
obtained = "\n".join(nx.generate_gexf(G))
|
||||
assert expected == obtained
|
||||
|
||||
def test_numpy_type(self):
|
||||
G = nx.path_graph(4)
|
||||
try:
|
||||
import numpy
|
||||
except ImportError:
|
||||
return
|
||||
nx.set_node_attributes(G, {n: n for n in numpy.arange(4)}, "number")
|
||||
G[0][1]["edge-number"] = numpy.float64(1.1)
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
expected = f"""<gexf version="1.2" xmlns="http://www.gexf.net/1.2draft"\
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation\
|
||||
="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd">
|
||||
<meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
|
||||
<creator>NetworkX {nx.__version__}</creator>
|
||||
</meta>
|
||||
<graph defaultedgetype="undirected" mode="static" name="">
|
||||
<attributes class="edge" mode="static">
|
||||
<attribute id="1" title="edge-number" type="float" />
|
||||
</attributes>
|
||||
<attributes class="node" mode="static">
|
||||
<attribute id="0" title="number" type="int" />
|
||||
</attributes>
|
||||
<nodes>
|
||||
<node id="0" label="0">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="0" />
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="1" label="1">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="1" />
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="2" label="2">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="2" />
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="3" label="3">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="3" />
|
||||
</attvalues>
|
||||
</node>
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1">
|
||||
<attvalues>
|
||||
<attvalue for="1" value="1.1" />
|
||||
</attvalues>
|
||||
</edge>
|
||||
<edge id="1" source="1" target="2" />
|
||||
<edge id="2" source="2" target="3" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>"""
|
||||
else:
|
||||
expected = f"""<gexf xmlns="http://www.gexf.net/1.2draft"\
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation\
|
||||
="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd"\
|
||||
version="1.2">
|
||||
<meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
|
||||
<creator>NetworkX {nx.__version__}</creator>
|
||||
</meta>
|
||||
<graph defaultedgetype="undirected" mode="static" name="">
|
||||
<attributes mode="static" class="edge">
|
||||
<attribute id="1" title="edge-number" type="float" />
|
||||
</attributes>
|
||||
<attributes mode="static" class="node">
|
||||
<attribute id="0" title="number" type="int" />
|
||||
</attributes>
|
||||
<nodes>
|
||||
<node id="0" label="0">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="0" />
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="1" label="1">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="1" />
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="2" label="2">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="2" />
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="3" label="3">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="3" />
|
||||
</attvalues>
|
||||
</node>
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge source="0" target="1" id="0">
|
||||
<attvalues>
|
||||
<attvalue for="1" value="1.1" />
|
||||
</attvalues>
|
||||
</edge>
|
||||
<edge source="1" target="2" id="1" />
|
||||
<edge source="2" target="3" id="2" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>"""
|
||||
obtained = "\n".join(nx.generate_gexf(G))
|
||||
assert expected == obtained
|
||||
|
||||
def test_bool(self):
|
||||
G = nx.Graph()
|
||||
G.add_node(1, testattr=True)
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert H.nodes[1]["testattr"]
|
||||
|
||||
# Test for NaN, INF and -INF
|
||||
def test_specials(self):
|
||||
from math import isnan
|
||||
|
||||
inf, nan = float("inf"), float("nan")
|
||||
G = nx.Graph()
|
||||
G.add_node(1, testattr=inf, strdata="inf", key="a")
|
||||
G.add_node(2, testattr=nan, strdata="nan", key="b")
|
||||
G.add_node(3, testattr=-inf, strdata="-inf", key="c")
|
||||
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
filetext = fh.read()
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
|
||||
assert b"INF" in filetext
|
||||
assert b"NaN" in filetext
|
||||
assert b"-INF" in filetext
|
||||
|
||||
assert H.nodes[1]["testattr"] == inf
|
||||
assert isnan(H.nodes[2]["testattr"])
|
||||
assert H.nodes[3]["testattr"] == -inf
|
||||
|
||||
assert H.nodes[1]["strdata"] == "inf"
|
||||
assert H.nodes[2]["strdata"] == "nan"
|
||||
assert H.nodes[3]["strdata"] == "-inf"
|
||||
|
||||
assert H.nodes[1]["networkx_key"] == "a"
|
||||
assert H.nodes[2]["networkx_key"] == "b"
|
||||
assert H.nodes[3]["networkx_key"] == "c"
|
||||
|
||||
def test_simple_list(self):
|
||||
G = nx.Graph()
|
||||
list_value = [(1, 2, 3), (9, 1, 2)]
|
||||
G.add_node(1, key=list_value)
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert H.nodes[1]["networkx_key"] == list_value
|
||||
|
||||
def test_dynamic_mode(self):
|
||||
G = nx.Graph()
|
||||
G.add_node(1, label="1", color="green")
|
||||
G.graph["mode"] = "dynamic"
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(sorted(e) for e in G.edges()) == sorted(
|
||||
sorted(e) for e in H.edges()
|
||||
)
|
||||
|
||||
def test_multigraph_with_missing_attributes(self):
|
||||
G = nx.MultiGraph()
|
||||
G.add_node(0, label="1", color="green")
|
||||
G.add_node(1, label="2", color="green")
|
||||
G.add_edge(0, 1, id="0", wight=3, type="undirected", start=0, end=1)
|
||||
G.add_edge(0, 1, id="1", label="foo", start=0, end=1)
|
||||
G.add_edge(0, 1)
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(sorted(e) for e in G.edges()) == sorted(
|
||||
sorted(e) for e in H.edges()
|
||||
)
|
||||
|
||||
def test_missing_viz_attributes(self):
|
||||
G = nx.Graph()
|
||||
G.add_node(0, label="1", color="green")
|
||||
G.nodes[0]["viz"] = {"size": 54}
|
||||
G.nodes[0]["viz"]["position"] = {"x": 0, "y": 1, "z": 0}
|
||||
G.nodes[0]["viz"]["color"] = {"r": 0, "g": 0, "b": 256}
|
||||
G.nodes[0]["viz"]["shape"] = "http://random.url"
|
||||
G.nodes[0]["viz"]["thickness"] = 2
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh, version="1.1draft")
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(sorted(e) for e in G.edges()) == sorted(
|
||||
sorted(e) for e in H.edges()
|
||||
)
|
||||
|
||||
# Second graph for the other branch
|
||||
G = nx.Graph()
|
||||
G.add_node(0, label="1", color="green")
|
||||
G.nodes[0]["viz"] = {"size": 54}
|
||||
G.nodes[0]["viz"]["position"] = {"x": 0, "y": 1, "z": 0}
|
||||
G.nodes[0]["viz"]["color"] = {"r": 0, "g": 0, "b": 256, "a": 0.5}
|
||||
G.nodes[0]["viz"]["shape"] = "ftp://random.url"
|
||||
G.nodes[0]["viz"]["thickness"] = 2
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(sorted(e) for e in G.edges()) == sorted(
|
||||
sorted(e) for e in H.edges()
|
||||
)
|
||||
|
||||
def test_slice_and_spell(self):
|
||||
# Test spell first, so version = 1.2
|
||||
G = nx.Graph()
|
||||
G.add_node(0, label="1", color="green")
|
||||
G.nodes[0]["spells"] = [(1, 2)]
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(sorted(e) for e in G.edges()) == sorted(
|
||||
sorted(e) for e in H.edges()
|
||||
)
|
||||
|
||||
G = nx.Graph()
|
||||
G.add_node(0, label="1", color="green")
|
||||
G.nodes[0]["slices"] = [(1, 2)]
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh, version="1.1draft")
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(sorted(e) for e in G.edges()) == sorted(
|
||||
sorted(e) for e in H.edges()
|
||||
)
|
||||
|
||||
def test_add_parent(self):
|
||||
G = nx.Graph()
|
||||
G.add_node(0, label="1", color="green", parents=[1, 2])
|
||||
fh = io.BytesIO()
|
||||
nx.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes()) == sorted(H.nodes())
|
||||
assert sorted(sorted(e) for e in G.edges()) == sorted(
|
||||
sorted(e) for e in H.edges()
|
||||
)
|
612
venv/Lib/site-packages/networkx/readwrite/tests/test_gml.py
Normal file
612
venv/Lib/site-packages/networkx/readwrite/tests/test_gml.py
Normal file
|
@ -0,0 +1,612 @@
|
|||
from ast import literal_eval
|
||||
import codecs
|
||||
from contextlib import contextmanager
|
||||
import io
|
||||
import pytest
|
||||
import networkx as nx
|
||||
from networkx.readwrite.gml import literal_stringizer, literal_destringizer
|
||||
import os
|
||||
import tempfile
|
||||
from textwrap import dedent
|
||||
|
||||
|
||||
class TestGraph:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.simple_data = """Creator "me"
|
||||
Version "xx"
|
||||
graph [
|
||||
comment "This is a sample graph"
|
||||
directed 1
|
||||
IsPlanar 1
|
||||
pos [ x 0 y 1 ]
|
||||
node [
|
||||
id 1
|
||||
label "Node 1"
|
||||
pos [ x 1 y 1 ]
|
||||
]
|
||||
node [
|
||||
id 2
|
||||
pos [ x 1 y 2 ]
|
||||
label "Node 2"
|
||||
]
|
||||
node [
|
||||
id 3
|
||||
label "Node 3"
|
||||
pos [ x 1 y 3 ]
|
||||
]
|
||||
edge [
|
||||
source 1
|
||||
target 2
|
||||
label "Edge from node 1 to node 2"
|
||||
color [line "blue" thickness 3]
|
||||
|
||||
]
|
||||
edge [
|
||||
source 2
|
||||
target 3
|
||||
label "Edge from node 2 to node 3"
|
||||
]
|
||||
edge [
|
||||
source 3
|
||||
target 1
|
||||
label "Edge from node 3 to node 1"
|
||||
]
|
||||
]
|
||||
"""
|
||||
|
||||
def test_parse_gml_cytoscape_bug(self):
|
||||
# example from issue #321, originally #324 in trac
|
||||
cytoscape_example = """
|
||||
Creator "Cytoscape"
|
||||
Version 1.0
|
||||
graph [
|
||||
node [
|
||||
root_index -3
|
||||
id -3
|
||||
graphics [
|
||||
x -96.0
|
||||
y -67.0
|
||||
w 40.0
|
||||
h 40.0
|
||||
fill "#ff9999"
|
||||
type "ellipse"
|
||||
outline "#666666"
|
||||
outline_width 1.5
|
||||
]
|
||||
label "node2"
|
||||
]
|
||||
node [
|
||||
root_index -2
|
||||
id -2
|
||||
graphics [
|
||||
x 63.0
|
||||
y 37.0
|
||||
w 40.0
|
||||
h 40.0
|
||||
fill "#ff9999"
|
||||
type "ellipse"
|
||||
outline "#666666"
|
||||
outline_width 1.5
|
||||
]
|
||||
label "node1"
|
||||
]
|
||||
node [
|
||||
root_index -1
|
||||
id -1
|
||||
graphics [
|
||||
x -31.0
|
||||
y -17.0
|
||||
w 40.0
|
||||
h 40.0
|
||||
fill "#ff9999"
|
||||
type "ellipse"
|
||||
outline "#666666"
|
||||
outline_width 1.5
|
||||
]
|
||||
label "node0"
|
||||
]
|
||||
edge [
|
||||
root_index -2
|
||||
target -2
|
||||
source -1
|
||||
graphics [
|
||||
width 1.5
|
||||
fill "#0000ff"
|
||||
type "line"
|
||||
Line [
|
||||
]
|
||||
source_arrow 0
|
||||
target_arrow 3
|
||||
]
|
||||
label "DirectedEdge"
|
||||
]
|
||||
edge [
|
||||
root_index -1
|
||||
target -1
|
||||
source -3
|
||||
graphics [
|
||||
width 1.5
|
||||
fill "#0000ff"
|
||||
type "line"
|
||||
Line [
|
||||
]
|
||||
source_arrow 0
|
||||
target_arrow 3
|
||||
]
|
||||
label "DirectedEdge"
|
||||
]
|
||||
]
|
||||
"""
|
||||
nx.parse_gml(cytoscape_example)
|
||||
|
||||
def test_parse_gml(self):
|
||||
G = nx.parse_gml(self.simple_data, label="label")
|
||||
assert sorted(G.nodes()) == ["Node 1", "Node 2", "Node 3"]
|
||||
assert [e for e in sorted(G.edges())] == [
|
||||
("Node 1", "Node 2"),
|
||||
("Node 2", "Node 3"),
|
||||
("Node 3", "Node 1"),
|
||||
]
|
||||
|
||||
assert [e for e in sorted(G.edges(data=True))] == [
|
||||
(
|
||||
"Node 1",
|
||||
"Node 2",
|
||||
{
|
||||
"color": {"line": "blue", "thickness": 3},
|
||||
"label": "Edge from node 1 to node 2",
|
||||
},
|
||||
),
|
||||
("Node 2", "Node 3", {"label": "Edge from node 2 to node 3"}),
|
||||
("Node 3", "Node 1", {"label": "Edge from node 3 to node 1"}),
|
||||
]
|
||||
|
||||
def test_read_gml(self):
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
fh = open(fname, "w")
|
||||
fh.write(self.simple_data)
|
||||
fh.close()
|
||||
Gin = nx.read_gml(fname, label="label")
|
||||
G = nx.parse_gml(self.simple_data, label="label")
|
||||
assert sorted(G.nodes(data=True)) == sorted(Gin.nodes(data=True))
|
||||
assert sorted(G.edges(data=True)) == sorted(Gin.edges(data=True))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_labels_are_strings(self):
|
||||
# GML requires labels to be strings (i.e., in quotes)
|
||||
answer = """graph [
|
||||
node [
|
||||
id 0
|
||||
label "1203"
|
||||
]
|
||||
]"""
|
||||
G = nx.Graph()
|
||||
G.add_node(1203)
|
||||
data = "\n".join(nx.generate_gml(G, stringizer=literal_stringizer))
|
||||
assert data == answer
|
||||
|
||||
def test_relabel_duplicate(self):
|
||||
data = """
|
||||
graph
|
||||
[
|
||||
label ""
|
||||
directed 1
|
||||
node
|
||||
[
|
||||
id 0
|
||||
label "same"
|
||||
]
|
||||
node
|
||||
[
|
||||
id 1
|
||||
label "same"
|
||||
]
|
||||
]
|
||||
"""
|
||||
fh = io.BytesIO(data.encode("UTF-8"))
|
||||
fh.seek(0)
|
||||
pytest.raises(nx.NetworkXError, nx.read_gml, fh, label="label")
|
||||
|
||||
def test_tuplelabels(self):
|
||||
# https://github.com/networkx/networkx/pull/1048
|
||||
# Writing tuple labels to GML failed.
|
||||
G = nx.OrderedGraph()
|
||||
G.add_edge((0, 1), (1, 0))
|
||||
data = "\n".join(nx.generate_gml(G, stringizer=literal_stringizer))
|
||||
answer = """graph [
|
||||
node [
|
||||
id 0
|
||||
label "(0,1)"
|
||||
]
|
||||
node [
|
||||
id 1
|
||||
label "(1,0)"
|
||||
]
|
||||
edge [
|
||||
source 0
|
||||
target 1
|
||||
]
|
||||
]"""
|
||||
assert data == answer
|
||||
|
||||
def test_quotes(self):
|
||||
# https://github.com/networkx/networkx/issues/1061
|
||||
# Encoding quotes as HTML entities.
|
||||
G = nx.path_graph(1)
|
||||
G.name = "path_graph(1)"
|
||||
attr = 'This is "quoted" and this is a copyright: ' + chr(169)
|
||||
G.nodes[0]["demo"] = attr
|
||||
fobj = tempfile.NamedTemporaryFile()
|
||||
nx.write_gml(G, fobj)
|
||||
fobj.seek(0)
|
||||
# Should be bytes in 2.x and 3.x
|
||||
data = fobj.read().strip().decode("ascii")
|
||||
answer = """graph [
|
||||
name "path_graph(1)"
|
||||
node [
|
||||
id 0
|
||||
label "0"
|
||||
demo "This is "quoted" and this is a copyright: ©"
|
||||
]
|
||||
]"""
|
||||
assert data == answer
|
||||
|
||||
def test_unicode_node(self):
|
||||
node = "node" + chr(169)
|
||||
G = nx.Graph()
|
||||
G.add_node(node)
|
||||
fobj = tempfile.NamedTemporaryFile()
|
||||
nx.write_gml(G, fobj)
|
||||
fobj.seek(0)
|
||||
# Should be bytes in 2.x and 3.x
|
||||
data = fobj.read().strip().decode("ascii")
|
||||
answer = """graph [
|
||||
node [
|
||||
id 0
|
||||
label "node©"
|
||||
]
|
||||
]"""
|
||||
assert data == answer
|
||||
|
||||
def test_float_label(self):
|
||||
node = 1.0
|
||||
G = nx.Graph()
|
||||
G.add_node(node)
|
||||
fobj = tempfile.NamedTemporaryFile()
|
||||
nx.write_gml(G, fobj)
|
||||
fobj.seek(0)
|
||||
# Should be bytes in 2.x and 3.x
|
||||
data = fobj.read().strip().decode("ascii")
|
||||
answer = """graph [
|
||||
node [
|
||||
id 0
|
||||
label "1.0"
|
||||
]
|
||||
]"""
|
||||
assert data == answer
|
||||
|
||||
def test_name(self):
|
||||
G = nx.parse_gml('graph [ name "x" node [ id 0 label "x" ] ]')
|
||||
assert "x" == G.graph["name"]
|
||||
G = nx.parse_gml('graph [ node [ id 0 label "x" ] ]')
|
||||
assert "" == G.name
|
||||
assert "name" not in G.graph
|
||||
|
||||
def test_graph_types(self):
|
||||
for directed in [None, False, True]:
|
||||
for multigraph in [None, False, True]:
|
||||
gml = "graph ["
|
||||
if directed is not None:
|
||||
gml += " directed " + str(int(directed))
|
||||
if multigraph is not None:
|
||||
gml += " multigraph " + str(int(multigraph))
|
||||
gml += ' node [ id 0 label "0" ]'
|
||||
gml += " edge [ source 0 target 0 ]"
|
||||
gml += " ]"
|
||||
G = nx.parse_gml(gml)
|
||||
assert bool(directed) == G.is_directed()
|
||||
assert bool(multigraph) == G.is_multigraph()
|
||||
gml = "graph [\n"
|
||||
if directed is True:
|
||||
gml += " directed 1\n"
|
||||
if multigraph is True:
|
||||
gml += " multigraph 1\n"
|
||||
gml += """ node [
|
||||
id 0
|
||||
label "0"
|
||||
]
|
||||
edge [
|
||||
source 0
|
||||
target 0
|
||||
"""
|
||||
if multigraph:
|
||||
gml += " key 0\n"
|
||||
gml += " ]\n]"
|
||||
assert gml == "\n".join(nx.generate_gml(G))
|
||||
|
||||
def test_data_types(self):
|
||||
data = [
|
||||
True,
|
||||
False,
|
||||
10 ** 20,
|
||||
-2e33,
|
||||
"'",
|
||||
'"&&&""',
|
||||
[{(b"\xfd",): "\x7f", chr(0x4444): (1, 2)}, (2, "3")],
|
||||
]
|
||||
try: # fails under IronPython
|
||||
data.append(chr(0x14444))
|
||||
except ValueError:
|
||||
data.append(chr(0x1444))
|
||||
data.append(literal_eval("{2.3j, 1 - 2.3j, ()}"))
|
||||
G = nx.Graph()
|
||||
G.name = data
|
||||
G.graph["data"] = data
|
||||
G.add_node(0, int=-1, data=dict(data=data))
|
||||
G.add_edge(0, 0, float=-2.5, data=data)
|
||||
gml = "\n".join(nx.generate_gml(G, stringizer=literal_stringizer))
|
||||
G = nx.parse_gml(gml, destringizer=literal_destringizer)
|
||||
assert data == G.name
|
||||
assert {"name": data, "data": data} == G.graph
|
||||
assert list(G.nodes(data=True)) == [(0, dict(int=-1, data=dict(data=data)))]
|
||||
assert list(G.edges(data=True)) == [(0, 0, dict(float=-2.5, data=data))]
|
||||
G = nx.Graph()
|
||||
G.graph["data"] = "frozenset([1, 2, 3])"
|
||||
G = nx.parse_gml(nx.generate_gml(G), destringizer=literal_eval)
|
||||
assert G.graph["data"] == "frozenset([1, 2, 3])"
|
||||
|
||||
def test_escape_unescape(self):
|
||||
gml = """graph [
|
||||
name "&"䑄��&unknown;"
|
||||
]"""
|
||||
G = nx.parse_gml(gml)
|
||||
assert (
|
||||
'&"\x0f' + chr(0x4444) + "��&unknown;"
|
||||
== G.name
|
||||
)
|
||||
gml = "\n".join(nx.generate_gml(G))
|
||||
alnu = "#1234567890;&#x1234567890abcdef"
|
||||
answer = (
|
||||
"""graph [
|
||||
name "&"䑄&"""
|
||||
+ alnu
|
||||
+ """;&unknown;"
|
||||
]"""
|
||||
)
|
||||
assert answer == gml
|
||||
|
||||
def test_exceptions(self):
|
||||
pytest.raises(ValueError, literal_destringizer, "(")
|
||||
pytest.raises(ValueError, literal_destringizer, "frozenset([1, 2, 3])")
|
||||
pytest.raises(ValueError, literal_destringizer, literal_destringizer)
|
||||
pytest.raises(ValueError, literal_stringizer, frozenset([1, 2, 3]))
|
||||
pytest.raises(ValueError, literal_stringizer, literal_stringizer)
|
||||
with tempfile.TemporaryFile() as f:
|
||||
f.write(codecs.BOM_UTF8 + b"graph[]")
|
||||
f.seek(0)
|
||||
pytest.raises(nx.NetworkXError, nx.read_gml, f)
|
||||
|
||||
def assert_parse_error(gml):
|
||||
pytest.raises(nx.NetworkXError, nx.parse_gml, gml)
|
||||
|
||||
assert_parse_error(["graph [\n\n", "]"])
|
||||
assert_parse_error("")
|
||||
assert_parse_error('Creator ""')
|
||||
assert_parse_error("0")
|
||||
assert_parse_error("graph ]")
|
||||
assert_parse_error("graph [ 1 ]")
|
||||
assert_parse_error("graph [ 1.E+2 ]")
|
||||
assert_parse_error('graph [ "A" ]')
|
||||
assert_parse_error("graph [ ] graph ]")
|
||||
assert_parse_error("graph [ ] graph [ ]")
|
||||
assert_parse_error("graph [ data [1, 2, 3] ]")
|
||||
assert_parse_error("graph [ node [ ] ]")
|
||||
assert_parse_error("graph [ node [ id 0 ] ]")
|
||||
nx.parse_gml('graph [ node [ id "a" ] ]', label="id")
|
||||
assert_parse_error("graph [ node [ id 0 label 0 ] node [ id 0 label 1 ] ]")
|
||||
assert_parse_error("graph [ node [ id 0 label 0 ] node [ id 1 label 0 ] ]")
|
||||
assert_parse_error("graph [ node [ id 0 label 0 ] edge [ ] ]")
|
||||
assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 0 ] ]")
|
||||
nx.parse_gml("graph [edge [ source 0 target 0 ] node [ id 0 label 0 ] ]")
|
||||
assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 1 target 0 ] ]")
|
||||
assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 0 target 1 ] ]")
|
||||
assert_parse_error(
|
||||
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
|
||||
"edge [ source 0 target 1 ] edge [ source 1 target 0 ] ]"
|
||||
)
|
||||
nx.parse_gml(
|
||||
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
|
||||
"edge [ source 0 target 1 ] edge [ source 1 target 0 ] "
|
||||
"directed 1 ]"
|
||||
)
|
||||
nx.parse_gml(
|
||||
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
|
||||
"edge [ source 0 target 1 ] edge [ source 0 target 1 ]"
|
||||
"multigraph 1 ]"
|
||||
)
|
||||
nx.parse_gml(
|
||||
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
|
||||
"edge [ source 0 target 1 key 0 ] edge [ source 0 target 1 ]"
|
||||
"multigraph 1 ]"
|
||||
)
|
||||
assert_parse_error(
|
||||
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
|
||||
"edge [ source 0 target 1 key 0 ] edge [ source 0 target 1 key 0 ]"
|
||||
"multigraph 1 ]"
|
||||
)
|
||||
nx.parse_gml(
|
||||
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
|
||||
"edge [ source 0 target 1 key 0 ] edge [ source 1 target 0 key 0 ]"
|
||||
"directed 1 multigraph 1 ]"
|
||||
)
|
||||
|
||||
# Tests for string convertable alphanumeric id and label values
|
||||
nx.parse_gml("graph [edge [ source a target a ] node [ id a label b ] ]")
|
||||
nx.parse_gml(
|
||||
"graph [ node [ id n42 label 0 ] node [ id x43 label 1 ]"
|
||||
"edge [ source n42 target x43 key 0 ]"
|
||||
"edge [ source x43 target n42 key 0 ]"
|
||||
"directed 1 multigraph 1 ]"
|
||||
)
|
||||
assert_parse_error(
|
||||
"graph [edge [ source u'u\4200' target u'u\4200' ] "
|
||||
+ "node [ id u'u\4200' label b ] ]"
|
||||
)
|
||||
|
||||
def assert_generate_error(*args, **kwargs):
|
||||
pytest.raises(
|
||||
nx.NetworkXError, lambda: list(nx.generate_gml(*args, **kwargs))
|
||||
)
|
||||
|
||||
G = nx.Graph()
|
||||
G.graph[3] = 3
|
||||
assert_generate_error(G)
|
||||
G = nx.Graph()
|
||||
G.graph["3"] = 3
|
||||
assert_generate_error(G)
|
||||
G = nx.Graph()
|
||||
G.graph["data"] = frozenset([1, 2, 3])
|
||||
assert_generate_error(G, stringizer=literal_stringizer)
|
||||
G = nx.Graph()
|
||||
G.graph["data"] = []
|
||||
assert_generate_error(G)
|
||||
assert_generate_error(G, stringizer=len)
|
||||
|
||||
def test_label_kwarg(self):
|
||||
G = nx.parse_gml(self.simple_data, label="id")
|
||||
assert sorted(G.nodes) == [1, 2, 3]
|
||||
labels = [G.nodes[n]["label"] for n in sorted(G.nodes)]
|
||||
assert labels == ["Node 1", "Node 2", "Node 3"]
|
||||
|
||||
G = nx.parse_gml(self.simple_data, label=None)
|
||||
assert sorted(G.nodes) == [1, 2, 3]
|
||||
labels = [G.nodes[n]["label"] for n in sorted(G.nodes)]
|
||||
assert labels == ["Node 1", "Node 2", "Node 3"]
|
||||
|
||||
def test_outofrange_integers(self):
|
||||
# GML restricts integers to 32 signed bits.
|
||||
# Check that we honor this restriction on export
|
||||
G = nx.Graph()
|
||||
# Test export for numbers that barely fit or don't fit into 32 bits,
|
||||
# and 3 numbers in the middle
|
||||
numbers = {
|
||||
"toosmall": (-(2 ** 31)) - 1,
|
||||
"small": -(2 ** 31),
|
||||
"med1": -4,
|
||||
"med2": 0,
|
||||
"med3": 17,
|
||||
"big": (2 ** 31) - 1,
|
||||
"toobig": 2 ** 31,
|
||||
}
|
||||
G.add_node("Node", **numbers)
|
||||
|
||||
fd, fname = tempfile.mkstemp()
|
||||
try:
|
||||
nx.write_gml(G, fname)
|
||||
# Check that the export wrote the nonfitting numbers as strings
|
||||
G2 = nx.read_gml(fname)
|
||||
for attr, value in G2.nodes["Node"].items():
|
||||
if attr == "toosmall" or attr == "toobig":
|
||||
assert type(value) == str
|
||||
else:
|
||||
assert type(value) == int
|
||||
finally:
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def byte_file():
|
||||
_file_handle = io.BytesIO()
|
||||
yield _file_handle
|
||||
_file_handle.seek(0)
|
||||
|
||||
|
||||
class TestPropertyLists:
|
||||
def test_writing_graph_with_multi_element_property_list(self):
|
||||
g = nx.Graph()
|
||||
g.add_node("n1", properties=["element", 0, 1, 2.5, True, False])
|
||||
with byte_file() as f:
|
||||
nx.write_gml(g, f)
|
||||
result = f.read().decode()
|
||||
|
||||
assert result == dedent(
|
||||
"""\
|
||||
graph [
|
||||
node [
|
||||
id 0
|
||||
label "n1"
|
||||
properties "element"
|
||||
properties 0
|
||||
properties 1
|
||||
properties 2.5
|
||||
properties 1
|
||||
properties 0
|
||||
]
|
||||
]
|
||||
"""
|
||||
)
|
||||
|
||||
def test_writing_graph_with_one_element_property_list(self):
|
||||
g = nx.Graph()
|
||||
g.add_node("n1", properties=["element"])
|
||||
with byte_file() as f:
|
||||
nx.write_gml(g, f)
|
||||
result = f.read().decode()
|
||||
|
||||
assert result == dedent(
|
||||
"""\
|
||||
graph [
|
||||
node [
|
||||
id 0
|
||||
label "n1"
|
||||
properties "_networkx_list_start"
|
||||
properties "element"
|
||||
]
|
||||
]
|
||||
"""
|
||||
)
|
||||
|
||||
def test_reading_graph_with_list_property(self):
|
||||
with byte_file() as f:
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
graph [
|
||||
node [
|
||||
id 0
|
||||
label "n1"
|
||||
properties "element"
|
||||
properties 0
|
||||
properties 1
|
||||
properties 2.5
|
||||
]
|
||||
]
|
||||
"""
|
||||
).encode("ascii")
|
||||
)
|
||||
f.seek(0)
|
||||
graph = nx.read_gml(f)
|
||||
assert graph.nodes(data=True)["n1"] == {"properties": ["element", 0, 1, 2.5]}
|
||||
|
||||
def test_reading_graph_with_single_element_list_property(self):
|
||||
with byte_file() as f:
|
||||
f.write(
|
||||
dedent(
|
||||
"""
|
||||
graph [
|
||||
node [
|
||||
id 0
|
||||
label "n1"
|
||||
properties "_networkx_list_start"
|
||||
properties "element"
|
||||
]
|
||||
]
|
||||
"""
|
||||
).encode("ascii")
|
||||
)
|
||||
f.seek(0)
|
||||
graph = nx.read_gml(f)
|
||||
assert graph.nodes(data=True)["n1"] == {"properties": ["element"]}
|
|
@ -0,0 +1,79 @@
|
|||
import os
|
||||
import tempfile
|
||||
|
||||
import networkx as nx
|
||||
from networkx.testing.utils import (
|
||||
assert_graphs_equal,
|
||||
assert_edges_equal,
|
||||
assert_nodes_equal,
|
||||
)
|
||||
|
||||
|
||||
class TestGpickle:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
G = nx.Graph(name="test")
|
||||
e = [("a", "b"), ("b", "c"), ("c", "d"), ("d", "e"), ("e", "f"), ("a", "f")]
|
||||
G.add_edges_from(e, width=10)
|
||||
G.add_node("g", color="green")
|
||||
G.graph["number"] = 1
|
||||
DG = nx.DiGraph(G)
|
||||
MG = nx.MultiGraph(G)
|
||||
MG.add_edge("a", "a")
|
||||
MDG = nx.MultiDiGraph(G)
|
||||
MDG.add_edge("a", "a")
|
||||
fG = G.copy()
|
||||
fDG = DG.copy()
|
||||
fMG = MG.copy()
|
||||
fMDG = MDG.copy()
|
||||
nx.freeze(fG)
|
||||
nx.freeze(fDG)
|
||||
nx.freeze(fMG)
|
||||
nx.freeze(fMDG)
|
||||
cls.G = G
|
||||
cls.DG = DG
|
||||
cls.MG = MG
|
||||
cls.MDG = MDG
|
||||
cls.fG = fG
|
||||
cls.fDG = fDG
|
||||
cls.fMG = fMG
|
||||
cls.fMDG = fMDG
|
||||
|
||||
def test_gpickle(self):
|
||||
for G in [
|
||||
self.G,
|
||||
self.DG,
|
||||
self.MG,
|
||||
self.MDG,
|
||||
self.fG,
|
||||
self.fDG,
|
||||
self.fMG,
|
||||
self.fMDG,
|
||||
]:
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_gpickle(G, fname)
|
||||
Gin = nx.read_gpickle(fname)
|
||||
assert_nodes_equal(list(G.nodes(data=True)), list(Gin.nodes(data=True)))
|
||||
assert_edges_equal(list(G.edges(data=True)), list(Gin.edges(data=True)))
|
||||
assert_graphs_equal(G, Gin)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_protocol(self):
|
||||
for G in [
|
||||
self.G,
|
||||
self.DG,
|
||||
self.MG,
|
||||
self.MDG,
|
||||
self.fG,
|
||||
self.fDG,
|
||||
self.fMG,
|
||||
self.fMDG,
|
||||
]:
|
||||
with tempfile.TemporaryFile() as f:
|
||||
nx.write_gpickle(G, f, 0)
|
||||
f.seek(0)
|
||||
Gin = nx.read_gpickle(f)
|
||||
assert_nodes_equal(list(G.nodes(data=True)), list(Gin.nodes(data=True)))
|
||||
assert_edges_equal(list(G.edges(data=True)), list(Gin.edges(data=True)))
|
||||
assert_graphs_equal(G, Gin)
|
118
venv/Lib/site-packages/networkx/readwrite/tests/test_graph6.py
Normal file
118
venv/Lib/site-packages/networkx/readwrite/tests/test_graph6.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
from io import BytesIO
|
||||
import tempfile
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
import networkx.readwrite.graph6 as g6
|
||||
from networkx.testing.utils import assert_edges_equal
|
||||
from networkx.testing.utils import assert_nodes_equal
|
||||
|
||||
|
||||
class TestGraph6Utils:
|
||||
def test_n_data_n_conversion(self):
|
||||
for i in [0, 1, 42, 62, 63, 64, 258047, 258048, 7744773, 68719476735]:
|
||||
assert g6.data_to_n(g6.n_to_data(i))[0] == i
|
||||
assert g6.data_to_n(g6.n_to_data(i))[1] == []
|
||||
assert g6.data_to_n(g6.n_to_data(i) + [42, 43])[1] == [42, 43]
|
||||
|
||||
|
||||
class TestFromGraph6Bytes:
|
||||
def test_from_graph6_bytes(self):
|
||||
data = b"DF{"
|
||||
G = nx.from_graph6_bytes(data)
|
||||
assert_nodes_equal(G.nodes(), [0, 1, 2, 3, 4])
|
||||
assert_edges_equal(
|
||||
G.edges(), [(0, 3), (0, 4), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
|
||||
)
|
||||
|
||||
def test_read_equals_from_bytes(self):
|
||||
data = b"DF{"
|
||||
G = nx.from_graph6_bytes(data)
|
||||
fh = BytesIO(data)
|
||||
Gin = nx.read_graph6(fh)
|
||||
assert_nodes_equal(G.nodes(), Gin.nodes())
|
||||
assert_edges_equal(G.edges(), Gin.edges())
|
||||
|
||||
|
||||
class TestReadGraph6:
|
||||
def test_read_many_graph6(self):
|
||||
"""Test for reading many graphs from a file into a list."""
|
||||
data = b"DF{\nD`{\nDqK\nD~{\n"
|
||||
fh = BytesIO(data)
|
||||
glist = nx.read_graph6(fh)
|
||||
assert len(glist) == 4
|
||||
for G in glist:
|
||||
assert sorted(G) == list(range(5))
|
||||
|
||||
|
||||
class TestWriteGraph6:
|
||||
"""Unit tests for writing a graph to a file in graph6 format."""
|
||||
|
||||
def test_null_graph(self):
|
||||
result = BytesIO()
|
||||
nx.write_graph6(nx.null_graph(), result)
|
||||
assert result.getvalue() == b">>graph6<<?\n"
|
||||
|
||||
def test_trivial_graph(self):
|
||||
result = BytesIO()
|
||||
nx.write_graph6(nx.trivial_graph(), result)
|
||||
assert result.getvalue() == b">>graph6<<@\n"
|
||||
|
||||
def test_complete_graph(self):
|
||||
result = BytesIO()
|
||||
nx.write_graph6(nx.complete_graph(4), result)
|
||||
assert result.getvalue() == b">>graph6<<C~\n"
|
||||
|
||||
def test_large_complete_graph(self):
|
||||
result = BytesIO()
|
||||
nx.write_graph6(nx.complete_graph(67), result, header=False)
|
||||
assert result.getvalue() == b"~?@B" + b"~" * 368 + b"w\n"
|
||||
|
||||
def test_no_header(self):
|
||||
result = BytesIO()
|
||||
nx.write_graph6(nx.complete_graph(4), result, header=False)
|
||||
assert result.getvalue() == b"C~\n"
|
||||
|
||||
def test_complete_bipartite_graph(self):
|
||||
result = BytesIO()
|
||||
G = nx.complete_bipartite_graph(6, 9)
|
||||
nx.write_graph6(G, result, header=False)
|
||||
# The expected encoding here was verified by Sage.
|
||||
assert result.getvalue() == b"N??F~z{~Fw^_~?~?^_?\n"
|
||||
|
||||
def test_no_directed_graphs(self):
|
||||
with pytest.raises(nx.NetworkXNotImplemented):
|
||||
nx.write_graph6(nx.DiGraph(), BytesIO())
|
||||
|
||||
def test_length(self):
|
||||
for i in list(range(13)) + [31, 47, 62, 63, 64, 72]:
|
||||
g = nx.random_graphs.gnm_random_graph(i, i * i // 4, seed=i)
|
||||
gstr = BytesIO()
|
||||
nx.write_graph6(g, gstr, header=False)
|
||||
# Strip the trailing newline.
|
||||
gstr = gstr.getvalue().rstrip()
|
||||
assert len(gstr) == ((i - 1) * i // 2 + 5) // 6 + (1 if i < 63 else 4)
|
||||
|
||||
def test_roundtrip(self):
|
||||
for i in list(range(13)) + [31, 47, 62, 63, 64, 72]:
|
||||
G = nx.random_graphs.gnm_random_graph(i, i * i // 4, seed=i)
|
||||
f = BytesIO()
|
||||
nx.write_graph6(G, f)
|
||||
f.seek(0)
|
||||
H = nx.read_graph6(f)
|
||||
assert_nodes_equal(G.nodes(), H.nodes())
|
||||
assert_edges_equal(G.edges(), H.edges())
|
||||
|
||||
def test_write_path(self):
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
g6.write_graph6_file(nx.null_graph(), f)
|
||||
f.seek(0)
|
||||
assert f.read() == b">>graph6<<?\n"
|
||||
|
||||
def test_relabeling(self):
|
||||
G = nx.Graph([(0, 1)])
|
||||
assert g6.to_graph6_bytes(G) == b">>graph6<<A_\n"
|
||||
G = nx.Graph([(1, 2)])
|
||||
assert g6.to_graph6_bytes(G) == b">>graph6<<A_\n"
|
||||
G = nx.Graph([(1, 42)])
|
||||
assert g6.to_graph6_bytes(G) == b">>graph6<<A_\n"
|
1254
venv/Lib/site-packages/networkx/readwrite/tests/test_graphml.py
Normal file
1254
venv/Lib/site-packages/networkx/readwrite/tests/test_graphml.py
Normal file
File diff suppressed because it is too large
Load diff
29
venv/Lib/site-packages/networkx/readwrite/tests/test_leda.py
Normal file
29
venv/Lib/site-packages/networkx/readwrite/tests/test_leda.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
import networkx as nx
|
||||
import io
|
||||
|
||||
|
||||
class TestLEDA:
|
||||
def test_parse_leda(self):
|
||||
data = """#header section \nLEDA.GRAPH \nstring\nint\n-1\n#nodes section\n5 \n|{v1}| \n|{v2}| \n|{v3}| \n|{v4}| \n|{v5}| \n\n#edges section\n7 \n1 2 0 |{4}| \n1 3 0 |{3}| \n2 3 0 |{2}| \n3 4 0 |{3}| \n3 5 0 |{7}| \n4 5 0 |{6}| \n5 1 0 |{foo}|"""
|
||||
G = nx.parse_leda(data)
|
||||
G = nx.parse_leda(data.split("\n"))
|
||||
assert sorted(G.nodes()) == ["v1", "v2", "v3", "v4", "v5"]
|
||||
assert sorted(G.edges(data=True)) == [
|
||||
("v1", "v2", {"label": "4"}),
|
||||
("v1", "v3", {"label": "3"}),
|
||||
("v2", "v3", {"label": "2"}),
|
||||
("v3", "v4", {"label": "3"}),
|
||||
("v3", "v5", {"label": "7"}),
|
||||
("v4", "v5", {"label": "6"}),
|
||||
("v5", "v1", {"label": "foo"}),
|
||||
]
|
||||
|
||||
def test_read_LEDA(self):
|
||||
fh = io.BytesIO()
|
||||
data = """#header section \nLEDA.GRAPH \nstring\nint\n-1\n#nodes section\n5 \n|{v1}| \n|{v2}| \n|{v3}| \n|{v4}| \n|{v5}| \n\n#edges section\n7 \n1 2 0 |{4}| \n1 3 0 |{3}| \n2 3 0 |{2}| \n3 4 0 |{3}| \n3 5 0 |{7}| \n4 5 0 |{6}| \n5 1 0 |{foo}|"""
|
||||
G = nx.parse_leda(data)
|
||||
fh.write(data.encode("UTF-8"))
|
||||
fh.seek(0)
|
||||
Gin = nx.read_leda(fh)
|
||||
assert sorted(G.nodes()) == sorted(Gin.nodes())
|
||||
assert sorted(G.edges()) == sorted(Gin.edges())
|
61
venv/Lib/site-packages/networkx/readwrite/tests/test_p2g.py
Normal file
61
venv/Lib/site-packages/networkx/readwrite/tests/test_p2g.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
import networkx as nx
|
||||
import io
|
||||
from networkx.readwrite.p2g import read_p2g, write_p2g
|
||||
from networkx.testing import assert_edges_equal
|
||||
|
||||
|
||||
class TestP2G:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = nx.Graph(name="test")
|
||||
e = [("a", "b"), ("b", "c"), ("c", "d"), ("d", "e"), ("e", "f"), ("a", "f")]
|
||||
cls.G.add_edges_from(e)
|
||||
cls.G.add_node("g")
|
||||
cls.DG = nx.DiGraph(cls.G)
|
||||
|
||||
def test_read_p2g(self):
|
||||
s = b"""\
|
||||
name
|
||||
3 4
|
||||
a
|
||||
1 2
|
||||
b
|
||||
|
||||
c
|
||||
0 2
|
||||
"""
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = read_p2g(bytesIO)
|
||||
assert G.name == "name"
|
||||
assert sorted(G) == ["a", "b", "c"]
|
||||
edges = [(str(u), str(v)) for u, v in G.edges()]
|
||||
assert_edges_equal(G.edges(), [("a", "c"), ("a", "b"), ("c", "a"), ("c", "c")])
|
||||
|
||||
def test_write_p2g(self):
|
||||
s = b"""foo
|
||||
3 2
|
||||
1
|
||||
1
|
||||
2
|
||||
2
|
||||
3
|
||||
|
||||
"""
|
||||
fh = io.BytesIO()
|
||||
G = nx.OrderedDiGraph()
|
||||
G.name = "foo"
|
||||
G.add_edges_from([(1, 2), (2, 3)])
|
||||
write_p2g(G, fh)
|
||||
fh.seek(0)
|
||||
r = fh.read()
|
||||
assert r == s
|
||||
|
||||
def test_write_read_p2g(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.DiGraph()
|
||||
G.name = "foo"
|
||||
G.add_edges_from([("a", "b"), ("b", "c")])
|
||||
write_p2g(G, fh)
|
||||
fh.seek(0)
|
||||
H = read_p2g(fh)
|
||||
assert_edges_equal(G.edges(), H.edges())
|
129
venv/Lib/site-packages/networkx/readwrite/tests/test_pajek.py
Normal file
129
venv/Lib/site-packages/networkx/readwrite/tests/test_pajek.py
Normal file
|
@ -0,0 +1,129 @@
|
|||
"""
|
||||
Pajek tests
|
||||
"""
|
||||
import networkx as nx
|
||||
import os
|
||||
import tempfile
|
||||
from networkx.testing import assert_edges_equal, assert_nodes_equal
|
||||
|
||||
|
||||
class TestPajek:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.data = """*network Tralala\n*vertices 4\n 1 "A1" 0.0938 0.0896 ellipse x_fact 1 y_fact 1\n 2 "Bb" 0.8188 0.2458 ellipse x_fact 1 y_fact 1\n 3 "C" 0.3688 0.7792 ellipse x_fact 1\n 4 "D2" 0.9583 0.8563 ellipse x_fact 1\n*arcs\n1 1 1 h2 0 w 3 c Blue s 3 a1 -130 k1 0.6 a2 -130 k2 0.6 ap 0.5 l "Bezier loop" lc BlueViolet fos 20 lr 58 lp 0.3 la 360\n2 1 1 h2 0 a1 120 k1 1.3 a2 -120 k2 0.3 ap 25 l "Bezier arc" lphi 270 la 180 lr 19 lp 0.5\n1 2 1 h2 0 a1 40 k1 2.8 a2 30 k2 0.8 ap 25 l "Bezier arc" lphi 90 la 0 lp 0.65\n4 2 -1 h2 0 w 1 k1 -2 k2 250 ap 25 l "Circular arc" c Red lc OrangeRed\n3 4 1 p Dashed h2 0 w 2 c OliveGreen ap 25 l "Straight arc" lc PineGreen\n1 3 1 p Dashed h2 0 w 5 k1 -1 k2 -20 ap 25 l "Oval arc" c Brown lc Black\n3 3 -1 h1 6 w 1 h2 12 k1 -2 k2 -15 ap 0.5 l "Circular loop" c Red lc OrangeRed lphi 270 la 180"""
|
||||
cls.G = nx.MultiDiGraph()
|
||||
cls.G.add_nodes_from(["A1", "Bb", "C", "D2"])
|
||||
cls.G.add_edges_from(
|
||||
[
|
||||
("A1", "A1"),
|
||||
("A1", "Bb"),
|
||||
("A1", "C"),
|
||||
("Bb", "A1"),
|
||||
("C", "C"),
|
||||
("C", "D2"),
|
||||
("D2", "Bb"),
|
||||
]
|
||||
)
|
||||
|
||||
cls.G.graph["name"] = "Tralala"
|
||||
(fd, cls.fname) = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "wb") as fh:
|
||||
fh.write(cls.data.encode("UTF-8"))
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
os.unlink(cls.fname)
|
||||
|
||||
def test_parse_pajek_simple(self):
|
||||
# Example without node positions or shape
|
||||
data = """*Vertices 2\n1 "1"\n2 "2"\n*Edges\n1 2\n2 1"""
|
||||
G = nx.parse_pajek(data)
|
||||
assert sorted(G.nodes()) == ["1", "2"]
|
||||
assert_edges_equal(G.edges(), [("1", "2"), ("1", "2")])
|
||||
|
||||
def test_parse_pajek(self):
|
||||
G = nx.parse_pajek(self.data)
|
||||
assert sorted(G.nodes()) == ["A1", "Bb", "C", "D2"]
|
||||
assert_edges_equal(
|
||||
G.edges(),
|
||||
[
|
||||
("A1", "A1"),
|
||||
("A1", "Bb"),
|
||||
("A1", "C"),
|
||||
("Bb", "A1"),
|
||||
("C", "C"),
|
||||
("C", "D2"),
|
||||
("D2", "Bb"),
|
||||
],
|
||||
)
|
||||
|
||||
def test_parse_pajet_mat(self):
|
||||
data = """*Vertices 3\n1 "one"\n2 "two"\n3 "three"\n*Matrix\n1 1 0\n0 1 0\n0 1 0\n"""
|
||||
G = nx.parse_pajek(data)
|
||||
assert set(G.nodes()) == {"one", "two", "three"}
|
||||
assert G.nodes["two"] == {"id": "2"}
|
||||
assert_edges_equal(
|
||||
set(G.edges()),
|
||||
{("one", "one"), ("two", "one"), ("two", "two"), ("two", "three")},
|
||||
)
|
||||
|
||||
def test_read_pajek(self):
|
||||
G = nx.parse_pajek(self.data)
|
||||
Gin = nx.read_pajek(self.fname)
|
||||
assert sorted(G.nodes()) == sorted(Gin.nodes())
|
||||
assert_edges_equal(G.edges(), Gin.edges())
|
||||
assert self.G.graph == Gin.graph
|
||||
for n in G:
|
||||
assert G.nodes[n] == Gin.nodes[n]
|
||||
|
||||
def test_write_pajek(self):
|
||||
import io
|
||||
|
||||
G = nx.parse_pajek(self.data)
|
||||
fh = io.BytesIO()
|
||||
nx.write_pajek(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_pajek(fh)
|
||||
assert_nodes_equal(list(G), list(H))
|
||||
assert_edges_equal(list(G.edges()), list(H.edges()))
|
||||
# Graph name is left out for now, therefore it is not tested.
|
||||
# assert_equal(G.graph, H.graph)
|
||||
|
||||
def test_ignored_attribute(self):
|
||||
import io
|
||||
|
||||
G = nx.Graph()
|
||||
fh = io.BytesIO()
|
||||
G.add_node(1, int_attr=1)
|
||||
G.add_node(2, empty_attr=" ")
|
||||
G.add_edge(1, 2, int_attr=2)
|
||||
G.add_edge(2, 3, empty_attr=" ")
|
||||
|
||||
import warnings
|
||||
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
nx.write_pajek(G, fh)
|
||||
assert len(w) == 4
|
||||
|
||||
def test_noname(self):
|
||||
# Make sure we can parse a line such as: *network
|
||||
# Issue #952
|
||||
line = "*network\n"
|
||||
other_lines = self.data.split("\n")[1:]
|
||||
data = line + "\n".join(other_lines)
|
||||
G = nx.parse_pajek(data)
|
||||
|
||||
def test_unicode(self):
|
||||
import io
|
||||
|
||||
G = nx.Graph()
|
||||
name1 = chr(2344) + chr(123) + chr(6543)
|
||||
name2 = chr(5543) + chr(1543) + chr(324)
|
||||
G.add_edge(name1, "Radiohead", foo=name2)
|
||||
fh = io.BytesIO()
|
||||
nx.write_pajek(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.read_pajek(fh)
|
||||
assert_nodes_equal(list(G), list(H))
|
||||
assert_edges_equal(list(G.edges()), list(H.edges()))
|
||||
assert G.graph == H.graph
|
287
venv/Lib/site-packages/networkx/readwrite/tests/test_shp.py
Normal file
287
venv/Lib/site-packages/networkx/readwrite/tests/test_shp.py
Normal file
|
@ -0,0 +1,287 @@
|
|||
"""Unit tests for shp.
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import pytest
|
||||
|
||||
ogr = pytest.importorskip("osgeo.ogr")
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestShp:
|
||||
def setup_method(self):
|
||||
def createlayer(driver, layerType=ogr.wkbLineString):
|
||||
lyr = driver.CreateLayer("edges", None, layerType)
|
||||
namedef = ogr.FieldDefn("Name", ogr.OFTString)
|
||||
namedef.SetWidth(32)
|
||||
lyr.CreateField(namedef)
|
||||
return lyr
|
||||
|
||||
drv = ogr.GetDriverByName("ESRI Shapefile")
|
||||
|
||||
testdir = os.path.join(tempfile.gettempdir(), "shpdir")
|
||||
shppath = os.path.join(tempfile.gettempdir(), "tmpshp.shp")
|
||||
multi_shppath = os.path.join(tempfile.gettempdir(), "tmp_mshp.shp")
|
||||
|
||||
self.deletetmp(drv, testdir, shppath, multi_shppath)
|
||||
os.mkdir(testdir)
|
||||
|
||||
self.names = ["a", "b", "c", "c"] # edgenames
|
||||
self.paths = (
|
||||
[(1.0, 1.0), (2.0, 2.0)],
|
||||
[(2.0, 2.0), (3.0, 3.0)],
|
||||
[(0.9, 0.9), (4.0, 0.9), (4.0, 2.0)],
|
||||
)
|
||||
|
||||
self.simplified_names = ["a", "b", "c"] # edgenames
|
||||
self.simplified_paths = (
|
||||
[(1.0, 1.0), (2.0, 2.0)],
|
||||
[(2.0, 2.0), (3.0, 3.0)],
|
||||
[(0.9, 0.9), (4.0, 2.0)],
|
||||
)
|
||||
|
||||
self.multi_names = ["a", "a", "a", "a"] # edgenames
|
||||
|
||||
shp = drv.CreateDataSource(shppath)
|
||||
lyr = createlayer(shp)
|
||||
|
||||
for path, name in zip(self.paths, self.names):
|
||||
feat = ogr.Feature(lyr.GetLayerDefn())
|
||||
g = ogr.Geometry(ogr.wkbLineString)
|
||||
for p in path:
|
||||
g.AddPoint_2D(*p)
|
||||
feat.SetGeometry(g)
|
||||
feat.SetField("Name", name)
|
||||
lyr.CreateFeature(feat)
|
||||
|
||||
# create single record multiline shapefile for testing
|
||||
multi_shp = drv.CreateDataSource(multi_shppath)
|
||||
multi_lyr = createlayer(multi_shp, ogr.wkbMultiLineString)
|
||||
|
||||
multi_g = ogr.Geometry(ogr.wkbMultiLineString)
|
||||
for path in self.paths:
|
||||
|
||||
g = ogr.Geometry(ogr.wkbLineString)
|
||||
for p in path:
|
||||
g.AddPoint_2D(*p)
|
||||
|
||||
multi_g.AddGeometry(g)
|
||||
|
||||
multi_feat = ogr.Feature(multi_lyr.GetLayerDefn())
|
||||
multi_feat.SetGeometry(multi_g)
|
||||
multi_feat.SetField("Name", "a")
|
||||
multi_lyr.CreateFeature(multi_feat)
|
||||
|
||||
self.shppath = shppath
|
||||
self.multi_shppath = multi_shppath
|
||||
self.testdir = testdir
|
||||
self.drv = drv
|
||||
|
||||
def deletetmp(self, drv, *paths):
|
||||
for p in paths:
|
||||
if os.path.exists(p):
|
||||
drv.DeleteDataSource(p)
|
||||
|
||||
def testload(self):
|
||||
def compare_graph_paths_names(g, paths, names):
|
||||
expected = nx.DiGraph()
|
||||
for p in paths:
|
||||
nx.add_path(expected, p)
|
||||
assert sorted(expected.nodes) == sorted(g.nodes)
|
||||
assert sorted(expected.edges()) == sorted(g.edges())
|
||||
g_names = [g.get_edge_data(s, e)["Name"] for s, e in g.edges()]
|
||||
assert names == sorted(g_names)
|
||||
|
||||
# simplified
|
||||
G = nx.read_shp(self.shppath)
|
||||
compare_graph_paths_names(G, self.simplified_paths, self.simplified_names)
|
||||
|
||||
# unsimplified
|
||||
G = nx.read_shp(self.shppath, simplify=False)
|
||||
compare_graph_paths_names(G, self.paths, self.names)
|
||||
|
||||
# multiline unsimplified
|
||||
G = nx.read_shp(self.multi_shppath, simplify=False)
|
||||
compare_graph_paths_names(G, self.paths, self.multi_names)
|
||||
|
||||
def checkgeom(self, lyr, expected):
|
||||
feature = lyr.GetNextFeature()
|
||||
actualwkt = []
|
||||
while feature:
|
||||
actualwkt.append(feature.GetGeometryRef().ExportToWkt())
|
||||
feature = lyr.GetNextFeature()
|
||||
assert sorted(expected) == sorted(actualwkt)
|
||||
|
||||
def test_geometryexport(self):
|
||||
expectedpoints_simple = (
|
||||
"POINT (1 1)",
|
||||
"POINT (2 2)",
|
||||
"POINT (3 3)",
|
||||
"POINT (0.9 0.9)",
|
||||
"POINT (4 2)",
|
||||
)
|
||||
expectedlines_simple = (
|
||||
"LINESTRING (1 1,2 2)",
|
||||
"LINESTRING (2 2,3 3)",
|
||||
"LINESTRING (0.9 0.9,4.0 0.9,4 2)",
|
||||
)
|
||||
expectedpoints = (
|
||||
"POINT (1 1)",
|
||||
"POINT (2 2)",
|
||||
"POINT (3 3)",
|
||||
"POINT (0.9 0.9)",
|
||||
"POINT (4.0 0.9)",
|
||||
"POINT (4 2)",
|
||||
)
|
||||
expectedlines = (
|
||||
"LINESTRING (1 1,2 2)",
|
||||
"LINESTRING (2 2,3 3)",
|
||||
"LINESTRING (0.9 0.9,4.0 0.9)",
|
||||
"LINESTRING (4.0 0.9,4 2)",
|
||||
)
|
||||
|
||||
tpath = os.path.join(tempfile.gettempdir(), "shpdir")
|
||||
G = nx.read_shp(self.shppath)
|
||||
nx.write_shp(G, tpath)
|
||||
shpdir = ogr.Open(tpath)
|
||||
self.checkgeom(shpdir.GetLayerByName("nodes"), expectedpoints_simple)
|
||||
self.checkgeom(shpdir.GetLayerByName("edges"), expectedlines_simple)
|
||||
|
||||
# Test unsimplified
|
||||
# Nodes should have additional point,
|
||||
# edges should be 'flattened'
|
||||
G = nx.read_shp(self.shppath, simplify=False)
|
||||
nx.write_shp(G, tpath)
|
||||
shpdir = ogr.Open(tpath)
|
||||
self.checkgeom(shpdir.GetLayerByName("nodes"), expectedpoints)
|
||||
self.checkgeom(shpdir.GetLayerByName("edges"), expectedlines)
|
||||
|
||||
def test_attributeexport(self):
|
||||
def testattributes(lyr, graph):
|
||||
feature = lyr.GetNextFeature()
|
||||
while feature:
|
||||
coords = []
|
||||
ref = feature.GetGeometryRef()
|
||||
last = ref.GetPointCount() - 1
|
||||
edge_nodes = (ref.GetPoint_2D(0), ref.GetPoint_2D(last))
|
||||
name = feature.GetFieldAsString("Name")
|
||||
assert graph.get_edge_data(*edge_nodes)["Name"] == name
|
||||
feature = lyr.GetNextFeature()
|
||||
|
||||
tpath = os.path.join(tempfile.gettempdir(), "shpdir")
|
||||
|
||||
G = nx.read_shp(self.shppath)
|
||||
nx.write_shp(G, tpath)
|
||||
shpdir = ogr.Open(tpath)
|
||||
edges = shpdir.GetLayerByName("edges")
|
||||
testattributes(edges, G)
|
||||
|
||||
# Test export of node attributes in nx.write_shp (#2778)
|
||||
def test_nodeattributeexport(self):
|
||||
tpath = os.path.join(tempfile.gettempdir(), "shpdir")
|
||||
|
||||
G = nx.DiGraph()
|
||||
A = (0, 0)
|
||||
B = (1, 1)
|
||||
C = (2, 2)
|
||||
G.add_edge(A, B)
|
||||
G.add_edge(A, C)
|
||||
label = "node_label"
|
||||
for n, d in G.nodes(data=True):
|
||||
d["label"] = label
|
||||
nx.write_shp(G, tpath)
|
||||
|
||||
H = nx.read_shp(tpath)
|
||||
for n, d in H.nodes(data=True):
|
||||
assert d["label"] == label
|
||||
|
||||
def test_wkt_export(self):
|
||||
G = nx.DiGraph()
|
||||
tpath = os.path.join(tempfile.gettempdir(), "shpdir")
|
||||
points = ("POINT (0.9 0.9)", "POINT (4 2)")
|
||||
line = ("LINESTRING (0.9 0.9,4 2)",)
|
||||
G.add_node(1, Wkt=points[0])
|
||||
G.add_node(2, Wkt=points[1])
|
||||
G.add_edge(1, 2, Wkt=line[0])
|
||||
try:
|
||||
nx.write_shp(G, tpath)
|
||||
except Exception as e:
|
||||
assert False, e
|
||||
shpdir = ogr.Open(tpath)
|
||||
self.checkgeom(shpdir.GetLayerByName("nodes"), points)
|
||||
self.checkgeom(shpdir.GetLayerByName("edges"), line)
|
||||
|
||||
def teardown_method(self):
|
||||
self.deletetmp(self.drv, self.testdir, self.shppath)
|
||||
|
||||
|
||||
def test_read_shp_nofile():
|
||||
with pytest.raises(RuntimeError):
|
||||
G = nx.read_shp("hopefully_this_file_will_not_be_available")
|
||||
|
||||
|
||||
class TestMissingGeometry:
|
||||
def setup_method(self):
|
||||
self.setup_path()
|
||||
self.delete_shapedir()
|
||||
self.create_shapedir()
|
||||
|
||||
def teardown_method(self):
|
||||
self.delete_shapedir()
|
||||
|
||||
def setup_path(self):
|
||||
self.path = os.path.join(tempfile.gettempdir(), "missing_geometry")
|
||||
|
||||
def create_shapedir(self):
|
||||
drv = ogr.GetDriverByName("ESRI Shapefile")
|
||||
shp = drv.CreateDataSource(self.path)
|
||||
lyr = shp.CreateLayer("nodes", None, ogr.wkbPoint)
|
||||
feature = ogr.Feature(lyr.GetLayerDefn())
|
||||
feature.SetGeometry(None)
|
||||
lyr.CreateFeature(feature)
|
||||
feature.Destroy()
|
||||
|
||||
def delete_shapedir(self):
|
||||
drv = ogr.GetDriverByName("ESRI Shapefile")
|
||||
if os.path.exists(self.path):
|
||||
drv.DeleteDataSource(self.path)
|
||||
|
||||
def test_missing_geometry(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.read_shp(self.path)
|
||||
|
||||
|
||||
class TestMissingAttrWrite:
|
||||
def setup_method(self):
|
||||
self.setup_path()
|
||||
self.delete_shapedir()
|
||||
|
||||
def teardown_method(self):
|
||||
self.delete_shapedir()
|
||||
|
||||
def setup_path(self):
|
||||
self.path = os.path.join(tempfile.gettempdir(), "missing_attributes")
|
||||
|
||||
def delete_shapedir(self):
|
||||
drv = ogr.GetDriverByName("ESRI Shapefile")
|
||||
if os.path.exists(self.path):
|
||||
drv.DeleteDataSource(self.path)
|
||||
|
||||
def test_missing_attributes(self):
|
||||
G = nx.DiGraph()
|
||||
A = (0, 0)
|
||||
B = (1, 1)
|
||||
C = (2, 2)
|
||||
G.add_edge(A, B, foo=100)
|
||||
G.add_edge(A, C)
|
||||
|
||||
nx.write_shp(G, self.path)
|
||||
H = nx.read_shp(self.path)
|
||||
|
||||
for u, v, d in H.edges(data=True):
|
||||
if u == A and v == B:
|
||||
assert d["foo"] == 100
|
||||
if u == A and v == C:
|
||||
assert d["foo"] is None
|
173
venv/Lib/site-packages/networkx/readwrite/tests/test_sparse6.py
Normal file
173
venv/Lib/site-packages/networkx/readwrite/tests/test_sparse6.py
Normal file
|
@ -0,0 +1,173 @@
|
|||
from io import BytesIO
|
||||
import tempfile
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.testing.utils import assert_edges_equal
|
||||
from networkx.testing.utils import assert_nodes_equal
|
||||
|
||||
|
||||
class TestSparseGraph6:
|
||||
def test_from_sparse6_bytes(self):
|
||||
data = b":Q___eDcdFcDeFcE`GaJ`IaHbKNbLM"
|
||||
G = nx.from_sparse6_bytes(data)
|
||||
assert_nodes_equal(
|
||||
sorted(G.nodes()),
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
|
||||
)
|
||||
assert_edges_equal(
|
||||
G.edges(),
|
||||
[
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(0, 3),
|
||||
(1, 12),
|
||||
(1, 14),
|
||||
(2, 13),
|
||||
(2, 15),
|
||||
(3, 16),
|
||||
(3, 17),
|
||||
(4, 7),
|
||||
(4, 9),
|
||||
(4, 11),
|
||||
(5, 6),
|
||||
(5, 8),
|
||||
(5, 9),
|
||||
(6, 10),
|
||||
(6, 11),
|
||||
(7, 8),
|
||||
(7, 10),
|
||||
(8, 12),
|
||||
(9, 15),
|
||||
(10, 14),
|
||||
(11, 13),
|
||||
(12, 16),
|
||||
(13, 17),
|
||||
(14, 17),
|
||||
(15, 16),
|
||||
],
|
||||
)
|
||||
|
||||
def test_from_bytes_multigraph_graph(self):
|
||||
graph_data = b":An"
|
||||
G = nx.from_sparse6_bytes(graph_data)
|
||||
assert type(G) == nx.Graph
|
||||
multigraph_data = b":Ab"
|
||||
M = nx.from_sparse6_bytes(multigraph_data)
|
||||
assert type(M) == nx.MultiGraph
|
||||
|
||||
def test_read_sparse6(self):
|
||||
data = b":Q___eDcdFcDeFcE`GaJ`IaHbKNbLM"
|
||||
G = nx.from_sparse6_bytes(data)
|
||||
fh = BytesIO(data)
|
||||
Gin = nx.read_sparse6(fh)
|
||||
assert_nodes_equal(G.nodes(), Gin.nodes())
|
||||
assert_edges_equal(G.edges(), Gin.edges())
|
||||
|
||||
def test_read_many_graph6(self):
|
||||
# Read many graphs into list
|
||||
data = b":Q___eDcdFcDeFcE`GaJ`IaHbKNbLM\n" b":Q___dCfDEdcEgcbEGbFIaJ`JaHN`IM"
|
||||
fh = BytesIO(data)
|
||||
glist = nx.read_sparse6(fh)
|
||||
assert len(glist) == 2
|
||||
for G in glist:
|
||||
assert_nodes_equal(
|
||||
G.nodes(),
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
|
||||
)
|
||||
|
||||
|
||||
class TestWriteSparse6:
|
||||
"""Unit tests for writing graphs in the sparse6 format.
|
||||
|
||||
Most of the test cases were checked against the sparse6 encoder in Sage.
|
||||
|
||||
"""
|
||||
|
||||
def test_null_graph(self):
|
||||
G = nx.null_graph()
|
||||
result = BytesIO()
|
||||
nx.write_sparse6(G, result)
|
||||
assert result.getvalue() == b">>sparse6<<:?\n"
|
||||
|
||||
def test_trivial_graph(self):
|
||||
G = nx.trivial_graph()
|
||||
result = BytesIO()
|
||||
nx.write_sparse6(G, result)
|
||||
assert result.getvalue() == b">>sparse6<<:@\n"
|
||||
|
||||
def test_empty_graph(self):
|
||||
G = nx.empty_graph(5)
|
||||
result = BytesIO()
|
||||
nx.write_sparse6(G, result)
|
||||
assert result.getvalue() == b">>sparse6<<:D\n"
|
||||
|
||||
def test_large_empty_graph(self):
|
||||
G = nx.empty_graph(68)
|
||||
result = BytesIO()
|
||||
nx.write_sparse6(G, result)
|
||||
assert result.getvalue() == b">>sparse6<<:~?@C\n"
|
||||
|
||||
def test_very_large_empty_graph(self):
|
||||
G = nx.empty_graph(258049)
|
||||
result = BytesIO()
|
||||
nx.write_sparse6(G, result)
|
||||
assert result.getvalue() == b">>sparse6<<:~~???~?@\n"
|
||||
|
||||
def test_complete_graph(self):
|
||||
G = nx.complete_graph(4)
|
||||
result = BytesIO()
|
||||
nx.write_sparse6(G, result)
|
||||
assert result.getvalue() == b">>sparse6<<:CcKI\n"
|
||||
|
||||
def test_no_header(self):
|
||||
G = nx.complete_graph(4)
|
||||
result = BytesIO()
|
||||
nx.write_sparse6(G, result, header=False)
|
||||
assert result.getvalue() == b":CcKI\n"
|
||||
|
||||
def test_padding(self):
|
||||
codes = (b":Cdv", b":DaYn", b":EaYnN", b":FaYnL", b":GaYnLz")
|
||||
for n, code in enumerate(codes, start=4):
|
||||
G = nx.path_graph(n)
|
||||
result = BytesIO()
|
||||
nx.write_sparse6(G, result, header=False)
|
||||
assert result.getvalue() == code + b"\n"
|
||||
|
||||
def test_complete_bipartite(self):
|
||||
G = nx.complete_bipartite_graph(6, 9)
|
||||
result = BytesIO()
|
||||
nx.write_sparse6(G, result)
|
||||
# Compared with sage
|
||||
expected = b">>sparse6<<:Nk" + b"?G`cJ" * 9 + b"\n"
|
||||
assert result.getvalue() == expected
|
||||
|
||||
def test_read_write_inverse(self):
|
||||
for i in list(range(13)) + [31, 47, 62, 63, 64, 72]:
|
||||
m = min(2 * i, i * i // 2)
|
||||
g = nx.random_graphs.gnm_random_graph(i, m, seed=i)
|
||||
gstr = BytesIO()
|
||||
nx.write_sparse6(g, gstr, header=False)
|
||||
# Strip the trailing newline.
|
||||
gstr = gstr.getvalue().rstrip()
|
||||
g2 = nx.from_sparse6_bytes(gstr)
|
||||
assert g2.order() == g.order()
|
||||
assert_edges_equal(g2.edges(), g.edges())
|
||||
|
||||
def test_no_directed_graphs(self):
|
||||
with pytest.raises(nx.NetworkXNotImplemented):
|
||||
nx.write_sparse6(nx.DiGraph(), BytesIO())
|
||||
|
||||
def test_write_path(self):
|
||||
# On Windows, we can't reopen a file that is open
|
||||
# So, for test we get a valid name from tempfile but close it.
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
fullfilename = f.name
|
||||
# file should be closed now, so write_sparse6 can open it
|
||||
nx.write_sparse6(nx.null_graph(), fullfilename)
|
||||
fh = open(fullfilename, mode="rb")
|
||||
assert fh.read() == b">>sparse6<<:?\n"
|
||||
fh.close()
|
||||
import os
|
||||
|
||||
os.remove(fullfilename)
|
50
venv/Lib/site-packages/networkx/readwrite/tests/test_yaml.py
Normal file
50
venv/Lib/site-packages/networkx/readwrite/tests/test_yaml.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
"""
|
||||
Unit tests for yaml.
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import pytest
|
||||
|
||||
yaml = pytest.importorskip("yaml")
|
||||
|
||||
import networkx as nx
|
||||
from networkx.testing import assert_edges_equal, assert_nodes_equal
|
||||
|
||||
|
||||
class TestYaml:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.build_graphs()
|
||||
|
||||
@classmethod
|
||||
def build_graphs(cls):
|
||||
cls.G = nx.Graph(name="test")
|
||||
e = [("a", "b"), ("b", "c"), ("c", "d"), ("d", "e"), ("e", "f"), ("a", "f")]
|
||||
cls.G.add_edges_from(e)
|
||||
cls.G.add_node("g")
|
||||
|
||||
cls.DG = nx.DiGraph(cls.G)
|
||||
|
||||
cls.MG = nx.MultiGraph()
|
||||
cls.MG.add_weighted_edges_from([(1, 2, 5), (1, 2, 5), (1, 2, 1), (3, 3, 42)])
|
||||
|
||||
def assert_equal(self, G, data=False):
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
nx.write_yaml(G, fname)
|
||||
Gin = nx.read_yaml(fname)
|
||||
|
||||
assert_nodes_equal(list(G), list(Gin))
|
||||
assert_edges_equal(G.edges(data=data), Gin.edges(data=data))
|
||||
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def testUndirected(self):
|
||||
self.assert_equal(self.G, data=False)
|
||||
|
||||
def testDirected(self):
|
||||
self.assert_equal(self.DG, data=False)
|
||||
|
||||
def testMultiGraph(self):
|
||||
self.assert_equal(self.MG, data=True)
|
Loading…
Add table
Add a link
Reference in a new issue