I have the figure shown below. Presently the figure's colorscheme uses the entire range of the colormap (mpl.cm.Paired). What I want to do, and have been unable to figure out, is how to limit matplotlib to use only a subset of the colormap. In this case I am trying to get the starting color to be a darker shade of blue. Here's the plotting section of my code:
Figure = plt.figure(figsize=(22,10))
Map = Basemap(projection='robin', lon_0=0, resolution='l')
x, y = Map(LONS, LATS)
levels = np.arange(0, 4100, 100)
fcp = Map.contourf(x, y, data, levels, interpolation="bicubic", cmap=mpl.cm.Paired)
cb = Map.colorbar(fcp, "bottom", size="5%", pad='5%', extendrect=False)
cb.ax.tick_params(labelsize=18)
cb.solids.set_edgecolor("face")
cb.set_label("metres",fontsize=18)
cb.ax.set_aspect(0.047)
Map.drawcoastlines(linewidth=1)
Map.drawmapboundary(linewidth=1)
Map.drawmeridians([-150,-100,-50,0,50,100, 150],labels=[1,1,1,0],fontsize=18)
Map.drawparallels([-60,-30,0,30,60],labels=[1,1,1,1],fontsize=18)
One way to do this would be to call the function mpl.cm.Paired() for a subset of the normalised range (i.e., [0-1]) and then use the list of colors that it returns to define a new colormap:
import matplotlib.colors as mcol
lvTmp = np.linspace(0.1,1.0,len(levels)-1)
cmTmp = mlp.cm.Paired(lvTmp)
newCmap = mcol.ListedColormap(cmTmp)
You'll need to fiddle about with the 0.1 value in that linspace to get the start color that you want from the built in colormap.
Related
I am creating a scatterplot with a colorbar
plt.scatter(X, Y, c=Z)
plt.colorbar()
plt.show()
plt.close()
where X and Y are float arrays and Z is an integer array.
Even though Z is an integer array (here 1-14), the colorbar displays floats.
How can I display a discrete colorbar 1-14?
I found something attempting to answer a similar question here, but I don't understand the answer (containing some complications to make 0 be gray) well enough to apply it.
Check out the second answer to your linked question. If you discretize your colourmap before calling scatter, it will automatically work as you want it to:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
n = 14
X = np.random.rand(20)
Y = np.random.rand(20)
Z = np.random.randint(low=0,high=n,size=X.shape)
plt.figure()
plt.scatter(X,Y,c=Z,cmap=cm.hot)
plt.colorbar()
plt.figure()
plt.scatter(X,Y,c=Z,cmap=cm.get_cmap('hot',n))
plt.colorbar()
Results for comparison:
Note that the default colourmap is jet. But only until viridis kicks in starting from version 2.0 as the new (and wonderful) default.
If what's bothering you is that the numbers are floating-point on the colourbar, you can set manual ticks in it, irrespective of the discretization of colours:
plt.figure()
plt.scatter(X,Y,c=Z,cmap=cm.jet)
plt.colorbar(ticks=np.unique(Z))
#or
#plt.colorbar(ticks=range(Z.min(),Z.max()+1))
Result:
Note that since I used a few random-generated points, not every number is present in Z, so unique might not be the best approach (see the missing ticks in the above figure). This is why I also added a solution based on min/max. You can tailor the limits to your needs depending on your actual application.
Here is my discrete colorbar for land use type, it seems like your work,because the Z value is also an interger array from 1-14.
My method
creat the colormap and colorbar label manually learned from here
My Code
cMap = ListedColormap(['white', '#8dd3c7','#ffffb3','#bebada', \
'#b2182b','#80b1d3','#fdb462','#b3de69','#6a3d9a',\
'#b2df8a', '#1f78b4', '#ccebc5','#ffed6f'])
## If you want to use the colormap from plt.cm..., you can use(take 'jet' for example)
cMap = plt.cm.get_cmap("jet",lut=13)
### here you can change your data in
lulc = plt.pcolormesh(lulc,cmap = cMap,alpha = 0.7)
z_range = np.linspace(1,14,14)
list = z_range.astype('S10')
k = -0.05
for i in range(0,13,1):
k = k + 1/13.0
ax.annotate(list[i],xycoords='axes fraction',xy=(1.12,k),fontsize = 14, \
fontstyle = 'italic',zorder =3)
cbar = plt.colorbar(lulc,ticks = [ ])
for label in cbar.ax.yaxis.get_ticklabels()[::-1]:
label.set_visible(False)
My result
(source: tietuku.com)
Wish it can help!
Here is my question.
When I want use a lot of colormap, I could use
CMAP = ["summer_r", "brg_r", "Dark2", "prism", "PuOr_r", "afmhot_r", "terrain_r", "PuBuGn_r", "RdPu", \
"gist_ncar_r", "gist_yarg_r", "Dark2_r", "YlGnBu", "RdYlBu", "hot_r"]
## value was a 3-d array, the first dimension represent the amount of 2-d array with the value (0, 1).
## I just plot the value 1 for each value[i,:,:]
for i in range(0,len(CMAP),1):
plt.pcolor(xx,yy,value[i,:,:], cmap = CMAP[i])
And I can get this:
http://i8.tietuku.com/cdcdcd5f539c124b.png
But I can't clearly realize the each grid's color befor generating the figure.
Because some colormap which I add in CMAP may have the same start color. SO, some value[ i, :, :] grids will be hard to distinguish.
My idea
Using one colormap instead and split into single color for each value[ i, :, :]. So, each value grid has a different color.
For example:
## 1. cut the colormap, take "jet" for example
cMap = plt.cm.get_cmap("jet",lut=6)
http://i4.tietuku.com/be127c44e87a03fc.png
## 2. I havn't figured it out
## This is the fake code
CMAP = Func[one color -> colormap](cMap)
Update -2016-01-18
This is my code to set different cmap and loop, but it was a bit of rigid.
cmap1 = colors.ListedColormap(["w",'red'])
cmap2 = colors.ListedColormap(["w",'blue'])
cmap3 = colors.ListedColormap(["w",'yellow'])
CMAP = [cmap1,cmap2,cmap3]
Then, I can cope with my original attempt.
But I was wondering is there a smart way to generate the cmap1,cmap2,......?
The hard part of this is coming up with N distinctive colors. In practice, it's usually easiest to just grab random colors as long as N is small. If you'd prefer a bit nicer way of getting N distinct colors, have a look at how seaborn's husl_palette and hsl_palette are implemented. They choose N evenly spaced colors in HSL/HUSL space and convert it back to RGB.
At any rate, there are two parts to tying specific values to specific colors in matplotlib. One is the colormap and the other is the norm. The Normalize instance (the norm) handles transforming the data ranges into a 0-1 space for the colormap.
There's a function to make this use-case easier:matplotlib.colors.from_levels_and_colors. It returns a cmap and norm instance that you can pass in to imshow/pcolormesh/scatter/etc.
As a stand-alone example, let's generate data with a random number of unique integer values. We'll use random pastel colors instead of trying to do something fancy.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors
nvals = np.random.randint(2, 20)
data = np.random.randint(0, nvals, (10, 10))
colors = np.random.random((nvals, 3))
# Make the colors pastels...
colors = colors / 2.5 + 0.55
levels = np.arange(nvals + 1) - 0.5
cmap, norm = from_levels_and_colors(levels, colors)
fig, ax = plt.subplots()
im = ax.imshow(data, interpolation='nearest', cmap=cmap, norm=norm)
fig.colorbar(im, ticks=np.arange(nvals))
plt.show()
Not the nicest looking color palette, but it's not awful. Here's another run:
Even with 17 values, we're still getting fairly distinct colors by choosing random values.
Assuming one needs to change the edge colour of a matplotlib marker depending on some variable, is it possible to assign a some sort of discrete colour-map for the edge colour of the marker?
This is similar to changing the face-colour of the marker by cmap.
When showing limits using arrows outside a plot's range, I cannot seem to vary the arrow colour depending on another variable.
eg: in the code below the colour of the arrow doesn't change as a function of z.
plt.scatter(x,y, c=z, marker=u'$\u2191$', s=40,cmap=discrete_cmap(4, 'cubehelix') )
You can do this using the edgecolors argument to scatter.
You need to make a list of colours to feed to scatter. We can do this using your chosen colormap and a Normalize instance, to rescale to z function to the 0-1 range.
I'm assuming your discrete_cmap function is something like the one linked here.
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import numpy as np
# def discrete_cmap() is omitted here...
# some sample data
x = np.linspace(0,10,11)
y = np.linspace(0,10,11)
z = x+y
# setup a Normalization instance
norm = colors.Normalize(z.min(),z.max())
# define the colormap
cmap = discrete_cmap(4, 'cubehelix')
# Use the norm and cmap to define the edge colours
edgecols = cmap(norm(z))
# Use that with the `edgecolors` argument. Set c='None' to turn off the facecolor
plt.scatter(x,y, edgecolors=edgecols, c = 'None', marker='o', s=40 )
plt.show()
Somehow, assigning colors to circles works different from assigning colors in scatter plots:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(6,6)) # give plots a rectangular frame
N = 4
r = 0.1
pos = 2.*np.random.rand(N,2) -1
# give different points different color
col = 1./N*np.arange(0,N)
# Method 1
for i,j,k in zip(pos[:,0],pos[:,1],col):
circle = plt.Circle((i,j), r, color = k)
fig.gca().add_artist(circle)
plt.show()
# Method 2
plt.scatter(pos[:,0],pos[:,1], c = col)
plt.show()
Why does Method 2 work while Method 1 gives the following error:
ValueError: to_rgba: Invalid rgba arg "0.0"
to_rgb: Invalid rgb arg "0.0"
cannot convert argument to rgb sequence
The error you're getting is because you need to use the string representation of the float rather than the float value directly, for example:
circle = plt.Circle((i,j), r, color=`k`) # or str(k)
Note in the above I'm using backward ticks, a shorthand for str(k), which converts a float to string, like str(.75) = "0.75", and will give different colors for each k value.
Here are the docs on to_rgba to which the error refers.
Edit:
There are many ways to specify a color in matplotlib. In the above, you set the float that references a colormap through a string representation of a float. The colormap for this could then be set through a PolyCollection.
In your case, to use Circle more like scatter, it's probably easiest to just set the color directly, and that can be done using an rgba tuple, for example, one that can be looked up from a colormap.
Below is an example using three different colormaps for the different y ranges.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as clrs
import matplotlib
N, r = 200, .1
cms = matplotlib.cm
maps = [cms.jet, cms.gray, cms.autumn]
fig = plt.figure(figsize=(6,6)) # give plots a rectangular frame
ax = fig.add_subplot(111)
pos = 2.999*np.random.rand(N,2)
for x, y in pos:
cmi = int(y) # an index for which map to use based on y-value
#fc = np.random.random() # use this for random colors selected from regional map
fc = x/3. # use this for x-based colors
color = maps[cmi](fc) # get the right map, and get the color from the map
# ie, this is like, eg, color=cm.jet(.75) or color=(1.0, 0.58, 0.0, 1.0)
circle = plt.Circle((x,y), r, color=color) # create the circle with the color
ax.add_artist(circle)
ax.set_xlim(0, 3)
ax.set_ylim(0, 3)
plt.show()
In the above I made the color for each band vary with x because I thought it looked good, but you can also do random colors, of course. Just switch which fc line is being used:
In order to use the predetermined colors of matplot lib, you should pass strings to the color field. In this case 'k' will be black color instead of simply k.
This code did not give errors for me:
for i,j,k in zip(pos[:,0],pos[:,1],col):
circle = plt.Circle((i,j), r, color = 'k')
fig.gca().add_artist(circle)
plt.show()
Please make sure in your next question you provide code that is runnable. In this case variables N and r were not defined.
I want to draw a rectangle, with a gradient color fill from left to right, at an arbitrary position with arbitrary dimensions in my axes instance (ax1) coordinate system.
My first thought was to create a path patch and somehow set its fill as a color gradient. But according to THIS POST there isn't a way to do that.
Next I tried using a colorbar. I created a second axes instance ax2 using fig.add_axes([left, bottom, width, height]) and added a color bar to that.
ax2 = fig.add_axes([0, 0, width, height/8])
colors = [grad_start_color, grad_end_color]
index = [0.0, 1.0]
cm = LinearSegmentedColormap.from_list('my_colormap', zip(index, colors))
colorbar.ColorbarBase(ax2, cmap=cm, orientation='horizontal')
But the positional parameters passed to fig.add_axes() are in the coordinate system of fig, and don't match up with the coordinate system of ax1.
How can I do this?
I have been asking myself a similar question and spent some time looking for the answer to find in the end that this can quite easily be done by imshow:
from matplotlib import pyplot
pyplot.imshow([[0.,1.], [0.,1.]],
cmap = pyplot.cm.Greens,
interpolation = 'bicubic'
)
It is possible to specify a colormap, what interpolation to use and much more. One additional thing, I find very interesting, is the possibility to specify which part of the colormap to use. This is done by means of vmin and vmax:
pyplot.imshow([[64, 192], [64, 192]],
cmap = pyplot.cm.Greens,
interpolation = 'bicubic',
vmin = 0, vmax = 255
)
Inspired by this example
Additional Note:
I chose X = [[0.,1.], [0.,1.]] to make the gradient change from left to right. By setting the array to something like X = [[0.,0.], [1.,1.]], you get a gradient from top to bottom. In general, it is possible to specify the colour for each corner where in X = [[i00, i01],[i10, i11]], i00, i01, i10 and i11 specify colours for the upper-left, upper-right, lower-left and lower-right corners respectively. Increasing the size of X obviously allows to set colours for more specific points.
did you ever solve this? I wanted the same thing and found the answer using the coordinate mapping from here,
#Map axis to coordinate system
def maptodatacoords(ax, dat_coord):
tr1 = ax.transData.transform(dat_coord)
#create an inverse transversion from display to figure coordinates:
fig = ax.get_figure()
inv = fig.transFigure.inverted()
tr2 = inv.transform(tr1)
#left, bottom, width, height are obtained like this:
datco = [tr2[0,0], tr2[0,1], tr2[1,0]-tr2[0,0],tr2[1,1]-tr2[0,1]]
return datco
#Plot a new axis with a colorbar inside
def crect(ax,x,y,w,h,c,**kwargs):
xa, ya, wa, ha = maptodatacoords(ax, [(x,y),(x+w,y+h)])
fig = ax.get_figure()
axnew = fig.add_axes([xa, ya, wa, ha])
cp = mpl.colorbar.ColorbarBase(axnew, cmap=plt.get_cmap("Reds"),
orientation='vertical',
ticks=[],
**kwargs)
cp.outline.set_linewidth(0.)
plt.sca(ax)
Hopefully this helps anyone in the future who needs similar functionality. I ended up using a grid of patch objects instead.