I have the following 3x3 matrix which I would like to plot:
import matplotlib.cm
import matplotlib.pyplot as plt
import numpy as np
import copy
cmap = copy.copy(cm.get_cmap("Blues"))
cmap.set_bad('white')
fig = plt.figure(figsize=(15, 10))
img = np.array([[-0.9, -0.5599234, 0.21042876],[-0.42735877, 0.61514954, -0.74305015],[0.61958201, -0.04358633, 0.78672511]])
im = plt.imshow(img, origin='upper', cmap=cmap)
The result looks as follows:
As visible the top left entry is smallest and should be displayed as white. How can I change it in such a way so that the smallest entry is displayed in white?
Second, is there a way to adapt the colormap such that it starts with darker values?
One way to have a colormap start with white, is to create a ListedColormap, e.g. going from white to darkblue. To start with the darkest color, just reverse the list of colors for the ListedColormap.
A standard colormap can be reversed, just by appending _r at the end of its name.
One way to create a colormap going from a mid-range to a dark blue, is creating a ListedColormap where the rgb-values are given as hexadecimal.
Here are some examples:
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import numpy as np
img = np.array([[-0.9, -0.5599234, 0.21042876], [-0.42735877, 0.61514954, -0.74305015], [0.61958201, -0.04358633, 0.78672511]])
fig, axs = plt.subplots(ncols=3, figsize=(12, 5))
cmap0 = LinearSegmentedColormap.from_list('', ['white', 'darkblue'])
cmap1 = 'Blues_r'
cmap2 = LinearSegmentedColormap.from_list('', ['#aaddee', '#000077'])
for ax, cmap in zip(axs, [cmap0, cmap1, cmap2]):
im = ax.imshow(img, origin='upper', cmap=cmap)
plt.colorbar(im, ax=ax, orientation='horizontal', pad=0.05)
ax.set_xticks([0, 1, 2])
ax.set_yticks([0, 1, 2])
ax.tick_params(labelbottom=False, labelleft=False, length=0) # hide ticks, but use position for a grid
ax.grid(True, color='white')
axs[0].set_title("Colormap from white to darkblue")
axs[1].set_title("Reversed blues colormap")
axs[2].set_title("Custom darker blues colormap")
plt.show()
Also of interest might be Seaborn's palette functions, which provide additional ways to create colormaps (the parameter as_cmap=True is needed for these functions to return a colormap).
Related
When creating a matplotlib colorbar, it is possible to set drawedges to True which separates the colors of the colorbar with black lines. However, when the colorbar is extended using extend='both', the black lines at the extremities do not show up. Is that a bug? Is there a possibility to draw those edges otherwise?
Here is the code:
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors
my_cmap = mpl.cm.viridis
bounds = np.arange(10)
nb_colors = len(bounds) + 1
colors = my_cmap(np.linspace(100, 255, nb_colors).astype(int))
my_cmap, my_norm = from_levels_and_colors(bounds, colors, extend='both')
plt.figure(figsize=(5, 1))
ax = plt.subplot(111)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=my_cmap, norm=my_norm, orientation='horizontal', drawedges=True)
plt.subplots_adjust(left=0.05, bottom=0.4, right=0.95, top=0.9)
plt.show()
and the figure it gives:
I looked into it from your question and found a way to change the color of the border and vertical lines of the color bar. I used that to change them to red. The result I got was that the extended outline was red, so my guess is that I just pulled the short sides of the normal color bar rectangle to the left and right.
I found this response helpful.
cbar.outline.set_edgecolor('red')
cbar.dividers.set_color('red')
So I think the only way to do this is to add vertical lines.
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors
my_cmap = mpl.cm.viridis
bounds = np.arange(10)
nb_colors = len(bounds) + 1
colors = my_cmap(np.linspace(100, 255, nb_colors).astype(int))
my_cmap, my_norm = from_levels_and_colors(bounds, colors, extend='both')
plt.figure(figsize=(6, 2))
ax = plt.subplot(111)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=my_cmap, norm=my_norm, orientation='horizontal', drawedges=True)
# update
cbar.outline.set_edgecolor('red')
cbar.dividers.set_color('red')
plt.axvline(max(bounds), color='red', alpha=0.3, linewidth=3.5)
plt.axvline(min(bounds), color='red', alpha=0.3, linewidth=3.5)
plt.subplots_adjust(left=0.05, bottom=0.4, right=0.95, top=0.9)
plt.show()
I want to make a 3x2 subplot image in python. With the images in third row I have added a colorbar. But it the image size gets small as compared to the top rows. Is there anyway to fix the image size the same as of top two rows while having a colorbar in the third row?
Here's my python code
#Imports
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib.image as image
import matplotlib.colors
from matplotlib.colors import ListedColormap
#data
bird = image.imread('Desktop/bird.jpeg')
fig, (ax1, ax2, ax3) = plt.subplots(3,2,figsize=(5,5))
ax1[0].imshow(bird)
ax1[0].set_ylabel('Row 1', size=8)
ax1[0].set_yticks([]) #display no ticks
ax1[0].set_xticks([])
ax1[1].imshow(bird)
ax1[1].set_yticks([])
ax1[1].set_xticks([])
ax2[0].imshow(bird)
ax2[0].set_yticks([])
ax2[0].set_xticks([])
ax2[0].set_ylabel('Row 2', size=8)
ax2[1].imshow(bird)
ax2[1].set_yticks([])
ax2[1].set_xticks([])
#Generating Color Map
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ["Red","Green","Blue"])
# Right Image
bird_3 = ax3[1].imshow(bird, cmap = cmap)
ax3[1].set_yticks([])
ax3[1].set_xticks([])
cbar_int = fig.colorbar(bird_3,orientation='horizontal', ax=ax3[1])
cbar_int.set_label('CBar', size=8, rotation=0)
cbar_int.ax.tick_params(labelsize=8)
bird_3.set_clim(vmin=-1, vmax=1)
# Left Image
bird_4 = ax3[0].imshow(bird, cmap = cmap)
ax3[0].set_yticks([])
ax3[0].set_xticks([])
ax3[0].set_ylabel('Row 3', size=8)
cbar_int = fig.colorbar(bird_4,orientation='horizontal', ax=ax3[0])
cbar_int.set_label('CBar', size=8, rotation=0)
cbar_int.ax.tick_params(labelsize=8)
bird_3.set_clim(vmin=-1, vmax=1)
plt.show()
The following results I get with it. You see row 3 images are small compared to row 1 and 2.
Matplotlib steals space from the host axes. However, you can specify more than one axes to steal space from. So above you can easily do:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
fig, axs = plt.subplots(3, 2)
for ax in axs.flat:
pc = ax.imshow(np.random.randn(20,40))
fig.colorbar(pc, ax=axs[:, 1], orientation='horizontal')
fig.colorbar(pc, ax=axs[:, 0], orientation='horizontal')
plt.show()
and space is stolen from all three axes in each column.
You can also specify constrained_layout=True for slightly better layout.
Note that with imshow the axes have a fixed aspect ratio, so there is always going to be issues with white space.
I am trying to explore a subplot 2 plots with square in shape rotated by 45 degree.
import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np
data = np.random.rand(10, 10) * 20
# create discrete colormap
cmap = colors.ListedColormap(['red', 'blue','green'])
bounds = [0,5,10,15]
norm = colors.BoundaryNorm(bounds, cmap.N)
fig, ax= plt.subplots(1,2)
ax[0].imshow(data, cmap=cmap, norm=norm)
# draw gridlines
ax[0].grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
ax[0].set_xticks(np.arange(-.5, 10, 1));
ax[0].set_yticks(np.arange(-.5, 10, 1));
ax[1].imshow(data, cmap=cmap, norm=norm)
# draw gridlines
ax[1].grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
ax[1].set_xticks(np.arange(-.5, 10, 1));
ax[1].set_yticks(np.arange(-.5, 10, 1));
plt.show()
Actual Result is :-
I want to rotate individual plot by 45 degree. Something like:-
I am trying to find in Matplotlib Documentation. Still not getting. Any help?
Please note this is NOT DUPLICATE OF
Is there a way to rotate a matplotlib plot by 45 degrees?
The mentioned URL is for a plot. and the solution is to rotate IMAGE. However this is pertaining to Subplot. I want to rotate PLOT not image as whole.
Based on this link and documentation about floating_axes, you can try something like this:
from mpl_toolkits.axisartist.grid_finder import DictFormatter
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib import colors
import numpy as np
def setup_axes1(fig, rect, angle):
tr = Affine2D().scale(2, 2).rotate_deg(angle)
#We create dictionarys to keep the xticks and yticks after the rotation
dictio={i:str(val) for i,val in enumerate(np.arange(-.5, 10, 1).tolist())}
reversedictio={i:dictio[val] for i,val in enumerate(list(reversed(sorted(dictio.keys()))))}
grid_helper = floating_axes.GridHelperCurveLinear(
tr, extremes=(-0.5, 9.5,-0.5, 9.5), tick_formatter1= DictFormatter(dictio),
tick_formatter2=DictFormatter(reversedictio))
ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)
fig.add_subplot(ax1)
aux_ax = ax1.get_aux_axes(tr)
grid_helper.grid_finder.grid_locator1._nbins = 10 #Number of rows
grid_helper.grid_finder.grid_locator2._nbins = 10 #Number of columns
return aux_ax
fig1, axes=plt.subplots(2,figsize=(20,20))
plt.rcParams.update({'font.size': 27})
#We erase the first previous axes
fig1.delaxes(axes[0])
fig1.delaxes(axes[1])
data = np.random.rand(10, 10) * 20
#We create the floating_axes
ax0 = setup_axes1(fig1, 121,-45)
ax1 = setup_axes1(fig1, 122,-45)
# create discrete colormap
cmap = colors.ListedColormap(['red', 'blue','green'])
bounds = [0,5,10,15]
norm = colors.BoundaryNorm(bounds, cmap.N)
ax0.imshow(data, cmap=cmap, norm=norm,interpolation="nearest")
# draw gridlines
ax0.grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
ax1.imshow(data, cmap=cmap, norm=norm,interpolation="nearest")
# draw gridlines
ax1.grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
plt.show()
Output:
Or, as an other alternative, I found a "tricky" way to do it, and it's about catching the figures in the buffer, rotate them -45 degrees, and then merge them into a single image, and since you have the same two images, you can try something like this:
import matplotlib
import io
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np
##PLOTING THE FIGURE##
data = np.random.rand(10, 10) * 20
# create discrete colormap
cmap = colors.ListedColormap(['red', 'blue','green'])
bounds = [0,5,10,15]
norm = colors.BoundaryNorm(bounds, cmap.N)
#We change style values to get the image with better quality
plt.rcParams.update({'font.size': 46})
plt.figure(figsize=(20,20))
plt.imshow(data, cmap=cmap, norm=norm)
# draw gridlines
plt.grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
plt.gca().set_xticks(np.arange(-.5, 10, 1));
plt.gca().set_yticks(np.arange(-.5, 10, 1));
##SAVING THE FIGURE INTO AN IMAGE##
#We save the current figure as a Image
buf = io.BytesIO()
plt.savefig(buf, format='png',bbox_inches='tight')
buf.seek(0)
im = Image.open(buf) #We open the current image saved in the buffer
#We rotate the image and fill the background with white
img_01=im.rotate(-45, Image.NEAREST, expand = 1, fillcolor = (255,255,255))
buf.close()
##MERGING THE TWO FIGURES##
new_im = Image.new('RGB', (2*img_01.size[0]+20,img_01.size[1]), 'white')
mouse_mask = img_01.convert('RGBA')
new_im.paste(img_01, (0,0))
new_im.paste(img_01, (img_01.size[0]+8,0))
new_im.save("merged_images.png", 'PNG') #Important(just to clarify): save the image, since the buffer is renewed every time you run the script
new_im.show()
Output:
I helped myself with these links:
How-to-merge-images-with-same-size-using-the-python-3-module-pillow
how-to-save-a-pylab-figure-into-in-memory-file-which-can-be-read-into-pil-image
python-pillow-rotate-image-90-180-270-degrees
specify-image-filling-color-when-rotating-in-python-with-pil-and-setting-expand
Say I have the following plot:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
data = np.sort(np.random.rand(8,12))
plt.figure()
c = plt.pcolor(data, edgecolors='k', linewidths=4, cmap='Blues', vmin=0.0, vmax=1.0)
plt.colorbar(c)
plt.show()
The colorbar has the (almost) white color assigned to the lowest values. How do I make it slightly darker? I want that instead of the colorbar ranging from white to blue, it should range from light blue to dark blue. Like, the color for the value 0 should be something like what it is for the value 0.4 in the plot above.
I found this when searching about it, but the question (and the solutions) is about making all the colors darker, which is not what I am looking for.
Although the suggestion of #user3483203 is very good, you do re-interpolate the colormap. You could avoid this by first getting the colormap as a matrix of colors (based on the original interpolation) and then select a part of this matrix as your new colormap:
import matplotlib as mpl
cmap = mpl.cm.Blues(np.linspace(0,1,20))
cmap = mpl.colors.ListedColormap(cmap[10:,:-1])
Your example then becomes
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
cmap = mpl.cm.Blues(np.linspace(0,1,20))
cmap = mpl.colors.ListedColormap(cmap[10:,:-1])
np.random.seed(1)
data = np.sort(np.random.rand(8,12))
plt.figure()
c = plt.pcolor(data, edgecolors='k', linewidths=4, cmap=cmap, vmin=0.0, vmax=1.0)
plt.colorbar(c)
plt.show()
which gives
which is in this case probably equivalent to re-interpolated colormap, as Blues itself comes from some interpolation.
For other colormaps the results may be quite different. For example, for jet:
No new interpolation, but just a subset of the original colormap (i.e. current solution):
Using re-interpolation (i.e. #user3483203's solution):
Simply define your own custom colormap:
from matplotlib.colors import LinearSegmentedColormap
colors = [(0.6, 0.76, 0.98), (0, 0.21, 0.46)] # Experiment with this
cm = LinearSegmentedColormap.from_list('test', colors, N=10)
Then just plug it in for the cmap parameter:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
data = np.sort(np.random.rand(8,12))
plt.figure()
c = plt.pcolor(data, edgecolors='k', linewidths=4, cmap=cm, vmin=0.0, vmax=1.0)
plt.colorbar(c)
plt.show()
And the result:
Using set_clim is a simple way to get your colors adjusted the way you probably want:
c.set_clim(-0.5, 1.0)
This sets the color limit (first value is vmin and second is vmax).
↳ https://matplotlib.org/api/_as_gen/matplotlib.pyplot.clim.html
I really like the "RdBu_r" colormap, but I want to cutout the white part between the blues and reds. Is there an easy way to do this?
Yes, but in your case, it's probably easier to make a colormap that interpolates between blue and red instead.
For example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list('name', ['red', 'blue'])
fig, ax = plt.subplots()
im = ax.imshow(np.random.random((10, 10)), cmap=cmap)
fig.colorbar(im)
plt.show()
Note that you could substitute the exact RGB values if you wanted a shade of red that isn't an HTML color name.
However, if you did want to "cut out the middle" of another colormap, you'd evaluate it on a range that didn't include the middle and create a new colormap:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
# Remove the middle 40% of the RdBu_r colormap
interval = np.hstack([np.linspace(0, 0.3), np.linspace(0.7, 1)])
colors = plt.cm.RdBu_r(interval)
cmap = LinearSegmentedColormap.from_list('name', colors)
# Plot a comparison of the two colormaps
fig, axes = plt.subplots(ncols=2)
data = np.random.random((10, 10))
im = axes[0].imshow(data, cmap=plt.cm.RdBu_r, vmin=0, vmax=1)
fig.colorbar(im, ax=axes[0], orientation='horizontal', ticks=[0, 0.5, 1])
axes[0].set(title='Original Colormap')
im = axes[1].imshow(data, cmap=cmap, vmin=0, vmax=1)
fig.colorbar(im, ax=axes[1], orientation='horizontal', ticks=[0, 0.5, 1])
axes[1].set(title='New Colormap')
plt.show()