Networkx: How to change node indexing - python

I am working with a regular network of 100x100=10000 nodes. The network is created just like this:
import networkx as nx
import matplotlib.pyplot as plt
N=100
G=nx.grid_2d_graph(N,N) #2D regular graph of 10000 nodes
pos = dict( (n, n) for n in G.nodes() ) #Dict of positions
labels = dict( ((i, j), i + (N-1-j) * N ) for i, j in G.nodes() )
nx.relabel_nodes(G,labels,False)
pos = {y:x for x,y in labels.iteritems()} #An attempt to change node indexing
I want to have node 0 in the upper left corner, and node 9999 in the lower right. This is why you see a second call to pos: it is an attempt to change node indexing according to my will.
However, I have noticed that after I run the script:
pos[0]=(0,99), pos[99]=(99,99), pos[9900]=(0,0), pos[9999]=(99,0).
This means that networkx sees the origin of the grid in the bottom left corner and that the farthest position from the origin, (99,99), belongs to the 99th node.
Now, I want to change that so to have my origin in the upper left corner. This means that I want to have:
pos[0]=(0,0), pos[99]=(0,99), pos[9900]=(99,0), pos[9999]=(99,99).
What should I change in pos?

I'm assuming you are following the example here: Remove rotation effect when drawing a square grid of MxM nodes in networkx using grid_2d_graph
With that being said, your picture will look like theirs if you do it just like they did. If you just want 'pos' to look different you can use:
inds = labels.keys()
vals = labels.values()
inds.sort()
vals.sort()
pos2 = dict(zip(vals,inds))
In [42]: pos2[0]
Out[42]: (0, 0)
In [43]: pos2[99]
Out[43]: (0, 99)
In [44]: pos2[9900]
Out[44]: (99, 0)
In [45]: pos2[9999]
Out[45]: (99, 99)

Related

Connecting Nodes using Assignment Matrix

In typical facility location problems, I have three facilities (Fi, i=1,2,3) and six nodes (Dj, j=1,2,3,4,5,6). I want to plot all Fi and Dj and then connect nodes Dj to facilities Fi, based on an assignment matrix Xij.
The matrix Xij is given as:
Xij = np.array([[1,0,0,1,1,1],
[0,1,1,0,0,0],
[0,0,0,0,0,0]])
The first row of Xij shows that nodes Dj (j=0,3,4,5) are allocated to the facility Fi (i=0). Second row shows Nodes Dj (j=1,2) are allocated to second facility Fi (i=2). Third row shows that no node is allocated to Facility Fi(i=2).
I tried to do it in matplotlib, to plot the nodes at specified locations, but don't know how to connect them.
fx = np.array([30, 30, 30])
fy = np.array([10, 20, 30])
f = np.vstack((fx, fy))
px = np.array([50, 50, 50, 50, 50])
py = np.array([10, 15, 20, 25, 30])
p = np.vstack((px, py))
plt.scatter(fx,fy, marker='D', s=100)
plt.scatter(px,py, marker='o', s=100)
Then I read about the Networkx library and tried to plot them as:
G1 = nx.Graph()
G2 = nx.Graph()
Fi = {0: (10,10),
1: (10,20),
2: (10,30)}
Dj ={0: (20,5),
1: (20,10),
2: (20,15),
3: (20,20),
4: (20,25),
5: (20,30)}
nx.draw_networkx(G1, Fi, node_color= 'gray', node_size=500)
nx.draw_networkx(G2, Dj, node_color= 'gray', node_size=300)
However, can't figure out how to connect these nodes easily in any tool?
The given problem is just a simple version of a bigger network.
One way of doing this is to convert your bipartite assignment matrix into a full adjacency matrix, then using that to populate your nx graph.
Xij = np.array([[1,0,0,1,1,1],
[0,1,1,0,0,0],
[0,0,0,0,0,0]])
A = Xij
At = A.T
Z_top_left = np.zeros((A.shape[0], At.shape[1]))
Z_bottom_right = np.zeros((At.shape[0], A.shape[1]))
G = nx.from_numpy_matrix(np.vstack([np.hstack([Z_top_left,A]) , np.hstack([At, Z_bottom_right])]))
Then you can draw your G graph (having set out the positions using methods outlined elsewhere here) and it will contain the edges you're looking for.
To get from an assignment matrix X, you need to compose an array consisting of X and the transpose of X in the top right and bottom left corners, filling in the rest with zeros, since there are no edges from Facility to Facility or Node to Node (to use your terms). It's a bipartite graph. That's what the hstack and vstack calls are doing in the above.
Alternately, you could loop through your assignment array, with i and j as row/column iterators and do:
G.add_edge(i,j)
This will create the nodes, and connect them with edges. One of the nx.draw family of commands will then lay them out graphically. I also notice there's an upcoming bipartite_layout option coming to networkx at sometime in the future.
You need to use pos for drawing in the right location, and for the edges, you should iterate over the matrix:
import numpy as np
import networkx as nx
from matplotlib import pyplot as plt
Xij = np.array([[1,0,0,1,1,1],
[0,1,1,0,0,0],
[0,0,0,0,0,0]])
Fi = {'F0': [10,10],
'F1': [10,20],
'F2': [10,30]}
Dj ={'D0': [20,5],
'D1': [20,10],
'D2': [20,15],
'D3': [20,20],
'D4': [20,25],
'D5': [20,30]}
newD = dict(Dj.items()) #Build a dictionary with all the items, for position
newD.update(Fi.items())
G1 = nx.Graph()
G1.add_nodes_from(newD)
for i in range(Xij.shape[0]): # Add an edge according to the matrix
for j in range(Xij.shape[1]):
if Xij[i,j] == 1:
G1.add_edge('F'+str(i), 'D'+str(j))
nx.draw(G1, with_labels=True, pos = newD) #Draw, with locations and edges
With result:
Added explanation inline with the code.
Edit:
For colors you need to define a color for each node:
colors = ['r' if x[0] == 'D' else 'b' for x in list(G1.nodes)]
nx.draw(G1, with_labels=True,node_color=colors, pos = newD) #Draw, with locations and edges, and colors

