Matplotlib - two different colormaps with different ranges - python

I'm trying to combine two figures that were separate before.
One is a 3 panel figure (ax1,ax2,ax3) (all generated with imshow), where I was using one single colormap on the side. Now, I want to add another figure (ax0) that I load from a png file, using imread and get_sample_data (from matplotlib.cbook).
The problem is that this new figure takes the same colormap as the one from the 3 panels, thus yielding one simple uniform color in the newly added panel. The 3 other panels still have the right color.
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
g1 = gridspec.GridSpec(4, 1, height_ratios=[4,1,1,1])
g1.update(wspace=0.05, hspace=0.2) # set the spacing between axes.
f, ((ax0), (ax1), (ax2), (ax3)) = plt.subplots(4, 1, sharex='col', sharey='row')
ax0 = subplot(g1[0])
ax1 = subplot(g1[1])
ax2 = subplot(g1[2])
ax3 = subplot(g1[3])
from matplotlib.cbook import get_sample_data
im2 = plt.imread(get_sample_data('PathToFig/Figure.png'))
ax0.imshow(im2, vmax=1)
ziAA = np.random.rand(4, 4, 4)
ziAB = np.random.rand(4, 4, 4)
ziBA = np.random.rand(4, 4, 4)
ax1.imshow(ziAA,origin="lower",vmin=0,vmax=0.03,aspect="auto",extent=[-0.15,0.15,0,4])
ax2.imshow(ziAB,origin="lower",vmin=0,vmax=0.03,aspect="auto",extent=[-0.15,0.15,0,4])
im = ax3.imshow(ziBA,origin="lower",vmin=0,vmax=0.03,aspect="auto",extent=[-0.15,0.15,0,4])
from matplotlib import ticker
tick_locator = ticker.MaxNLocator(nbins=5)
f.subplots_adjust(right=0.85)
cbar_ax = f.add_axes([1.0, 0.15, 0.01, 0.7])
cbar = f.colorbar(im, cax=cbar_ax)
cbar.locator = tick_locator
cbar.update_ticks()
cbar.solids.set_rasterized(True)
cbar.solids.set_edgecolor("face")
ziAB, ziBA and ziAA were generated in a previous griddata interpolation call.
I tried specifying two different colormaps inside each imshow call, I tried changing the values of vmax. But to no avail...
If I put ax0 after ax1-3, then, it's ax0 who gets the right colors, and not ax1-3.
I've looked at the other Similar Questions (Two different color colormaps in the same imshow matplotlib) that talk about creating masked arrays or my own colormap, but because of the png file origin for ax0, I don't really see how I should proceed.
EDIT :
Toy data:
ziAA = np.random.rand(4, 4, 4)
ziAB = np.random.rand(4, 4, 4)
ziBA = np.random.rand(4, 4, 4)
Toy png figure:
With this figure, the uniform color turns out to be white. My actual figure gives uniform red. This suggests that by properly tuning vmin, vmax and maybe other control parameters, one might get the png figure to display properly. However, I'd be interested in something that would work for any png figure...

With:
im0 = ax0.imshow(im2, aspect='auto',extent=[-0.15,0.15,0,4])
which produces the following result:

Related

matplotlib combining subplots into a single plot with no axis and no gaps

