For a graph in networkx, I have made a layout to draw a network graph using code below:
data = pd.read_csv('data\\email-dept3.csv')
edges = [edge for edge in zip(data['source'],data['target'])]
print(len(edges))
G = nx.Graph()
G.add_edges_from(edges)
node_pos = nx.kamada_kawai_layout(G)
#I want to get the edge length as one attributes, but I don't know how to code this function
edge_length = calculate_edge_length()
nx.draw_networkx_nodes(G,node_pos,**options)#draw nodes
[nx.draw_networkx_edges(G,node_pos,edgelist=[key],alpha=np.amin([1,value*100]),width=2) for key,value in cent.items()]
plt.show()
And the result is:
What I want to do is get the every edge's length in this graph. Because after layout, every node has a position in screen, and the edge has its length according to its two nodes' position. But in networkx's API, I can't find the method to get the edge's length. And I also don't know how to calculate this value.
If you need more information, please contact me.
I am trying all kinds of methods to adjust the transparency of edges. The length of line is one of my consideration.
Interesting idea! Seems like a worthwhile experiment; I'll let you decide if it works well or not. :-)
But in networkx's API, I can't find the method to get the edge's length
I think you have to compute them yourself. Fortunately, that's not too hard. Here's an example.
import numpy as np
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (10,10)
def example_graph():
"""
Return the classic Karate Club network, but give text labels to the nodes.
"""
labels = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJZKLMNOPQRSTUVWXYZ'
kg = nx.karate_club_graph()
edges = [(labels[i], labels[j]) for i,j in kg.edges()]
G = nx.Graph()
G.add_edges_from(edges)
return G
# Test network
G = example_graph()
# Determine layout node positions
node_pos = nx.kamada_kawai_layout(G)
# Determine edge distances (from the node positions)
node_pos_df = pd.DataFrame(node_pos.values(), columns=['x', 'y'], index=node_pos.keys())
node_pos_df = node_pos_df.rename_axis('label').sort_index()
edges = np.array(G.edges())
u_pos = node_pos_df.loc[edges[:, 0]].values
v_pos = node_pos_df.loc[edges[:, 1]].values
distances = np.linalg.norm(u_pos - v_pos, axis=1)
## Optional: Add the distances as edge attributes
#edge_distances = {(u,v): d for (u,v), d in zip(G.edges(), distances)}
#nx.set_edge_attributes(G, edge_distances, "layout_distance")
# Compute alpha: Set 0.15 as minimum alpha, 1.0 as maximum alpha
d_min, d_max = distances.min(), distances.max()
alphas = 1.0 - 0.85 * (distances - d_min) / (d_max - d_min)
# Draw graph
nx.draw_networkx_nodes(G, node_pos)
nx.draw_networkx_edges(G, node_pos, edgelist=G.edges(), alpha=alphas, width=2)
plt.show()
I want to take all the intersections of a set of lines and find all the convex quadrilaterals they create. I am not sure if there is an algorithm that works perfect for this, or if I need to loop through and create my own.
I have an array of lines, and all their intersections.
Lines and intersections:
Example Quadrilaterals 1:
Example Quadrilaterals 2
In this case, I would come out with 8 quadrilaterals.
How can I achieve this? If there isn't an algorithm I can implement for this, how can I check each intersection with other intersections to determine if they make a convex quadrilateral?
There is a simple, non-speedy, brute-force over-all algorithm to find those quadrilaterals. However, first you would need to clarify some definitions, especially that of a "quadrilateral." Do you count it as a quadrilateral if it has zero area, such as when all the vertices are collinear? Do you count it as a quadrilateral if it self-intersects or crosses? Do you count it if it is not convex? Do you count it if two adjacent sides are straight (which includes consecutive vertices identical)? What about if the polygon "doubles back" on itself so the result looks like a triangle with one side extended?
Here is a top-level algorithm: Consider all combinations of the line segments taken four at a time. If there are n line segments then there are n*(n-1)*(n-2)*(n-3)/24 combinations. For each combination, look at the intersections of pairs of these segments: there will be at most 6 intersections. Now see if you can make a quadrilateral from those intersections and segments.
This is brute-force, but at least it is polynomial in execution time, O(n^4). For your example of 8 line segments that means considering 70 combinations of segments--not too bad. This could be sped up somewhat by pre-calculating the intersection points: there are at most n*(n-1)/2 of them, 28 in your example.
Does this overall algorithm meet your needs? Is your last question "how can I check each intersection with other intersections to determine if they make a quadrilateral?" asking how to implement my statement "see if you can make a quadrilateral from those intersections and segments"? Do you still need an answer to that? You would need to clarify your definition of a quadrilateral before I could answer that question.
I'll explain the definitions of "quadrilateral" more. This diagram shows four line segments "in general position," where each segment intersects all the others and no three segments intersect in the same point.
Here are (some of) the "quadrilaterals" arising from those four line segments and six intersection points.
1 simple and convex (ABDE)
1 simple and not convex (ACDF)
1 crossing (BCEF)
4 triangles with an extra vertex on a triangle's side (ABCE, ACDE, ABDF, ABFE). Note that the first two define the same region with different vertices, and the same is true of the last two.
4 "double-backs" which looks like a triangle with one side extended (ACEF, BCDF, BDEF, CDEF)
Depending on how you define "quadrilateral" and "equal" you could get anywhere from 1 to 11 of them in that diagram. Wikipedia's definition would include only the first, second, and fourth in my list--I am not sure how that counts the "duplicates" in my fourth group. And I am not even sure that I found all the possibilities in my diagram, so there could be even more.
I see we are now defining a quadrilateral as outlined by four distinct line segments that are sub-segments of the given line segments that form a polygon that is strictly convex--the vertex angles are all less than a straight angle. This still leaves an ambiguity in a few edge cases--what if two line segments overlap more than at one point--but let's leave that aside other than defining that two such line segments have no intersection point. Then this algorithm, pseudo-code based on Python, should work.
We need a function intersection_point(seg1, seg2) that returns the intersection point of the two given line segments or None if there is none or the segments overlap. We also need a function polygon_is_strictly_convex(tuple of points) that returns True or False depending on if the tuple of points defines a strictly-convex polygon, with the addition that if any of the points is None then False is returned. Both those functions are standard in computational geometry. Note that "combination" in the following means that for each returned combination the items are in sorted order, so of (seg1, seg2) and (seg2, seg1) we will get exactly one of them. Python's itertools.combinations() does this nicely.
intersections = {} # empty dictionary/hash table
for each combination (seg1, seg2) of given line segments:
intersections[(seg1, seg2)] = intersection_point(seg1, seg2)
quadrilaterals = emptyset
for each combination (seg1, seg2, seg3, seg4) of given line segments:
for each tuple (sega, segb, segc, segc) in [
(seg1, seg2, seg3, seg4),
(seg1, seg2, seg4, seg3),
(seg1, seg3, seg2, seg4)]:
a_quadrilateral = (intersections[(sega, segb)],
intersections[(segb, segc)],
intersections[(segc, segd)],
intersections[(segd, sega)])
if polygon_is_strictly_convex(a_quadrilateral):
quadrilaterals.add(a_quadrilateral)
break # only one possible strictly convex quad per 4 segments
Here is my actual, tested, Python 3.6 code, which for your segments gives your eight polygons. First, here are the utility, geometry routines, collected into module rdgeometry.
def segments_intersection_point(segment1, segment2):
"""Return the intersection of two line segments. If none, return
None.
NOTES: 1. This version returns None if the segments are parallel,
even if they overlap or intersect only at endpoints.
2. This is optimized for assuming most segments intersect.
"""
try:
pt1seg1, pt2seg1 = segment1 # points defining the segment
pt1seg2, pt2seg2 = segment2
seg1_delta_x = pt2seg1[0] - pt1seg1[0]
seg1_delta_y = pt2seg1[1] - pt1seg1[1]
seg2_delta_x = pt2seg2[0] - pt1seg2[0]
seg2_delta_y = pt2seg2[1] - pt1seg2[1]
denom = seg2_delta_x * seg1_delta_y - seg1_delta_x * seg2_delta_y
if denom == 0.0: # lines containing segments are parallel or equal
return None
# solve for scalars t_seg1 and t_seg2 in the vector equation
# pt1seg1 + t_seg1 * (pt2seg1 - pt1seg1)
# = pt1seg2 + t_seg2(pt2seg2 - pt1seg2) and note the segments
# intersect iff 0 <= t_seg1 <= 1, 0 <= t_seg2 <= 1 .
pt1seg1pt1seg2_delta_x = pt1seg2[0] - pt1seg1[0]
pt1seg1pt1seg2_delta_y = pt1seg2[1] - pt1seg1[1]
t_seg1 = (seg2_delta_x * pt1seg1pt1seg2_delta_y
- pt1seg1pt1seg2_delta_x * seg2_delta_y) / denom
t_seg2 = (seg1_delta_x * pt1seg1pt1seg2_delta_y
- pt1seg1pt1seg2_delta_x * seg1_delta_y) / denom
if 0 <= t_seg1 <= 1 and 0 <= t_seg2 <= 1:
return (pt1seg1[0] + t_seg1 * seg1_delta_x,
pt1seg1[1] + t_seg1 * seg1_delta_y)
else:
return None
except ArithmeticError:
return None
def orientation3points(pt1, pt2, pt3):
"""Return the orientation of three 2D points in order.
Moving from Pt1 to Pt2 to Pt3 in cartesian coordinates:
1 means counterclockwise (as in standard trigonometry),
0 means straight, back, or stationary (collinear points),
-1 means counterclockwise,
"""
signed = ((pt2[0] - pt1[0]) * (pt3[1] - pt1[1])
- (pt2[1] - pt1[1]) * (pt3[0] - pt1[0]))
return 1 if signed > 0.0 else (-1 if signed < 0.0 else 0)
def is_convex_quadrilateral(pt1, pt2, pt3, pt4):
"""Return True if the quadrilateral defined by the four 2D points is
'strictly convex', not a triangle nor concave nor self-intersecting.
This version allows a 'point' to be None: if so, False is returned.
NOTES: 1. Algorithm: check that no points are None and that all
angles are clockwise or all counter-clockwise.
2. This does not generalize to polygons with > 4 sides
since it misses star polygons.
"""
if pt1 and pt2 and pt3 and pt4:
orientation = orientation3points(pt4, pt1, pt2)
if (orientation != 0 and orientation
== orientation3points(pt1, pt2, pt3)
== orientation3points(pt2, pt3, pt4)
== orientation3points(pt3, pt4, pt1)):
return True
return False
def polygon_in_canonical_order(point_seq):
"""Return a polygon, reordered so that two different
representations of the same geometric polygon get the same result.
The result is a tuple of the polygon's points. `point_seq` must be
a sequence of 'points' (which can be anything).
NOTES: 1. This is intended for the points to be distinct. If two
points are equal and minimal or adjacent to the minimal
point, which result is returned is undefined.
"""
pts = tuple(point_seq)
length = len(pts)
ndx = min(range(length), key=pts.__getitem__) # index of minimum
if pts[(ndx + 1) % length] < pts[(ndx - 1) % length]:
return (pts[ndx],) + pts[ndx+1:] + pts[:ndx] # forward
else:
return (pts[ndx],) + pts[:ndx][::-1] + pts[ndx+1:][::-1] # back
def sorted_pair(val1, val2):
"""Return a 2-tuple in sorted order from two given values."""
if val1 <= val2:
return (val1, val2)
else:
return (val2, val1)
And here is the code for my algorithm. I added a little complexity to use only a "canonical form" of a pair of line segments and for a polygon, to reduce the memory usage of the intersections and polygons containers.
from itertools import combinations
from rdgeometry import segments_intersection_point, \
is_strictly_convex_quadrilateral, \
polygon_in_canonical_order, \
sorted_pair
segments = [(( 2, 16), (22, 10)),
(( 4, 4), (14, 14)),
(( 4, 6), (12.54, 0.44)),
(( 4, 14), (20, 6)),
(( 4, 18), (14, 2)),
(( 8, 2), (22, 16))]
intersections = dict()
for seg1, seg2 in combinations(segments, 2):
intersections[sorted_pair(seg1, seg2)] = (
segments_intersection_point(seg1, seg2))
quadrilaterals = set()
for seg1, seg2, seg3, seg4 in combinations(segments, 4):
for sega, segb, segc, segd in [(seg1, seg2, seg3, seg4),
(seg1, seg2, seg4, seg3),
(seg1, seg3, seg2, seg4)]:
a_quadrilateral = (intersections[sorted_pair(sega, segb)],
intersections[sorted_pair(segb, segc)],
intersections[sorted_pair(segc, segd)],
intersections[sorted_pair(segd, sega)])
if is_strictly_convex_quadrilateral(*a_quadrilateral):
quadrilaterals.add(polygon_in_canonical_order(a_quadrilateral))
break # only one possible strictly convex quadr per 4 segments
print('\nThere are {} strictly convex quadrilaterals, namely:'
.format(len(quadrilaterals)))
for p in sorted(quadrilaterals):
print(p)
And the printout from that is:
There are 8 strictly convex quadrilaterals, namely:
((5.211347517730497, 5.211347517730497), (8.845390070921987, 2.8453900709219857), (11.692307692307693, 5.692307692307692), (9.384615384615383, 9.384615384615383))
((5.211347517730497, 5.211347517730497), (8.845390070921987, 2.8453900709219857), (14.666666666666666, 8.666666666666668), (10.666666666666666, 10.666666666666666))
((5.211347517730497, 5.211347517730497), (8.845390070921987, 2.8453900709219857), (17.384615384615387, 11.384615384615383), (12.769230769230768, 12.76923076923077))
((6.0, 14.8), (7.636363636363637, 12.181818181818182), (10.666666666666666, 10.666666666666666), (12.769230769230768, 12.76923076923077))
((6.0, 14.8), (7.636363636363637, 12.181818181818182), (14.666666666666666, 8.666666666666668), (17.384615384615387, 11.384615384615383))
((9.384615384615383, 9.384615384615383), (10.666666666666666, 10.666666666666666), (14.666666666666666, 8.666666666666668), (11.692307692307693, 5.692307692307692))
((9.384615384615383, 9.384615384615383), (11.692307692307693, 5.692307692307692), (17.384615384615387, 11.384615384615383), (12.769230769230768, 12.76923076923077))
((10.666666666666666, 10.666666666666666), (12.769230769230768, 12.76923076923077), (17.384615384615387, 11.384615384615383), (14.666666666666666, 8.666666666666668))
A O(intersection_count2) algorithm is as follows:
For each intersection:
Add the the intersection point to
a hash table with the lines as the key.
Let int be a lookup function that returns
true iff the inputted lines intersect.
RectCount = 0
For each distinct pair of intersections a,b:
Let A be the list of lines that pass
through point a but not through b.
Let B '' '' '' through b but not a.
For each pair of lines c,d in A:
For each pair of lines e,f in B:
If (int(c,e) and int(d,f) and
!int(c,f) and !int(d,e)) or
(int(c,f) and int(d,e) and
!int(c,e) and !int(d,f)):
RectCount += 1
So i got this problem while practicing python, but i guess it applies to all languages. I am to draw all the diagonals in a regular n-gon and that is fine, I managed to do that. But, there is another criteria aswell, the same line isn't to be drawn more than once. The way I interpret this is that the turtle (I use turtle graphics btw) cannot travel the between the same two corners twice, and not just that you are to lift the pen. I have been trying too find a solution to this for a while now but I can't seem to figure it out and have started to wonder if it is even possible.
Do anyone on here know if it is possible to do for all n-gons? And if it is, could you give me a hint?
Here are two regular n-gons for those who don't know what that is.
(I sure as hell didn't)
Nonagon
Octagon
/Q
EDIT
Thanks to John Kahn i was able to do the solvable part, as he pointed out it can only be done on regular n-gons of an uneven degree.
Here is the code for my solution. What do you think?
def nhorning(r, n, ):
if n % 2 == 0:
print("It can't be done")
return None
angl = (2 * pi) / n # angle for calculating all the coordinates of the n-gon
a = {} # contains the destinations for each corners diagonals
cord = {} # contains the coordinates of each corner
for x in range(n):
cord[x] = [float("%.2f" % (r * cos(x * angl))), float("%.2f" % (r * sin(x * angl)))] # all corners coordinates
for i in range(n): # the diagonals that are to be drawn from the corner "i"
a[i] = [x for x in range(n)]
a[i].remove(i) # can't draw a diagonal to itself
count = 0
pu()
goto(cord[0]) # you have to start on a corner
pd()
cordstring = str(cord) # a list isn't hashable, so making the lists to a string
while count < (((n-1) * n) / 2): # loops until all diagonals are drawn
if str(list(pos())) in cordstring: # should always be on the circles radius except in the beginning
for i in range(len(cord)):
if cord[i] == list(pos()): # finds what corner the turtle is on
where = i
diags = a[where] # the diagonals not drawn from the corner
dest = diags.pop() # the corner to which a diagonal is to be drawn,
# removes it since the diagonal to that corner will be drawn
nwhere = a[dest] # the diagonals not drawn from the corner where a
# diagonal is to be drawn next
nwhere.remove(where) # the diagonal from the destination corner to the current corner will be drawn next,
# so can't be drawn again
goto(cord[dest]) # draw the diagonal
count += 1
done()
TLDR
You are looking for a Eulerian Path.
It is possible to do this with an odd number of vertices, but is impossible with an even number.
Explanation
"To see why this is true, note that every time the path passes through a vertex, it uses two of the edges connected to the vertex. Because of this, all vertices except the first and last one on the path need to have even degree. In the case of a cycle, the first and last vertex is the same, so all the vertices need to have even degree." - For a square, but the concept applies to n-gons
I am working in Python (package: NetworkX) with a network of, say, 100 nodes. I create it and then fragment it by removing a fraction of its nodes (removal), as shown below. The script computes the length of the largest component and its center node(s).
import networkx as nx
import matplotlib.pyplot as plt
import numpy
N = 10
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)
pos = {y:x for x,y in labels.iteritems()}
nx.draw_networkx(G, pos=pos, with_labels=True, node_size = 300)
plt.axis('off')
plt.show()
plt.close()
removal=numpy.array([1,5,18,23,54,8,36,95,41,75,77,56,29,39,81,76,27,34,52,50,53,64,45,85])
G.remove_nodes_from(removal)
nx.draw_networkx(G, pos=pos, with_labels=True, node_size = 300)
plt.axis('off')
plt.show()
giant = max(nx.connected_component_subgraphs(G), key=len) #The largest component
center_nodes = nx.center(giant) #The center node(s)
print len(giant)
print center_nodes
This gives me:
len(giant)=29 and center_nodes=[12,13].
What the network looks like after removal:
My network is embedded in a 2D grid which measures (N+1)x(N+1), and has its own coordinate system. Every node of the network is to be seen as if it was placed at the intersection of each cell in the grid below:
My problem: How can I "translate" the result given by center_nodes=[12,13] into the location of cell A in the grid? In this case, I would like to have center_nodes=[12,13] -> center_coord=13.
PS: if I change removal, len(center_nodes) changes, as does the shape of the connected subgraphs. Thus, cell A will not be in the same position as above. To account for this, I would like to be able to always get the grid coordinates of the cell at the top left corner of the center_nodes cluster, regardless of its shape and location within the network.