'Getting individual colors from a color map in matplotlib
If you have a Colormap cmap
, for example:
cmap = matplotlib.cm.get_cmap('Spectral')
How can you get a particular colour out of it between 0 and 1, where 0 is the first colour in the map and 1 is the last colour in the map?
Ideally, I would be able to get the middle colour in the map by doing:
>>> do_some_magic(cmap, 0.5) # Return an RGBA tuple
(0.1, 0.2, 0.3, 1.0)
Solution 1:[1]
You can do this with the code below, and the code in your question was actually very close to what you needed, all you have to do is call the cmap
object you have.
import matplotlib
cmap = matplotlib.cm.get_cmap('Spectral')
rgba = cmap(0.5)
print(rgba) # (0.99807766255210428, 0.99923106502084169, 0.74602077638401709, 1.0)
For values outside of the range [0.0, 1.0] it will return the under and over colour (respectively). This, by default, is the minimum and maximum colour within the range (so 0.0 and 1.0). This default can be changed with cmap.set_under()
and cmap.set_over()
.
For "special" numbers such as np.nan
and np.inf
the default is to use the 0.0 value, this can be changed using cmap.set_bad()
similarly to under and over as above.
Finally it may be necessary for you to normalize your data such that it conforms to the range [0.0, 1.0]
. This can be done using matplotlib.colors.Normalize
simply as shown in the small example below where the arguments vmin
and vmax
describe what numbers should be mapped to 0.0 and 1.0 respectively.
import matplotlib
norm = matplotlib.colors.Normalize(vmin=10.0, vmax=20.0)
print(norm(15.0)) # 0.5
A logarithmic normaliser (matplotlib.colors.LogNorm) is also available for data ranges with a large range of values.
(Thanks to both Joe Kington and tcaswell for suggestions on how to improve the answer.)
Solution 2:[2]
In order to get rgba integer value instead of float value, we can do
rgba = cmap(0.5,bytes=True)
So to simplify the code based on answer from Ffisegydd, the code would be like this:
#import colormap
from matplotlib import cm
#normalize item number values to colormap
norm = matplotlib.colors.Normalize(vmin=0, vmax=1000)
#colormap possible values = viridis, jet, spectral
rgba_color = cm.jet(norm(400),bytes=True)
#400 is one of value between 0 and 1000
Solution 3:[3]
I once ran into a similar situation where I needed "n" no. of colors from a colormap so that I can assign each color to my data. I have compiled a code to this in a package called "mycolorpy". You can pip install it using:
pip install mycolorpy
You can then do:
from mycolorpy import colorlist as mcp
import numpy as np
Example: To create a list of 5 hex strings from camp "winter
color1=mcp.gen_color(cmap="winter",n=5)
print(color1)
Output:
['#0000ff', '#0040df', '#0080bf', '#00c09f', '#00ff80']
Another example to generate 16 list of colors from camp bwr:
color2=mcp.gen_color(cmap="bwr",n=16)
print(color2)
Output:
['#0000ff', '#2222ff', '#4444ff', '#6666ff', '#8888ff', '#aaaaff', '#ccccff', '#eeeeff', '#ffeeee', '#ffcccc', '#ffaaaa', '#ff8888', '#ff6666', '#ff4444', '#ff2222', '#ff0000']
There is a python notebook with usage examples to better visualize this.
Say you want to generate a list of colors from a cmap that is normalized to a given data. You can do that using:
a=random.randint(1000, size=(200))
a=np.array(a)
color1=mcp.gen_color_normalized(cmap="seismic",data_arr=a)
plt.scatter(a,a,c=color1)
You can also reverse the color using:
color1=mcp.gen_color_normalized(cmap="seismic",data_arr=a,reverse=True)
plt.scatter(a,a,c=color1)
Solution 4:[4]
I had precisely this problem, but I needed sequential plots to have highly contrasting color. I was also doing plots with a common sub-plot containing reference data, so I wanted the color sequence to be consistently repeatable.
I initially tried simply generating colors randomly, reseeding the RNG before each plot. This worked OK (commented-out in code below), but could generate nearly indistinguishable colors. I wanted highly contrasting colors, ideally sampled from a colormap containing all colors.
I could have as many as 31 data series in a single plot, so I chopped the colormap into that many steps. Then I walked the steps in an order that ensured I wouldn't return to the neighborhood of a given color very soon.
My data is in a highly irregular time series, so I wanted to see the points and the lines, with the point having the 'opposite' color of the line.
Given all the above, it was easiest to generate a dictionary with the relevant parameters for plotting the individual series, then expand it as part of the call.
Here's my code. Perhaps not pretty, but functional.
from matplotlib import cm
cmap = cm.get_cmap('gist_rainbow') #('hsv') #('nipy_spectral')
max_colors = 31 # Constant, max mumber of series in any plot. Ideally prime.
color_number = 0 # Variable, incremented for each series.
def restart_colors():
global color_number
color_number = 0
#np.random.seed(1)
def next_color():
global color_number
color_number += 1
#color = tuple(np.random.uniform(0.0, 0.5, 3))
color = cmap( ((5 * color_number) % max_colors) / max_colors )
return color
def plot_args(): # Invoked for each plot in a series as: '**(plot_args())'
mkr = next_color()
clr = (1 - mkr[0], 1 - mkr[1], 1 - mkr[2], mkr[3]) # Give line inverse of marker color
return {
"marker": "o",
"color": clr,
"mfc": mkr,
"mec": mkr,
"markersize": 0.5,
"linewidth": 1,
}
My context is JupyterLab and Pandas, so here's sample plot code:
restart_colors() # Repeatable color sequence for every plot
fig, axs = plt.subplots(figsize=(15, 8))
plt.title("%s + T-meter"%name)
# Plot reference temperatures:
axs.set_ylabel("°C", rotation=0)
for s in ["T1", "T2", "T3", "T4"]:
df_tmeter.plot(ax=axs, x="Timestamp", y=s, label="T-meter:%s" % s, **(plot_args()))
# Other series gets their own axis labels
ax2 = axs.twinx()
ax2.set_ylabel(units)
for c in df_uptime_sensors:
df_uptime[df_uptime["UUID"] == c].plot(
ax=ax2, x="Timestamp", y=units, label="%s - %s" % (units, c), **(plot_args())
)
fig.tight_layout()
plt.show()
The resulting plot may not be the best example, but it becomes more relevant when interactively zoomed in.
Solution 5:[5]
To build on the solutions from Ffisegydd and amaliammr, here's an example where we make CSV representation for a custom colormap:
#! /usr/bin/env python3
import matplotlib
import numpy as np
vmin = 0.1
vmax = 1000
norm = matplotlib.colors.Normalize(np.log10(vmin), np.log10(vmax))
lognum = norm(np.log10([.5, 2., 10, 40, 150,1000]))
cdict = {
'red':
(
(0., 0, 0),
(lognum[0], 0, 0),
(lognum[1], 0, 0),
(lognum[2], 1, 1),
(lognum[3], 0.8, 0.8),
(lognum[4], .7, .7),
(lognum[5], .7, .7)
),
'green':
(
(0., .6, .6),
(lognum[0], 0.8, 0.8),
(lognum[1], 1, 1),
(lognum[2], 1, 1),
(lognum[3], 0, 0),
(lognum[4], 0, 0),
(lognum[5], 0, 0)
),
'blue':
(
(0., 0, 0),
(lognum[0], 0, 0),
(lognum[1], 0, 0),
(lognum[2], 0, 0),
(lognum[3], 0, 0),
(lognum[4], 0, 0),
(lognum[5], 1, 1)
)
}
mycmap = matplotlib.colors.LinearSegmentedColormap('my_colormap', cdict, 256)
norm = matplotlib.colors.LogNorm(vmin, vmax)
colors = {}
count = 0
step_size = 0.001
for value in np.arange(vmin, vmax+step_size, step_size):
count += 1
print("%d/%d %f%%" % (count, vmax*(1./step_size), 100.*count/(vmax*(1./step_size))))
rgba = mycmap(norm(value), bytes=True)
color = (rgba[0], rgba[1], rgba[2])
if color not in colors.values():
colors[value] = color
print ("value, red, green, blue")
for value in sorted(colors.keys()):
rgb = colors[value]
print("%s, %s, %s, %s" % (value, rgb[0], rgb[1], rgb[2]))
Solution 6:[6]
Colormaps come with their own normalize method, so if you have a plot already made you can access the color at a certain value.
import matplotlib.pyplot as plt
import numpy as np
cmap = plt.cm.viridis
cm = plt.pcolormesh(np.random.randn(10, 10), cmap=cmap)
print(cmap(cm.norm(2.2)))
Solution 7:[7]
For a quick and dirty you can use the map directly. Or you can just do what @amaliammr says.
data_size = 23 # range 0..23
colors = plt.cm.turbo
color_normal = colours.N/data_size
for i in range(data_size):
col = colours.colors[int(i*color_normal)]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | JohanC |
Solution 2 | |
Solution 3 | |
Solution 4 | BobC |
Solution 5 | Morten |
Solution 6 | nedlrichards |
Solution 7 | Konchog |