I have an image which looks like so:
It was generated using matplotlib using:
for slice_idx in range(mandrill_t.highpasses[1].shape[2]):
print(slice_idx)
subplot(2, 3, slice_idx+1)
imshow(np.abs(mandrill_t.highpasses[1][:,:,slice_idx]), cmap='Spectral', clim=(0, 1))
However, for my use case, I would like all these 6 images in a single image with no gaps or axis - I do not have an example output image to show, but essentially, I would like them stacked horizontally (3 of them) and vertically (2 of them) so that the 6 images are a single image.
I tried looking around for similar problems to draw inspiration from, but no luck so far :(
Any pointers would be great.
You have to specify the grid parameters:
2 rows
3 columns
0 width space
0 height space
with matplotlib.pyplot.subplots:
fig, axes = plt.subplots(nrows = 2, ncols = 3, gridspec_kw = {'wspace': 0, 'hspace': 0})
Then you can loop over created axes and, for each one of them, you have to show the image and set axis to 'tight' firtsly and 'off' secondly:
for ax in axes.flatten():
ax.imshow(img)
ax.axis('tight')
ax.axis('off')
Your code would be slighlty different, since you are plotting different images for each ax.
Complete Code
import matplotlib.pyplot as plt
img = plt.imread('img.jpeg')
fig, axes = plt.subplots(nrows = 2, ncols = 3, gridspec_kw = {'wspace': 0, 'hspace': 0})
for ax in axes.flatten():
ax.imshow(img)
ax.axis('tight')
ax.axis('off')
plt.show()
That's what GridSpec is for (see plt.subplots docs):
Just add the following line at the start:
subplots(2, 3, gridspec_kw={"wspace": 0, "hspace": 0})
You might also have to set some plot elements to invisible but it's hard to figure out exactly which without an MCVE.

Python scatterplot: how to use a colormap that has the same colors as the colorcycle

I am trying to color clusters in a scatter plot and I managed with two different methods.
In the first I plot iteratively each cluster, in the second I plot all the data at once and colour the clusters according to their labels [0, 1, 2, 3 ,4].
I am happy with the result I get in example1 and example3 but I don't understand why the coloring changes so dramatically when coloring the clusters according to the labels instead of iteratively plotting each cluster.
Additionally, why the second cluster (despite having always label "1") has a different color in example1 and example3?
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight') #irrelevant here, but coherent with the examples=)
fig, ax = plt.subplots(figsize=(6,4))
for clust in range(kmeans.n_clusters):
ax.scatter(X[kmeans.labels_==clust],Y[kmeans.labels_==clust])
ax.set_title("example1")`
and
plt.figure(figsize = (6, 4))
plt.scatter(X,Y,c=kmeans.labels_.astype(float))
plt.title("example2")
(I know I can explicitly define a colormap for the second method but I couldn't find any that reproduces the results in example 1)
Here is a minimal working example
import matplotlib.pyplot as plt
import pandas as pd
plt.style.use('fivethirtyeight') #irrelevant here, but coherent with the examples=)
X=pd.Series([1, 2, 3, 4, 5, 11, 12, 13, 14, 15])
Y=pd.Series([1,1,1,1,1,2,2,2,2,2])
clusters=pd.Series([0,0,0,0,0,1,1,1,1,1])
fig, ax = plt.subplots(figsize=(6,4))
for clust in range(2):
ax.scatter(X[clusters==clust],Y[clusters==clust])
ax.set_title("example3")
plt.figure(figsize = (6, 4))
plt.scatter(X,Y, c=clusters)
plt.title("example4")
When you loop over the clusters and plot a scatter without specifying any color, the default colors of the active property cycler (color cycle) will be used. The active property cycler is defined in the rcParams. It is set via the style in use; in your case, using 'fivethirtyeight'
print(plt.rcParams["axes.prop_cycle"])
> cycler('color', ['#008fd5', '#fc4f30', '#e5ae38', '#6d904f', '#8b8b8b', '#810f7c'])
The first two colors of this ('#008fd5', '#fc4f30') are the one you see in the plot.
When you use a scatter with the clusters as color argument, those values will be mapped to a color via a colormap. If no colormap is specified it will take the default colormap defined in the rcParam.
print(plt.rcParams["image.cmap"])
> "viridis"
The 'fivethirtyeight' style does not define any special colormap, so the default would be unchanged. (The fact that you observe a different colormap than viridis in your picture is due to the fact that there was some other code still active which is not shown in the question.)
At this point I need to start interpreting; I would think that your question really is how to get the single scatter use a colormap that has the same colors as the colorcycle in it. None of the predefined colormaps has the fivethirtyeight cycler colors in it. Hence you would define that colormap manually, by taking the colors from the cycle,
import matplotlib.colors as mcolors
cmap = mcolors.ListedColormap(plt.rcParams['axes.prop_cycle'].by_key()['color'])
Now you need a way to index the colormap, because you have discrete clusters.
n = len(clusters.unique())
norm = mcolors.BoundaryNorm(np.arange(n+1)-0.5, n)
Of course this requires that the number of colors in the colormap is greater or equal the number of classes - which is the case here.
Putting it all together, (I added another category, to make it more illustrative)
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import matplotlib.colors as mcolors
plt.style.use('fivethirtyeight') #relevant here!!
X=pd.Series([1, 2, 3, 4, 5, 11, 12, 13, 14, 15])
Y=pd.Series([1,1,1,1,1,2,2,2,2,2])
clusters=pd.Series([0,0,0,0,0,1,1,1,1,2])
cmap = mcolors.ListedColormap(plt.rcParams['axes.prop_cycle'].by_key()['color'])
n = len(clusters.unique())
norm = mcolors.BoundaryNorm(np.arange(n+1)-0.5, n)
plt.figure(figsize = (6, 4))
sc = plt.scatter(X,Y, c=clusters, cmap=cmap, norm=norm)
plt.colorbar(sc, ticks=clusters.unique())
plt.title("example4")
plt.show()

Two kinds of subplots: two colorbars needed [duplicate]

I would like to have the following layout in matplotlib:
Image 1a Image 1b Image 2a Image 2b Colorbar a Colorbar b
The Colorbar a is for Image set a, and the Colobar b is for Image set b.
I have tried to use ImageGrid to create the axes for images, but no luck in making the colorbars right. For example:
fig = plt.figure()
grid = ImageGrid(fig, 111, (1,6), aspect=False, share_all=False)
# Get data1a, data1b, ...
im1a = grid[0].pcolormesh(data1a)
im1b = grid[1].pcolormesh(data1b)
im2a = grid[2].pcolormesh(data2a)
im2b = grid[3].pcolormesh(data2b)
plt.colorbar(im1a, cax=grid[4])
plt.colorbar(im1b, cax=grid[5])
The problem with this is that the calls to colorbar() messed up with the axis limits of the images even though I specified share_all=False in ImageGrid().
Is there any tip on this? Very much appreciated.
For future reference, it helps to have a full working example, so that someone could copy and paste your code and reproduce your issue directly. For example, I can see you've imported ImageGrid, but a full import statement would help with this, as would creating fake data sets for data1a, data1b, etc.
Also, it looks like you have a (1,6) where you should have (1,4) in your statement above: grid = ImageGrid(fig, 111, (1,4), aspect=False, share_all=False), though this is not the solution to your problem.
When I want two or more color bars, my approach is typically to use get_position() on an axis, which returns the coordinates for the axis corners as attributes x0,y0,x1,y1. From here, I define each colorbar's axis separately and place each precisely where I want it to go. To get this to suit your needs, you'll have to tinker with the details of fig.add_axes([1.01, bbox_ax.y0, 0.02, bbox_ax.y1-bbox_ax.y0]) in the code below. For example, the first two entries 1.01, bbox_ax.y0 mean "place the bottom corner at x=1.01 and y=bbox_ax.y0". The second two entries, 0.02, bbox_ax.y1-bbox_ax.y0 define the horizontal and vertical width of the colorbar axis, respectively. I like the colorbar axes to be flush with the plot axes, so I use bbox_ax.y1-bbox_ax.y0 for the vertical width.
Note that I'm using mp.subplots() instead of ImageGrid(), since I'm not as familiar with the latter, and I don't think it's necessary.
import matplotlib.pyplot as mp
import numpy
import mpl_toolkits.axes_grid1
data1a = numpy.random.rand(100,100)
data1b = numpy.random.rand(100,100)
data2a = numpy.random.rand(100,100)
data2b = numpy.random.rand(100,100)
fig, axes = mp.subplots(1, 4, figsize=(8,2))
im1a = axes[0].pcolormesh(data1a, cmap='magma')
im1b = axes[1].pcolormesh(data1b, cmap='magma')
im2a = axes[2].pcolormesh(data2a, cmap='viridis')
im2b = axes[3].pcolormesh(data2b, cmap='viridis')
fig.tight_layout()
# get bounding box information for the axes (since they're in a line, you only care about the top and bottom)
bbox_ax = axes[0].get_position()
# fig.add_axes() adds the colorbar axes
# they're bounded by [x0, y0, x_width, y_width]
cbar_im1a_ax = fig.add_axes([1.01, bbox_ax.y0, 0.02, bbox_ax.y1-bbox_ax.y0])
cbar_im1a = mp.colorbar(im1a, cax=cbar_im1a_ax)
cbar_im2a_ax = fig.add_axes([1.09, bbox_ax.y0, 0.02, bbox_ax.y1-bbox_ax.y0])
cbar_im1a = mp.colorbar(im2a, cax=cbar_im2a_ax)
This produces the figure below:
You can also do this as a 2x2 grid with slightly different syntax:
fig, axes = mp.subplots(2, 2, figsize=(4,4))
im1a = axes[0,0].pcolormesh(data1a, cmap='magma')
im1b = axes[0,1].pcolormesh(data1b, cmap='magma')
im2a = axes[1,0].pcolormesh(data2a, cmap='viridis')
im2b = axes[1,1].pcolormesh(data2b, cmap='viridis')
fig.tight_layout()
bbox_ax_top = axes[0,1].get_position()
bbox_ax_bottom = axes[1,1].get_position()
cbar_im1a_ax = fig.add_axes([1.01, bbox_ax_top.y0, 0.02, bbox_ax_top.y1-bbox_ax_top.y0])
cbar_im1a = mp.colorbar(im1a, cax=cbar_im1a_ax)
cbar_im2a_ax = fig.add_axes([1.01, bbox_ax_bottom.y0, 0.02, bbox_ax_bottom.y1-bbox_ax_bottom.y0])
cbar_im1a = mp.colorbar(im2a, cax=cbar_im2a_ax)
Which produces this figure:
Using pcolormesh, which by default is plotted to axes with automatic aspect, does not require any special treatment to create colorbars.
The easiest way of doing so is to use a grid with unequal column width. The rest comes automatically.
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(ncols=6,figsize=(7,2.2),
gridspec_kw={"width_ratios":[1,1,1,1, 0.08,0.08]})
fig.subplots_adjust(wspace=0.6)
im0 = axes[0].pcolormesh(np.random.rand(11,11), vmin=0, vmax=1, cmap="RdBu")
im1 = axes[1].pcolormesh(np.random.rand(11,11), vmin=0, vmax=1, cmap="RdBu")
im2 = axes[2].pcolormesh(np.random.rand(11,11), vmin=0, vmax=1)
im3 = axes[3].pcolormesh(np.random.rand(11,11), vmin=0, vmax=1)
axes[0].set_ylabel("y label")
fig.colorbar(im0, cax=axes[4])
fig.colorbar(im2, cax=axes[5])
plt.show()

How to remove the space between subplots in matplotlib.pyplot?

I am working on a project in which I need to put together a plot grid of 10 rows and 3 columns. Although I have been able to make the plots and arrange the subplots, I was not able to produce a nice plot without white space such as this one below from gridspec documentatation..
I tried the following posts, but still not able to completely remove the white space as in the example image. Can someone please give me some guidance? Thanks!
Matplotlib different size subplots
how to remove “empty” space
between subplots?
Here's my image:
Below is my code. The full script is here on GitHub.
Note: images_2 and images_fool are both numpy arrays of flattened images with shape (1032, 10), while delta is an image array of shape (28, 28).
def plot_im(array=None, ind=0):
"""A function to plot the image given a images matrix, type of the matrix: \
either original or fool, and the order of images in the matrix"""
img_reshaped = array[ind, :].reshape((28, 28))
imgplot = plt.imshow(img_reshaped)
# Output as a grid of 10 rows and 3 cols with first column being original, second being
# delta and third column being adversaril
nrow = 10
ncol = 3
n = 0
from matplotlib import gridspec
fig = plt.figure(figsize=(30, 30))
gs = gridspec.GridSpec(nrow, ncol, width_ratios=[1, 1, 1])
for row in range(nrow):
for col in range(ncol):
plt.subplot(gs[n])
if col == 0:
#plt.subplot(nrow, ncol, n)
plot_im(array=images_2, ind=row)
elif col == 1:
#plt.subplot(nrow, ncol, n)
plt.imshow(w_delta)
else:
#plt.subplot(nrow, ncol, n)
plot_im(array=images_fool, ind=row)
n += 1
plt.tight_layout()
#plt.show()
plt.savefig('grid_figure.pdf')
A note at the beginning: If you want to have full control over spacing, avoid using plt.tight_layout() as it will try to arange the plots in your figure to be equally and nicely distributed. This is mostly fine and produces pleasant results, but adjusts the spacing at its will.
The reason the GridSpec example you're quoting from the Matplotlib example gallery works so well is because the subplots' aspect is not predefined. That is, the subplots will simply expand on the grid and leave the set spacing (in this case wspace=0.0, hspace=0.0) independent of the figure size.
In contrast to that you are plotting images with imshow and the image's aspect is set equal by default (equivalent to ax.set_aspect("equal")). That said, you could of course put set_aspect("auto") to every plot (and additionally add wspace=0.0, hspace=0.0 as arguments to GridSpec as in the gallery example), which would produce a plot without spacings.
However when using images it makes a lot of sense to keep an equal aspect ratio such that every pixel is as wide as high and a square array is shown as a square image.
What you will need to do then is to play with the image size and the figure margins to obtain the expected result. The figsize argument to figure is the figure (width, height) in inch and here the ratio of the two numbers can be played with. And the subplot parameters wspace, hspace, top, bottom, left can be manually adjusted to give the desired result.
Below is an example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
nrow = 10
ncol = 3
fig = plt.figure(figsize=(4, 10))
gs = gridspec.GridSpec(nrow, ncol, width_ratios=[1, 1, 1],
wspace=0.0, hspace=0.0, top=0.95, bottom=0.05, left=0.17, right=0.845)
for i in range(10):
for j in range(3):
im = np.random.rand(28,28)
ax= plt.subplot(gs[i,j])
ax.imshow(im)
ax.set_xticklabels([])
ax.set_yticklabels([])
#plt.tight_layout() # do not use this!!
plt.show()
Edit:
It is of course desireable not having to tweak the parameters manually. So one could calculate some optimal ones according to the number of rows and columns.
nrow = 7
ncol = 7
fig = plt.figure(figsize=(ncol+1, nrow+1))
gs = gridspec.GridSpec(nrow, ncol,
wspace=0.0, hspace=0.0,
top=1.-0.5/(nrow+1), bottom=0.5/(nrow+1),
left=0.5/(ncol+1), right=1-0.5/(ncol+1))
for i in range(nrow):
for j in range(ncol):
im = np.random.rand(28,28)
ax= plt.subplot(gs[i,j])
ax.imshow(im)
ax.set_xticklabels([])
ax.set_yticklabels([])
plt.show()
Try to add to your code this line:
fig.subplots_adjust(wspace=0, hspace=0)
And for every an axis object set:
ax.set_xticklabels([])
ax.set_yticklabels([])
Following the answer by ImportanceOfBeingErnest, but if you want to use plt.subplots and its features:
fig, axes = plt.subplots(
nrow, ncol,
gridspec_kw=dict(wspace=0.0, hspace=0.0,
top=1. - 0.5 / (nrow + 1), bottom=0.5 / (nrow + 1),
left=0.5 / (ncol + 1), right=1 - 0.5 / (ncol + 1)),
figsize=(ncol + 1, nrow + 1),
sharey='row', sharex='col', # optionally
)
If you are using matplotlib.pyplot.subplots you can display as many images as you want using Axes arrays. You can remove the spaces between images by making some adjustments to the matplotlib.pyplot.subplots configuration.
import matplotlib.pyplot as plt
def show_dataset_overview(self, img_list):
"""show each image in img_list without space"""
img_number = len(img_list)
img_number_at_a_row = 3
row_number = int(img_number /img_number_at_a_row)
fig_size = (15*(img_number_at_a_row/row_number), 15)
_, axs = plt.subplots(row_number,
img_number_at_a_row,
figsize=fig_size ,
gridspec_kw=dict(
top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0
)
)
axs = axs.flatten()
for i in range(img_number):
axs[i].imshow(img_list[i])
axs[i].set_xticks([])
axs[i].set_yticks([])
Since we create subplots here first, we can give some parameters for grid_spec using the gridspec_kw parameter(source).
Among these parameters are the "top = 1, bottom = 0, right = 1, left = 0, hspace = 0, wspace = 0" parameters that will prevent inter-image spacing. To see other parameters, please visit here.
I usually use a figure size like (30,15) when setting the figure_size above. I generalized this a bit and added it to the code. If you wish, you can enter a manual size here.
Here's another simple approach using the ImageGrid class (adapted from this answer).
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
nrow = 5
ncol = 3
fig = plt.figure(figsize=(4, 10))
grid = ImageGrid(fig,
111, # as in plt.subplot(111)
nrows_ncols=(nrow,ncol),
axes_pad=0,
share_all=True,)
for row in grid.axes_column:
for ax in row:
im = np.random.rand(28,28)
ax.imshow(im)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)

plotting coordinate as a matrix matplotlib python

I have a set of coordinates, say [(2,3),(45,4),(3,65)]
I need to plot them as a matrix is there anyway I can do this in matplotlib so I want it to have this sort of look http://imgur.com/Q6LLhmk
Edit: My original answer used ax.scatter. There is a problem with this: If two points are side-by-side, ax.scatter may draw them with a bit of space in between, depending on the scale:
For example, with
data = np.array([(2,3),(3,3)])
Here is a zoomed-in detail:
So here is a alternative solution that fixes this problem:
import matplotlib.pyplot as plt
import numpy as np
data = np.array([(2,3),(3,3),(45,4),(3,65)])
N = data.max() + 5
# color the background white (1 is white)
arr = np.ones((N,N), dtype = 'bool')
# color the dots black (0)
arr[data[:,1], data[:,0]] = 0
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.imshow(arr, interpolation='nearest', cmap = 'gray')
ax.invert_yaxis()
# ax.axis('off')
plt.show()
No matter how much you zoom in, the adjacent squares at (2,3) and (3,3) will remain side-by-side.
Unfortunately, unlike ax.scatter, using ax.imshow requires building an N x N array, so it could be more memory-intensive than using ax.scatter. That should not be a problem unless data contains very large numbers, however.

Categories