I have the following code:
import matplotlib.pyplot as plt
cdict = {
'red' : ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
'green': ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
'blue' : ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}
cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)
plt.clf()
plt.pcolor(X, Y, v, cmap=cm)
plt.loglog()
plt.xlabel('X Axis')
plt.ylabel('Y Axis')
plt.colorbar()
plt.show()
So this produces a graph of the values 'v' on the axes X vs Y, using the specified colormap. The X and Y axes are perfect, but the colormap spreads between the min and max of v. I would like to force the colormap to range between 0 and 1.
I thought of using:
plt.axis(...)
To set the ranges of the axes, but this only takes arguments for the min and max of X and Y, not the colormap.
Edit:
For clarity, let's say I have one graph whose values range (0 ... 0.3), and another graph whose values (0.2 ... 0.8).
In both graphs, I will want the range of the colorbar to be (0 ... 1). In both graphs, I want this range of colour to be identical using the full range of cdict above (so 0.25 in both graphs will be the same colour). In the first graph, all colours between 0.3 and 1.0 won't feature in the graph, but will in the colourbar key at the side. In the other, all colours between 0 and 0.2, and between 0.8 and 1 will not feature in the graph, but will in the colourbar at the side.
Using vmin and vmax forces the range for the colors. Here's an example:
import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np
cdict = {
'red' : ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
'green': ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
'blue' : ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}
cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)
x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)
data = 2*( np.sin(X) + np.sin(3*Y) )
def do_plot(n, f, title):
#plt.clf()
plt.subplot(1, 3, n)
plt.pcolor(X, Y, f(data), cmap=cm, vmin=-4, vmax=4)
plt.title(title)
plt.colorbar()
plt.figure()
do_plot(1, lambda x:x, "all")
do_plot(2, lambda x:np.clip(x, -4, 0), "<0")
do_plot(3, lambda x:np.clip(x, 0, 4), ">0")
plt.show()
Use the CLIM function (equivalent to CAXIS function in MATLAB):
plt.pcolor(X, Y, v, cmap=cm)
plt.clim(-4,4) # identical to caxis([-4,4]) in MATLAB
plt.show()
Not sure if this is the most elegant solution (this is what I used), but you could scale your data to the range between 0 to 1 and then modify the colorbar:
import matplotlib as mpl
...
ax, _ = mpl.colorbar.make_axes(plt.gca(), shrink=0.5)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=cm,
norm=mpl.colors.Normalize(vmin=-0.5, vmax=1.5))
cbar.set_clim(-2.0, 2.0)
With the two different limits you can control the range and legend of the colorbar. In this example only the range between -0.5 to 1.5 is show in the bar, while the colormap covers -2 to 2 (so this could be your data range, which you record before the scaling).
So instead of scaling the colormap you scale your data and fit the colorbar to that.
Using figure environment and .set_clim()
Could be easier and safer this alternative if you have multiple plots:
import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np
cdict = {
'red' : ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
'green': ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
'blue' : ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}
cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)
x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)
data = 2*( np.sin(X) + np.sin(3*Y) )
data1 = np.clip(data,0,6)
data2 = np.clip(data,-6,0)
vmin = np.min(np.array([data,data1,data2]))
vmax = np.max(np.array([data,data1,data2]))
fig = plt.figure()
ax = fig.add_subplot(131)
mesh = ax.pcolormesh(data, cmap = cm)
mesh.set_clim(vmin,vmax)
ax1 = fig.add_subplot(132)
mesh1 = ax1.pcolormesh(data1, cmap = cm)
mesh1.set_clim(vmin,vmax)
ax2 = fig.add_subplot(133)
mesh2 = ax2.pcolormesh(data2, cmap = cm)
mesh2.set_clim(vmin,vmax)
# Visualizing colorbar part -start
fig.colorbar(mesh,ax=ax)
fig.colorbar(mesh1,ax=ax1)
fig.colorbar(mesh2,ax=ax2)
fig.tight_layout()
# Visualizing colorbar part -end
plt.show()
A single colorbar
The best alternative is then to use a single color bar for the entire plot. There are different ways to do that, this tutorial is very useful for understanding the best option. I prefer this solution that you can simply copy and paste instead of the previous visualizing colorbar part of the code.
fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
wspace=0.4, hspace=0.1)
cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
cbar = fig.colorbar(mesh, cax=cb_ax)
P.S.
I would suggest using pcolormesh instead of pcolor because it is faster (more infos here ).
Related
I was trying to create custom color map with exaples from documentation but I have no idea how setting color range works.
https://matplotlib.org/2.0.2/examples/pylab_examples/custom_cmap.html
This is the closest to what I need: (Full green from 1.0 to 0.916, full yellow from 0.916 to 0.75 and full red below 0.75)
cdict1 = {'red': ((0.0, 1.0, 1.0),
(0.75, 1.0, 1.0),
(1.0, 0.0, 0.0)),
'green': ((0.0, 0.0, 0.0),
(0.75, 1.0, 1.0),
(0.91666666666, 1.0, 1.0),
(1.0, 1.0, 1.0)),
'blue': ((0.0, 0.0, 0.0),
(0.5, 0.0, 0.0),
(1.0, 0.0, 0.0))}
I don't undestand why this colormap is a smooth transition between colors.
To create a colormap with 3 fixed colors with unequal boundaries, the recommended approach uses a BoundaryNorm.
If you really only want to work with a colormap, you could create one from a list of colors.
A LinearSegmentedColormap makes smooth transitions with specific colors at given values. To make it work with fixed colors, these values can be set equal. The function either works the "old" way manipulating rgb values, or with a list of (value, color) pairs (LinearSegmentedColormap.from_list()).
The following example code shows how this can work:
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap, BoundaryNorm, LinearSegmentedColormap
import numpy as np
x, y = np.random.rand(2, 100)
fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(14, 4))
# working with a BoundaryNorm
cmap1 = ListedColormap(['red', 'yellow', 'green'])
norm1 = BoundaryNorm([0, 0.75, 0.916, 1], ncolors=3)
scat1 = ax1.scatter(x, y, c=y, cmap=cmap1, norm=norm1)
plt.colorbar(scat1, ax=ax1, spacing='proportional')
ax1.set_title('working with BoundaryNorm')
# creating a special colormap
colors = ['green' if c > 0.916 else 'red' if c < 0.75 else 'yellow' for c in np.linspace(0, 1, 256)]
cmap2 = ListedColormap(colors)
scat2 = ax2.scatter(x, y, c=y, cmap=cmap2, vmin=0, vmax=1)
plt.colorbar(scat2, ax=ax2)
ax2.set_title('special list of colors')
cmap3 = LinearSegmentedColormap.from_list('', [(0, 'red'), (0.75, 'red'), (0.75, 'yellow'), (0.916, 'yellow'),
(0.916, 'green'), (1, 'green')])
scat3 = ax3.scatter(x, y, c=y, cmap=cmap3, vmin=0, vmax=1)
plt.colorbar(scat3, ax=ax3)
ax3.set_title('LinearSegmentedColormap')
plt.tight_layout()
plt.show()
The spacing='proportional' option of plt.colorbar shows the boundaries at their proportional location. The default shows 3 equally-spaced boundaries together with the values.
I am creating a "heatmap" using hexbin, and want this heatmap to be placed on top of an image. However, I would like to have the coloring of the plot fade to transparent as the frequency does (i.e. as the color fades to white, it disappears). I have tried changing the alpha value but that does not produce the desired effect.
My code is:
n = 100000
x = np.random.standard_normal(n)
img = imread("soccer.jpg")
y = 2.0 + 3.0 * x + 4.0 * np.random.standard_normal(n)
plt.hexbin(x,y, bins='log', cmap=plt.cm.Reds, alpha = 0.3)
plt.imshow(img,zorder=0, extent=[-10, 10, -20, 20])
#plt.show()
plt.savefig('map.png')
I am open to using a 2d-histogram or any other plotting function. Even just being transparent when there are no values in that hexagon would be great, as many of my areas have zero data points.
The image produced by my current code is:
Rough example to be going on with:
n = 100000
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
x = np.random.standard_normal(n)
img = plt.imread("soccer.jpg")
y = 2.0 + 3.0 * x + 4.0 * np.random.standard_normal(n)
red_high = ((0., 0., 0.),
(.3, .5, 0.5),
(1., 1., 1.))
blue_middle = ((0., .2, .2),
(.3, .5, .5),
(.8, .2, .2),
(1., .1, .1))
green_none = ((0,0,0),(1,0,0))
cdict3 = {'red': red_high,
'green': green_none,
'blue': blue_middle,
'alpha': ((0.0, 0.0, 0.0),
(0.3, 0.5, 0.5),
(1.0, 1.0, 1.0))
}
dropout_high = LinearSegmentedColormap('Dropout', cdict3)
plt.register_cmap(cmap = dropout_high)
plt.hexbin(x,y, bins='log', cmap=dropout_high)
plt.imshow(img,zorder=0, extent=[-10, 10, -20, 20])
plt.show()
#plt.savefig('map.png')
(I'm afraid my soccer field is sideways. I usually play as though it is, so.)
How do I exactly specify the colorbar labels in matplotlib? Frequently, I need to create very specific color scales, but the colorbar labels display so poorly you can't tell what the scale is. I would like to manually define the text next to the colorbar tick marks, or at least have them display in scientific notation.
Here is an example plot where you can't tell what the bottom four color bins represent:
And here is a working example of how that plot was created:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
# mock up some data
x = np.random.random(50)
y = np.random.random(50)
c = np.arange(0, 1, 1.0/50.0) # color of points
c[0] = 0.00001
c[1] = 0.0001
c[2] = 0.001
c[3] = 0.01
s = 500 * np.random.random(50) + 25 # size of points
# set up some custom color scaling
lcmap = colors.ListedColormap(['#FFFFFF', '#FF99FF', '#8000FF',
'#0000FF', '#0080FF', '#58FAF4',
'#00FF00', '#FFFF00', '#FF8000',
'#FF0000'])
bounds = [0.0, 0.000001, 0.00001, 0.0001,
0.001, 0.01, 0.1, 0.25, 0.5, 0.75, 1.0]
norm = colors.BoundaryNorm(bounds, lcmap.N)
# create some plot
fig, ax = plt.subplots()
im = ax.scatter(x, y, c=c, s=s, cmap=lcmap, norm=norm)
# add the colorbar
fig.colorbar(im, ax=ax)
fig.savefig('temp.jpg')
cbar = fig.colorbar(cax, ticks=[-1, 0, 1])
cbar.ax.set_xticklabels(['Low', 'Medium', 'High'])
and use whatever iterable you want instead of ['Low', 'Medium', 'High']
see: http://matplotlib.org/examples/pylab_examples/colorbar_tick_labelling_demo.html
I searched for ages (hours which is like ages) to find the answer to a really annoying (seemingly basic) problem, and because I cant find a question that quite fits the answer I am posting a question and answering it in the hope that it will save someone else the huge amount of time I just spent on my noobie plotting skills.
If you want to label your plot points using python matplotlib
from matplotlib import pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
A = anyarray
B = anyotherarray
plt.plot(A,B)
for i,j in zip(A,B):
ax.annotate('%s)' %j, xy=(i,j), xytext=(30,0), textcoords='offset points')
ax.annotate('(%s,' %i, xy=(i,j))
plt.grid()
plt.show()
I know that xytext=(30,0) goes along with the textcoords, you use those 30,0 values to position the data label point, so its on the 0 y axis and 30 over on the x axis on its own little area.
You need both the lines plotting i and j otherwise you only plot x or y data label.
You get something like this out (note the labels only):
Its not ideal, there is still some overlap - but its better than nothing which is what I had..
How about print (x, y) at once.
from matplotlib import pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
A = -0.75, -0.25, 0, 0.25, 0.5, 0.75, 1.0
B = 0.73, 0.97, 1.0, 0.97, 0.88, 0.73, 0.54
ax.plot(A,B)
for xy in zip(A, B): # <--
ax.annotate('(%s, %s)' % xy, xy=xy, textcoords='data') # <--
ax.grid()
plt.show()
I had a similar issue and ended up with this:
For me this has the advantage that data and annotation are not overlapping.
from matplotlib import pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
A = -0.75, -0.25, 0, 0.25, 0.5, 0.75, 1.0
B = 0.73, 0.97, 1.0, 0.97, 0.88, 0.73, 0.54
plt.plot(A,B)
# annotations at the side (ordered by B values)
x0,x1=ax.get_xlim()
y0,y1=ax.get_ylim()
for ii, ind in enumerate(np.argsort(B)):
x = A[ind]
y = B[ind]
xPos = x1 + .02 * (x1 - x0)
yPos = y0 + ii * (y1 - y0)/(len(B) - 1)
ax.annotate('',#label,
xy=(x, y), xycoords='data',
xytext=(xPos, yPos), textcoords='data',
arrowprops=dict(
connectionstyle="arc3,rad=0.",
shrinkA=0, shrinkB=10,
arrowstyle= '-|>', ls= '-', linewidth=2
),
va='bottom', ha='left', zorder=19
)
ax.text(xPos + .01 * (x1 - x0), yPos,
'({:.2f}, {:.2f})'.format(x,y),
transform=ax.transData, va='center')
plt.grid()
plt.show()
Using the text argument in .annotate ended up with unfavorable text positions.
Drawing lines between a legend and the data points is a mess, as the location of the legend is hard to address.
I would like to know how to simply reverse the color order of a given colormap in order to use it with plot_surface.
The standard colormaps also all have reversed versions. They have the same names with _r tacked on to the end. (Documentation here.)
The solution is pretty straightforward. Suppose you want to use the "autumn" colormap scheme. The standard version:
cmap = matplotlib.cm.autumn
To reverse the colormap color spectrum, use get_cmap() function and append '_r' to the colormap title like this:
cmap_reversed = matplotlib.cm.get_cmap('autumn_r')
In matplotlib a color map isn't a list, but it contains the list of its colors as colormap.colors. And the module matplotlib.colors provides a function ListedColormap() to generate a color map from a list. So you can reverse any color map by doing
colormap_r = ListedColormap(colormap.colors[::-1])
As of Matplotlib 2.0, there is a reversed() method for ListedColormap and LinearSegmentedColorMap objects, so you can just do
cmap_reversed = cmap.reversed()
Here is the documentation.
As a LinearSegmentedColormaps is based on a dictionary of red, green and blue, it's necessary to reverse each item:
import matplotlib.pyplot as plt
import matplotlib as mpl
def reverse_colourmap(cmap, name = 'my_cmap_r'):
"""
In:
cmap, name
Out:
my_cmap_r
Explanation:
t[0] goes from 0 to 1
row i: x y0 y1 -> t[0] t[1] t[2]
/
/
row i+1: x y0 y1 -> t[n] t[1] t[2]
so the inverse should do the same:
row i+1: x y1 y0 -> 1-t[0] t[2] t[1]
/
/
row i: x y1 y0 -> 1-t[n] t[2] t[1]
"""
reverse = []
k = []
for key in cmap._segmentdata:
k.append(key)
channel = cmap._segmentdata[key]
data = []
for t in channel:
data.append((1-t[0],t[2],t[1]))
reverse.append(sorted(data))
LinearL = dict(zip(k,reverse))
my_cmap_r = mpl.colors.LinearSegmentedColormap(name, LinearL)
return my_cmap_r
See that it works:
my_cmap
<matplotlib.colors.LinearSegmentedColormap at 0xd5a0518>
my_cmap_r = reverse_colourmap(my_cmap)
fig = plt.figure(figsize=(8, 2))
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15])
norm = mpl.colors.Normalize(vmin=0, vmax=1)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap = my_cmap, norm=norm,orientation='horizontal')
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap = my_cmap_r, norm=norm, orientation='horizontal')
EDIT
I don't get the comment of user3445587. It works fine on the rainbow colormap:
cmap = mpl.cm.jet
cmap_r = reverse_colourmap(cmap)
fig = plt.figure(figsize=(8, 2))
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15])
norm = mpl.colors.Normalize(vmin=0, vmax=1)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap = cmap, norm=norm,orientation='horizontal')
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap = cmap_r, norm=norm, orientation='horizontal')
But it especially works nice for custom declared colormaps, as there is not a default _r for custom declared colormaps. Following example taken from http://matplotlib.org/examples/pylab_examples/custom_cmap.html:
cdict1 = {'red': ((0.0, 0.0, 0.0),
(0.5, 0.0, 0.1),
(1.0, 1.0, 1.0)),
'green': ((0.0, 0.0, 0.0),
(1.0, 0.0, 0.0)),
'blue': ((0.0, 0.0, 1.0),
(0.5, 0.1, 0.0),
(1.0, 0.0, 0.0))
}
blue_red1 = mpl.colors.LinearSegmentedColormap('BlueRed1', cdict1)
blue_red1_r = reverse_colourmap(blue_red1)
fig = plt.figure(figsize=(8, 2))
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15])
norm = mpl.colors.Normalize(vmin=0, vmax=1)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap = blue_red1, norm=norm,orientation='horizontal')
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap = blue_red1_r, norm=norm, orientation='horizontal')
There is no built-in way (yet) of reversing arbitrary colormaps, but one simple solution is to actually not modify the colorbar but to create an inverting Normalize object:
from matplotlib.colors import Normalize
class InvertedNormalize(Normalize):
def __call__(self, *args, **kwargs):
return 1 - super(InvertedNormalize, self).__call__(*args, **kwargs)
You can then use this with plot_surface and other Matplotlib plotting functions by doing e.g.
inverted_norm = InvertedNormalize(vmin=10, vmax=100)
ax.plot_surface(..., cmap=<your colormap>, norm=inverted_norm)
This will work with any Matplotlib colormap.
There are two types of LinearSegmentedColormaps. In some, the _segmentdata is given explicitly, e.g., for jet:
>>> cm.jet._segmentdata
{'blue': ((0.0, 0.5, 0.5), (0.11, 1, 1), (0.34, 1, 1), (0.65, 0, 0), (1, 0, 0)), 'red': ((0.0, 0, 0), (0.35, 0, 0), (0.66, 1, 1), (0.89, 1, 1), (1, 0.5, 0.5)), 'green': ((0.0, 0, 0), (0.125, 0, 0), (0.375, 1, 1), (0.64, 1, 1), (0.91, 0, 0), (1, 0, 0))}
For rainbow, _segmentdata is given as follows:
>>> cm.rainbow._segmentdata
{'blue': <function <lambda> at 0x7fac32ac2b70>, 'red': <function <lambda> at 0x7fac32ac7840>, 'green': <function <lambda> at 0x7fac32ac2d08>}
We can find the functions in the source of matplotlib, where they are given as
_rainbow_data = {
'red': gfunc[33], # 33: lambda x: np.abs(2 * x - 0.5),
'green': gfunc[13], # 13: lambda x: np.sin(x * np.pi),
'blue': gfunc[10], # 10: lambda x: np.cos(x * np.pi / 2)
}
Everything you want is already done in matplotlib, just call cm.revcmap, which reverses both types of segmentdata, so
cm.revcmap(cm.rainbow._segmentdata)
should do the job - you can simply create a new LinearSegmentData from that. In revcmap, the reversal of function based SegmentData is done with
def _reverser(f):
def freversed(x):
return f(1 - x)
return freversed
while the other lists are reversed as usual
valnew = [(1.0 - x, y1, y0) for x, y0, y1 in reversed(val)]
So actually the whole thing you want, is
def reverse_colourmap(cmap, name = 'my_cmap_r'):
return mpl.colors.LinearSegmentedColormap(name, cm.revcmap(cmap._segmentdata))