Python: how to compute the Euclidean distance distribution of a regular network?

I have an NxN regular network, each node of which has an (X,Y) set of coordinates. The nodes are separated by the unit. The network looks like this:
(0,0) (1,0) (2,0)
(0,1) (1,1) (2,1)
(0,2) (1,2) (2,2)
I want to be able to compute the Euclidean distance from each node to all the others. Example:
#Euclidean distances from node (0,0):
0 sqrt(1) sqrt(4)
sqrt(1) sqrt(2) sqrt(5)
sqrt(4) sqrt(5) sqrt(8)
Then, I want to draw the distance distribution, which tells me with which frequency a given distance has a certain value. I want then to turn the graph into a log-log plot.
This is my attempt:
import networkx as nx
from networkx import *
import matplotlib.pyplot as plt
#Creating the regular network
N=10 #This can vary
G=nx.grid_2d_graph(N,N)
pos = dict( (n, n) for n in G.nodes() )
labels = dict( ((i, j), i + (N-1-j) * N ) for i, j in G.nodes() )
nx.relabel_nodes(G,labels,False)
inds=labels.keys()
vals=labels.values()
inds.sort()
vals.sort()
pos2=dict(zip(vals,inds)) #Dict storing the node coordinates
nx.draw_networkx(G, pos=pos2, with_labels=False, node_size = 15)
#Computing the edge length distribution
def plot_edge_length_distribution(): #Euclidean distances from all nodes
lengths={}
for k, item in pos2:
for t, elements in pos2:
if k==t:
lengths[k]=0
else:
lengths[k]=((pos2[t][2]-pos2[k][2])**2)+((pos2[t][1]-pos2[k][1])**2) #The square distance (it's ok to leave it like this)
items=sorted(lengths.items())
fig=plt.figure()
ax=fig.add_subplot(111)
ax.plot([k for (k,v) in items],[v for (k,v) in items],'ks-')
ax.set_xscale("log")
ax.set_yscale("log")
title_string=('Edge Length Distribution')
subtitle_string=('Lattice Network | '+str(N)+'x'+str(N)+' nodes')
plt.suptitle(title_string, y=0.99, fontsize=17)
plt.title(subtitle_string, fontsize=9)
plt.xlabel('Log l')
plt.ylabel('Log p(l)')
ax.grid(True,which="both")
plt.show()
plot_edge_length_distribution()
EDIT
When running, this script throws out the error: TypeError: 'int' object is not iterable, pointing at the line where I wrote for k, item in pos2:. Where is it that I go wrong?
The function scipy.spatial.distance.pdist does this about as efficiently as can be.
Consider the following:
from scipy.spatial import distance
import numpy as np
coords = [np.array(list(c)) for c in [(0,0),(1,0), (2,0)]]
>>> distance.pdist(coords)
array([ 1., 2., 1.])
The function returns the upper-right part of the distance matrix - the diagonals are 0, and the lower-left part can be obtained from the transpose.
E.g., the above corresponds to
0 1 2
1 0 1
2 1 0
with
the 0 diagonal and everything to its lower-left removed.
the upper-right "flattened" to [1, 2, 1].
It is not difficult to reconstruct the distances from the flattened result.

