"""
Handle colors and colormaps.
Highlights:
- Adds colormaps including ``'turbo'``, ``'parula'``, and ``'orangeblue'``
- :func:`sc.hex2rgb() <hex2rgb>`/:func:`sc.rgb2hex() <rgb2hex>`: convert between different color conventions
- :func:`sc.vectocolor() <vectocolor>`: map a list of sequential values onto a list of colors
- :func:`sc.gridcolors() <gridcolors>`: map a list of qualitative categories onto a list of colors
"""
##############################################################################
#%% Imports
##############################################################################
import struct
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import sciris as sc
##############################################################################
#%% Color functions
##############################################################################
__all__ = ['sanitizecolor', 'shifthue', 'rgb2hex', 'hex2rgb', 'rgb2hsv', 'hsv2rgb']
def _listify_colors(colors, origndim=None):
""" Do standard transformation on colors -- internal helper function """
if not origndim:
colors = sc.dcp(colors) # So we don't overwrite the original
origndim = np.ndim(colors) # Original dimensionality
if origndim==1:
colors = [colors] # Wrap it in another list if needed
colors = np.array(colors) # Just convert it to an array
return colors, origndim
else: # Reverse the transformation
if origndim==1:
colors = colors[0] # Pull it out again
return colors
[docs]
def sanitizecolor(color, asarray=False, alpha=None, normalize=True):
"""
Alias to :func:`matplotlib.colors.to_rgb`, but also handles numeric inputs.
Arg:
color (str/list/etc): the input color to sanitize into an RGB tuple (or array)
asarray (bool): whether to return an array instead of a tuple
alpha (float): if not None, include the alpha channel with this value
normalize (bool): whether to divide by 255 if any values are greater than 1
**Examples**::
green1 = sc.sanitizecolor('g')
green2 = sc.sanitizecolor('tab:green')
crimson1 = sc.sanitizecolor('crimson')
crimson2 = sc.sanitizecolor((220, 20, 60))
midgrey = sc.sanitizecolor(0.5)
"""
if isinstance(color, str):
try:
color = mpl.colors.to_rgb(color)
except ValueError as E:
errormsg = f'Could not understand "{color}" as a valid color: must be a standard Matplotlib color string'
raise ValueError(errormsg) from E
elif isinstance(color, float):
color = [color]*3 # Consider it grey
color = sc.toarray(color).astype(float) # Get it into consistent format for further operations
if len(color) not in [3,4]: # pragma: no cover
errormsg = f'Cannot parse {color} as a color: expecting length 3 (RGB) or 4 (RGBA)'
raise ValueError(errormsg)
if normalize and color.max()>1:
color /= 255
if alpha is not None and len(color) == 3:
color = sc.cat(color, float(alpha))
if not asarray:
color = tuple(color) # Convert back to tuple if desired
return color
def _processcolors(colors=None, asarray=False, ashex=False, reverse=False):
"""
Small helper function to do common transformations on the colors, once generated.
Expects colors to be an array. If asarray is True and reverse are False, returns
that array. Otherwise, does the required permutations.
"""
if asarray:
output = colors
if reverse: output = output[::-1] # Reverse the array
else:
output = []
for c in colors: # Gather output
output.append(tuple(c))
if reverse: # Reverse the list # pragma: no cover
output.reverse()
if ashex: # pragma: no cover
for c,color in enumerate(output):
output[c] = rgb2hex(color)
return output
[docs]
def shifthue(colors=None, hueshift=0.0):
"""
Shift the hue of the colors being fed in.
**Example**::
colors = sc.shifthue(colors=[(1,0,0),(0,1,0)], hueshift=0.5)
"""
colors, origndim = _listify_colors(colors)
for c,color in enumerate(colors):
color = sc.toarray(color, dtype=float) # Required for NumPy 2.0
hsvcolor = mpl.colors.rgb_to_hsv(color)
hsvcolor[0] = (hsvcolor[0]+hueshift) % 1.0 # Calculate new hue and return the modulus
rgbcolor = mpl.colors.hsv_to_rgb(hsvcolor)
colors[c] = rgbcolor
colors = _listify_colors(colors, origndim)
return colors
[docs]
def rgb2hex(arr):
"""
A little helper function to convert e.g. [0.53, 0.74, 0.15] to a pleasing shade of green.
**Example**::
hx = sc.rgb2hex([0.53, 0.74, 0.15]) # Returns '#87bc26'
"""
arr = np.array(arr)
if len(arr) != 3: # pragma: no cover
errormsg = f'Cannot convert "{arr}" to hex: wrong length'
raise ValueError(errormsg)
if all(arr<=1): arr *= 255. # Convert from 0-1 to 0-255
hexstr = '#%02x%02x%02x' % (int(arr[0]), int(arr[1]), int(arr[2]))
return hexstr
[docs]
def hex2rgb(string):
"""
A little helper function to convert e.g. '87bc26' to a pleasing shade of green.
**Example**::
rgb = sc.hex2rgb('#87bc26') # Returns array([0.52941176, 0.7372549 , 0.14901961])
"""
if string[0] == '#':
string = string[1:] # Trim leading #, if it exists
if len(string) == 3:
string = string[0]*2 + string[1]*2 + string[2]*2 # Convert e.g. '8b2' to '88bb22'
if len(string) != 6: # pragma: no cover
errormsg = f'Cannot convert "{string}" to an RGB color: must be 3 or 6 characters long'
raise ValueError(errormsg)
hexstring = bytes.fromhex(string) # Ensure it's the right type
rgb = np.array(struct.unpack('BBB',hexstring),dtype=float)/255.
return rgb
[docs]
def rgb2hsv(colors=None):
"""
Shortcut to Matplotlib's rgb_to_hsv method, accepts a color triplet or a list/array of color triplets.
**Example**::
hsv = sc.rgb2hsv([0.53, 0.74, 0.15]) # Returns array([0.2259887, 0.7972973, 0.74 ])
"""
colors, origndim = _listify_colors(colors)
for c,color in enumerate(colors):
hsvcolor = mpl.colors.rgb_to_hsv(color)
colors[c] = hsvcolor
colors = _listify_colors(colors, origndim)
return colors
[docs]
def hsv2rgb(colors=None):
"""
Shortcut to Matplotlib's hsv_to_rgb method, accepts a color triplet or a list/array of color triplets.
**Example**::
rgb = sc.hsv2rgb([0.23, 0.80, 0.74]) # Returns array([0.51504, 0.74 , 0.148 ])
"""
colors, origndim = _listify_colors(colors)
for c,color in enumerate(colors):
hsvcolor = mpl.colors.hsv_to_rgb(color)
colors[c] = hsvcolor
colors = _listify_colors(colors, origndim)
return colors
##############################################################################
#%% Colormap-related functions
##############################################################################
__all__ += ['vectocolor', 'arraycolors', 'gridcolors', 'midpointnorm', 'manualcolorbar', 'colormapdemo']
[docs]
def vectocolor(vector, cmap=None, asarray=True, reverse=False, minval=None, maxval=None, midpoint=None, nancolor=None):
"""
This function converts a vector (i.e., 1D array) of N values into an Nx3 matrix
of color values according to the current colormap. It automatically scales the
vector to provide maximum dynamic range for the color map.
Note: see sc.arraycolors() for multidimensional input.
Args:
vector (array): Input vector (or list, it's converted to an array)
cmap (str): is the colormap (default: current)
asarray (bool): whether to return as an array (otherwise, a list of tuples)
reverse (bool): whether to reverse the list of colors
minval (float): the minimum value to use
maxval (float): the maximum value to use
midpoint (float): the midpoint value to use
nancolor (color): if supplied, use this color for NaN entries
Returns:
colors (array): Nx4 array of RGB-alpha color values
**Example**::
n = 1000
x = np.random.randn(n,1);
y = np.random.randn(n,1);
c = sc.vectocolor(y);
plt.scatter(x, y, c=c, s=50)
| *New in version 1.2.0:* midpoint argument.
| *New in version 2.1.0:* nancolor argument and remove nans by default
| *New in version 3.0.0:* correct "midpoint" argument
"""
from numpy import array, zeros
if cmap is None:
cmap = plt.get_cmap() # Get current colormap
elif isinstance(cmap, str):
try:
cmap = plt.get_cmap(cmap)
except: # pragma: no cover
choices = sc.newlinejoin(plt.colormaps())
raise ValueError(f'{cmap} is not a valid color map; choices are:\n{choices}')
# If a scalar is supplied, convert it to a vector instead
if sc.isnumber(vector):
vector = np.linspace(0, 1, vector)
# Usual situation -- the vector has elements
vector = sc.dcp(vector) # To avoid in-place modification
vector = np.array(vector, dtype=float) # Just to be sure
if len(vector):
if minval is None:
minval = np.nanmin(vector)
if maxval is None:
maxval = np.nanmax(vector)
diff = maxval - minval
vector = (vector - minval)/diff # Normalize vector
if midpoint is not None:
vcenter = (midpoint - minval)/diff
assert 0 <= vcenter <= 1, f'Values not in order: must be minval={minval:n} <= midpoint={midpoint:n} <= maxval={maxval:n}'
norm = midpointnorm(vcenter=vcenter, vmin=0, vmax=1)
vector = np.array(norm(vector))
nelements = len(vector) # Count number of elements
colors = zeros((nelements,4))
for i in range(nelements):
point = vector[i]
if np.isnan(point) and nancolor is not None:
color = sanitizecolor(nancolor, alpha=True) # If it's NaN
else:
color = array(cmap(point)) # Main use case
colors[i,:] = color
# It doesn't; just return black
else:
colors = (0,0,0,1)
# Process output
output = _processcolors(colors=colors, asarray=asarray, reverse=reverse)
return output
[docs]
def arraycolors(arr, **kwargs):
"""
Map an N-dimensional array of values onto the current colormap. An extension
of vectocolor() for multidimensional arrays; see that function for additional
arguments.
Args:
arr (array): a multidimensional array to be converted to an array of colors
kwargs(dict): passed to :func:`sc.vectocolor() <vectocolor>`
**Example**::
n = 1000
ncols = 5
arr = np.random.rand(n,ncols)
for c in range(ncols):
arr[:,c] += c
x = np.random.rand(n)
y = np.random.rand(n)
colors = sc.arraycolors(arr)
plt.figure(figsize=(20,16))
for c in range(ncols):
plt.scatter(x+c, y, s=50, c=colors[:,c])
Version: 2020mar07
| *New in version 3.1.0:* Handle non-array output
"""
arr = sc.dcp(arr) # Avoid modifications
new_shape = arr.shape + (4,) # RGBα
colors = np.zeros(new_shape)
colorvec = vectocolor(vector=arr.reshape(-1), **kwargs)
if sc.isarray(colorvec):
colors = colorvec.reshape(new_shape)
else:
errormsg = 'Creating array colors as a list does not make sense; use sc.vectocolor(arr.flatten()) if you mean to do this'
raise ValueError(errormsg)
return colors
[docs]
def gridcolors(ncolors=10, limits=None, nsteps=20, asarray=False, ashex=False, reverse=False, hueshift=0, basis='default', demo=False):
"""
Create a qualitative "color map" by assigning points according to the maximum
pairwise distance in the color cube. Basically, the algorithm generates n points
that are maximally uniformly spaced in the [R, G, B] color cube.
By default, if there are <=9 colors, use Colorbrewer colors; if there are
10-19 colors, use Kelly's colors; if there are >=20 colors, use uniformly
spaced grid colors.
Args:
ncolors (int) : the number of colors to create
limits (float) : how close to the edges of the cube to make colors (to avoid white and black)
nsteps (int) : the discretization of the color cube (e.g. 10 = 10 units per side = 1000 points total)
ashex (bool) : whether to return colors in hexadecimal representation
asarray (bool) : whether to return the colors as an array instead of as a list of tuples
reverse (bool) : whether to reverse the list of colors
hueshift (float) : whether to shift the hue (hueshift > 0 and <=1) or not (0)
demo (bool) : whether or not to plot the color cube itself
basis (str) : what basis to use -- options are 'colorbrewer', 'kelly', 'default', or 'none'
**Example**::
import numpy as np
import matplotlib.pyplot as plt
import sciris as sc
ncolors = 10
piedata = np.random.rand(ncolors)
colors = sc.gridcolors(ncolors)
plt.pie(piedata, colors=colors)
sc.gridcolors(ncolors, demo=True)
plt.show()
| *New in version 2018oct30.*
| *New in version 3.2.0:* allow ncolors to be an iterable
"""
# Handle input arguments
if not sc.isnumber(ncolors):
if sc.isiterable(ncolors):
ncolors = len(ncolors)
else:
errormsg = f'Invalid input {ncolors}; must be an integer or an iterable'
raise TypeError(errormsg)
# Choose default colors
if basis == 'default':
if ncolors<=9: basis = 'colorbrewer' # Use these cos they're nicer
else: basis = 'kelly' # Use these cos there are more of them
# Steal colorbrewer colors for small numbers of colors
colorbrewercolors = np.array([
[ 55, 126, 184], # [27, 158, 119], # Old color
[228, 26, 28], # [217, 95, 2],
[ 77, 175, 74], # [117, 112, 179],
[162, 78, 153], # [231, 41, 138],
[255, 127, 0],
[200, 200, 51], # Was too bright yellow
[166, 86, 40],
[247, 129, 191],
[153, 153, 153],
])/255.
# Steal Kelly's colors from https://gist.github.com/ollieglass/f6ddd781eeae1d24e391265432297538, removing
# black: '222222', off-white: 'F2F3F4', mid-grey: '848482',
kellycolors = ['F3C300', '875692', 'F38400', 'A1CAF1', 'BE0032', 'C2B280', '008856', 'E68FAC', '0067A5', 'F99379', '604E97', 'F6A600', 'B3446C', 'DCD300', '882D17', '8DB600', '654522', 'E25822', '2B3D26']
for c,color in enumerate(kellycolors):
kellycolors[c] = list(hex2rgb(color))
kellycolors = np.array(kellycolors)
if basis == 'colorbrewer' and ncolors<=len(colorbrewercolors):
colors = colorbrewercolors[:ncolors]
elif basis == 'kelly' and ncolors<=len(kellycolors):
colors = kellycolors[:ncolors]
else: # Too many colors, calculate instead
## Calculate sliding limits if none provided
if limits is None:
colorrange = 1-1/float(ncolors**0.5)
limits = [0.5-colorrange/2, 0.5+colorrange/2]
## Calculate primitives and dot locations
primitive = np.linspace(limits[0], limits[1], nsteps) # Define primitive color vector
x, y, z = np.meshgrid(primitive, primitive, primitive) # Create grid of all possible points
dots = np.transpose(np.array([x.flatten(), y.flatten(), z.flatten()])) # Flatten into an array of dots
ndots = nsteps**3 # Calculate the number of dots
## Start from the colorbrewer colors
if basis=='colorbrewer' or basis=='kelly':
indices = [] # Initialize the array
if basis == 'colorbrewer': basiscolors = colorbrewercolors
elif basis == 'kelly': basiscolors = kellycolors
for color in basiscolors:
rgbdistances = dots - color # Calculate the distance in RGB space
totaldistances = np.linalg.norm(rgbdistances,axis=1)
closest = np.argmin(totaldistances)
indices.append(closest)
else:
indices = [0]
## Calculate the distances
for pt in range(ncolors-len(indices)): # Loop over each point
totaldistances = np.inf+np.zeros(ndots) # Initialize distances
for ind in indices: # Loop over each existing point
rgbdistances = dots - dots[ind] # Calculate the distance in RGB space
totaldistances = np.minimum(totaldistances, np.linalg.norm(rgbdistances,axis=1)) # Calculate the minimum Euclidean distance
maxindex = np.argmax(totaldistances) # Find the point that maximizes the minimum distance
indices.append(maxindex) # Append this index
colors = dots[indices,:]
## Wrap up -- turn color array into a list, or reverse
if hueshift: colors = shifthue(colors, hueshift=hueshift) # Shift hue if requested
output = _processcolors(colors=colors, asarray=asarray, ashex=ashex, reverse=reverse)
## For plotting -- optional
if demo:
ax = sc.scatter3d(colors[:,0], colors[:,1], colors[:,2], c=output, s=200, depthshade=False, lw=0, fig=True, figkwargs={'facecolor':'w'})
ax.set_xlabel('Red', fontweight='bold')
ax.set_ylabel('Green', fontweight='bold')
ax.set_zlabel('Blue', fontweight='bold')
ax.set_xlim((0,1))
ax.set_ylim((0,1))
ax.set_zlim((0,1))
return output
[docs]
def midpointnorm(vcenter=0, vmin=None, vmax=None):
"""
Alias to Matplotlib's TwoSlopeNorm. Used to place the center of the colormap
somewhere other than the center of the data.
Args:
vcenter (float): the center of the colormap (0 by default)
vmin (float): the minimum of the colormap
vmax (float): the maximum of the colormap
**Example**::
data = np.random.rand(10,10) - 0.2
plt.pcolor(data, cmap='bi', norm=sc.midpointnorm())
| *New in version 1.2.0.*
"""
norm = mpl.colors.TwoSlopeNorm(vcenter=vcenter, vmin=vmin, vmax=vmax)
return norm
[docs]
def manualcolorbar(data=None, vmin=0, vmax=1, vcenter=None, colors=None, values=None,
cmap=None, norm=None, label=None, labelkwargs=None, ticks=None,
ticklabels=None, fig=None, ax=None, cax=None, axkwargs=None, **kwargs):
"""
Add a colorbar to a plot that does not support one by default.
There are three main use cases, from least to most manual:
- The most common use case is to supply the data used for plotting directly via ``data``;
the function will the infer the lower and upper limits and construct the colorbar.
- Alternatively, the lower and upper limits can be provided manually via ``vmin`` and ``vmax``.
- Finally, the colors themselves can be provided via ``colors``, optionally mapped
to ``values``, and potentially also supplied with custom ``ticklabels``.
Args:
data (arr): if provided, compute the colorbar from these data
vmin (float): the minimum of the colormap (optional if data are provided)
vmax (float): the maximum of the colormap (optional if data are provided)
vcenter (float): the center of the colormap (optional)
colors (arr): if provided, use these colors directly instead
values (arr): if provided, the values corresponding to the specific colors
cmap (str/arr): the colormap to use
norm (Norm): the Matplotlib norm to use (if not provided, use the midpoint norm with vmin, vmax, etc.)
label (str): the label for the colorbar
labelkwargs (dict): passed to the colorbar label
ticks (list): the tick locations to use for the colorbar
ticklabels (list): the tick labels to use
ax (Axes): the "parent" axes to associate the colorbar with
cax (Axes): the axes to draw the colorbar into
axkwargs (dict): if creating a new colorbar axes, the arguments for creating it
kwargs (dict): passed to :func:`matplotlib.colorbar.ColorbarBase`
**Examples**::
# Create a default colorbar
sc.manualcolorbar()
# Add a colorbar to non-mappable data (e.g. a scatterplot)
n = 1000
x = np.random.randn(n)
y = np.random.randn(n)
c = x**2 + y**2
plt.scatter(x, y, c=c)
sc.manualcolorbar(c)
# Create a custom colorbar with a custom label
sc.manualcolorbar(
vmin=-20,
vmax=40,
vcenter=0,
cmap='orangeblue',
label='Cold/hot',
orientation='horizontal',
labelkwargs=dict(rotation=10, fontweight='bold'),
axkwargs=[0.1,0.5,0.8,0.1],
)
# Create a completely custom colorbar
n = 12
x = np.arange(n)
values = np.sqrt(np.arange(n))
colors = sc.gridcolors(n)
plt.scatter(x, values, c=colors)
plt.grid(True)
ticklabels = ['' for i in range(n)]
for i in [0, 2, 4, 10, 11]:
ticklabels[i] = f'Color {i} is nice'
cb = sc.manualcolorbar(
colors=colors,
values=values,
ticks=values,
ticklabels=ticklabels,
spacing='proportional'
)
| *New in version 3.1.0.*
"""
labelkwargs = sc.mergedicts(labelkwargs)
# Get the colorbar axes
if sc.checktype(axkwargs, 'arraylike'):
axarg = axkwargs
axkwargs = {}
else:
axarg = None
axkwargs = sc.mergedicts(axkwargs)
if cax is None and (axarg or axkwargs):
cax = plt.axes(arg=axarg, **axkwargs)
if ax is None and cax is None:
ax = plt.gca() # We need an axis or colorbar axis
# Handle explicit colors
if colors is not None:
if values is None: # Generate the boundaries automatically
values = np.arange(len(colors)+1)
if len(values) == len(colors): # Add an upper bound if not provided
values = sc.cat(values, values[-1] + np.diff(values[-2:])) # Add the last diff on again
cmap = mpl.colors.ListedColormap(colors)
norm = mpl.colors.BoundaryNorm(values, cmap.N)
sm = mpl.cm.ScalarMappable(cmap=cmap, norm=norm)
# Main use case: infer colors
else:
# If data is provided, use that to get the minimum and maximum
if data is not None:
data = np.array(data)
vmin = data.min()
vmax = data.max()
# Handle the center
if vcenter is None:
vcenter = (vmin + vmax) / 2
# Handle a custom norm
if norm is None:
norm = midpointnorm(vcenter=vcenter, vmin=vmin, vmax=vmax)
# Create the scalar mappable
sm = mpl.cm.ScalarMappable(norm=norm, cmap=cmap)
# Create the colorbar
cb = plt.colorbar(sm, ax=ax, cax=cax, ticks=ticks, **kwargs)
if label:
cb.set_label(label, **labelkwargs)
if ticklabels:
if cb.orientation == 'vertical':
cb.ax.set_yticklabels(ticklabels)
else:
cb.ax.set_xticklabels(ticklabels)
return cb
[docs]
def colormapdemo(cmap=None, n=None, smoothing=None, randseed=None, doshow=True):
"""
Demonstrate a color map using simulated elevation data, shown in both 2D and
3D. The argument can be either a colormap itself or a string describing a
colormap.
**Examples**::
sc.colormapdemo('inferno') # Use a registered Matplotlib colormap
sc.colormapdemo('parula') # Use a registered Sciris colormap
sc.colormapdemo(sc.alpinecolormap(), n=200, smoothing=20, randseed=2942) # Use a colormap object
Version: 2019aug22
"""
# Set data
if n is None: n = 100
if smoothing is None: smoothing = 40
if randseed is None: randseed = 8
if cmap is None: cmap = 'parula' # For no particular reason
maxheight = 1
horizontalsize = 4
np.random.seed(randseed)
kernel = np.array([0.25,0.5,0.25])
data = np.random.randn(n,n)
for s in range(smoothing): # Quick-and-dirty-and-slow smoothing
for i in range(n): data[:,i] = np.convolve(data[:,i],kernel,mode='same')
for i in range(n): data[i,:] = np.convolve(data[i,:],kernel,mode='same')
data -= data.min()
data /= data.max()
data *= maxheight
# Plot in 2D
fig1 = plt.figure(figsize=(10,8))
X = np.linspace(0,horizontalsize,n)
pcl = plt.pcolor(X, X, data, cmap=cmap, linewidth=0, antialiased=False, shading='auto')
cb2 = fig1.colorbar(pcl)
cb2.set_label('Height (km)',horizontalalignment='right', labelpad=50)
plt.xlabel('Position (km)')
plt.ylabel('Position (km)')
if doshow: # pragma: no cover
plt.show()
# Plot in 3D
fig2,ax2 = sc.fig3d(returnax=True, figsize=(12,8))
ax2.view_init(elev=45, azim=30)
X = np.linspace(0,horizontalsize,n)
X, Y = np.meshgrid(X, X)
surf = ax2.plot_surface(X, Y, data, rstride=1, cstride=1, cmap=cmap, linewidth=0, antialiased=False)
cb = fig2.colorbar(surf)
cb.set_label('Height (km)', horizontalalignment='right', labelpad=50)
plt.xlabel('Position (km)')
plt.ylabel('Position (km)')
if doshow: # pragma: no cover
plt.show()
return {'2d':fig1, '3d':fig2}
##############################################################################
#%% Colormaps
##############################################################################
__all__ += ['alpinecolormap', 'bicolormap', 'parulacolormap', 'turbocolormap', 'bandedcolormap', 'orangebluecolormap']
[docs]
def alpinecolormap(apply=False):
"""
This function generates a map based on ascending height. Based on data from
Kazakhstan.
**Test case**::
sc.colormapdemo('alpine')
**Usage example**::
import sciris as sc
import matplotlib.pyplot as plt
plt.imshow(np.random.randn(20,20), interpolation='none', cmap=sc.alpinecolormap())
Version: 2014aug06
"""
# Set parameters
water = np.array([3,18,59])/256.
desert = np.array([194,175,160*0.6])/256.
forest1 = np.array([61,86,46])/256.
forest2 = np.array([61,86,46])/256.*1.2
rock = np.array([119,111,109])/256.*1.3
snow = np.array([243,239,238])/256.
breaks = [0.0,0.5,0.7,0.8,0.9,1.0]
# Create dictionary
cdict = {'red': ((breaks[0], water[0], water[0]),
(breaks[1], desert[0], desert[0]),
(breaks[2], forest1[0], forest1[0]),
(breaks[3], forest2[0], forest2[0]),
(breaks[4], rock[0], rock[0]),
(breaks[5], snow[0], snow[0])),
'green': ((breaks[0], water[1], water[1]),
(breaks[1], desert[1], desert[1]),
(breaks[2], forest1[1], forest1[1]),
(breaks[3], forest2[1], forest2[1]),
(breaks[4], rock[1], rock[1]),
(breaks[5], snow[1], snow[1])),
'blue': ((breaks[0], water[2], water[2]),
(breaks[1], desert[2], desert[2]),
(breaks[2], forest1[2], forest1[2]),
(breaks[3], forest2[2], forest2[2]),
(breaks[4], rock[2], rock[2]),
(breaks[5], snow[2], snow[2]))}
# Make map
cmap = mpl.colors.LinearSegmentedColormap('alpine', cdict, 256)
if apply: # pragma: no cover
plt.set_cmap(cmap)
return cmap
[docs]
def bicolormap(gap=0.1, mingreen=0.2, redbluemix=0.5, epsilon=0.01, demo=False, apply=False):
"""
This function generators a two-color map, blue for negative, red for
positive changes, with grey in the middle. The input argument is how much
of a color gap there is between the red scale and the blue one.
Args:
gap (float): sets how big of a gap between red and blue color scales there is (0=no gap; 1=pure red and pure blue)
mingreen (float): how much green to include at the extremes of the red-blue color scale
redbluemix (float): how much red to mix with the blue and vice versa at the extremes of the scale
epsilon (float): what fraction of the colormap to make gray in the middle
demo (bool): whether to plot a demo bicolormap or not
apply (bool): whether to apply this colormap to the current figure
**Examples**::
sc.bicolormap(gap=0, mingreen=0, redbluemix=1, epsilon=0) # From pure red to pure blue with white in the middle
sc.bicolormap(gap=0, mingreen=0, redbluemix=0, epsilon=0.1) # Red -> yellow -> gray -> turquoise -> blue
sc.bicolormap(gap=0.3, mingreen=0.2, redbluemix=0, epsilon=0.01) # Red and blue with a sharp distinction between
Version: 2013sep13
"""
mng = mingreen # Minimum amount of green to add into the colors
mix = redbluemix # How much red to mix with the blue an vice versa
eps = epsilon # How much of the center of the colormap to make gray
omg = 1-gap # omg = one minus gap
cdict = {'red': ((0.00000, 0.0, 0.0),
(0.5-eps, mix, omg),
(0.50000, omg, omg),
(0.5+eps, omg, 1.0),
(1.00000, 1.0, 1.0)),
'green': ((0.00000, mng, mng),
(0.5-eps, omg, omg),
(0.50000, omg, omg),
(0.5+eps, omg, omg),
(1.00000, mng, mng)),
'blue': ((0.00000, 1.0, 1.0),
(0.5-eps, 1.0, omg),
(0.50000, omg, omg),
(0.5+eps, omg, mix),
(1.00000, 0.0, 0.0))}
cmap = mpl.colors.LinearSegmentedColormap('bi', cdict, 256)
if apply: # pragma: no cover
plt.set_cmap(cmap)
def demoplot(): # pragma: no cover
maps = []
maps.append(bicolormap()) # Default ,should work for most things
maps.append(bicolormap(gap=0,mingreen=0,redbluemix=1,epsilon=0)) # From pure red to pure blue with white in the middle
maps.append(bicolormap(gap=0,mingreen=0,redbluemix=0,epsilon=0.1)) # Red -> yellow -> gray -> turquoise -> blue
maps.append(bicolormap(gap=0.3,mingreen=0.2,redbluemix=0,epsilon=0.01)) # Red and blue with a sharp distinction between
nexamples=len(maps)
plt.figure(figsize=(5*nexamples, 4))
for m in range(nexamples):
plt.subplot(1, nexamples, m+1)
plt.imshow(np.random.rand(20,20), cmap=maps[m], interpolation='none')
plt.colorbar()
plt.show()
if demo: # pragma: no cover
demoplot()
return cmap
[docs]
def parulacolormap(apply=False):
"""
Create a map similar to Viridis, but brighter. Set apply=True to use
immediately.
**Demo and example**::
cmap = sc.parulacolormap()
sc.colormapdemo(cmap=cmap)
Version: 2019aug22
"""
data = [[0.2422,0.1504,0.6603], [0.2444,0.1534,0.6728], [0.2464,0.1569,0.6847], [0.2484,0.1607,0.6961], [0.2503,0.1648,0.7071], [0.2522,0.1689,0.7179], [0.2540,0.1732,0.7286], [0.2558,0.1773,0.7393],
[0.2576,0.1814,0.7501], [0.2594,0.1854,0.7610], [0.2611,0.1893,0.7719], [0.2628,0.1932,0.7828], [0.2645,0.1972,0.7937], [0.2661,0.2011,0.8043], [0.2676,0.2052,0.8148], [0.2691,0.2094,0.8249],
[0.2704,0.2138,0.8346], [0.2717,0.2184,0.8439], [0.2729,0.2231,0.8528], [0.2740,0.2280,0.8612], [0.2749,0.2330,0.8692], [0.2758,0.2382,0.8767], [0.2766,0.2435,0.8840], [0.2774,0.2489,0.8908],
[0.2781,0.2543,0.8973], [0.2788,0.2598,0.9035], [0.2794,0.2653,0.9094], [0.2798,0.2708,0.9150], [0.2802,0.2764,0.9204], [0.2806,0.2819,0.9255], [0.2809,0.2875,0.9305], [0.2811,0.2930,0.9352],
[0.2813,0.2985,0.9397], [0.2814,0.3040,0.9441], [0.2814,0.3095,0.9483], [0.2813,0.3150,0.9524], [0.2811,0.3204,0.9563], [0.2809,0.3259,0.9600], [0.2807,0.3313,0.9636], [0.2803,0.3367,0.9670],
[0.2798,0.3421,0.9702], [0.2791,0.3475,0.9733], [0.2784,0.3529,0.9763], [0.2776,0.3583,0.9791], [0.2766,0.3638,0.9817], [0.2754,0.3693,0.9840], [0.2741,0.3748,0.9862], [0.2726,0.3804,0.9881],
[0.2710,0.3860,0.9898], [0.2691,0.3916,0.9912], [0.2670,0.3973,0.9924], [0.2647,0.4030,0.9935], [0.2621,0.4088,0.9946], [0.2591,0.4145,0.9955], [0.2556,0.4203,0.9965], [0.2517,0.4261,0.9974],
[0.2473,0.4319,0.9983], [0.2424,0.4378,0.9991], [0.2369,0.4437,0.9996], [0.2311,0.4497,0.9995], [0.2250,0.4559,0.9985], [0.2189,0.4620,0.9968], [0.2128,0.4682,0.9948], [0.2066,0.4743,0.9926],
[0.2006,0.4803,0.9906], [0.1950,0.4861,0.9887], [0.1903,0.4919,0.9867], [0.1869,0.4975,0.9844], [0.1847,0.5030,0.9819], [0.1831,0.5084,0.9793], [0.1818,0.5138,0.9766], [0.1806,0.5191,0.9738],
[0.1795,0.5244,0.9709], [0.1785,0.5296,0.9677], [0.1778,0.5349,0.9641], [0.1773,0.5401,0.9602], [0.1768,0.5452,0.9560], [0.1764,0.5504,0.9516], [0.1755,0.5554,0.9473], [0.1740,0.5605,0.9432],
[0.1716,0.5655,0.9393], [0.1686,0.5705,0.9357], [0.1649,0.5755,0.9323], [0.1610,0.5805,0.9289], [0.1573,0.5854,0.9254], [0.1540,0.5902,0.9218], [0.1513,0.5950,0.9182], [0.1492,0.5997,0.9147],
[0.1475,0.6043,0.9113], [0.1461,0.6089,0.9080], [0.1446,0.6135,0.9050], [0.1429,0.6180,0.9022], [0.1408,0.6226,0.8998], [0.1383,0.6272,0.8975], [0.1354,0.6317,0.8953], [0.1321,0.6363,0.8932],
[0.1288,0.6408,0.8910], [0.1253,0.6453,0.8887], [0.1219,0.6497,0.8862], [0.1185,0.6541,0.8834], [0.1152,0.6584,0.8804], [0.1119,0.6627,0.8770], [0.1085,0.6669,0.8734], [0.1048,0.6710,0.8695],
[0.1009,0.6750,0.8653], [0.0964,0.6789,0.8609], [0.0914,0.6828,0.8562], [0.0855,0.6865,0.8513], [0.0789,0.6902,0.8462], [0.0713,0.6938,0.8409], [0.0628,0.6972,0.8355], [0.0535,0.7006,0.8299],
[0.0433,0.7039,0.8242], [0.0328,0.7071,0.8183], [0.0234,0.7103,0.8124], [0.0155,0.7133,0.8064], [0.0091,0.7163,0.8003], [0.0046,0.7192,0.7941], [0.0019,0.7220,0.7878], [0.0009,0.7248,0.7815],
[0.0018,0.7275,0.7752], [0.0046,0.7301,0.7688], [0.0094,0.7327,0.7623], [0.0162,0.7352,0.7558], [0.0253,0.7376,0.7492], [0.0369,0.7400,0.7426], [0.0504,0.7423,0.7359], [0.0638,0.7446,0.7292],
[0.0770,0.7468,0.7224], [0.0899,0.7489,0.7156], [0.1023,0.7510,0.7088], [0.1141,0.7531,0.7019], [0.1252,0.7552,0.6950], [0.1354,0.7572,0.6881], [0.1448,0.7593,0.6812], [0.1532,0.7614,0.6741],
[0.1609,0.7635,0.6671], [0.1678,0.7656,0.6599], [0.1741,0.7678,0.6527], [0.1799,0.7699,0.6454], [0.1853,0.7721,0.6379], [0.1905,0.7743,0.6303], [0.1954,0.7765,0.6225], [0.2003,0.7787,0.6146],
[0.2061,0.7808,0.6065], [0.2118,0.7828,0.5983], [0.2178,0.7849,0.5899], [0.2244,0.7869,0.5813], [0.2318,0.7887,0.5725], [0.2401,0.7905,0.5636], [0.2491,0.7922,0.5546], [0.2589,0.7937,0.5454],
[0.2695,0.7951,0.5360], [0.2809,0.7964,0.5266], [0.2929,0.7975,0.5170], [0.3052,0.7985,0.5074], [0.3176,0.7994,0.4975], [0.3301,0.8002,0.4876], [0.3424,0.8009,0.4774], [0.3548,0.8016,0.4669],
[0.3671,0.8021,0.4563], [0.3795,0.8026,0.4454], [0.3921,0.8029,0.4344], [0.4050,0.8031,0.4233], [0.4184,0.8030,0.4122], [0.4322,0.8028,0.4013], [0.4463,0.8024,0.3904], [0.4608,0.8018,0.3797],
[0.4753,0.8011,0.3691], [0.4899,0.8002,0.3586], [0.5044,0.7993,0.3480], [0.5187,0.7982,0.3374], [0.5329,0.7970,0.3267], [0.5470,0.7957,0.3159], [0.5609,0.7943,0.3050], [0.5748,0.7929,0.2941],
[0.5886,0.7913,0.2833], [0.6024,0.7896,0.2726], [0.6161,0.7878,0.2622], [0.6297,0.7859,0.2521], [0.6433,0.7839,0.2423], [0.6567,0.7818,0.2329], [0.6701,0.7796,0.2239], [0.6833,0.7773,0.2155],
[0.6963,0.7750,0.2075], [0.7091,0.7727,0.1998], [0.7218,0.7703,0.1924], [0.7344,0.7679,0.1852], [0.7468,0.7654,0.1782], [0.7590,0.7629,0.1717], [0.7710,0.7604,0.1658], [0.7829,0.7579,0.1608],
[0.7945,0.7554,0.1570], [0.8060,0.7529,0.1546], [0.8172,0.7505,0.1535], [0.8281,0.7481,0.1536], [0.8389,0.7457,0.1546], [0.8495,0.7435,0.1564], [0.8600,0.7413,0.1587], [0.8703,0.7392,0.1615],
[0.8804,0.7372,0.1650], [0.8903,0.7353,0.1695], [0.9000,0.7336,0.1749], [0.9093,0.7321,0.1815], [0.9184,0.7308,0.1890], [0.9272,0.7298,0.1973], [0.9357,0.7290,0.2061], [0.9440,0.7285,0.2151],
[0.9523,0.7284,0.2237], [0.9606,0.7285,0.2312], [0.9689,0.7292,0.2373], [0.9770,0.7304,0.2418], [0.9842,0.7330,0.2446], [0.9900,0.7365,0.2429], [0.9946,0.7407,0.2394], [0.9966,0.7458,0.2351],
[0.9971,0.7513,0.2309], [0.9972,0.7569,0.2267], [0.9971,0.7626,0.2224], [0.9969,0.7683,0.2181], [0.9966,0.7740,0.2138], [0.9962,0.7798,0.2095], [0.9957,0.7856,0.2053], [0.9949,0.7915,0.2012],
[0.9938,0.7974,0.1974], [0.9923,0.8034,0.1939], [0.9906,0.8095,0.1906], [0.9885,0.8156,0.1875], [0.9861,0.8218,0.1846], [0.9835,0.8280,0.1817], [0.9807,0.8342,0.1787], [0.9778,0.8404,0.1757],
[0.9748,0.8467,0.1726], [0.9720,0.8529,0.1695], [0.9694,0.8591,0.1665], [0.9671,0.8654,0.1636], [0.9651,0.8716,0.1608], [0.9634,0.8778,0.1582], [0.9619,0.8840,0.1557], [0.9608,0.8902,0.1532],
[0.9601,0.8963,0.1507], [0.9596,0.9023,0.1480], [0.9595,0.9084,0.1450], [0.9597,0.9143,0.1418], [0.9601,0.9203,0.1382], [0.9608,0.9262,0.1344], [0.9618,0.9320,0.1304], [0.9629,0.9379,0.1261],
[0.9642,0.9437,0.1216], [0.9657,0.9494,0.1168], [0.9674,0.9552,0.1116], [0.9692,0.9609,0.1061], [0.9711,0.9667,0.1001], [0.9730,0.9724,0.0938], [0.9749,0.9782,0.0872], [0.9769,0.9839,0.0805]]
cmap = mpl.colors.LinearSegmentedColormap.from_list('parula', data)
if apply: # pragma: no cover
plt.set_cmap(cmap)
return cmap
[docs]
def turbocolormap(apply=False):
"""
NOTE: as of Matplotlib 3.4.0, this colormap is included by default, and will
soon be removed from Sciris.
Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0
Author: Anton Mikhailov
Borrowed directly from https://gist.github.com/mikhailov-work/ee72ba4191942acecc03fe6da94fc73f, with thanks!
Create a map similar to Jet, but better. Set apply=True to use
immediately.
**Demo and example**::
cmap = sc.turbocolormap()
sc.colormapdemo(cmap=cmap)
Version: 2020mar20
"""
data = [[0.18995,0.07176,0.23217],[0.19483,0.08339,0.26149],[0.19956,0.09498,0.29024],[0.20415,0.10652,0.31844],[0.20860,0.11802,0.34607],[0.21291,0.12947,0.37314],[0.21708,0.14087,0.39964],[0.22111,0.15223,0.42558],
[0.22500,0.16354,0.45096],[0.22875,0.17481,0.47578],[0.23236,0.18603,0.50004],[0.23582,0.19720,0.52373],[0.23915,0.20833,0.54686],[0.24234,0.21941,0.56942],[0.24539,0.23044,0.59142],[0.24830,0.24143,0.61286],
[0.25107,0.25237,0.63374],[0.25369,0.26327,0.65406],[0.25618,0.27412,0.67381],[0.25853,0.28492,0.69300],[0.26074,0.29568,0.71162],[0.26280,0.30639,0.72968],[0.26473,0.31706,0.74718],[0.26652,0.32768,0.76412],
[0.26816,0.33825,0.78050],[0.26967,0.34878,0.79631],[0.27103,0.35926,0.81156],[0.27226,0.36970,0.82624],[0.27334,0.38008,0.84037],[0.27429,0.39043,0.85393],[0.27509,0.40072,0.86692],[0.27576,0.41097,0.87936],
[0.27628,0.42118,0.89123],[0.27667,0.43134,0.90254],[0.27691,0.44145,0.91328],[0.27701,0.45152,0.92347],[0.27698,0.46153,0.93309],[0.27680,0.47151,0.94214],[0.27648,0.48144,0.95064],[0.27603,0.49132,0.95857],
[0.27543,0.50115,0.96594],[0.27469,0.51094,0.97275],[0.27381,0.52069,0.97899],[0.27273,0.53040,0.98461],[0.27106,0.54015,0.98930],[0.26878,0.54995,0.99303],[0.26592,0.55979,0.99583],[0.26252,0.56967,0.99773],
[0.25862,0.57958,0.99876],[0.25425,0.58950,0.99896],[0.24946,0.59943,0.99835],[0.24427,0.60937,0.99697],[0.23874,0.61931,0.99485],[0.23288,0.62923,0.99202],[0.22676,0.63913,0.98851],[0.22039,0.64901,0.98436],
[0.21382,0.65886,0.97959],[0.20708,0.66866,0.97423],[0.20021,0.67842,0.96833],[0.19326,0.68812,0.96190],[0.18625,0.69775,0.95498],[0.17923,0.70732,0.94761],[0.17223,0.71680,0.93981],[0.16529,0.72620,0.93161],
[0.15844,0.73551,0.92305],[0.15173,0.74472,0.91416],[0.14519,0.75381,0.90496],[0.13886,0.76279,0.89550],[0.13278,0.77165,0.88580],[0.12698,0.78037,0.87590],[0.12151,0.78896,0.86581],[0.11639,0.79740,0.85559],
[0.11167,0.80569,0.84525],[0.10738,0.81381,0.83484],[0.10357,0.82177,0.82437],[0.10026,0.82955,0.81389],[0.09750,0.83714,0.80342],[0.09532,0.84455,0.79299],[0.09377,0.85175,0.78264],[0.09287,0.85875,0.77240],
[0.09267,0.86554,0.76230],[0.09320,0.87211,0.75237],[0.09451,0.87844,0.74265],[0.09662,0.88454,0.73316],[0.09958,0.89040,0.72393],[0.10342,0.89600,0.71500],[0.10815,0.90142,0.70599],[0.11374,0.90673,0.69651],
[0.12014,0.91193,0.68660],[0.12733,0.91701,0.67627],[0.13526,0.92197,0.66556],[0.14391,0.92680,0.65448],[0.15323,0.93151,0.64308],[0.16319,0.93609,0.63137],[0.17377,0.94053,0.61938],[0.18491,0.94484,0.60713],
[0.19659,0.94901,0.59466],[0.20877,0.95304,0.58199],[0.22142,0.95692,0.56914],[0.23449,0.96065,0.55614],[0.24797,0.96423,0.54303],[0.26180,0.96765,0.52981],[0.27597,0.97092,0.51653],[0.29042,0.97403,0.50321],
[0.30513,0.97697,0.48987],[0.32006,0.97974,0.47654],[0.33517,0.98234,0.46325],[0.35043,0.98477,0.45002],[0.36581,0.98702,0.43688],[0.38127,0.98909,0.42386],[0.39678,0.99098,0.41098],[0.41229,0.99268,0.39826],
[0.42778,0.99419,0.38575],[0.44321,0.99551,0.37345],[0.45854,0.99663,0.36140],[0.47375,0.99755,0.34963],[0.48879,0.99828,0.33816],[0.50362,0.99879,0.32701],[0.51822,0.99910,0.31622],[0.53255,0.99919,0.30581],
[0.54658,0.99907,0.29581],[0.56026,0.99873,0.28623],[0.57357,0.99817,0.27712],[0.58646,0.99739,0.26849],[0.59891,0.99638,0.26038],[0.61088,0.99514,0.25280],[0.62233,0.99366,0.24579],[0.63323,0.99195,0.23937],
[0.64362,0.98999,0.23356],[0.65394,0.98775,0.22835],[0.66428,0.98524,0.22370],[0.67462,0.98246,0.21960],[0.68494,0.97941,0.21602],[0.69525,0.97610,0.21294],[0.70553,0.97255,0.21032],[0.71577,0.96875,0.20815],
[0.72596,0.96470,0.20640],[0.73610,0.96043,0.20504],[0.74617,0.95593,0.20406],[0.75617,0.95121,0.20343],[0.76608,0.94627,0.20311],[0.77591,0.94113,0.20310],[0.78563,0.93579,0.20336],[0.79524,0.93025,0.20386],
[0.80473,0.92452,0.20459],[0.81410,0.91861,0.20552],[0.82333,0.91253,0.20663],[0.83241,0.90627,0.20788],[0.84133,0.89986,0.20926],[0.85010,0.89328,0.21074],[0.85868,0.88655,0.21230],[0.86709,0.87968,0.21391],
[0.87530,0.87267,0.21555],[0.88331,0.86553,0.21719],[0.89112,0.85826,0.21880],[0.89870,0.85087,0.22038],[0.90605,0.84337,0.22188],[0.91317,0.83576,0.22328],[0.92004,0.82806,0.22456],[0.92666,0.82025,0.22570],
[0.93301,0.81236,0.22667],[0.93909,0.80439,0.22744],[0.94489,0.79634,0.22800],[0.95039,0.78823,0.22831],[0.95560,0.78005,0.22836],[0.96049,0.77181,0.22811],[0.96507,0.76352,0.22754],[0.96931,0.75519,0.22663],
[0.97323,0.74682,0.22536],[0.97679,0.73842,0.22369],[0.98000,0.73000,0.22161],[0.98289,0.72140,0.21918],[0.98549,0.71250,0.21650],[0.98781,0.70330,0.21358],[0.98986,0.69382,0.21043],[0.99163,0.68408,0.20706],
[0.99314,0.67408,0.20348],[0.99438,0.66386,0.19971],[0.99535,0.65341,0.19577],[0.99607,0.64277,0.19165],[0.99654,0.63193,0.18738],[0.99675,0.62093,0.18297],[0.99672,0.60977,0.17842],[0.99644,0.59846,0.17376],
[0.99593,0.58703,0.16899],[0.99517,0.57549,0.16412],[0.99419,0.56386,0.15918],[0.99297,0.55214,0.15417],[0.99153,0.54036,0.14910],[0.98987,0.52854,0.14398],[0.98799,0.51667,0.13883],[0.98590,0.50479,0.13367],
[0.98360,0.49291,0.12849],[0.98108,0.48104,0.12332],[0.97837,0.46920,0.11817],[0.97545,0.45740,0.11305],[0.97234,0.44565,0.10797],[0.96904,0.43399,0.10294],[0.96555,0.42241,0.09798],[0.96187,0.41093,0.09310],
[0.95801,0.39958,0.08831],[0.95398,0.38836,0.08362],[0.94977,0.37729,0.07905],[0.94538,0.36638,0.07461],[0.94084,0.35566,0.07031],[0.93612,0.34513,0.06616],[0.93125,0.33482,0.06218],[0.92623,0.32473,0.05837],
[0.92105,0.31489,0.05475],[0.91572,0.30530,0.05134],[0.91024,0.29599,0.04814],[0.90463,0.28696,0.04516],[0.89888,0.27824,0.04243],[0.89298,0.26981,0.03993],[0.88691,0.26152,0.03753],[0.88066,0.25334,0.03521],
[0.87422,0.24526,0.03297],[0.86760,0.23730,0.03082],[0.86079,0.22945,0.02875],[0.85380,0.22170,0.02677],[0.84662,0.21407,0.02487],[0.83926,0.20654,0.02305],[0.83172,0.19912,0.02131],[0.82399,0.19182,0.01966],
[0.81608,0.18462,0.01809],[0.80799,0.17753,0.01660],[0.79971,0.17055,0.01520],[0.79125,0.16368,0.01387],[0.78260,0.15693,0.01264],[0.77377,0.15028,0.01148],[0.76476,0.14374,0.01041],[0.75556,0.13731,0.00942],
[0.74617,0.13098,0.00851],[0.73661,0.12477,0.00769],[0.72686,0.11867,0.00695],[0.71692,0.11268,0.00629],[0.70680,0.10680,0.00571],[0.69650,0.10102,0.00522],[0.68602,0.09536,0.00481],[0.67535,0.08980,0.00449],
[0.66449,0.08436,0.00424],[0.65345,0.07902,0.00408],[0.64223,0.07380,0.00401],[0.63082,0.06868,0.00401],[0.61923,0.06367,0.00410],[0.60746,0.05878,0.00427],[0.59550,0.05399,0.00453],[0.58336,0.04931,0.00486],
[0.57103,0.04474,0.00529],[0.55852,0.04028,0.00579],[0.54583,0.03593,0.00638],[0.53295,0.03169,0.00705],[0.51989,0.02756,0.00780],[0.50664,0.02354,0.00863],[0.49321,0.01963,0.00955],[0.47960,0.01583,0.01055]]
cmap = mpl.colors.LinearSegmentedColormap.from_list('turbo', data)
if apply: # pragma: no cover
plt.set_cmap(cmap)
return cmap
[docs]
def bandedcolormap(minvalue=None, minsaturation=None, hueshift=None, saturationscale=None, npts=None, apply=False):
"""
Map colors onto bands of hue and saturation, with lightness mapped linearly.
Unlike most colormaps, this colormap does not aim to be percentually uniform,
but rather aims to make it easy to relate colors to as-exact-as-possible numbers
(while still maintaining a semblance of overall trend from low to high).
**Demo and example**::
cmap = sc.bandedcolormap(minvalue=0, minsaturation=0)
sc.colormapdemo(cmap=cmap)
Version: 2019aug22
"""
# Set parameters
if minvalue is None: minvalue = 0.1
if hueshift is None: hueshift = 0.8
if minsaturation is None: minsaturation = 0.5
if saturationscale is None: saturationscale = 4.3
if npts is None: npts = 256
# Calculate
x = np.linspace(0, np.pi, npts)
hsv = np.zeros((npts, 3))
hsv[:,2] = np.sqrt(np.linspace(minvalue,1,npts)) # Value: map linearly
hsv[:,0] = np.sin(x+hueshift)**2 # Hue: a big sine wave
hsv[:,1] = minsaturation+(1-minsaturation)*np.sin(saturationscale*x)**2 # Saturation: a small sine wave
data = hsv2rgb(hsv)
# Create and use
cmap = mpl.colors.LinearSegmentedColormap.from_list('banded', data)
if apply: # pragma: no cover
plt.set_cmap(cmap)
return cmap
[docs]
def orangebluecolormap(apply=False):
"""
Create an orange-blue colormap; most like RdYlBu but more pleasing. Created
by Prashanth Selvaraj.
**Demo and example**::
cmap = sc.orangebluecolormap()
sc.colormapdemo(cmap=cmap)
| *New in version 1.0.0.*
"""
bottom = plt.get_cmap('Oranges', 128)
top = plt.get_cmap('Blues_r', 128)
x = np.linspace(0, 1, 128)
data = np.vstack((top(x), bottom(x)))
# Create and use
cmap = mpl.colors.LinearSegmentedColormap.from_list('orangeblue', data)
if apply: # pragma: no cover
plt.set_cmap(cmap)
return cmap
# Register colormaps -- under their original names as well as with the "sciris-" prefix
try: # Regression support for Matplotlib
register_func = mpl.colormaps.register # Matplotlib >=3.5
except AttributeError:
register_func = mpl.cm.register_cmap # Matplotlib <=3.4
existing = plt.colormaps()
colormap_map = dict(
alpine = alpinecolormap(),
parula = parulacolormap(),
banded = bandedcolormap(),
bi = bicolormap(),
orangeblue = orangebluecolormap(),
turbo = turbocolormap(),
)
for basename,cmap in colormap_map.items():
sc_name = f'sciris-{basename}'
for name in [basename, sc_name]:
if name not in existing: # Avoid re-registering already registered colormaps
register_func(cmap=cmap, name=name)