I am trying to represent a Graph, given and adjacency matrix, using networkX.
I managed to represent the graph with weights, however, self-loops do not show the weights. I have tried several things.
import networkx as nx
import matplotlib.pyplot as plt
# Define the adjacency matrix
adj_matrix = [[5, 1, 3, 0],
[0, 10, 2, 1],
[0, 0, 0, 0],
[4, 0, 1, 0]]
# Create a directed graph from the adjacency matrix
G = nx.DiGraph()
for i in range(len(adj_matrix)):
for j in range(len(adj_matrix)):
if adj_matrix[i][j] != 0:
G.add_edge(i, j, weight=adj_matrix[i][j], label=adj_matrix[i][j])
# Draw the graph with edge labels and node labels
fig = plt.figure(figsize=(10, 10))
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos, node_size=1000)
nx.draw_networkx_edges(G, pos, width=2)
edge_labels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
node_labels = nx.get_node_attributes(G, 'weight')
nx.draw_networkx_labels(G, pos, labels=node_labels)
plt.show()
Thank you!
Networkx currently does not properly support the labeling of curved edges, including self-loops, as it ignores the edge path and simply places the label in the center (by default) of the straight line between the source and target node positions. For self-loops, the label position ends up being the node position itself. Various fixes have been discussed for years but none is implemented (AFAIK).
If you are open to using other libraries, you can use netgraph, which is a visualization library I wrote and maintain that works well with networkx. Crucially, it uses the edge paths to position the edge labels.
import numpy as np
import matplotlib.pyplot as plt
from netgraph import Graph # pip install netgraph
adj_matrix = np.array([[5, 1, 3, 0],
[0, 10, 2, 1],
[0, 0, 0, 0],
[4, 0, 1, 0]])
sources, targets = np.where(adj_matrix)
weights = adj_matrix[sources, targets]
edges = list(zip(sources, targets))
edge_labels = dict(zip(edges, weights))
fig, ax = plt.subplots()
Graph(edges, edge_labels=edge_labels, edge_label_position=0.66, arrows=True, ax=ax)
plt.show()
Related
I want to plot (filled) polygons that are given by a sequence of points that define the boundary in 3d. Unfortunately these polygons intersect eachother.
Here is a minimal example that shows two squares that intersect, with the issue that they are not plotted correctly. In my actual application these polygons are generated on the fly, so it is also not possible to manually define a triangulation of the polygon. I'm aware that with Poly3DCollection, there is no chance of doing this correctly as the polygons will only be filled after the projection.
Can anyone recommend another method that allows drawing polygons in 3d with correct intersections?
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
verts1 = np.array([
[0, 0, 0.5],
[1, 0, 0.5],
[1, 1, 0.5],
[0, 1, 0.5]
])
verts2 = verts1[:, [1, 2, 0]]
verts = [verts1, verts2]
ax.add_collection3d(p3c := Poly3DCollection(verts))
p3c.set_facecolor([(1, 0, 0), (0, 1, 0)])
plt.show()
I am trying to plot a celestial region with the Basemap. When I reverse the x axis (RA), the tick labels appear at the wrong sides. How to fixed it ?
from mpl_toolkits.basemap import Basemap
import pylab as pl
width = 250000
m = Basemap(width=width, height=width, projection='aeqd',
lat_0=57.1, lon_0=35.2)
m.drawmeridians(np.arange(32,37.8,1),labels=[True]*5)
m.drawparallels(np.arange(56,58,0.5),labels=[True]*5)
pl.gca().invert_xaxis()
pl.show()
To draw parallels' labels properly, these lines of code are needed in place of the related one:
m.drawparallels(np.arange(56,58,0.5), ha= 'right', labels=[0, 1, 0, 0]) # for labels on left side
m.drawparallels(np.arange(56,58,0.5), ha= 'left', labels=[1, 0, 0, 0]) # for labels on right side
The output will be:
The complete code:
from mpl_toolkits.basemap import Basemap
import numpy as np
from matplotlib import pyplot as plt
width = 250000
m = Basemap(width=width, height=width, projection='aeqd',
lat_0=57.1, lon_0=35.2)
m.drawmeridians(np.arange(32,37.8,1), labels=[0, 0, 1, 1])
m.drawparallels(np.arange(56,58,0.5), ha= 'right', labels=[0, 1, 0, 0]) # for labels on left side
m.drawparallels(np.arange(56,58,0.5), ha= 'left', labels=[1, 0, 0, 0]) # for labels on right side
plt.gca().invert_xaxis()
plt.show()
Edit
To enable running the code with Google Colab, follow this Gist
I have next code:
import networkx
grafoPetersen = {
1: [2,5,6],
2: [3,1,7],
3: [4,2,8],
4: [5,3,9],
5: [1,4,10],
6: [1,8,9],
7:[2,9,10],
8: [3,10,6],
9: [4,6,7],
10: [5,7,8]
}
for k in grafoPetersen:
grafoPetersen[k].append(-1)
grafoPetersen[k].append(-2)
grafoPetersen[-1] = list(range(1,11))
grafoPetersen[-2] = list(range(1,11))
rutaHamiltoniana = [8, 3, 4, 5, 10, 7, 2, 1, 6, 9];
g = networkx.Graph()
for k, vs in grafoPetersen.items():
for v in vs:
if v in [-1, -2] or k in [-1, -2]:
continue
if abs(rutaHamiltoniana.index(k) - rutaHamiltoniana.index(v)) == 1:
g.add_edge(k,v, color='red', width=1.5)
else:
g.add_edge(k,v, color='black', width=0.5)
posicion = networkx.circular_layout(g)
edges = g.edges()
colores = [g[u][v]['color'] for u,v in edges]
anchuras = [g[u][v]['width'] for u,v in edges]
networkx.draw(g, posicion, edges=edges, edge_color=colores, width=anchuras, with_labels = True)
And I get an output like:
My graph is a Petersen's graph, and I want show it like:
But with same style of I have set in the first pict.
I have tryed it like:
options = {
'with_labels': True,
'node_color': 'black',
'node_size': 200,
'width': 3,
}
networkx.draw_shell(g, nlist=[range(5,10), range(5)], **options)
as here suggest: How to show a graph like a Petersen's graph but I get several errors.
Any idea how can I parse first pict into second pict maintaning first pict styles?
EDIT 1: Here is full code how I get a path of Petersen's graph and draw it like first pict: Hamiltonian path inside Petersen's graph algorithm
The lists within the nlist define groupings of nodes that are going to be placed on concentric circles (shells). The nodes are defined by their ID which we defined in the grafoPetersen: 1, 2, ..., 10
networkx.draw_shell(g, nlist=[range(5,10), range(5)])
This call groups nodes range(5,10)=[5,6,7,8,9] on one concentric circle and range(5)=[0,1,2,3,4] on second concentric circle. There is, however, no node defined in grafoPetersen with ID 0. Furthermore, we have defined a node with ID 10, which is not represented by either of the two ranges.
So in order to fix this, we have to fix the ranges:
networkx.draw_shell(g, nlist=[range(6,11), range(1,6)])
and with all the fancy options:
networkx.draw_shell(g, nlist=[range(6,11), range(1,6)], edge_color=colors, width=widths)
import networkx as nx
import matplotlib.pyplot as plt
# rutaHamiltoniana = [8, 3, 4, 5, 0, 7, 2, 1, 6, 9]
fig = plt.figure(1, figsize=(8, 7), dpi=60)
G = nx.petersen_graph()
shells = [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]
pos = nx.shell_layout(G, shells)
nx.draw(G, pos=pos, node_color="red", with_labels=True)
nx.draw_networkx_edges(
G,
pos = pos,
# list of tuples for hamiltonian path, not list !!!!!
edgelist = [(8,3), (3,4), (4,5), (5,0), (0,7), (7,2), (2,1), (1,6), (6,9), (9,8)],
width=8,
alpha=0.5,
edge_color="blue"
)
I have this problem. I try to triangulate points cloud by scipy.spatial.Delaunay. I used:
tri = Delaunay(points) # points: np.array() of 3d points
indices = tri.simplices
vertices = points[indices]
But, this code return tetrahedron. How is it possible return triangle of surface only?
Thanks
To get it to work as in code form, you have to parametrize the surface to 2D. For example in the case of ball (r,theta, psi), radius is constant (drop it out) and points are given by (theta,psi) which is 2D.
Scipy Delaunay is N-dimensional triangulation, so if you give 3D points it returns 3D objects. Give it 2D points and it returns 2D objects.
Below is a script that I used to create polyhedra for openSCAD. U and V are my parametrization (x and y) and these are the coordinates that I give to Delaunay. Note that now the "Delaunay triangulation properties" apply only in u,v coordinates (angles are maximized in uv -space not xyz -space, etc).
The example is a modified copy from http://matplotlib.org/1.3.1/mpl_toolkits/mplot3d/tutorial.html which originally uses Triangulation function (maps to Delaunay eventually?)
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.tri as mtri
from scipy.spatial import Delaunay
# u, v are parameterisation variables
u = np.array([0,0,0.5,1,1])
v = np.array([0,1,0.5,0,1])
x = u
y = v
z = np.array([0,0,1,0,0])
# Triangulate parameter space to determine the triangles
#tri = mtri.Triangulation(u, v)
tri = Delaunay(np.array([u,v]).T)
print 'polyhedron(faces = ['
#for vert in tri.triangles:
for vert in tri.simplices:
print '[%d,%d,%d],' % (vert[0],vert[1],vert[2]),
print '], points = ['
for i in range(x.shape[0]):
print '[%f,%f,%f],' % (x[i], y[i], z[i]),
print ']);'
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
# The triangles in parameter space determine which x, y, z points are
# connected by an edge
#ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=plt.cm.Spectral)
ax.plot_trisurf(x, y, z, triangles=tri.simplices, cmap=plt.cm.Spectral)
plt.show()
Below is the (slightly more structured) text output:
polyhedron(
faces = [[2,1,0], [3,2,0], [4,2,3], [2,4,1], ],
points = [[0.000000,0.000000,0.000000],
[0.000000,1.000000,0.000000],
[0.500000,0.500000,1.000000],
[1.000000,0.000000,0.000000],
[1.000000,1.000000,0.000000], ]);
It looks like you want to compute the convex hull of your point cloud. I think this is what you want to do:
from scipy.spatial import ConvexHull
hull = ConvexHull(points)
indices = hull.simplices
vertices = points[indices]
Following Jaime's answer, but elaborating a bit more with an example:
import matplotlib as mpl
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as a3
import numpy as np
import scipy as sp
from scipy import spatial as sp_spatial
def icosahedron():
h = 0.5*(1+np.sqrt(5))
p1 = np.array([[0, 1, h], [0, 1, -h], [0, -1, h], [0, -1, -h]])
p2 = p1[:, [1, 2, 0]]
p3 = p1[:, [2, 0, 1]]
return np.vstack((p1, p2, p3))
def cube():
points = np.array([
[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1],
[1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1],
])
return points
points = icosahedron()
# points = cube()
hull = sp_spatial.ConvexHull(points)
indices = hull.simplices
faces = points[indices]
print('area: ', hull.area)
print('volume: ', hull.volume)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.dist = 30
ax.azim = -140
ax.set_xlim([0, 2])
ax.set_ylim([0, 2])
ax.set_zlim([0, 2])
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
for f in faces:
face = a3.art3d.Poly3DCollection([f])
face.set_color(mpl.colors.rgb2hex(sp.rand(3)))
face.set_edgecolor('k')
face.set_alpha(0.5)
ax.add_collection3d(face)
plt.show()
Which should depict the following figure:
How can we plot 2D math vectors with matplotlib? Does anyone have an example or suggestion about that?
I have a couple of vectors stored as 2D numpy arrays, and I would like to plot them as directed edges.
The vectors to be plotted are constructed as below:
import numpy as np
# a list contains 3 vectors;
# each list is constructed as the tail and the head of the vector
a = np.array([[0, 0, 3, 2], [0, 0, 1, 1], [0, 0, 9, 9]])
Edit:
I just added the plot of the final answer of tcaswell for anyone interested in the output and want to plot 2d vectors with matplotlib:
The suggestion in the comments by halex is correct, you want to use quiver (doc), but you need to tweak the properties a bit.
import numpy as np
import matplotlib.pyplot as plt
soa = np.array([[0, 0, 3, 2], [0, 0, 1, 1], [0, 0, 9, 9]])
X, Y, U, V = zip(*soa)
plt.figure()
ax = plt.gca()
ax.quiver(X, Y, U, V, angles='xy', scale_units='xy', scale=1)
ax.set_xlim([-1, 10])
ax.set_ylim([-1, 10])
plt.draw()
plt.show()
It's pretty straightforward. Hope this example helps.
import matplotlib.pyplot as plt
import numpy as np
x = np.random.normal(10,5,100)
y = 3 + .5*x + np.random.normal(0,1,100)
myvec = np.array([x,y])
plt.plot(myvec[0,],myvec[1,],'ro')
plt.show()
Will produce:
To plot the arrays you can just slice them up into 1D vectors and plot them. I'd read the full documentation of matplotlib for all the different options. But you can treat a numpy vector as if it were a normal tuple for most of the examples.