border/edge operations on numpy arrays

Suppose I have a 3D numpy array of nonzero values and "background" = 0. As an example I will take a sphere of random values:
array = np.random.randint(1, 5, size = (100,100,100))
z,y,x = np.ogrid[-50:50, -50:50, -50:50]
mask = x**2 + y**2 + z**2<= 20**2
array[np.invert(mask)] = 0
First, I would like to find the "border voxels" (all nonzero values that have a zero within their 3x3x3 neigbourhood). Second, I would like to replace all border voxels with the mean of their nonzero neighbours. So far I tried to use scipy's generic filter in the following way:
Function to apply at each element:
def borderCheck(values):
#check if the footprint center is on a nonzero value
if values[13] != 0:
#replace border voxels with the mean of nonzero neighbours
if 0 in values:
return np.sum(values)/np.count_nonzero(values)
else:
return values[13]
else:
return 0
Generic filter:
from scipy import ndimage
result = ndimage.generic_filter(array, borderCheck, footprint = np.ones((3,3,3)))
Is this a proper way to handle this problem? I feel that I am trying to reinvent the wheel here and that there must be a shorter, nicer way to achieve the result. Are there any other suitable (numpy, scipy ) functions that I can use?
EDIT
I messed one thing up: I would like to replace all border voxels with the mean of their nonzero AND non-border neighbours. For this, I tried to clean up the neighbours from ali_m's code (2D case):
#for each neighbour voxel, check whether it also appears in the border/edges
non_border_neighbours = []
for each in neighbours:
non_border_neighbours.append([i for i in each if nonzero_idx[i] not in edge_idx])
Now I can't figure out why non_border_neighbours comes back empty?
Furthermore, correct me if I am wrong but doesn't tree.query_ball_point with radius 1 adress only the 6 next neighbours (euclidean distance 1)? Should I set sqrt(3) (3D case) as radius to get the 26-neighbourhood?
I think it's best to start out with the 2D case first, since it can be visualized much more easily:
import numpy as np
from matplotlib import pyplot as plt
A = np.random.randint(1, 5, size=(100, 100)).astype(np.double)
y, x = np.ogrid[-50:50, -50:50]
mask = x**2 + y**2 <= 30**2
A[~mask] = 0
To find the edge pixels you could perform binary erosion on your mask, then XOR the result with your mask
# rank 2 structure with full connectivity
struct = ndimage.generate_binary_structure(2, 2)
erode = ndimage.binary_erosion(mask, struct)
edges = mask ^ erode
One approach to find the nearest non-zero neighbours of each edge pixel would be to use a scipy.spatial.cKDTree:
from scipy.spatial import cKDTree
# the indices of the non-zero locations and their corresponding values
nonzero_idx = np.vstack(np.where(mask)).T
nonzero_vals = A[mask]
# build a k-D tree
tree = cKDTree(nonzero_idx)
# use it to find the indices of all non-zero values that are at most 1 pixel
# away from each edge pixel
edge_idx = np.vstack(np.where(edges)).T
neighbours = tree.query_ball_point(edge_idx, r=1, p=np.inf)
# take the average value for each set of neighbours
new_vals = np.hstack(np.mean(nonzero_vals[n]) for n in neighbours)
# use these to replace the values of the edge pixels
A_new = A.astype(np.double, copy=True)
A_new[edges] = new_vals
Some visualisation:
fig, ax = plt.subplots(1, 3, figsize=(10, 4), sharex=True, sharey=True)
norm = plt.Normalize(0, A.max())
ax[0].imshow(A, norm=norm)
ax[0].set_title('Original', fontsize='x-large')
ax[1].imshow(edges)
ax[1].set_title('Edges', fontsize='x-large')
ax[2].imshow(A_new, norm=norm)
ax[2].set_title('Averaged', fontsize='x-large')
for aa in ax:
aa.set_axis_off()
ax[0].set_xlim(20, 50)
ax[0].set_ylim(50, 80)
fig.tight_layout()
plt.show()
This approach will also generalize to the 3D case:
B = np.random.randint(1, 5, size=(100, 100, 100)).astype(np.double)
z, y, x = np.ogrid[-50:50, -50:50, -50:50]
mask = x**2 + y**2 + z**2 <= 20**2
B[~mask] = 0
struct = ndimage.generate_binary_structure(3, 3)
erode = ndimage.binary_erosion(mask, struct)
edges = mask ^ erode
nonzero_idx = np.vstack(np.where(mask)).T
nonzero_vals = B[mask]
tree = cKDTree(nonzero_idx)
edge_idx = np.vstack(np.where(edges)).T
neighbours = tree.query_ball_point(edge_idx, r=1, p=np.inf)
new_vals = np.hstack(np.mean(nonzero_vals[n]) for n in neighbours)
B_new = B.astype(np.double, copy=True)
B_new[edges] = new_vals
Test against your version:
def borderCheck(values):
#check if the footprint center is on a nonzero value
if values[13] != 0:
#replace border voxels with the mean of nonzero neighbours
if 0 in values:
return np.sum(values)/np.count_nonzero(values)
else:
return values[13]
else:
return 0
result = ndimage.generic_filter(B, borderCheck, footprint=np.ones((3, 3, 3)))
print(np.allclose(B_new, result))
# True
I'm sure this isn't the most efficient way to do it, but it will still be significantly faster than using generic_filter.
Update
The performance could be further improved by reducing the number of points that are considered as candidate neighbours of the edge pixels/voxels:
# ...
# the edge pixels/voxels plus their immediate non-zero neighbours
erode2 = ndimage.binary_erosion(erode, struct)
candidate_neighbours = mask ^ erode2
nonzero_idx = np.vstack(np.where(candidate_neighbours)).T
nonzero_vals = B[candidate_neighbours]
# ...

