Grid of images in matplotlib with no padding - python

I'm trying to make a grid of images in matplotlib using gridspec. The problem is, I can't seem to get it to get rid of the padding between the rows.
Here's my attempt at the solution.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
import numpy as np
from os import listdir
from os import chdir
from PIL import Image
import matplotlib.gridspec as gridspec
chdir('/home/matthew/Dropbox/Work/writing/'+
'paper_preperation/jump_figs')
files = listdir('/home/matthew/Dropbox/Work/writing/'+
'paper_preperation/jump_figs')
images = [Image.open(f) for f in files]
"""
fig = plt.figure()
grid = ImageGrid(fig, 111, # similar to subplot(111)
nrows_ncols = (2, 5), # creates 2x2 grid of axes
axes_pad=0.1, # pad between axes in inch.
)
"""
num_rows = 2
num_cols = 5
fig = plt.figure()
gs = gridspec.GridSpec(num_rows, num_cols, wspace=0.0)
ax = [plt.subplot(gs[i]) for i in range(num_rows*num_cols)]
gs.update(hspace=0)
#gs.tight_layout(fig, h_pad=0,w_pad=0)
for i,im in enumerate(images):
ax[i].imshow(im)
ax[i].axis('off')
#ax_grid[i/num_cols,i-(i/num_cols)*num_cols].imshow(im) # The AxesGrid object work as a list of axes.
#ax_grid[i/num_cols,i-(i/num_cols)*num_cols].axis('off')
"""
all_axes = fig.get_axes()
for ax in all_axes:
for sp in ax.spines.values():
sp.set_visible(False)
if ax.is_first_row():
ax.spines['top'].set_visible(True)
if ax.is_last_row():
ax.spines['bottom'].set_visible(True)
if ax.is_first_col():
ax.spines['left'].set_visible(True)
if ax.is_last_col():
ax.spines['right'].set_visible(True)
"""
plt.show()
Also does anyone know how to make each subplot bigger?

For me a combination of aspect="auto" and subplots_adjust worked. Also I always try to make the subplots quadratic. For the individual subplot size figsize can be adjusted.
fig, axes = plt.subplots(nrows=max_rows, ncols=max_cols, figsize=(20,20))
for idx, image in enumerate(images):
row = idx // max_cols
col = idx % max_cols
axes[row, col].axis("off")
axes[row, col].imshow(image, cmap="gray", aspect="auto")
plt.subplots_adjust(wspace=.05, hspace=.05)
plt.show()

Related

plotting a grid of png with matplotlib

I have found multiple similar questions with this subject but so far I couldn't adapt any solution to my needs, so I'm sorry for reposting.
I'm trying to plot a grid of png images using matplotlib, the closest I've got to what I want is using the code below, which can be found here https://matplotlib.org/stable/gallery/axes_grid1/simple_axesgrid.html .
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
import numpy as np
im1 = np.arange(100).reshape((10, 10))
im2 = im1.T
im3 = np.flipud(im1)
im4 = np.fliplr(im2)
fig = plt.figure(figsize=(4., 4.))
grid = ImageGrid(fig, 111, # similar to subplot(111)
nrows_ncols=(2, 2), # creates 2x2 grid of axes
axes_pad=0.1, # pad between axes in inch.
)
for ax, im in zip(grid, [im1, im2, im3, im4]):
# Iterating over the grid returns the Axes.
ax.imshow(im)
plt.show()
My question is, how do I get rid of the x and y ticks/labels and also give each image a title?
Again, I'm sorry for repeating the question.
This code
import matplotlib.pyplot as plt
image = plt.imread("sample.png")
fig, axes = plt.subplots(2, 3)
for row in [0, 1]:
for column in [0, 1, 2]:
ax = axes[row, column]
ax.set_title(f"Image ({row}, {column})")
ax.axis('off')
ax.imshow(image)
plt.show()
is going to produce

Ticks not aligned with the cell in matplotlib table

