I have data I want to plot with extreme edge values, given below is a generic example:
import matplotlib.pyplot as plt
plt.style.use('seaborn-white')
import numpy as np
Z = np.random.rand(100,100)
plt.contourf(Z, 100, cmap='RdGy', vmin=0, vmax=1)
plt.colorbar()
plt.show()
Using the above code I get this plot:
But if I change only one row of Z to contain extreme values it "dominates" the whole plot:
import matplotlib.pyplot as plt
plt.style.use('seaborn-white')
import numpy as np
Z = np.random.rand(100,100)
Z[:1] *= 100
plt.contourf(Z, 100, cmap='RdGy', vmin=0, vmax=1)
plt.colorbar()
plt.show()
My question is so: In the second example, although I have extreme values, all the interesting things obviously happen in the range of 0 and 1, which is totally dominated by the multiplication I entered in the 1st row, even though I set vmin and vmax accordingly. How do I keep the data as is, while "focusing" on the 0-1 range? I don't really care what goes on in the first row, for all I care there can be a single color for the values 1-100.
Many Thanks.
This seems to be a known behavior, as reported in this GH issue.
A workaround (given in the issue comments) is to use an iterable for the levels arguments instead of relying on vmin and vmax.
Here is a code snippet to exhibit how vmin and vmax can be used with pcolormesh (as you said in the comment) but how to achieve a similar result with contourf.
import matplotlib.pyplot as plt
plt.style.use('seaborn-white')
import numpy as np
def main():
fig, axs = plt.subplots(2)
Z = np.random.rand(100,100)
Z[:1] *= 100
cmap = plt.get_cmap("viridis")
p1 = axs[0].pcolormesh(Z, vmin=0., vmax=1, cmap=cmap)
fig.colorbar(p1, ax=axs[0])
p2 = axs[1].contourf(Z, levels=np.linspace(0, 1, 100), cmap=cmap)
fig.colorbar(p2, ax=axs[1], ticks=np.linspace(0, 1, 5))
plt.show()
if __name__ == '__main__':
main()
Related
I am reading the following discussion:
setting axis scale in matplotlib contour plot
From the discussion above, to get arbitrary ratio, we could use
plt.figure(figsize=(8,2))
# ...
plt.tight_layout()
However, this setting is for figure not for contourf.
I used the above codes in my codes
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import pandas as pd
import math
rm = pd.read_excel("test_3d.xlsx", header = None)
# find min values of noise
rec = np.shape(rm)
# grid
X = np.arange(1,rec[1]+1,1)
Y = np.arange(1,rec[0]+1,1)
x , y = np.meshgrid(X,Y)
# plots
plt.clf()
con = plt.contourf(x,y,rm, cmap=cm.jet)
plt.figure(figsize=(8,2))
plt.tight_layout()
plt.title('2457MHz')
plt.show()
The result I got is
The ratio of bottom plot is what I want; however, I use plt.figure(figsize=(8,2)), which is not for contourf. Therefore, I did not get the correct result.
Is there any way that I can plot arbitrary ratio for contourf?
Instead of setting the figsize, use Axes.set_aspect to change the aspect ratio of the contour plot's Axes:
fig, ax = plt.subplots()
ax.contourf(x, y, rm, cmap='viridis')
ax.set_aspect(0.25)
If you prefer to stick with the plt syntax, access the Axes using plt.gca:
plt.contourf(x, y, rm, cmap='viridis')
plt.gca().set_aspect(0.25)
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
Is there an easy way to modify this code so that the plots are bigger without changing the scale on the axes?
import numpy as np
import matplotlib.pyplot as plt
import math
%matplotlib inline
a, c = -10, 10
x = np.linspace(a,c,100)
x = np.array(x)
def y(x): return np.arctan(x)
h = 0.0000001
def grad(x,h): return (y(x+h)-y(x))/h
m = grad(x,h)
plt.figure(1)
plt.subplot(121)
plt.plot(x, y(x), 'b')
plt.xlim([a,c])
plt.ylim([min(y(x)),max(y(x))])
plt.gca().set_aspect('equal', adjustable='box')
plt.subplot(122)
plt.plot(x,m,'b')
plt.xlim([a,c])
plt.ylim([min(m),max(m)])
plt.gca().set_aspect('equal', adjustable='box')
plt.subplots_adjust(wspace = 0.5)
plt.show()
If I get rid of plt.gca().set_aspect('equal', adjustable='box') the plots come out a decent size but they are not to scale.
The subplots are shrunk such that their aspect is equal. This seems to be desired; and thus it is not really clear what "bigger" refers to.
You can still make the figure larger, e.g.
plt.figure(1, figsize=(12,2))
and then adjust the margins and spacings using plt.subplots_adjust.
You can also let the axes scale and only set the equal aspect to the data,
plt.gca().set_aspect('equal', adjustable='datalim')
Finally plotting the subplots beneath each other makes them bigger as well. So you might use plt.subplot(211) and plt.subplot(212).
I try to do a 2D histogram plot and to obtain a "smooth" picture by a sort of interpolation. Thus I do the following combining plt.hist2d and plt.imshow
import matplotlib.pyplot as plt
import numpy as np
data = np.loadtxt("parametre_optMC.dat", skiprows=50, usecols=(1,2))
h, x, y, p = plt.hist2d(data[:,0], data[:,1], bins = 20)
plt.imshow(h, origin = "lower", interpolation = "gaussian")
plt.savefig("test.pdf")
As you can see on the picture below, the two plots are superimposed and that is the problem for which I need some help
Adding clf works but I lose axes dimenions :
import matplotlib.pyplot as plt
import numpy as np
data = np.loadtxt("parametre_optMC.dat", skiprows=50, usecols=(1,2))
h, x, y, p = plt.hist2d(data[:,0], data[:,1], bins = 20)
plt.clf()
plt.imshow(h, origin = "lower", interpolation = "gaussian")
plt.savefig("test.pdf")
Perhaps it would be better to plot a kernel density estimate?
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
data = np.random.multivariate_normal([0, 0], [(1, .6), (.6, 1)], 100)
f, ax = plt.subplots(figsize=(7, 7))
sns.kdeplot(data, shade=True, ax=ax)
To your first question:
You need to clear data from a previous plot, putting the following before you plot should do this:
plt.clf()
plt.close()
To your second question:
To change the axis values I'd suggest the extent parameter (see this answer).
e.g. something like:
plt.imshow(h, origin = "lower", interpolation = "gaussian",extent=[-100,100,-75,75])
You need to add the 'extent' parameter to you imshow command. imshow accepts a grid of arbitrary values but does not know the dimensions.
I want to make 4 imshow subplots but all of them share the same colormap. Matplotlib automatically adjusts the scale on the colormap depending on the entries of the matrices. For example, if one of my matrices has all entires as 10 and the other one has all entries equal to 5 and I use the Greys colormap then one of my subplots should be completely black and the other one should be completely grey. But both of them end up becoming completely black. How to make all the subplots share the same scale on the colormap?
To get this right you need to have all the images with the same intensity scale, otherwise the colorbar() colours are meaningless. To do that, use the vmin and vmax arguments of imshow(), and make sure they are the same for all your images.
E.g., if the range of values you want to show goes from 0 to 10, you can use the following:
import pylab as plt
import numpy as np
my_image1 = np.linspace(0, 10, 10000).reshape(100,100)
my_image2 = np.sqrt(my_image1.T) + 3
plt.subplot(1, 2, 1)
plt.imshow(my_image1, vmin=0, vmax=10, cmap='jet', aspect='auto')
plt.subplot(1, 2, 2)
plt.imshow(my_image2, vmin=0, vmax=10, cmap='jet', aspect='auto')
plt.colorbar()
When the ranges of data (data1 and data2) sets are unknown and you want to use the same colour bar for both/all plots, find the overall minimum and maximum to use as vmin and vmax in the call to imshow:
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=1, ncols=2)
# generate randomly populated arrays
data1 = np.random.rand(10,10)*10
data2 = np.random.rand(10,10)*10 -7.5
# find minimum of minima & maximum of maxima
minmin = np.min([np.min(data1), np.min(data2)])
maxmax = np.max([np.max(data1), np.max(data2)])
im1 = axes[0].imshow(data1, vmin=minmin, vmax=maxmax,
extent=(-5,5,-5,5), aspect='auto', cmap='viridis')
im2 = axes[1].imshow(data2, vmin=minmin, vmax=maxmax,
extent=(-5,5,-5,5), aspect='auto', cmap='viridis')
# add space for colour bar
fig.subplots_adjust(right=0.85)
cbar_ax = fig.add_axes([0.88, 0.15, 0.04, 0.7])
fig.colorbar(im2, cax=cbar_ax)
It may be that you don't know beforehand the ranges of your data, but you may know that somehow they are compatible. In that case, you may prefer to let matplotlib choose those ranges for the first plot and use the same range for the remaining plots. Here is how you can do it. The key is to get the limits with properties()['clim']
import numpy as np
import matplotlib.pyplot as plt
my_image1 = np.linspace(0, 10, 10000).reshape(100,100)
my_image2 = np.sqrt(my_image1.T) + 3
fig, axes = plt.subplots(nrows=1, ncols=2)
im = axes[0].imshow(my_image1)
clim=im.properties()['clim']
axes[1].imshow(my_image2, clim=clim)
fig.colorbar(im, ax=axes.ravel().tolist(), shrink=0.5)
plt.show()