I am reforming the 2D coordinate number in a aligned way which was not aligned (coordinate numbers were suffled) before.
I have below input coordinates,
X = [2, 2, 3, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 5, 4, 3, 5, 5, 5]
Y = [2, 3, 3, 3, 4, 5, 6, 6, 6, 5, 4, 3, 2, 2, 2, 2, 3, 4, 5]
I have to make it aligned. Therefore, I first applied Sorted function on this coordinates. I got below output after it.
merged_list1 = sorted(zip(X, Y))
output
X1_coordinate_reformed = [2, 2, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6]
Y1_coordinate_reformed = [2, 3, 2, 3, 2, 3, 4, 5, 6, 2, 3, 4, 5, 6, 2, 3, 4, 5, 6]
Still it iot aligned properly. I want two consecutive nodes place next to each other. Therefore I am applying the approach to find the nearest coordinate from origin to find the very first node. Then from the first node, I found another nearest coordinate and so on...For that, I have applied below code,
First I wrote a function which calculates the distance and gives index of the nearest coordinate from the list.
def solve(pts, pt):
x, y = pt
idx = -1
smallest = float("inf")
for p in pts:
if p[0] == x or p[1] == y:
dist = abs(x - p[0]) + abs(y - p[1])
if dist < smallest:
idx = pts.index(p)
smallest = dist
elif dist == smallest:
if pts.index(p) < idx:
idx = pts.index(p)
smallest = dist
return idx
coor2 = list(zip(X1_coordinate_reformed, Y1_coordinate_reformed)) # make a list which contains tuples of X and Y coordinates
pts2 = coor2.copy()
origin1 = (0, 0)
new_coor1 = []
for i in range(len(pts2)):
pt = origin1
index_num1 = solve(pts2, pt)
print('index is', index_num1)
origin1 = pts2[index_num1]
new_coor1.append(pts2[index_num1])
del pts2[index_num1]
After running the code, I got below output,
[(6, 6), (5, 6), (4, 6), (4, 5), (4, 4), (4, 3), (3, 3), (2, 3), (2, 2), (3, 2), (4, 2), (5, 2), (5, 3), (5, 4), (5, 5), (6, 5), (6, 4), (6, 3), (6, 2)]
Which is not correct because it can be clearly understand that,
coor2 = [(2, 2), (2, 3), (3, 2), (3, 3), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6)]
origin = (0, 0)
if we find the distance between Origin which was (0, 0) in very first and from every coordinate from above coor2 list, we will get (2,2) is nearest coordinate. Then How come my code gives (6,6) is the nearest coordinate??
The interesting thing is, if I apply the same procedure (sorting followed by finding nearest coordinate) on below coordinates,
X2_coordinate = [2, 4, 4, 2, 3, 2, 4, 3, 1, 3, 4, 3, 1, 2, 0, 3, 4, 2, 0]
Y2_coordinate = [3, 4, 2, 1, 3, 2, 1, 0, 0, 2, 3, 4, 1, 4, 0, 1, 0, 0, 1]
After applying sorted function
X2_coordinate_reformed = [0, 0, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4]
Y2_coordinate_reformed = [0, 1, 0, 1, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
After applying method of searching nearest coordinates mentioned above, the result I got
[(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 4), (3, 3), (3, 2), (3, 1), (3, 0), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]
Kindly suggest me where I am doing wrong and what should I change??
It is better to use scipy for finding closest coordinate.
The code given below works.
from scipy import spatial
pts = merged_list1.copy()
origin = (0, 0)
origin = np.array(origin)
new_coordi = []
for i in range(len(pts)):
x = origin
distance,index = spatial.KDTree(pts).query(x)
new_coordi.append(pts[index])
origin = np.array(pts[index])
del pts[index]
Related
I'm using subplot2grid to define a grid of plots as shown below.
Works great, it's a good functionality.
plot_axes_1 = plt.subplot2grid((6, 4), (0, 0), rowspan=2, colspan=3) ##1
plot_axes_2 = plt.subplot2grid((6, 4), (2, 0), rowspan=2, colspan=3, sharex=scatter_axes_1) ##2
x_hist_axes_2 = plt.subplot2grid((6, 4), (4, 0), colspan=3, sharex=scatter_axes_2) ##3
y_hist_axes_1 = plt.subplot2grid((6, 4), (0, 3), rowspan=2, sharey=scatter_axes_1) ##4
y_hist_axes_2 = plt.subplot2grid((6, 4), (2, 3), rowspan=2, sharey=scatter_axes_2, sharex= y_hist_axes_1) ##5
Now I want to consider the 5 plots from the image as a unit, and plot 6 copies of it, arranged on 3 rows and 2 columns.
fig, ax= plt.subplots(3,2)
for l in range(3):
for m in range(2):
ax[l,m].subplot2grid((6, 4), (0, 0), rowspan=2, colspan=3) ##1
ax[l,m].subplot2grid((6, 4), (2, 0), rowspan=2, colspan=3, sharex=scatter_axes_1) ##2
ax[l,m].subplot2grid((6, 4), (4, 0), colspan=3, sharex=scatter_axes_2) ##3
ax[l,m].subplot2grid((6, 4), (0, 3), rowspan=2, sharey=scatter_axes_1) ##4
ax[l,m].subplot2grid((6, 4), (2, 3), rowspan=2, sharey=scatter_axes_2, sharex= y_hist_axes_1) ##5
But I can't use subplot2grid like this, I get the error
'AxesSubplot' object has no attribute 'subplot2grid'
Is there another function I can use with AxesSubplot to do that?
I'm a little confused by what you are trying to do. However, a perhaps an alternate way to deal with different widths and heights is to use width ratios?
EDIT: use subfigure to keep logical groups of axes.
import matplotlib.pyplot as plt
fig = plt.figure(constrained_layout=True, figsize=(8, 12))
sfigs = fig.subfigures(3, 2)
for nn, sf in enumerate(sfigs.flat):
sf.suptitle(nn)
axs = sf.subplots(3, 2, gridspec_kw={'width_ratios': [2, 1],
'height_ratios': [2, 2, 1]})
sf.delaxes(axs[2, 1])
plt.show()
I think this is a job for matplotlib's sematic figure composition function, i.e., the subplot_mosaic function. This is available in matplotlib > 3.3. You will need to define a basic layout for your 5 panels, and then generate a full layout depending on how many rows/columns you want. As far as I can see, this will be quite convoluted and hard (although not impossible!) to create by subplot2grid or Gridspec or any of the other approaches.
import matplotlib.pyplot as plt
import numpy as np
def layout(panel, rows=3, cols=2, empty_sentinal=999):
"""Takes in a single layout and arranges it in multiple
rows and columns"""
npanels = rows * cols
panel[panel >= empty_sentinal] = empty_sentinal
minipanels = len(np.unique(panel))
panels = np.array([i * (minipanels) + panel for i in range(npanels)])
panel_rows = [np.hstack(panels[i : i + cols]) for i in range(0, npanels, cols)]
panel_cols = np.vstack(panel_rows)
panel_cols[panel_cols > empty_sentinal] = empty_sentinal
return panel_cols
A) Generating a single panel:
single_panel = np.array([
[1, 1, 1, 1, 1, 1, 2, 2, 999],
[1, 1, 1, 1, 1, 1, 2, 2, 999],
[1, 1, 1, 1, 1, 1, 2, 2, 999],
[1, 1, 1, 1, 1, 1, 2, 2, 999],
[3, 3, 3, 3, 3, 3, 4, 4, 999],
[3, 3, 3, 3, 3, 3, 4, 4, 999],
[3, 3, 3, 3, 3, 3, 4, 4, 999],
[3, 3, 3, 3, 3, 3, 4, 4, 999],
[5, 5, 5, 5, 5, 5, 999, 999, 999],
[5, 5, 5, 5, 5, 5, 999, 999, 999],
[5, 5, 5, 5, 5, 5, 999, 999, 999],
[999] * 9,
[999] * 9,
])
fig, ax = plt.subplot_mosaic(single_panel, figsize=(10, 10), empty_sentinel=999)
for k, v in ax.items():
v.set_xticklabels([])
v.set_yticklabels([])
v.text(0.5, 0.5, k, ha="center", va="center", fontsize=25)
plt.show()
(B) "Tiling" the above single panel
my_layout = layout(panel=single_panel, rows=3, cols=2)
fig, ax = plt.subplot_mosaic(my_layout, figsize=(10, 10), empty_sentinel=999)
for k, v in ax.items():
v.set_xticklabels([])
v.set_yticklabels([])
v.text(0.5, 0.5, k, ha="center", va="center", fontsize=25)
plt.show()
Some Notes:
The empty_sentinal is set to 999. If you have more than 999 subplots, increase that to a higher number.
Each "mini-panel" can be individually acessed. You might need to write other functions to access "panel-group"
I need to concatenate a uid from uids column to each of the uids in the list of the friends column, as shown in the following example:
Given a pandas.DataFrame object A:
uid friends
0 1 [10, 2, 1, 5]
1 2 [1, 2]
2 3 [5, 4]
3 4 [10, 5]
4 5 [1, 2, 5]
the desired output is:
uid friends in_edges
0 1 [10, 2, 1, 5] [(1, 10), (1, 2), (1, 1), (1, 5)]
1 2 [1, 2] [(2, 1), (2, 2)]
2 3 [5, 4] [(3, 5), (3, 4)]
3 4 [10, 5] [(4, 10), (4, 5)]
4 5 [1, 2, 5] [(5, 1), (5, 2), (5, 5)]
I use the following code to achieve this outcome:
import numpy as np
import pandas as pd
A = pd.DataFrame(dict(uid=[1, 2, 3, 4, 5], friends=[[10, 2, 1, 5], [1, 2], [5, 4], [10, 5], [1, 2, 5]]))
A.loc[:, 'in_edges'] = A.loc[:, 'uid'].apply(lambda uid: [(uid, f) for f in A.loc[A.loc[:, 'uid']==uid, 'friends'].values[0]])
but it the A.loc[A.loc[:, 'uid']==uid, 'friends'] part looks kind of cumbersome to me, so I wondered if there is an easier way to accomplish this task?
Thanks in advance.
You can use .apply() with axis=1 parameter:
df["in_edges"] = df[["uid", "friends"]].apply(
lambda x: [(x["uid"], f) for f in x["friends"]], axis=1
)
print(df)
Prints:
uid friends in_edges
0 1 [10, 2, 1, 5] [(1, 10), (1, 2), (1, 1), (1, 5)]
1 2 [1, 2] [(2, 1), (2, 2)]
2 3 [5, 4] [(3, 5), (3, 4)]
3 4 [10, 5] [(4, 10), (4, 5)]
4 5 [1, 2, 5] [(5, 1), (5, 2), (5, 5)]
Why not try product
import itertools
A['in_edges'] = A.apply(lambda x : [*itertools.product([x['uid']], x['friends'])],axis=1)
A
Out[50]:
uid friends in_edges
0 1 [10, 2, 1, 5] [(1, 10), (1, 2), (1, 1), (1, 5)]
1 2 [1, 2] [(2, 1), (2, 2)]
2 3 [5, 4] [(3, 5), (3, 4)]
3 4 [10, 5] [(4, 10), (4, 5)]
4 5 [1, 2, 5] [(5, 1), (5, 2), (5, 5)]
I've the following graph,
ed_ls = [(0, 1), (0, 63), (1, 2), (1, 3), (54, 0)]
ed_w = [1, 2, 3, 4, 5]
G = nx.Graph()
G.add_edges_from(ed_ls)
edge_w = OrderedDict(zip(G.edges, ed_w))
nx.set_edge_attributes(G, edge_w, 'weight')
print(G.edges)
print(nx.get_edge_attributes(G, 'weight'))
Output obtained:
{(0, 1): 1, (0, 63): 2, (0, 54): 3, (1, 2): 4, (1, 3): 5}
The edge weights in ed_w are in the same order of edges in ed_ls. Since the order of edges
is not preserved, wrong edge weights are assigned. I could use nx.DiGraph to avoid this problem. However, I want to use nx.k_core later on and this doesn't work on directed graphs. Suggestions on
how to go ahead will be highly appreciated.
You can simplify this by using Graph.add_weighted_edges_from:
ed_ls = [(0, 1), (0, 63), (1, 2), (1, 3), (54, 0)]
ed_w = [1, 2, 3, 4, 5]
G = nx.Graph()
G.add_weighted_edges_from(((*edge, w) for edge, w in zip(ed_ls, ed_w)))
G.edges(data=True)
EdgeDataView([(0, 1, {'weight': 1}), (0, 63, {'weight': 2}),
(0, 54, {'weight': 5}), (1, 2, {'weight': 3}),
(1, 3, {'weight': 4})])
If you're using a python version above 3.7, dictionaries maintain insertion order, but the order you seen when printing G.edges(data=True) is not necessarily the order in which edges where added, it rather agrees with node adding. As you can see in this example (54, 0) is shown before
(1, 2) since node 0 was added before.
Why dont you assign the weights to the edges at the time of adding them to the graph?
ed_ls = [(0, 1), (0, 63), (1, 2), (1, 3), (54, 0)]
ed_w = [1, 2, 3, 4, 5]
G = nx.Graph()
for i in range(len(ed_ls)):
src, dst = ed_ls[i]
G.add_edge(src, dst, weight=ed_w[i])
According to the more_itertools.windowed specification, you can do:
list(windowed(seq=[1, 2, 3, 4], n=2, step=1))
>>> [(1, 2), (2, 3), (3, 4)]
But what if I want to run it all to the end? Is it possible to get:
>>> [(1, 2), (2, 3), (3, 4), (4, None)]
A workaround but not the best solution is to append None with the sequence.
list(windowed(seq=[1, 2, 3, 4,None], n=2, step=1))
I believe you can do this programmatically based on the step= value which I refer to as win_step in the following code. I also removed hardcoding where possible to make it easier to test various sequence_list, win_width, and win_step data sets:
sequence_list = [1, 2, 3, 4]
win_width = 2
win_step = 1
none_list = []
for i in range(win_step):
none_list.append(None)
sequence_list.extend(none_list)
tuple_list = list(windowed(seq=sequence_list, n=win_width, step=win_step))
print('tuple_list:', tuple_list)
Here are my results based on your original question's data set, and on the current data set:
For original, where:
sequence_list = [1, 2, 3, 4, 5, 6]
win_width = 3
win_step = 2
The result is:
tuple_list: [(1, 2, 3), (3, 4, 5), (5, 6, None), (None, None, None)]
And for the present data set, where:
sequence_list = [1, 2, 3, 4]
win_width = 2
win_step = 1
The result is:
tuple_list: [(1, 2), (2, 3), (3, 4), (4, None)]
I am working on weighted graphs and I would like to assign a random weight for the edges of the graph, such that,
weight of edge(a, a) = 0
weight of (a, b) = weight of edge(b, a) = K
where K is some random number. This goes on for all the edges of the graphs.
For that, I am using random.randint() method. I am actually using the logic of sum. If sum of both the edges is same, then assign some random integer.
Here is my code,
nodelist = list(range(1, num_nodes + 1))
edgelist = []
for i in nodelist:
for j in nodelist:
if i == j:
edgelist.append((i, j, 0))
if (i != j and sum((i, j)) == sum((j, i))):
rand = random.randint(5, 25)
edgelist.append((i, j, rand))
print(edgelist)
Actual result,
[(1, 1, 0), (1, 2, 18), (1, 3, 6), (2, 1, 13), (2, 2, 0), (2, 3, 21), (3, 1, 20), (3, 2, 17), (3, 3, 0)]
Expected result,
[(1, 1, 0), (1, 2, K), (1, 3, H), (2, 1, K), (2, 2, 0), (2, 3, P), (3, 1, H), (3, 2, P), (3, 3, 0)]
where, K, H, P are some random integers.
If the ordering of the result is not important following code gives the desired output:
import random
num_nodes = 3
nodelist = list(range(1, num_nodes + 1))
edgelist = []
for i in nodelist:
for j in nodelist:
if j > i:
break
if i == j:
edgelist.append((i, j, 0))
else:
rand = random.randint(5, 25)
edgelist.append((i, j, rand))
edgelist.append((j, i, rand))
print(edgelist)
# [(1, 1, 0), (2, 1, 7), (1, 2, 7), (2, 2, 0), (3, 1, 18), (1, 3, 18), (3, 2, 13), (2, 3, 13), (3, 3, 0)]
In case you need the edges sorted, simply use:
print(sorted(edgelist))
# [(1, 1, 0), (1, 2, 20), (1, 3, 16), (2, 1, 20), (2, 2, 0), (2, 3, 23), (3, 1, 16), (3, 2, 23), (3, 3, 0)]
Just a little change in your code will do the trick.
Here is the solution I found to obtain your expected output
num_nodes = 3
nodelist = list(range(1, num_nodes + 1))
edgelist = []
for i in nodelist:
for j in nodelist:
if i == j:
edgelist.append((i, j, 0))
elif i < j:
rand = random.randint(5, 25)
edgelist.append((i, j, rand))
edgelist.append((j, i, rand))
print(sorted(edgelist))
This code outputs :
[(1, 1, 0), (1, 2, 15), (1, 3, 15), (2, 1, 15), (2, 2, 0), (2, 3, 21), (3, 1, 15), (3, 2, 21), (3, 3, 0)]
So I figured out something interesting. Say below matrix shows edges in a complete graph of 5 nodes,
[1, 1] [1, 2] [1, 3] [1, 4] [1, 5]
[2, 1] [2, 2] [2, 3] [2, 4] [2, 5]
[3, 1] [3, 2] [3, 3] [3, 4] [3, 5]
[4, 1] [4, 2] [4, 3] [4, 4] [4, 5]
[5, 1] [5, 2] [5, 3] [5, 4] [5, 5]
now, moving right side from principal diagonal, we have lists whose first element is less than second element. We just got to target them and append new random weight to it.
Here is my code,
nodelist = list(range(1, num_nodes + 1))
edgelist = []
for i in nodelist:
for j in nodelist:
edgelist.append([i, j])
p = 0
eff_edgelist = []
while p < len(edgelist):
if edgelist[p][0] <= edgelist[p][1]:
eff_edgelist.append(edgelist[p])
p += 1
for i in eff_edgelist:
if i[0] == i[1]:
i.append(0)
else:
i.append(random.randint(5, 50))
eff_edgelist = [tuple(i) for i in eff_edgelist]
for i in list(G.edges(data=True)):
print([i])
and the result,
[(1, 1, {'weight': 0})]
[(1, 2, {'weight': 12})]
[(1, 3, {'weight': 37})]
[(1, 4, {'weight': 38})]
[(1, 5, {'weight': 6})]
[(2, 2, {'weight': 0})]
[(2, 3, {'weight': 12})]
[(2, 4, {'weight': 40})]
[(2, 5, {'weight': 8})]
[(3, 3, {'weight': 0})]
[(3, 4, {'weight': 15})]
[(3, 5, {'weight': 38})]
[(4, 4, {'weight': 0})]
[(4, 5, {'weight': 41})]
[(5, 5, {'weight': 0})]
and if you check, print(G[2][1]), the output will be {'weight': 12},
which means weight of edge(a, b) = weight of edge(b, a).