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"
Related
I have 2 tensors of shape [2, 1, 9] and [2, 1, 3]. I'd like to concatenate across the 3rd dimension alternatively (once every 4).
For example:
a = [[[1,2,3,4,5,6,7,8,9]],[[11,12,13,14,15,16,17,18,19]]]
b = [[[10, 20, 30]], [[1, 2, 3]]]
result = [[[1,2,3,10,4,5,6,20,7,8,9,30]],[[11,12,13,1,14,15,16,2,17,18,19,3]]]
How can I do this in pytorch?
This would do the trick:
torch.concat([a.reshape((2, 1, 3, 3)), b.reshape(2, 1, 3, 1)], axis=-1).reshape((2, 1, -1))
There's probably a smarter way to do this, but hey, it works.
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]
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])
I want to reshape array of shape (2, *(x, y)) to (1, *(x,y), 2) while preserving the values of (x, y)?
(2, *(x,y)) where 2 represents the frames of game screen with (x, y) being an array with pixel values. I wish to convert it into an array of shape of (1, *(x, y), 2), such that the number 2 still represents the frame index, while (x,y) array value is preserved. 1 will be used to index the batch for training the neural network.
numpy.reshape(1, *(x,y), 2) doesn't preserve the (x,y) array.
Use numpy.transpose(), e.g.:
import numpy as np
arr = np.arange(2 * 3 * 4).reshape((2, 3, 4))
arr.shape
# (2, 3, 4)
arr.transpose(1, 2, 0).shape
# (3, 4, 2)
new_arr = arr.transpose(1, 2, 0)[None, ...]
new_arr.shape
# (1, 3, 4, 2)
# the `(3, 4)` array is preserved:
arr.transpose(1, 2, 0)[:, :, 0]
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])
arr[0, :, :]
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])
I am trying to create a series of graphs that share x and y labels. I can get the graphs to each have a label (explained well here!), but this is not what I am looking for.
I want one label that covers the y axis of both graphs, and same for the x axis.
I've been looking at the matplotlib and pandas documentation and I was unable to find anything that addresses this issues when the using by argument.
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame({'A': [1, 2, 1, 2, 3, 4, 3, 4],
'B': [1, 7, 2, 4, 1, 4, 8, 3],
'C': [1, 4, 8, 3, 1, 7, 3, 4],
'D': [1, 2, 6, 5, 8, 3, 1, 7]},
index=[0, 1, 2, 3, 5, 6, 7, 8])
histo = df.hist(by=df['A'], sharey=True, sharex=True)
plt.ylabel('ylabel') # I assume the label is created on the 4th graph and then deleted?
plt.xlabel('xlabel') # Creates a label on the 4th graph.
plt.tight_layout()
plt.show()
The ouput looks like this.
Is there any way that I can create a Y Label that goes across the entire left side of the image (not each graph individually) and the same for the X Label.
As you can see, the x label only appears on the last graph created, and there is no y label.
Help?
This is one way to do it indirectly using the x- and y-labels as texts. I am not aware of a direct way using plt.xlabel or plt.ylabel. When passing an axis object to df.hist, the sharex and sharey arguments have to be passed in plt.subplots(). Here you can manually control/specify the position where you want to put the labels. For example, if you think the x-label is too close to the ticks, you can use 0.5, -0.02, 'X-label' to shift it slightly below.
import matplotlib.pyplot as plt
import pandas as pd
f, ax = plt.subplots(2, 2, figsize=(8, 6), sharex=True, sharey=True)
df = pd.DataFrame({'A': [1, 2, 1, 2, 3, 4, 3, 4],
'B': [1, 7, 2, 4, 1, 4, 8, 3],
'C': [1, 4, 8, 3, 1, 7, 3, 4],
'D': [1, 2, 6, 5, 8, 3, 1, 7]},
index=[0, 1, 2, 3, 5, 6, 7, 8])
histo = df.hist(by=df['A'], ax=ax)
f.text(0, 0.5, 'Y-label', ha='center', va='center', fontsize=20, rotation='vertical')
f.text(0.5, 0, 'X-label', ha='center', va='center', fontsize=20)
plt.tight_layout()
I fixed the issue with the variable number of sub-plots using something like this:
cols = 3
n = len(set(df['A']))
rows = int(n / cols) + (0 if n % cols == 0 else 1)
fig, axes = plt.subplots(rows, cols)
extra = rows * cols - n
if extra:
newaxes = []
count = 0
for row in range(rows):
for col in range(cols):
if count < n:
newaxes.append(axes[row][col])
else:
axes[row][col].axis('off')
count += 1
else:
newaxes = axes
hist = df.hist(by=df['A'], ax=newaxes)