python + maya: Returns me a list of nodes with incorrect names

I've created a function which creates a grid of circles and I need to collect the circle nodes created into a list so I can later manipulate the nodes. The problem is that I noticed the nodeList is given the nodes name before it's auto-renamed by maya to be unique. You'll notice when you run this script that the collected names are all the same but when you selected them in maya they are incremented to be unique.
I'm returned this
[u'mainShape_00', u'makeNurbCircle1']
[u'|mainShape_00', u'makeNurbCircle2']
[u'|mainShape_00', u'makeNurbCircle3']...
When it should be
[u'mainShape_00', u'makeNurbCircle1']
[u'|mainShape_01', u'makeNurbCircle2']
[u'|mainShape_02', u'makeNurbCircle3']...
Here is the script
# Import Modules
import maya.cmds as cmds
import random
# Scene setup
try:
cmds.select(all=True)
cmds.delete()
except:
pass
# create 2D grid of circles
numRows = 4
numColumns = 3
radiusMin = .1
radiusMax = .75
#create empty group for nodes
nodeGroup = cmds.group(em=True, name='main_group_00')
nodeList = []
for r in range(0,numRows):
for c in range(0,numColumns):
# Calculate random radius
radius = random.uniform(radiusMin,radiusMax)
# Create circle shape and transform it
node = cmds.circle(n='mainShape_00', ch=True, o=True, nr=(0, 0, 1), c=(0, 0, 0), r=radius)
cmds.xform(node, t=(r*(radiusMax*2), c*(radiusMax*2), 0) )
# Parent node under the group node
cmds.parent(node[0], nodeGroup, relative=False)
# Append nodes to list
nodeList.append(node)
for n in nodeList:
shape = n
print shape
node is 'mainShape_00' because at that time, that's what it's named. There is no collision until it's parented under nodeGroup. Grab the real name after parenting:
node[0] = cmds.parent(node[0], nodeGroup, relative=False)[0]
This substitutes the original node[0] with the newly parented node[0]
Why not naming yourself your nodes as this :
x = 0
padding = str(x).zfill(2)
mainShapeName = 'mainShape_' + padding
x += 1
# Create circle shape and transform it
node = cmds.circle(n=mainShapeName, ch=True, o=True, nr=(0, 0, 1), c=(0, 0, 0), r=radius)
cmds.xform(node, t=(r*(radiusMax*2), c*(radiusMax*2), 0) )
By incrementing yourself, you avoid maya problems.
You should even gave unique name throught every groups.
Cheers.