I am using matplotlib table to create a heatmap that contains different values. While creating the table, ticks are not aligned at the center of each cell in both x-axis and y-axis.
I want to create something like this.
But rather I am getting ticks that are not uniform in both axes. Ticks starts at the middle of the cell from bottom-left column and gets distorted while moving to top and right cell.
I am using this code to generate this matplotlip table.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
import os
import sys
from matplotlib.table import Table
fig,ax=plt.subplots(figsize=(15,15))
tb = Table(ax,bbox=[0,0,1,1])
nrows, ncols = 20,20
width, height = 1.0 / ncols, 1.0 / nrows
headers = 20
data = np.zeros((nrows, ncols))
ax.set_xticks(np.arange(0.5,headers+0.5))
ax.set_yticks(np.arange(0.5,headers+0.5))
ax.xaxis.set_tick_params(labelsize=12)
ax.yaxis.set_tick_params(labelsize=12)
for (i,j),val in np.ndenumerate(data):
tb.add_cell(i, j, width, height)
tb.add_cell(i, j, width, height, loc='center')
tb.auto_set_font_size(False)
tb.set_fontsize(15)
ax.add_table(tb)
ax.set_aspect('equal')
plt.show()
Is there anything I am missing?
this will do the work
plt.ylim(0,headers)
plt.xlim(0,headers)
also adding this line plt.xticks(rotation=90) will help you rotate values in the xaxis and avoid overlapping
full program will be like
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
import os
import sys
from matplotlib.table import Table
fig, ax = plt.subplots(figsize=(15, 15))#
tb = Table(ax, bbox=[0, 0, 1, 1])
nrows, ncols = 20,20
width, height = 1.0 / ncols, 1.0 / nrows
headers = 20
data = np.zeros((nrows, ncols))
ax.set_xticks(np.arange(0.5,headers+0.5))
ax.set_yticks(np.arange(0.5,headers+0.5))
plt.ylim(0,headers)
plt.xlim(0,headers)
plt.xticks(rotation=90)
ax.xaxis.set_tick_params(labelsize=12)
ax.yaxis.set_tick_params(labelsize=12)
for (i, j), val in np.ndenumerate(data):
tb.add_cell(i, j, width, height)
tb.add_cell(i, j, width, height, loc='center')
tb.auto_set_font_size(False)
tb.set_fontsize(15)
ax.add_table(tb)
ax.set_aspect('equal')
plt.show()
output
Have you heard of seaborn? This is a plotting library built on top of matplotlib that takes care of those things for you. They have a function heatmap which does exactly this, and handles the placing of the ticks automatically: https://seaborn.pydata.org/generated/seaborn.heatmap.html#seaborn.heatmap
Example:
import seaborn as sns
ax = sns.heatmap(np.zeros(20,20))
plt.show()
gives this:
You can then play with the ticklabels and modify them at will like you would do on any matplotlib plot. For example, ax.set_xticklabels(np.arange(0.5, 20, 0.5)) will rename your x-ticks like on your picture.
The answers have been accepted through a lot of trial and error, but we're changing the idea and color mapping. How about customizing it and using a heat map to solve the problem? It would be convenient to process various things. I will share it with you for your reference.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
mat = np.random.rand(10,10)
# RGB -> Hex
# ['rgb(0,0,205)', 'rgb(50,205,50)', 'rgb(255,69,0)'] -> ['#0000cd', '#32cd32', '#ff4500']
# ['mediumblue', 'limegreen', 'orangered'])
rgb_c = [(0,0,205), (50,205,50), (255,69,0)]
# RGB convert to Hex
cols = ['#%02x%02x%02x' % (c[0],c[1],c[2]) for c in rgb_c]
cm = matplotlib.colors.ListedColormap(cols)
fig, ax = plt.subplots()
heatmap = ax.pcolor(mat, cmap=cm)
ax.set_aspect('equal')
plt.show()

How to force aspect ratio of subplot while keeping the width identical?

I'm trying to create a plot with two subplots. 1 column, 2 rows.
Similar to the image but without the subplot to the right
How can I enforce one subplot to be square and the other one to have the same width without being square?
I tried gridspec without success:
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig = plt.figure(constrained_layout=True)
gs = GridSpec(nrows=4, ncols=3, height_ratios=[1, 1, 1, 1], figure=fig)
ax1 = fig.add_subplot(gs[:-1,:])
ax2 = fig.add_subplot(gs[-1,:])
I also tried setting the aspect ratio for both subplots resulting in different widths the subplots:
fig, axs = plt.subplots(2)
axs[0].set_aspect(1)
axs[1].set_aspect(2)
I also tried... but this fixes the x range of the subplots to the same value.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
F = plt.figure()
grid = ImageGrid(F, 111,
nrows_ncols=(2, 1),
axes_pad=0.1,
)
grid[1].set_aspect(.4)
Thanks for any suggestions...
One working solution I could come up with following the suggestions of ImportanceOfBeingErnest:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
fig = plt.figure(figsize=(10,12))
axScatter = plt.subplot(111)
axScatter.set_aspect(1.)
divider = make_axes_locatable(axScatter)
axHistx = divider.append_axes("bottom", size=.8, pad=0.2)
Thank you!

How to make the size of subplot equally?

