add padding function to imgScalePadding()
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,42 +0,0 @@
|
||||
"""
|
||||
========================
|
||||
DAG - Topological Layout
|
||||
========================
|
||||
|
||||
This example combines the `topological_generations` generator with
|
||||
`multipartite_layout` to show how to visualize a DAG in topologically-sorted
|
||||
order.
|
||||
"""
|
||||
|
||||
import networkx as nx
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
G = nx.DiGraph(
|
||||
[
|
||||
("f", "a"),
|
||||
("a", "b"),
|
||||
("a", "e"),
|
||||
("b", "c"),
|
||||
("b", "d"),
|
||||
("d", "e"),
|
||||
("f", "c"),
|
||||
("f", "g"),
|
||||
("h", "f"),
|
||||
]
|
||||
)
|
||||
|
||||
for layer, nodes in enumerate(nx.topological_generations(G)):
|
||||
# `multipartite_layout` expects the layer as a node attribute, so add the
|
||||
# numeric layer value as a node attribute
|
||||
for node in nodes:
|
||||
G.nodes[node]["layer"] = layer
|
||||
|
||||
# Compute the multipartite_layout using the "layer" node attribute
|
||||
pos = nx.multipartite_layout(G, subset_key="layer")
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
nx.draw_networkx(G, pos=pos, ax=ax)
|
||||
ax.set_title("DAG layout in topological order")
|
||||
fig.tight_layout()
|
||||
plt.show()
|
||||
@@ -1,36 +0,0 @@
|
||||
"""
|
||||
===============
|
||||
Degree Sequence
|
||||
===============
|
||||
|
||||
Random graph from given degree sequence.
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
import networkx as nx
|
||||
|
||||
# Specify seed for reproducibility
|
||||
seed = 668273
|
||||
|
||||
z = [5, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
|
||||
print(nx.is_graphical(z))
|
||||
|
||||
print("Configuration model")
|
||||
G = nx.configuration_model(
|
||||
z, seed=seed
|
||||
) # configuration model, seed for reproducibility
|
||||
degree_sequence = [d for n, d in G.degree()] # degree sequence
|
||||
print(f"Degree sequence {degree_sequence}")
|
||||
print("Degree histogram")
|
||||
hist = {}
|
||||
for d in degree_sequence:
|
||||
if d in hist:
|
||||
hist[d] += 1
|
||||
else:
|
||||
hist[d] = 1
|
||||
print("degree #nodes")
|
||||
for d in hist:
|
||||
print(f"{d:4} {hist[d]:6}")
|
||||
|
||||
pos = nx.spring_layout(G, seed=seed) # Seed layout for reproducibility
|
||||
nx.draw(G, pos=pos)
|
||||
plt.show()
|
||||
@@ -1,36 +0,0 @@
|
||||
"""
|
||||
===========
|
||||
Erdos Renyi
|
||||
===========
|
||||
|
||||
Create an G{n,m} random graph with n nodes and m edges
|
||||
and report some properties.
|
||||
|
||||
This graph is sometimes called the Erdős-Rényi graph
|
||||
but is different from G{n,p} or binomial_graph which is also
|
||||
sometimes called the Erdős-Rényi graph.
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import networkx as nx
|
||||
|
||||
n = 10 # 10 nodes
|
||||
m = 20 # 20 edges
|
||||
seed = 20160 # seed random number generators for reproducibility
|
||||
|
||||
# Use seed for reproducibility
|
||||
G = nx.gnm_random_graph(n, m, seed=seed)
|
||||
|
||||
# some properties
|
||||
print("node degree clustering")
|
||||
for v in nx.nodes(G):
|
||||
print(f"{v} {nx.degree(G, v)} {nx.clustering(G, v)}")
|
||||
|
||||
print()
|
||||
print("the adjacency list")
|
||||
for line in nx.generate_adjlist(G):
|
||||
print(line)
|
||||
|
||||
pos = nx.spring_layout(G, seed=seed) # Seed for reproducible layout
|
||||
nx.draw(G, pos=pos)
|
||||
plt.show()
|
||||
@@ -1,20 +0,0 @@
|
||||
"""
|
||||
========================
|
||||
Expected Degree Sequence
|
||||
========================
|
||||
|
||||
Random graph from given degree sequence.
|
||||
"""
|
||||
|
||||
import networkx as nx
|
||||
|
||||
# make a random graph of 500 nodes with expected degrees of 50
|
||||
n = 500 # n nodes
|
||||
p = 0.1
|
||||
w = [p * n for i in range(n)] # w = p*n for all nodes
|
||||
G = nx.expected_degree_graph(w) # configuration model
|
||||
print("Degree histogram")
|
||||
print("degree (#nodes) ****")
|
||||
dh = nx.degree_histogram(G)
|
||||
for i, d in enumerate(dh):
|
||||
print(f"{i:2} ({d:2}) {'*'*d}")
|
||||
@@ -1,44 +0,0 @@
|
||||
"""
|
||||
========
|
||||
Football
|
||||
========
|
||||
|
||||
Load football network in GML format and compute some network statistcs.
|
||||
|
||||
Shows how to download GML graph in a zipped file, unpack it, and load
|
||||
into a NetworkX graph.
|
||||
|
||||
Requires Internet connection to download the URL
|
||||
http://www-personal.umich.edu/~mejn/netdata/football.zip
|
||||
"""
|
||||
|
||||
import urllib.request
|
||||
import io
|
||||
import zipfile
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import networkx as nx
|
||||
|
||||
url = "http://www-personal.umich.edu/~mejn/netdata/football.zip"
|
||||
|
||||
sock = urllib.request.urlopen(url) # open URL
|
||||
s = io.BytesIO(sock.read()) # read into BytesIO "file"
|
||||
sock.close()
|
||||
|
||||
zf = zipfile.ZipFile(s) # zipfile object
|
||||
txt = zf.read("football.txt").decode() # read info file
|
||||
gml = zf.read("football.gml").decode() # read gml data
|
||||
# throw away bogus first line with # from mejn files
|
||||
gml = gml.split("\n")[1:]
|
||||
G = nx.parse_gml(gml) # parse gml data
|
||||
|
||||
print(txt)
|
||||
# print degree for each team - number of games
|
||||
for n, d in G.degree():
|
||||
print(f"{n:20} {d:2}")
|
||||
|
||||
options = {"node_color": "black", "node_size": 50, "linewidths": 0, "width": 0.1}
|
||||
|
||||
pos = nx.spring_layout(G, seed=1969) # Seed for reproducible layout
|
||||
nx.draw(G, pos, **options)
|
||||
plt.show()
|
||||
@@ -1,25 +0,0 @@
|
||||
"""
|
||||
===========
|
||||
Karate Club
|
||||
===========
|
||||
|
||||
Zachary's Karate Club graph
|
||||
|
||||
Data file from:
|
||||
http://vlado.fmf.uni-lj.si/pub/networks/data/Ucinet/UciData.htm
|
||||
|
||||
Zachary W. (1977).
|
||||
An information flow model for conflict and fission in small groups.
|
||||
Journal of Anthropological Research, 33, 452-473.
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import networkx as nx
|
||||
|
||||
G = nx.karate_club_graph()
|
||||
print("Node Degree")
|
||||
for v in G:
|
||||
print(f"{v:4} {G.degree(v):6}")
|
||||
|
||||
nx.draw_circular(G, with_labels=True)
|
||||
plt.show()
|
||||
@@ -1,97 +0,0 @@
|
||||
"""
|
||||
==========
|
||||
Morse Trie
|
||||
==========
|
||||
|
||||
A prefix tree (aka a "trie") representing the Morse encoding of the alphabet.
|
||||
A letter can be encoded by tracing the path from the corresponding node in the
|
||||
tree to the root node, reversing the order of the symbols encountered along
|
||||
the path.
|
||||
"""
|
||||
import networkx as nx
|
||||
|
||||
# Unicode characters to represent the dots/dashes (or dits/dahs) of Morse code
|
||||
dot = "•"
|
||||
dash = "—"
|
||||
|
||||
# Start with the direct mapping of letter -> code
|
||||
morse_direct_mapping = {
|
||||
"a": dot + dash,
|
||||
"b": dash + dot * 3,
|
||||
"c": dash + dot + dash + dot,
|
||||
"d": dash + dot * 2,
|
||||
"e": dot,
|
||||
"f": dot * 2 + dash + dot,
|
||||
"g": dash * 2 + dot,
|
||||
"h": dot * 4,
|
||||
"i": dot * 2,
|
||||
"j": dot + dash * 3,
|
||||
"k": dash + dot + dash,
|
||||
"l": dot + dash + dot * 2,
|
||||
"m": dash * 2,
|
||||
"n": dash + dot,
|
||||
"o": dash * 3,
|
||||
"p": dot + dash * 2 + dot,
|
||||
"q": dash * 2 + dot + dash,
|
||||
"r": dot + dash + dot,
|
||||
"s": dot * 3,
|
||||
"t": dash,
|
||||
"u": dot * 2 + dash,
|
||||
"v": dot * 3 + dash,
|
||||
"w": dot + dash * 2,
|
||||
"x": dash + dot * 2 + dash,
|
||||
"y": dash + dot + dash * 2,
|
||||
"z": dash * 2 + dot * 2,
|
||||
}
|
||||
|
||||
### Manually construct the prefix tree from this mapping
|
||||
|
||||
# Some preprocessing: sort the original mapping by code length and character
|
||||
# value
|
||||
morse_mapping_sorted = dict(
|
||||
sorted(morse_direct_mapping.items(), key=lambda item: (len(item[1]), item[1]))
|
||||
)
|
||||
|
||||
# More preprocessing: create the reverse mapping to simplify lookup
|
||||
reverse_mapping = {v: k for k, v in morse_direct_mapping.items()}
|
||||
reverse_mapping[""] = "" # Represent the "root" node with an empty string
|
||||
|
||||
# Construct the prefix tree from the sorted mapping
|
||||
G = nx.DiGraph()
|
||||
for node, char in morse_mapping_sorted.items():
|
||||
pred = char[:-1]
|
||||
# Store the dot/dash relating the two letters as an edge attribute "char"
|
||||
G.add_edge(reverse_mapping[pred], node, char=char[-1])
|
||||
|
||||
# For visualization purposes, layout the nodes in topological order
|
||||
for i, layer in enumerate(nx.topological_generations(G)):
|
||||
for n in layer:
|
||||
G.nodes[n]["layer"] = i
|
||||
pos = nx.multipartite_layout(G, subset_key="layer", align="horizontal")
|
||||
# Flip the layout so the root node is on top
|
||||
for k in pos:
|
||||
pos[k][-1] *= -1
|
||||
|
||||
# Visualize the trie
|
||||
nx.draw(G, pos=pos, with_labels=True)
|
||||
elabels = {(u, v): l for u, v, l in G.edges(data="char")}
|
||||
nx.draw_networkx_edge_labels(G, pos, edge_labels=elabels)
|
||||
|
||||
|
||||
# A letter can be encoded by following the path from the given letter (node) to
|
||||
# the root node
|
||||
def morse_encode(letter):
|
||||
pred = next(G.predecessors(letter)) # Each letter has only 1 predecessor
|
||||
symbol = G[pred][letter]["char"]
|
||||
if pred != "":
|
||||
return morse_encode(pred) + symbol # Traversing the trie in reverse
|
||||
return symbol
|
||||
|
||||
|
||||
# Verify that the trie encoding is correct
|
||||
import string
|
||||
|
||||
for letter in string.ascii_lowercase:
|
||||
assert morse_encode(letter) == morse_direct_mapping[letter]
|
||||
|
||||
print(" ".join([morse_encode(ltr) for ltr in "ilovenetworkx"]))
|
||||
@@ -1,50 +0,0 @@
|
||||
"""
|
||||
=====================
|
||||
Minimum Spanning Tree
|
||||
=====================
|
||||
|
||||
A minimum spanning tree (MST) is a subset of edges in a weighted,
|
||||
connected graph that connects all vertices together with the
|
||||
minimum possible total edge weight. The `minimum_spanning_tree`
|
||||
function is used to compare the original graph with its MST.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import networkx as nx
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Create a graph
|
||||
G = nx.Graph()
|
||||
G.add_edges_from(
|
||||
[
|
||||
(0, 1, {"weight": 4}),
|
||||
(0, 7, {"weight": 8}),
|
||||
(1, 7, {"weight": 11}),
|
||||
(1, 2, {"weight": 8}),
|
||||
(2, 8, {"weight": 2}),
|
||||
(2, 5, {"weight": 4}),
|
||||
(2, 3, {"weight": 7}),
|
||||
(3, 4, {"weight": 9}),
|
||||
(3, 5, {"weight": 14}),
|
||||
(4, 5, {"weight": 10}),
|
||||
(5, 6, {"weight": 2}),
|
||||
(6, 8, {"weight": 6}),
|
||||
(7, 8, {"weight": 7}),
|
||||
]
|
||||
)
|
||||
|
||||
# Find the minimum spanning tree
|
||||
T = nx.minimum_spanning_tree(G)
|
||||
|
||||
# Visualize the graph and the minimum spanning tree
|
||||
pos = nx.spring_layout(G)
|
||||
nx.draw_networkx_nodes(G, pos, node_color="lightblue", node_size=500)
|
||||
nx.draw_networkx_edges(G, pos, edge_color="grey")
|
||||
nx.draw_networkx_labels(G, pos, font_size=12, font_family="sans-serif")
|
||||
nx.draw_networkx_edge_labels(
|
||||
G, pos, edge_labels={(u, v): d["weight"] for u, v, d in G.edges(data=True)}
|
||||
)
|
||||
nx.draw_networkx_edges(T, pos, edge_color="green", width=2)
|
||||
plt.axis("off")
|
||||
plt.show()
|
||||
@@ -1,133 +0,0 @@
|
||||
"""
|
||||
=========================
|
||||
Napoleon Russian Campaign
|
||||
=========================
|
||||
|
||||
Minard's data from Napoleon's 1812-1813 Russian Campaign.
|
||||
https://web.archive.org/web/20080112042656/http://www.math.yorku.ca/SCS/Gallery/minard/minard.txt
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def minard_graph():
|
||||
data1 = """\
|
||||
24.0,54.9,340000,A,1
|
||||
24.5,55.0,340000,A,1
|
||||
25.5,54.5,340000,A,1
|
||||
26.0,54.7,320000,A,1
|
||||
27.0,54.8,300000,A,1
|
||||
28.0,54.9,280000,A,1
|
||||
28.5,55.0,240000,A,1
|
||||
29.0,55.1,210000,A,1
|
||||
30.0,55.2,180000,A,1
|
||||
30.3,55.3,175000,A,1
|
||||
32.0,54.8,145000,A,1
|
||||
33.2,54.9,140000,A,1
|
||||
34.4,55.5,127100,A,1
|
||||
35.5,55.4,100000,A,1
|
||||
36.0,55.5,100000,A,1
|
||||
37.6,55.8,100000,A,1
|
||||
37.7,55.7,100000,R,1
|
||||
37.5,55.7,98000,R,1
|
||||
37.0,55.0,97000,R,1
|
||||
36.8,55.0,96000,R,1
|
||||
35.4,55.3,87000,R,1
|
||||
34.3,55.2,55000,R,1
|
||||
33.3,54.8,37000,R,1
|
||||
32.0,54.6,24000,R,1
|
||||
30.4,54.4,20000,R,1
|
||||
29.2,54.3,20000,R,1
|
||||
28.5,54.2,20000,R,1
|
||||
28.3,54.3,20000,R,1
|
||||
27.5,54.5,20000,R,1
|
||||
26.8,54.3,12000,R,1
|
||||
26.4,54.4,14000,R,1
|
||||
25.0,54.4,8000,R,1
|
||||
24.4,54.4,4000,R,1
|
||||
24.2,54.4,4000,R,1
|
||||
24.1,54.4,4000,R,1"""
|
||||
data2 = """\
|
||||
24.0,55.1,60000,A,2
|
||||
24.5,55.2,60000,A,2
|
||||
25.5,54.7,60000,A,2
|
||||
26.6,55.7,40000,A,2
|
||||
27.4,55.6,33000,A,2
|
||||
28.7,55.5,33000,R,2
|
||||
29.2,54.2,30000,R,2
|
||||
28.5,54.1,30000,R,2
|
||||
28.3,54.2,28000,R,2"""
|
||||
data3 = """\
|
||||
24.0,55.2,22000,A,3
|
||||
24.5,55.3,22000,A,3
|
||||
24.6,55.8,6000,A,3
|
||||
24.6,55.8,6000,R,3
|
||||
24.2,54.4,6000,R,3
|
||||
24.1,54.4,6000,R,3"""
|
||||
cities = """\
|
||||
24.0,55.0,Kowno
|
||||
25.3,54.7,Wilna
|
||||
26.4,54.4,Smorgoni
|
||||
26.8,54.3,Moiodexno
|
||||
27.7,55.2,Gloubokoe
|
||||
27.6,53.9,Minsk
|
||||
28.5,54.3,Studienska
|
||||
28.7,55.5,Polotzk
|
||||
29.2,54.4,Bobr
|
||||
30.2,55.3,Witebsk
|
||||
30.4,54.5,Orscha
|
||||
30.4,53.9,Mohilow
|
||||
32.0,54.8,Smolensk
|
||||
33.2,54.9,Dorogobouge
|
||||
34.3,55.2,Wixma
|
||||
34.4,55.5,Chjat
|
||||
36.0,55.5,Mojaisk
|
||||
37.6,55.8,Moscou
|
||||
36.6,55.3,Tarantino
|
||||
36.5,55.0,Malo-Jarosewii"""
|
||||
|
||||
c = {}
|
||||
for line in cities.split("\n"):
|
||||
x, y, name = line.split(",")
|
||||
c[name] = (float(x), float(y))
|
||||
|
||||
g = []
|
||||
|
||||
for data in [data1, data2, data3]:
|
||||
G = nx.Graph()
|
||||
i = 0
|
||||
G.pos = {} # location
|
||||
G.pop = {} # size
|
||||
last = None
|
||||
for line in data.split("\n"):
|
||||
x, y, p, r, n = line.split(",")
|
||||
G.pos[i] = (float(x), float(y))
|
||||
G.pop[i] = int(p)
|
||||
if last is None:
|
||||
last = i
|
||||
else:
|
||||
G.add_edge(i, last, **{r: int(n)})
|
||||
last = i
|
||||
i = i + 1
|
||||
g.append(G)
|
||||
|
||||
return g, c
|
||||
|
||||
|
||||
(g, city) = minard_graph()
|
||||
|
||||
plt.figure(1, figsize=(11, 5))
|
||||
plt.clf()
|
||||
colors = ["b", "g", "r"]
|
||||
for G in g:
|
||||
c = colors.pop(0)
|
||||
node_size = [G.pop[n] // 300 for n in G]
|
||||
nx.draw_networkx_edges(G, G.pos, edge_color=c, width=4, alpha=0.5)
|
||||
nx.draw_networkx_nodes(G, G.pos, node_size=node_size, node_color=c, alpha=0.5)
|
||||
nx.draw_networkx_nodes(G, G.pos, node_size=5, node_color="k")
|
||||
|
||||
for c in city:
|
||||
x, y = city[c]
|
||||
plt.text(x, y + 0.1, c)
|
||||
plt.show()
|
||||
@@ -1,80 +0,0 @@
|
||||
"""
|
||||
=====
|
||||
Roget
|
||||
=====
|
||||
|
||||
Build a directed graph of 1022 categories and 5075 cross-references as defined
|
||||
in the 1879 version of Roget's Thesaurus. This example is described in Section
|
||||
1.2 of
|
||||
|
||||
Donald E. Knuth, "The Stanford GraphBase: A Platform for Combinatorial
|
||||
Computing", ACM Press, New York, 1993.
|
||||
http://www-cs-faculty.stanford.edu/~knuth/sgb.html
|
||||
|
||||
Note that one of the 5075 cross references is a self loop yet it is included in
|
||||
the graph built here because the standard networkx `DiGraph` class allows self
|
||||
loops. (cf. 400pungency:400 401 403 405).
|
||||
|
||||
The data file can be found at:
|
||||
|
||||
- https://github.com/networkx/networkx/blob/main/examples/graph/roget_dat.txt.gz
|
||||
"""
|
||||
|
||||
import gzip
|
||||
import re
|
||||
import sys
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def roget_graph():
|
||||
"""Return the thesaurus graph from the roget.dat example in
|
||||
the Stanford Graph Base.
|
||||
"""
|
||||
# open file roget_dat.txt.gz
|
||||
fh = gzip.open("roget_dat.txt.gz", "r")
|
||||
|
||||
G = nx.DiGraph()
|
||||
|
||||
for line in fh.readlines():
|
||||
line = line.decode()
|
||||
if line.startswith("*"): # skip comments
|
||||
continue
|
||||
if line.startswith(" "): # this is a continuation line, append
|
||||
line = oldline + line
|
||||
if line.endswith("\\\n"): # continuation line, buffer, goto next
|
||||
oldline = line.strip("\\\n")
|
||||
continue
|
||||
|
||||
(headname, tails) = line.split(":")
|
||||
|
||||
# head
|
||||
numfind = re.compile(r"^\d+") # re to find the number of this word
|
||||
head = numfind.findall(headname)[0] # get the number
|
||||
|
||||
G.add_node(head)
|
||||
|
||||
for tail in tails.split():
|
||||
if head == tail:
|
||||
print("skipping self loop", head, tail, file=sys.stderr)
|
||||
G.add_edge(head, tail)
|
||||
|
||||
return G
|
||||
|
||||
|
||||
G = roget_graph()
|
||||
print("Loaded roget_dat.txt containing 1022 categories.")
|
||||
print(G)
|
||||
UG = G.to_undirected()
|
||||
print(nx.number_connected_components(UG), "connected components")
|
||||
|
||||
options = {
|
||||
"node_color": "black",
|
||||
"node_size": 1,
|
||||
"edge_color": "gray",
|
||||
"linewidths": 0,
|
||||
"width": 0.1,
|
||||
}
|
||||
nx.draw_circular(UG, **options)
|
||||
plt.show()
|
||||
@@ -1,63 +0,0 @@
|
||||
"""
|
||||
======
|
||||
Triads
|
||||
======
|
||||
According to the paper by Snijders, T. (2012). “Transitivity and triads.”
|
||||
University of Oxford, there are 16 Triad Types possible. This plot shows
|
||||
the 16 Triad Types that can be identified within directed networks.
|
||||
Triadic relationships are especially useful when analysing Social Networks.
|
||||
The first three digits refer to the number of mutual, asymmetric and null
|
||||
dyads (bidirectional, unidirection and nonedges) and the letter gives
|
||||
the Orientation as Up (U), Down (D) , Cyclical (C) or Transitive (T).
|
||||
"""
|
||||
|
||||
import networkx as nx
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fig, axes = plt.subplots(4, 4, figsize=(10, 10))
|
||||
triads = {
|
||||
"003": [],
|
||||
"012": [(1, 2)],
|
||||
"102": [(1, 2), (2, 1)],
|
||||
"021D": [(3, 1), (3, 2)],
|
||||
"021U": [(1, 3), (2, 3)],
|
||||
"021C": [(1, 3), (3, 2)],
|
||||
"111D": [(1, 2), (2, 1), (3, 1)],
|
||||
"111U": [(1, 2), (2, 1), (1, 3)],
|
||||
"030T": [(1, 2), (3, 2), (1, 3)],
|
||||
"030C": [(1, 3), (3, 2), (2, 1)],
|
||||
"201": [(1, 2), (2, 1), (3, 1), (1, 3)],
|
||||
"120D": [(1, 2), (2, 1), (3, 1), (3, 2)],
|
||||
"120U": [(1, 2), (2, 1), (1, 3), (2, 3)],
|
||||
"120C": [(1, 2), (2, 1), (1, 3), (3, 2)],
|
||||
"210": [(1, 2), (2, 1), (1, 3), (3, 2), (2, 3)],
|
||||
"300": [(1, 2), (2, 1), (2, 3), (3, 2), (1, 3), (3, 1)],
|
||||
}
|
||||
|
||||
for (title, triad), ax in zip(triads.items(), axes.flatten()):
|
||||
G = nx.DiGraph()
|
||||
G.add_nodes_from([1, 2, 3])
|
||||
G.add_edges_from(triad)
|
||||
nx.draw_networkx(
|
||||
G,
|
||||
ax=ax,
|
||||
with_labels=False,
|
||||
node_color=["green"],
|
||||
node_size=200,
|
||||
arrowsize=20,
|
||||
width=2,
|
||||
pos=nx.planar_layout(G),
|
||||
)
|
||||
ax.set_xlim(val * 1.2 for val in ax.get_xlim())
|
||||
ax.set_ylim(val * 1.2 for val in ax.get_ylim())
|
||||
ax.text(
|
||||
0,
|
||||
0,
|
||||
title,
|
||||
fontsize=15,
|
||||
fontweight="extra bold",
|
||||
horizontalalignment="center",
|
||||
bbox={"boxstyle": "square,pad=0.3", "fc": "none"},
|
||||
)
|
||||
fig.tight_layout()
|
||||
plt.show()
|
||||
@@ -1,88 +0,0 @@
|
||||
"""
|
||||
==================
|
||||
Words/Ladder Graph
|
||||
==================
|
||||
|
||||
Generate an undirected graph over the 5757 5-letter words in the datafile
|
||||
`words_dat.txt.gz`. Two words are connected by an edge if they differ in one
|
||||
letter, resulting in 14,135 edges. This example is described in Section 1.1 of
|
||||
|
||||
Donald E. Knuth, "The Stanford GraphBase: A Platform for Combinatorial
|
||||
Computing", ACM Press, New York, 1993.
|
||||
http://www-cs-faculty.stanford.edu/~knuth/sgb.html
|
||||
|
||||
The data file can be found at:
|
||||
|
||||
- https://github.com/networkx/networkx/blob/main/examples/graph/words_dat.txt.gz
|
||||
"""
|
||||
|
||||
import gzip
|
||||
from string import ascii_lowercase as lowercase
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def generate_graph(words):
|
||||
G = nx.Graph(name="words")
|
||||
lookup = {c: lowercase.index(c) for c in lowercase}
|
||||
|
||||
def edit_distance_one(word):
|
||||
for i in range(len(word)):
|
||||
left, c, right = word[0:i], word[i], word[i + 1 :]
|
||||
j = lookup[c] # lowercase.index(c)
|
||||
for cc in lowercase[j + 1 :]:
|
||||
yield left + cc + right
|
||||
|
||||
candgen = (
|
||||
(word, cand)
|
||||
for word in sorted(words)
|
||||
for cand in edit_distance_one(word)
|
||||
if cand in words
|
||||
)
|
||||
G.add_nodes_from(words)
|
||||
for word, cand in candgen:
|
||||
G.add_edge(word, cand)
|
||||
return G
|
||||
|
||||
|
||||
def words_graph():
|
||||
"""Return the words example graph from the Stanford GraphBase"""
|
||||
fh = gzip.open("words_dat.txt.gz", "r")
|
||||
words = set()
|
||||
for line in fh.readlines():
|
||||
line = line.decode()
|
||||
if line.startswith("*"):
|
||||
continue
|
||||
w = str(line[0:5])
|
||||
words.add(w)
|
||||
return generate_graph(words)
|
||||
|
||||
|
||||
G = words_graph()
|
||||
print("Loaded words_dat.txt containing 5757 five-letter English words.")
|
||||
print("Two words are connected if they differ in one letter.")
|
||||
print(G)
|
||||
print(f"{nx.number_connected_components(G)} connected components")
|
||||
|
||||
for source, target in [("chaos", "order"), ("nodes", "graph"), ("pound", "marks")]:
|
||||
print(f"Shortest path between {source} and {target} is")
|
||||
try:
|
||||
shortest_path = nx.shortest_path(G, source, target)
|
||||
for n in shortest_path:
|
||||
print(n)
|
||||
except nx.NetworkXNoPath:
|
||||
print("None")
|
||||
|
||||
|
||||
# draw a subset of the graph
|
||||
boundary = list(nx.node_boundary(G, shortest_path))
|
||||
G.add_nodes_from(shortest_path, color="red")
|
||||
G.add_nodes_from(boundary, color="blue")
|
||||
H = G.subgraph(shortest_path + boundary)
|
||||
colors = nx.get_node_attributes(H, "color")
|
||||
options = {"node_size": 1500, "alpha": 0.3, "node_color": colors.values()}
|
||||
pos = nx.kamada_kawai_layout(H)
|
||||
nx.draw(H, pos, **options)
|
||||
nx.draw_networkx_labels(H, pos, font_weight="bold")
|
||||
plt.show()
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user