Drawing multiplex graphs with networkx?

I'm trying to visualize a few graphs whose nodes represent different objects. I want to create an image that looks like the one here:
Basically, I need a 3D plot and the ability to draw edges between nodes on the same level or nodes on different levels.
This answer below may not be a complete solution, but is a working demo for rendering 3D graphs using networkx.
networkx as such cannot render 3D graphs. We will have to install mayavi for that to happen.
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
from mayavi import mlab
import random
def draw_graph3d(graph, graph_colormap='winter', bgcolor = (1, 1, 1),
node_size=0.03,
edge_color=(0.8, 0.8, 0.8), edge_size=0.002,
text_size=0.008, text_color=(0, 0, 0)):
H=nx.Graph()
# add edges
for node, edges in graph.items():
for edge, val in edges.items():
if val == 1:
H.add_edge(node, edge)
G=nx.convert_node_labels_to_integers(H)
graph_pos=nx.spring_layout(G, dim=3)
# numpy array of x,y,z positions in sorted node order
xyz=np.array([graph_pos[v] for v in sorted(G)])
# scalar colors
scalars=np.array(G.nodes())+5
mlab.figure(1, bgcolor=bgcolor)
mlab.clf()
#----------------------------------------------------------------------------
# the x,y, and z co-ordinates are here
# manipulate them to obtain the desired projection perspective
pts = mlab.points3d(xyz[:,0], xyz[:,1], xyz[:,2],
scalars,
scale_factor=node_size,
scale_mode='none',
colormap=graph_colormap,
resolution=20)
#----------------------------------------------------------------------------
for i, (x, y, z) in enumerate(xyz):
label = mlab.text(x, y, str(i), z=z,
width=text_size, name=str(i), color=text_color)
label.property.shadow = True
pts.mlab_source.dataset.lines = np.array(G.edges())
tube = mlab.pipeline.tube(pts, tube_radius=edge_size)
mlab.pipeline.surface(tube, color=edge_color)
mlab.show() # interactive window
# create tangled hypercube
def make_graph(nodes):
def make_link(graph, i1, i2):
graph[i1][i2] = 1
graph[i2][i1] = 1
n = len(nodes)
if n == 1: return {nodes[0]:{}}
nodes1 = nodes[0:n/2]
nodes2 = nodes[n/2:]
G1 = make_graph(nodes1)
G2 = make_graph(nodes2)
# merge G1 and G2 into a single graph
G = dict(G1.items() + G2.items())
# link G1 and G2
random.shuffle(nodes1)
random.shuffle(nodes2)
for i in range(len(nodes1)):
make_link(G, nodes1[i], nodes2[i])
return G
# graph example
nodes = range(10)
graph = make_graph(nodes)
draw_graph3d(graph)
This code was modified from one of the examples here.
Please post the code in this case, when you succeed in reaching the objective.

Categories

Resources