I am using matplotlib and GridSpec to plot 9 images in 3x3 subplots.
fig = plt.figure(figsize=(30,40))
fig.patch.set_facecolor('white')
gs1 = gridspec.GridSpec(3,3)
gs1.update(wspace=0.05, hspace=0.05)
ax1 = plt.subplot(gs1[0])
ax2 = plt.subplot(gs1[1])
ax3 = plt.subplot(gs1[2])
ax4 = plt.subplot(gs1[3])
ax5 = plt.subplot(gs1[4])
ax6 = plt.subplot(gs1[5])
ax7 = plt.subplot(gs1[6])
ax8 = plt.subplot(gs1[7])
ax9 = plt.subplot(gs1[8])
ax1.imshow(img1,cmap='gray')
ax2.imshow(img2,cmap='gray')
...
ax9.imshow(img9,cmap='gray')
However, the images have a different size from each row. For example, the first-row images size is 256x256, the images in the second row have a size of 200x200 and the third row has a size of 128x128
I want to plot the images in the subplot with same size. How should I use it in python? Thanks
This is an example of 4x3 subplot
Don't use matplotlib.gridspec, but use figure.add_subplot as demonstrated with the runnable code below. However, when doing some plotting, you need to set_autoscale_on(False) to suppress its behavior of size adjusting.
import numpy as np
import matplotlib.pyplot as plt
# a function that creates image array for `imshow()`
def make_img(h):
return np.random.randint(16, size=(h,h))
fig = plt.figure(figsize=(8, 12))
columns = 3
rows = 4
axs = []
for i in range(columns*rows):
axs.append( fig.add_subplot(rows, columns, i+1) )
# axs[-1] is the new axes, write its title as `axs[number]`
axs[-1].set_title("axs[%d]" % (i))
# plot raster image on this axes
plt.imshow(make_img(i+1), cmap='viridis', alpha=(i+1.)/(rows*columns))
# maniputate axs[-1] here, plot something on it
axs[-1].set_autoscale_on(False) # suppress auto sizing
axs[-1].plot(np.random.randint(2*(i+1), size=(i+1)), color="red", linewidth=2.5)
fig.subplots_adjust(wspace=0.3, hspace=0.4)
plt.show()
The resulting plot:
I suppose you want to show the images in different sizes, such that all pixels of the different images are equally sized.
This is in general hard, but for the case where all images in a row (or column) of the subplot grid are of the same size, it becomes easy. The idea can be to use the gridspec's height_ratios (or width_ratios in case of columns) argument and set it to the image's pixel height (width).
import matplotlib.pyplot as plt
import numpy as np
images = [np.random.rand(r,r) for r in [25,20,12] for _ in range(3)]
r = [im.shape[0] for im in images[::3]]
fig, axes = plt.subplots(3,3, gridspec_kw=dict(height_ratios=r, hspace=0.3))
for ax, im in zip(axes.flat, images):
ax.imshow(im)
plt.show()

Scaling images generated by imshow

The following code snippet
import matplotlib.pyplot as plt
import numpy as np
arr1 = np.arange(100).reshape((10,10))
arr2 = np.arange(25).reshape((5,5))
fig, (ax1, ax2, ) = plt.subplots(nrows=2, figsize=(3,5))
ax1.imshow(arr1, interpolation="none")
ax2.imshow(arr2, interpolation="none")
plt.tight_layout()
plt.show()
produces two images with the same size, but a much lower "pixel density" in the second one.
I would like to have the second image plotted at the same scale (i.e. pixel density) of the first, without filling the subfigure, possibly correctly aligned (i.e. the origin of the image in the same subplot position as the first one.)
Edit
The shapes of arr1 and arr2 are only an example to show the problem. What I'm looking for is a way to ensure that two different images generated by imshow in different portions of the figure are drawn at exactly the same scale.
The simplest thing I could think of didn't work, but gridspec does. The origins here aren't aligned explicitly, it just takes advantage of how gridspec fills rows (and there's an unused subplot as a spacer).
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import gridspec
sizes = (10, 5)
arr1 = np.arange(sizes[0]*sizes[0]).reshape((sizes[0],sizes[0]))
arr2 = np.arange(sizes[1]*sizes[1]).reshape((sizes[1],sizes[1]))
# Maybe sharex, sharey? No, we pad one and lose data in the other
#fig, (ax1, ax2, ) = plt.subplots(nrows=2, figsize=(3,5), sharex=True, sharey=True)
fig = plt.figure(figsize=(3,5))
# wspace so the unused lower-right subplot doesn't squeeze lower-left
gs = gridspec.GridSpec(2, 2, height_ratios = [sizes[0], sizes[1]], wspace = 0.0)
ax1 = plt.subplot(gs[0,:])
ax2 = plt.subplot(gs[1,0])
ax1.imshow(arr1, interpolation="none")
ax2.imshow(arr2, interpolation="none")
plt.tight_layout()
plt.show()

Categories