mirror of
https://github.com/Estom/notes.git
synced 2026-04-01 18:11:42 +08:00
matplotlib & pandas
This commit is contained in:
123
Python/matplotlab/colors/colorbar_only.md
Normal file
123
Python/matplotlab/colors/colorbar_only.md
Normal file
@@ -0,0 +1,123 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Customized Colorbars Tutorial
|
||||
|
||||
This tutorial shows how to build colorbars without an attached plot.
|
||||
|
||||
## Customized Colorbars
|
||||
|
||||
[``ColorbarBase``](https://matplotlib.orgapi/colorbar_api.html#matplotlib.colorbar.ColorbarBase) puts a colorbar in a specified axes,
|
||||
and can make a colorbar for a given colormap; it does not need a mappable
|
||||
object like an image. In this tutorial we will explore what can be done with
|
||||
standalone colorbar.
|
||||
|
||||
### Basic continuous colorbar
|
||||
|
||||
Set the colormap and norm to correspond to the data for which the colorbar
|
||||
will be used. Then create the colorbar by calling
|
||||
[``ColorbarBase``](https://matplotlib.orgapi/colorbar_api.html#matplotlib.colorbar.ColorbarBase) and specify axis, colormap, norm
|
||||
and orientation as parameters. Here we create a basic continuous colorbar
|
||||
with ticks and labels. For more information see the
|
||||
[``colorbar``](https://matplotlib.orgapi/colorbar_api.html#module-matplotlib.colorbar) API.
|
||||
|
||||
``` python
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib as mpl
|
||||
|
||||
fig, ax = plt.subplots(figsize=(6, 1))
|
||||
fig.subplots_adjust(bottom=0.5)
|
||||
|
||||
cmap = mpl.cm.cool
|
||||
norm = mpl.colors.Normalize(vmin=5, vmax=10)
|
||||
|
||||
cb1 = mpl.colorbar.ColorbarBase(ax, cmap=cmap,
|
||||
norm=norm,
|
||||
orientation='horizontal')
|
||||
cb1.set_label('Some Units')
|
||||
fig.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Discrete intervals colorbar
|
||||
|
||||
The second example illustrates the use of a
|
||||
[``ListedColormap``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.ListedColormap.html#matplotlib.colors.ListedColormap) which generates a colormap from a
|
||||
set of listed colors, ``colors.BoundaryNorm()`` which generates a colormap
|
||||
index based on discrete intervals and extended ends to show the "over" and
|
||||
"under" value colors. Over and under are used to display data outside of the
|
||||
normalized [0,1] range. Here we pass colors as gray shades as a string
|
||||
encoding a float in the 0-1 range.
|
||||
|
||||
If a [``ListedColormap``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.ListedColormap.html#matplotlib.colors.ListedColormap) is used, the length of the
|
||||
bounds array must be one greater than the length of the color list. The
|
||||
bounds must be monotonically increasing.
|
||||
|
||||
This time we pass some more arguments in addition to previous arguments to
|
||||
[``ColorbarBase``](https://matplotlib.orgapi/colorbar_api.html#matplotlib.colorbar.ColorbarBase). For the out-of-range values to
|
||||
display on the colorbar, we have to use the *extend* keyword argument. To use
|
||||
*extend*, you must specify two extra boundaries. Finally spacing argument
|
||||
ensures that intervals are shown on colorbar proportionally.
|
||||
|
||||
``` python
|
||||
fig, ax = plt.subplots(figsize=(6, 1))
|
||||
fig.subplots_adjust(bottom=0.5)
|
||||
|
||||
cmap = mpl.colors.ListedColormap(['red', 'green', 'blue', 'cyan'])
|
||||
cmap.set_over('0.25')
|
||||
cmap.set_under('0.75')
|
||||
|
||||
bounds = [1, 2, 4, 7, 8]
|
||||
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
|
||||
cb2 = mpl.colorbar.ColorbarBase(ax, cmap=cmap,
|
||||
norm=norm,
|
||||
boundaries=[0] + bounds + [13],
|
||||
extend='both',
|
||||
ticks=bounds,
|
||||
spacing='proportional',
|
||||
orientation='horizontal')
|
||||
cb2.set_label('Discrete intervals, some other units')
|
||||
fig.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Colorbar with custom extension lengths
|
||||
|
||||
Here we illustrate the use of custom length colorbar extensions, used on a
|
||||
colorbar with discrete intervals. To make the length of each extension the
|
||||
same as the length of the interior colors, use ``extendfrac='auto'``.
|
||||
|
||||
``` python
|
||||
fig, ax = plt.subplots(figsize=(6, 1))
|
||||
fig.subplots_adjust(bottom=0.5)
|
||||
|
||||
cmap = mpl.colors.ListedColormap(['royalblue', 'cyan',
|
||||
'yellow', 'orange'])
|
||||
cmap.set_over('red')
|
||||
cmap.set_under('blue')
|
||||
|
||||
bounds = [-1.0, -0.5, 0.0, 0.5, 1.0]
|
||||
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
|
||||
cb3 = mpl.colorbar.ColorbarBase(ax, cmap=cmap,
|
||||
norm=norm,
|
||||
boundaries=[-10] + bounds + [10],
|
||||
extend='both',
|
||||
extendfrac='auto',
|
||||
ticks=bounds,
|
||||
spacing='uniform',
|
||||
orientation='horizontal')
|
||||
cb3.set_label('Custom extension lengths, some other units')
|
||||
fig.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: colorbar_only.py](https://matplotlib.org/_downloads/23690f47313380b801750e3adc4c317e/colorbar_only.py)
|
||||
- [Download Jupyter notebook: colorbar_only.ipynb](https://matplotlib.org/_downloads/4d3eb6ad2b03a5eb988f576ea050f104/colorbar_only.ipynb)
|
||||
|
||||
311
Python/matplotlab/colors/colormap-manipulation.md
Normal file
311
Python/matplotlab/colors/colormap-manipulation.md
Normal file
@@ -0,0 +1,311 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Creating Colormaps in Matplotlib
|
||||
|
||||
Matplotlib has a number of built-in colormaps accessible via
|
||||
[``matplotlib.cm.get_cmap``](https://matplotlib.orgapi/cm_api.html#matplotlib.cm.get_cmap). There are also external libraries like
|
||||
[palettable](https://jiffyclub.github.io/palettable/) that have many extra colormaps.
|
||||
|
||||
However, we often want to create or manipulate colormaps in Matplotlib.
|
||||
This can be done using the class [``ListedColormap``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.ListedColormap.html#matplotlib.colors.ListedColormap) and a Nx4 numpy array of
|
||||
values between 0 and 1 to represent the RGBA values of the colormap. There
|
||||
is also a [``LinearSegmentedColormap``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.LinearSegmentedColormap.html#matplotlib.colors.LinearSegmentedColormap) class that allows colormaps to be
|
||||
specified with a few anchor points defining segments, and linearly
|
||||
interpolating between the anchor points.
|
||||
|
||||
## Getting colormaps and accessing their values
|
||||
|
||||
First, getting a named colormap, most of which are listed in
|
||||
[Choosing Colormaps in Matplotlib](colormaps.html) requires the use of
|
||||
[``matplotlib.cm.get_cmap``](https://matplotlib.orgapi/cm_api.html#matplotlib.cm.get_cmap), which returns a
|
||||
[``matplotlib.colors.ListedColormap``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.ListedColormap.html#matplotlib.colors.ListedColormap) object. The second argument gives
|
||||
the size of the list of colors used to define the colormap, and below we
|
||||
use a modest value of 12 so there are not a lot of values to look at.
|
||||
|
||||
``` python
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import cm
|
||||
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
|
||||
|
||||
viridis = cm.get_cmap('viridis', 12)
|
||||
print(viridis)
|
||||
```
|
||||
|
||||
Out:
|
||||
|
||||
```
|
||||
<matplotlib.colors.ListedColormap object at 0x7f7f3c724390>
|
||||
```
|
||||
|
||||
The object ``viridis`` is a callable, that when passed a float between
|
||||
0 and 1 returns an RGBA value from the colormap:
|
||||
|
||||
``` python
|
||||
print(viridis(0.56))
|
||||
```
|
||||
|
||||
Out:
|
||||
|
||||
```
|
||||
(0.119512, 0.607464, 0.540218, 1.0)
|
||||
```
|
||||
|
||||
The list of colors that comprise the colormap can be directly accessed using
|
||||
the ``colors`` property,
|
||||
or it can be accessed indirectly by calling ``viridis`` with an array
|
||||
of values matching the length of the colormap. Note that the returned list
|
||||
is in the form of an RGBA Nx4 array, where N is the length of the colormap.
|
||||
|
||||
``` python
|
||||
print('viridis.colors', viridis.colors)
|
||||
print('viridis(range(12))', viridis(range(12)))
|
||||
print('viridis(np.linspace(0, 1, 12))', viridis(np.linspace(0, 1, 12)))
|
||||
```
|
||||
|
||||
Out:
|
||||
|
||||
```
|
||||
viridis.colors [[0.267004 0.004874 0.329415 1. ]
|
||||
[0.283072 0.130895 0.449241 1. ]
|
||||
[0.262138 0.242286 0.520837 1. ]
|
||||
[0.220057 0.343307 0.549413 1. ]
|
||||
[0.177423 0.437527 0.557565 1. ]
|
||||
[0.143343 0.522773 0.556295 1. ]
|
||||
[0.119512 0.607464 0.540218 1. ]
|
||||
[0.166383 0.690856 0.496502 1. ]
|
||||
[0.319809 0.770914 0.411152 1. ]
|
||||
[0.525776 0.833491 0.288127 1. ]
|
||||
[0.762373 0.876424 0.137064 1. ]
|
||||
[0.993248 0.906157 0.143936 1. ]]
|
||||
viridis(range(12)) [[0.267004 0.004874 0.329415 1. ]
|
||||
[0.283072 0.130895 0.449241 1. ]
|
||||
[0.262138 0.242286 0.520837 1. ]
|
||||
[0.220057 0.343307 0.549413 1. ]
|
||||
[0.177423 0.437527 0.557565 1. ]
|
||||
[0.143343 0.522773 0.556295 1. ]
|
||||
[0.119512 0.607464 0.540218 1. ]
|
||||
[0.166383 0.690856 0.496502 1. ]
|
||||
[0.319809 0.770914 0.411152 1. ]
|
||||
[0.525776 0.833491 0.288127 1. ]
|
||||
[0.762373 0.876424 0.137064 1. ]
|
||||
[0.993248 0.906157 0.143936 1. ]]
|
||||
viridis(np.linspace(0, 1, 12)) [[0.267004 0.004874 0.329415 1. ]
|
||||
[0.283072 0.130895 0.449241 1. ]
|
||||
[0.262138 0.242286 0.520837 1. ]
|
||||
[0.220057 0.343307 0.549413 1. ]
|
||||
[0.177423 0.437527 0.557565 1. ]
|
||||
[0.143343 0.522773 0.556295 1. ]
|
||||
[0.119512 0.607464 0.540218 1. ]
|
||||
[0.166383 0.690856 0.496502 1. ]
|
||||
[0.319809 0.770914 0.411152 1. ]
|
||||
[0.525776 0.833491 0.288127 1. ]
|
||||
[0.762373 0.876424 0.137064 1. ]
|
||||
[0.993248 0.906157 0.143936 1. ]]
|
||||
```
|
||||
|
||||
The colormap is a lookup table, so "oversampling" the colormap returns
|
||||
nearest-neighbor interpolation (note the repeated colors in the list below)
|
||||
|
||||
``` python
|
||||
print('viridis(np.linspace(0, 1, 15))', viridis(np.linspace(0, 1, 15)))
|
||||
```
|
||||
|
||||
Out:
|
||||
|
||||
```
|
||||
viridis(np.linspace(0, 1, 15)) [[0.267004 0.004874 0.329415 1. ]
|
||||
[0.267004 0.004874 0.329415 1. ]
|
||||
[0.283072 0.130895 0.449241 1. ]
|
||||
[0.262138 0.242286 0.520837 1. ]
|
||||
[0.220057 0.343307 0.549413 1. ]
|
||||
[0.177423 0.437527 0.557565 1. ]
|
||||
[0.143343 0.522773 0.556295 1. ]
|
||||
[0.119512 0.607464 0.540218 1. ]
|
||||
[0.119512 0.607464 0.540218 1. ]
|
||||
[0.166383 0.690856 0.496502 1. ]
|
||||
[0.319809 0.770914 0.411152 1. ]
|
||||
[0.525776 0.833491 0.288127 1. ]
|
||||
[0.762373 0.876424 0.137064 1. ]
|
||||
[0.993248 0.906157 0.143936 1. ]
|
||||
[0.993248 0.906157 0.143936 1. ]]
|
||||
```
|
||||
|
||||
## Creating listed colormaps
|
||||
|
||||
This is essential the inverse operation of the above where we supply a
|
||||
Nx4 numpy array with all values between 0 and 1,
|
||||
to [``ListedColormap``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.ListedColormap.html#matplotlib.colors.ListedColormap) to make a new colormap. This means that
|
||||
any numpy operations that we can do on a Nx4 array make carpentry of
|
||||
new colormaps from existing colormaps quite straight forward.
|
||||
|
||||
Suppose we want to make the first 25 entries of a 256-length "viridis"
|
||||
colormap pink for some reason:
|
||||
|
||||
``` python
|
||||
viridis = cm.get_cmap('viridis', 256)
|
||||
newcolors = viridis(np.linspace(0, 1, 256))
|
||||
pink = np.array([248/256, 24/256, 148/256, 1])
|
||||
newcolors[:25, :] = pink
|
||||
newcmp = ListedColormap(newcolors)
|
||||
|
||||
|
||||
def plot_examples(cms):
|
||||
"""
|
||||
helper function to plot two colormaps
|
||||
"""
|
||||
np.random.seed(19680801)
|
||||
data = np.random.randn(30, 30)
|
||||
|
||||
fig, axs = plt.subplots(1, 2, figsize=(6, 3), constrained_layout=True)
|
||||
for [ax, cmap] in zip(axs, cms):
|
||||
psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4)
|
||||
fig.colorbar(psm, ax=ax)
|
||||
plt.show()
|
||||
|
||||
plot_examples([viridis, newcmp])
|
||||
```
|
||||
|
||||

|
||||
|
||||
We can easily reduce the dynamic range of a colormap; here we choose the
|
||||
middle 0.5 of the colormap. However, we need to interpolate from a larger
|
||||
colormap, otherwise the new colormap will have repeated values.
|
||||
|
||||
``` python
|
||||
viridisBig = cm.get_cmap('viridis', 512)
|
||||
newcmp = ListedColormap(viridisBig(np.linspace(0.25, 0.75, 256)))
|
||||
plot_examples([viridis, newcmp])
|
||||
```
|
||||
|
||||

|
||||
|
||||
and we can easily concatenate two colormaps:
|
||||
|
||||
``` python
|
||||
top = cm.get_cmap('Oranges_r', 128)
|
||||
bottom = cm.get_cmap('Blues', 128)
|
||||
|
||||
newcolors = np.vstack((top(np.linspace(0, 1, 128)),
|
||||
bottom(np.linspace(0, 1, 128))))
|
||||
newcmp = ListedColormap(newcolors, name='OrangeBlue')
|
||||
plot_examples([viridis, newcmp])
|
||||
```
|
||||
|
||||

|
||||
|
||||
Of course we need not start from a named colormap, we just need to create
|
||||
the Nx4 array to pass to [``ListedColormap``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.ListedColormap.html#matplotlib.colors.ListedColormap). Here we create a
|
||||
brown colormap that goes to white....
|
||||
|
||||
``` python
|
||||
N = 256
|
||||
vals = np.ones((N, 4))
|
||||
vals[:, 0] = np.linspace(90/256, 1, N)
|
||||
vals[:, 1] = np.linspace(39/256, 1, N)
|
||||
vals[:, 2] = np.linspace(41/256, 1, N)
|
||||
newcmp = ListedColormap(vals)
|
||||
plot_examples([viridis, newcmp])
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Creating linear segmented colormaps
|
||||
|
||||
[``LinearSegmentedColormap``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.LinearSegmentedColormap.html#matplotlib.colors.LinearSegmentedColormap) class specifies colormaps using anchor points
|
||||
between which RGB(A) values are interpolated.
|
||||
|
||||
The format to specify these colormaps allows discontinuities at the anchor
|
||||
points. Each anchor point is specified as a row in a matrix of the
|
||||
form ``[x[i] yleft[i] yright[i]]``, where ``x[i]`` is the anchor, and
|
||||
``yleft[i]`` and ``yright[i]`` are the values of the color on either
|
||||
side of the anchor point.
|
||||
|
||||
If there are no discontinuities, then ``yleft[i]=yright[i]``:
|
||||
|
||||
``` python
|
||||
cdict = {'red': [[0.0, 0.0, 0.0],
|
||||
[0.5, 1.0, 1.0],
|
||||
[1.0, 1.0, 1.0]],
|
||||
'green': [[0.0, 0.0, 0.0],
|
||||
[0.25, 0.0, 0.0],
|
||||
[0.75, 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, 1.0, 1.0]]}
|
||||
|
||||
|
||||
def plot_linearmap(cdict):
|
||||
newcmp = LinearSegmentedColormap('testCmap', segmentdata=cdict, N=256)
|
||||
rgba = newcmp(np.linspace(0, 1, 256))
|
||||
fig, ax = plt.subplots(figsize=(4, 3), constrained_layout=True)
|
||||
col = ['r', 'g', 'b']
|
||||
for xx in [0.25, 0.5, 0.75]:
|
||||
ax.axvline(xx, color='0.7', linestyle='--')
|
||||
for i in range(3):
|
||||
ax.plot(np.arange(256)/256, rgba[:, i], color=col[i])
|
||||
ax.set_xlabel('index')
|
||||
ax.set_ylabel('RGB')
|
||||
plt.show()
|
||||
|
||||
plot_linearmap(cdict)
|
||||
```
|
||||
|
||||

|
||||
|
||||
In order to make a discontinuity at an anchor point, the third column is
|
||||
different than the second. The matrix for each of "red", "green", "blue",
|
||||
and optionally "alpha" is set up as:
|
||||
|
||||
``` python
|
||||
cdict['red'] = [...
|
||||
[x[i] yleft[i] yright[i]],
|
||||
[x[i+1] yleft[i+1] yright[i+1]],
|
||||
...]
|
||||
```
|
||||
|
||||
and for values passed to the colormap between ``x[i]`` and ``x[i+1]``,
|
||||
the interpolation is between ``yright[i]`` and ``yleft[i+1]``.
|
||||
|
||||
In the example below there is a discontinuity in red at 0.5. The
|
||||
interpolation between 0 and 0.5 goes from 0.3 to 1, and between 0.5 and 1
|
||||
it goes from 0.9 to 1. Note that red[0, 1], and red[2, 2] are both
|
||||
superfluous to the interpolation because red[0, 1] is the value to the
|
||||
left of 0, and red[2, 2] is the value to the right of 1.0.
|
||||
|
||||
``` python
|
||||
cdict['red'] = [[0.0, 0.0, 0.3],
|
||||
[0.5, 1.0, 0.9],
|
||||
[1.0, 1.0, 1.0]]
|
||||
plot_linearmap(cdict)
|
||||
```
|
||||
|
||||

|
||||
|
||||
### References
|
||||
|
||||
The use of the following functions, methods, classes and modules is shown
|
||||
in this example:
|
||||
|
||||
``` python
|
||||
import matplotlib
|
||||
matplotlib.axes.Axes.pcolormesh
|
||||
matplotlib.figure.Figure.colorbar
|
||||
matplotlib.colors
|
||||
matplotlib.colors.LinearSegmentedColormap
|
||||
matplotlib.colors.ListedColormap
|
||||
matplotlib.cm
|
||||
matplotlib.cm.get_cmap
|
||||
```
|
||||
|
||||
**Total running time of the script:** ( 0 minutes 2.220 seconds)
|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: colormap-manipulation.py](https://matplotlib.org/_downloads/f55e73a6ac8441fd68270d3c6f2a7c7c/colormap-manipulation.py)
|
||||
- [Download Jupyter notebook: colormap-manipulation.ipynb](https://matplotlib.org/_downloads/fd9acfdbb45f341d3bb04199f0868a38/colormap-manipulation.ipynb)
|
||||
|
||||
281
Python/matplotlab/colors/colormapnorms.md
Normal file
281
Python/matplotlab/colors/colormapnorms.md
Normal file
@@ -0,0 +1,281 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Colormap Normalization
|
||||
|
||||
Objects that use colormaps by default linearly map the colors in the
|
||||
colormap from data values *vmin* to *vmax*. For example:
|
||||
|
||||
``` python
|
||||
pcm = ax.pcolormesh(x, y, Z, vmin=-1., vmax=1., cmap='RdBu_r')
|
||||
```
|
||||
|
||||
will map the data in *Z* linearly from -1 to +1, so *Z=0* will
|
||||
give a color at the center of the colormap *RdBu_r* (white in this
|
||||
case).
|
||||
|
||||
Matplotlib does this mapping in two steps, with a normalization from
|
||||
the input data to [0, 1] occurring first, and then mapping onto the
|
||||
indices in the colormap. Normalizations are classes defined in the
|
||||
[``matplotlib.colors()``](https://matplotlib.orgapi/colors_api.html#module-matplotlib.colors) module. The default, linear normalization
|
||||
is [``matplotlib.colors.Normalize()``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.Normalize.html#matplotlib.colors.Normalize).
|
||||
|
||||
Artists that map data to color pass the arguments *vmin* and *vmax* to
|
||||
construct a [``matplotlib.colors.Normalize()``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.Normalize.html#matplotlib.colors.Normalize) instance, then call it:
|
||||
|
||||
``` python
|
||||
In [1]: import matplotlib as mpl
|
||||
|
||||
In [2]: norm = mpl.colors.Normalize(vmin=-1.,vmax=1.)
|
||||
|
||||
In [3]: norm(0.)
|
||||
Out[3]: 0.5
|
||||
```
|
||||
|
||||
However, there are sometimes cases where it is useful to map data to
|
||||
colormaps in a non-linear fashion.
|
||||
|
||||
## Logarithmic
|
||||
|
||||
One of the most common transformations is to plot data by taking its logarithm
|
||||
(to the base-10). This transformation is useful to display changes across
|
||||
disparate scales. Using [``colors.LogNorm``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.LogNorm.html#matplotlib.colors.LogNorm) normalizes the data via
|
||||
\(log_{10}\). In the example below, there are two bumps, one much smaller
|
||||
than the other. Using [``colors.LogNorm``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.LogNorm.html#matplotlib.colors.LogNorm), the shape and location of each bump
|
||||
can clearly be seen:
|
||||
|
||||
``` python
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.colors as colors
|
||||
import matplotlib.cbook as cbook
|
||||
|
||||
N = 100
|
||||
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
|
||||
|
||||
# A low hump with a spike coming out of the top right. Needs to have
|
||||
# z/colour axis on a log scale so we see both hump and spike. linear
|
||||
# scale only shows the spike.
|
||||
Z1 = np.exp(-(X)**2 - (Y)**2)
|
||||
Z2 = np.exp(-(X * 10)**2 - (Y * 10)**2)
|
||||
Z = Z1 + 50 * Z2
|
||||
|
||||
fig, ax = plt.subplots(2, 1)
|
||||
|
||||
pcm = ax[0].pcolor(X, Y, Z,
|
||||
norm=colors.LogNorm(vmin=Z.min(), vmax=Z.max()),
|
||||
cmap='PuBu_r')
|
||||
fig.colorbar(pcm, ax=ax[0], extend='max')
|
||||
|
||||
pcm = ax[1].pcolor(X, Y, Z, cmap='PuBu_r')
|
||||
fig.colorbar(pcm, ax=ax[1], extend='max')
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Symmetric logarithmic
|
||||
|
||||
Similarly, it sometimes happens that there is data that is positive
|
||||
and negative, but we would still like a logarithmic scaling applied to
|
||||
both. In this case, the negative numbers are also scaled
|
||||
logarithmically, and mapped to smaller numbers; e.g., if ``vmin=-vmax``,
|
||||
then they the negative numbers are mapped from 0 to 0.5 and the
|
||||
positive from 0.5 to 1.
|
||||
|
||||
Since the logarithm of values close to zero tends toward infinity, a
|
||||
small range around zero needs to be mapped linearly. The parameter
|
||||
*linthresh* allows the user to specify the size of this range
|
||||
(-*linthresh*, *linthresh*). The size of this range in the colormap is
|
||||
set by *linscale*. When *linscale* == 1.0 (the default), the space used
|
||||
for the positive and negative halves of the linear range will be equal
|
||||
to one decade in the logarithmic range.
|
||||
|
||||
``` python
|
||||
N = 100
|
||||
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
|
||||
Z1 = np.exp(-X**2 - Y**2)
|
||||
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
|
||||
Z = (Z1 - Z2) * 2
|
||||
|
||||
fig, ax = plt.subplots(2, 1)
|
||||
|
||||
pcm = ax[0].pcolormesh(X, Y, Z,
|
||||
norm=colors.SymLogNorm(linthresh=0.03, linscale=0.03,
|
||||
vmin=-1.0, vmax=1.0),
|
||||
cmap='RdBu_r')
|
||||
fig.colorbar(pcm, ax=ax[0], extend='both')
|
||||
|
||||
pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', vmin=-np.max(Z))
|
||||
fig.colorbar(pcm, ax=ax[1], extend='both')
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Power-law
|
||||
|
||||
Sometimes it is useful to remap the colors onto a power-law
|
||||
relationship (i.e. \(y=x^{\gamma}\), where \(\gamma\) is the
|
||||
power). For this we use the ``colors.PowerNorm()``. It takes as an
|
||||
argument *gamma* (*gamma* == 1.0 will just yield the default linear
|
||||
normalization):
|
||||
|
||||
::: tip Note
|
||||
|
||||
There should probably be a good reason for plotting the data using
|
||||
this type of transformation. Technical viewers are used to linear
|
||||
and logarithmic axes and data transformations. Power laws are less
|
||||
common, and viewers should explicitly be made aware that they have
|
||||
been used.
|
||||
|
||||
:::
|
||||
|
||||
``` python
|
||||
N = 100
|
||||
X, Y = np.mgrid[0:3:complex(0, N), 0:2:complex(0, N)]
|
||||
Z1 = (1 + np.sin(Y * 10.)) * X**(2.)
|
||||
|
||||
fig, ax = plt.subplots(2, 1)
|
||||
|
||||
pcm = ax[0].pcolormesh(X, Y, Z1, norm=colors.PowerNorm(gamma=0.5),
|
||||
cmap='PuBu_r')
|
||||
fig.colorbar(pcm, ax=ax[0], extend='max')
|
||||
|
||||
pcm = ax[1].pcolormesh(X, Y, Z1, cmap='PuBu_r')
|
||||
fig.colorbar(pcm, ax=ax[1], extend='max')
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Discrete bounds
|
||||
|
||||
Another normaization that comes with Matplotlib is
|
||||
``colors.BoundaryNorm()``. In addition to *vmin* and *vmax*, this
|
||||
takes as arguments boundaries between which data is to be mapped. The
|
||||
colors are then linearly distributed between these "bounds". For
|
||||
instance:
|
||||
|
||||
``` python
|
||||
In [4]: import matplotlib.colors as colors
|
||||
|
||||
In [5]: bounds = np.array([-0.25, -0.125, 0, 0.5, 1])
|
||||
|
||||
In [6]: norm = colors.BoundaryNorm(boundaries=bounds, ncolors=4)
|
||||
|
||||
In [7]: print(norm([-0.2,-0.15,-0.02, 0.3, 0.8, 0.99]))
|
||||
[0 0 1 2 3 3]
|
||||
```
|
||||
|
||||
Note unlike the other norms, this norm returns values from 0 to *ncolors*-1.
|
||||
|
||||
``` python
|
||||
N = 100
|
||||
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
|
||||
Z1 = np.exp(-X**2 - Y**2)
|
||||
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
|
||||
Z = (Z1 - Z2) * 2
|
||||
|
||||
fig, ax = plt.subplots(3, 1, figsize=(8, 8))
|
||||
ax = ax.flatten()
|
||||
# even bounds gives a contour-like effect
|
||||
bounds = np.linspace(-1, 1, 10)
|
||||
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
|
||||
pcm = ax[0].pcolormesh(X, Y, Z,
|
||||
norm=norm,
|
||||
cmap='RdBu_r')
|
||||
fig.colorbar(pcm, ax=ax[0], extend='both', orientation='vertical')
|
||||
|
||||
# uneven bounds changes the colormapping:
|
||||
bounds = np.array([-0.25, -0.125, 0, 0.5, 1])
|
||||
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
|
||||
pcm = ax[1].pcolormesh(X, Y, Z, norm=norm, cmap='RdBu_r')
|
||||
fig.colorbar(pcm, ax=ax[1], extend='both', orientation='vertical')
|
||||
|
||||
pcm = ax[2].pcolormesh(X, Y, Z, cmap='RdBu_r', vmin=-np.max(Z))
|
||||
fig.colorbar(pcm, ax=ax[2], extend='both', orientation='vertical')
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## DivergingNorm: Different mapping on either side of a center
|
||||
|
||||
Sometimes we want to have a different colormap on either side of a
|
||||
conceptual center point, and we want those two colormaps to have
|
||||
different linear scales. An example is a topographic map where the land
|
||||
and ocean have a center at zero, but land typically has a greater
|
||||
elevation range than the water has depth range, and they are often
|
||||
represented by a different colormap.
|
||||
|
||||
``` python
|
||||
filename = cbook.get_sample_data('topobathy.npz', asfileobj=False)
|
||||
with np.load(filename) as dem:
|
||||
topo = dem['topo']
|
||||
longitude = dem['longitude']
|
||||
latitude = dem['latitude']
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
# make a colormap that has land and ocean clearly delineated and of the
|
||||
# same length (256 + 256)
|
||||
colors_undersea = plt.cm.terrain(np.linspace(0, 0.17, 256))
|
||||
colors_land = plt.cm.terrain(np.linspace(0.25, 1, 256))
|
||||
all_colors = np.vstack((colors_undersea, colors_land))
|
||||
terrain_map = colors.LinearSegmentedColormap.from_list('terrain_map',
|
||||
all_colors)
|
||||
|
||||
# make the norm: Note the center is offset so that the land has more
|
||||
# dynamic range:
|
||||
divnorm = colors.DivergingNorm(vmin=-500., vcenter=0, vmax=4000)
|
||||
|
||||
pcm = ax.pcolormesh(longitude, latitude, topo, rasterized=True, norm=divnorm,
|
||||
cmap=terrain_map,)
|
||||
# Simple geographic plot, set aspect ratio beecause distance between lines of
|
||||
# longitude depends on latitude.
|
||||
ax.set_aspect(1 / np.cos(np.deg2rad(49)))
|
||||
fig.colorbar(pcm, shrink=0.6)
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Custom normalization: Manually implement two linear ranges
|
||||
|
||||
The [``DivergingNorm``](https://matplotlib.orgapi/_as_gen/matplotlib.colors.DivergingNorm.html#matplotlib.colors.DivergingNorm) described above makes a useful example for
|
||||
defining your own norm.
|
||||
|
||||
``` python
|
||||
class MidpointNormalize(colors.Normalize):
|
||||
def __init__(self, vmin=None, vmax=None, vcenter=None, clip=False):
|
||||
self.vcenter = vcenter
|
||||
colors.Normalize.__init__(self, vmin, vmax, clip)
|
||||
|
||||
def __call__(self, value, clip=None):
|
||||
# I'm ignoring masked values and all kinds of edge cases to make a
|
||||
# simple example...
|
||||
x, y = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1]
|
||||
return np.ma.masked_array(np.interp(value, x, y))
|
||||
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
midnorm = MidpointNormalize(vmin=-500., vcenter=0, vmax=4000)
|
||||
|
||||
pcm = ax.pcolormesh(longitude, latitude, topo, rasterized=True, norm=midnorm,
|
||||
cmap=terrain_map)
|
||||
ax.set_aspect(1 / np.cos(np.deg2rad(49)))
|
||||
fig.colorbar(pcm, shrink=0.6, extend='both')
|
||||
plt.show()
|
||||
```
|
||||
|
||||

|
||||
|
||||
**Total running time of the script:** ( 0 minutes 1.895 seconds)
|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: colormapnorms.py](https://matplotlib.org/_downloads/56fa91958fd427757e621c21de870bda/colormapnorms.py)
|
||||
- [Download Jupyter notebook: colormapnorms.ipynb](https://matplotlib.org/_downloads/59a7c8f3db252ae16cd43fd50d6a004c/colormapnorms.ipynb)
|
||||
|
||||
521
Python/matplotlab/colors/colormaps.md
Normal file
521
Python/matplotlab/colors/colormaps.md
Normal file
@@ -0,0 +1,521 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Choosing Colormaps in Matplotlib
|
||||
|
||||
Matplotlib has a number of built-in colormaps accessible via
|
||||
[``matplotlib.cm.get_cmap``](https://matplotlib.orgapi/cm_api.html#matplotlib.cm.get_cmap). There are also external libraries like
|
||||
[[palettable]](#palettable) and [[colorcet]](#colorcet) that have many extra colormaps.
|
||||
Here we briefly discuss how to choose between the many options. For
|
||||
help on creating your own colormaps, see
|
||||
[Creating Colormaps in Matplotlib](colormap-manipulation.html).
|
||||
|
||||
## Overview
|
||||
|
||||
The idea behind choosing a good colormap is to find a good representation in 3D
|
||||
colorspace for your data set. The best colormap for any given data set depends
|
||||
on many things including:
|
||||
|
||||
- Whether representing form or metric data ([[Ware]](#ware))
|
||||
- Your knowledge of the data set (*e.g.*, is there a critical value
|
||||
from which the other values deviate?)
|
||||
- If there is an intuitive color scheme for the parameter you are plotting
|
||||
- If there is a standard in the field the audience may be expecting
|
||||
|
||||
For many applications, a perceptually uniform colormap is the best
|
||||
choice --- one in which equal steps in data are perceived as equal
|
||||
steps in the color space. Researchers have found that the human brain
|
||||
perceives changes in the lightness parameter as changes in the data
|
||||
much better than, for example, changes in hue. Therefore, colormaps
|
||||
which have monotonically increasing lightness through the colormap
|
||||
will be better interpreted by the viewer. A wonderful example of
|
||||
perceptually uniform colormaps is [[colorcet]](#colorcet).
|
||||
|
||||
Color can be represented in 3D space in various ways. One way to represent color
|
||||
is using CIELAB. In CIELAB, color space is represented by lightness,
|
||||
\(L^*\); red-green, \(a^*\); and yellow-blue, \(b^*\). The lightness
|
||||
parameter \(L^*\) can then be used to learn more about how the matplotlib
|
||||
colormaps will be perceived by viewers.
|
||||
|
||||
An excellent starting resource for learning about human perception of colormaps
|
||||
is from [[IBM]](#ibm).
|
||||
|
||||
## Classes of colormaps
|
||||
|
||||
Colormaps are often split into several categories based on their function (see,
|
||||
*e.g.*, [[Moreland]](#moreland)):
|
||||
|
||||
1. Sequential: change in lightness and often saturation of color
|
||||
incrementally, often using a single hue; should be used for
|
||||
representing information that has ordering.
|
||||
1. Diverging: change in lightness and possibly saturation of two
|
||||
different colors that meet in the middle at an unsaturated color;
|
||||
should be used when the information being plotted has a critical
|
||||
middle value, such as topography or when the data deviates around
|
||||
zero.
|
||||
1. Cyclic: change in lightness of two different colors that meet in
|
||||
the middle and beginning/end at an unsaturated color; should be
|
||||
used for values that wrap around at the endpoints, such as phase
|
||||
angle, wind direction, or time of day.
|
||||
1. Qualitative: often are miscellaneous colors; should be used to
|
||||
represent information which does not have ordering or
|
||||
relationships.
|
||||
|
||||
``` python
|
||||
# sphinx_gallery_thumbnail_number = 2
|
||||
|
||||
import numpy as np
|
||||
import matplotlib as mpl
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import cm
|
||||
from colorspacious import cspace_converter
|
||||
from collections import OrderedDict
|
||||
|
||||
cmaps = OrderedDict()
|
||||
```
|
||||
|
||||
### Sequential
|
||||
|
||||
For the Sequential plots, the lightness value increases monotonically through
|
||||
the colormaps. This is good. Some of the \(L^*\) values in the colormaps
|
||||
span from 0 to 100 (binary and the other grayscale), and others start around
|
||||
\(L^*=20\). Those that have a smaller range of \(L^*\) will accordingly
|
||||
have a smaller perceptual range. Note also that the \(L^*\) function varies
|
||||
amongst the colormaps: some are approximately linear in \(L^*\) and others
|
||||
are more curved.
|
||||
|
||||
``` python
|
||||
cmaps['Perceptually Uniform Sequential'] = [
|
||||
'viridis', 'plasma', 'inferno', 'magma', 'cividis']
|
||||
|
||||
cmaps['Sequential'] = [
|
||||
'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
|
||||
'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
|
||||
'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']
|
||||
```
|
||||
|
||||
### Sequential2
|
||||
|
||||
Many of the \(L^*\) values from the Sequential2 plots are monotonically
|
||||
increasing, but some (autumn, cool, spring, and winter) plateau or even go both
|
||||
up and down in \(L^*\) space. Others (afmhot, copper, gist_heat, and hot)
|
||||
have kinks in the \(L^*\) functions. Data that is being represented in a
|
||||
region of the colormap that is at a plateau or kink will lead to a perception of
|
||||
banding of the data in those values in the colormap (see [[mycarta-banding]](#mycarta-banding) for
|
||||
an excellent example of this).
|
||||
|
||||
``` python
|
||||
cmaps['Sequential (2)'] = [
|
||||
'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink',
|
||||
'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia',
|
||||
'hot', 'afmhot', 'gist_heat', 'copper']
|
||||
```
|
||||
|
||||
### Diverging
|
||||
|
||||
For the Diverging maps, we want to have monotonically increasing \(L^*\)
|
||||
values up to a maximum, which should be close to \(L^*=100\), followed by
|
||||
monotonically decreasing \(L^*\) values. We are looking for approximately
|
||||
equal minimum \(L^*\) values at opposite ends of the colormap. By these
|
||||
measures, BrBG and RdBu are good options. coolwarm is a good option, but it
|
||||
doesn't span a wide range of \(L^*\) values (see grayscale section below).
|
||||
|
||||
``` python
|
||||
cmaps['Diverging'] = [
|
||||
'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu',
|
||||
'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']
|
||||
```
|
||||
|
||||
### Cyclic
|
||||
|
||||
For Cyclic maps, we want to start and end on the same color, and meet a
|
||||
symmetric center point in the middle. \(L^*\) should change monotonically
|
||||
from start to middle, and inversely from middle to end. It should be symmetric
|
||||
on the increasing and decreasing side, and only differ in hue. At the ends and
|
||||
middle, \(L^*\) will reverse direction, which should be smoothed in
|
||||
\(L^*\) space to reduce artifacts. See [[kovesi-colormaps]](#kovesi-colormaps) for more
|
||||
information on the design of cyclic maps.
|
||||
|
||||
The often-used HSV colormap is included in this set of colormaps, although it
|
||||
is not symmetric to a center point. Additionally, the \(L^*\) values vary
|
||||
widely throughout the colormap, making it a poor choice for representing data
|
||||
for viewers to see perceptually. See an extension on this idea at
|
||||
[[mycarta-jet]](#mycarta-jet).
|
||||
|
||||
``` python
|
||||
cmaps['Cyclic'] = ['twilight', 'twilight_shifted', 'hsv']
|
||||
```
|
||||
|
||||
### Qualitative
|
||||
|
||||
Qualitative colormaps are not aimed at being perceptual maps, but looking at the
|
||||
lightness parameter can verify that for us. The \(L^*\) values move all over
|
||||
the place throughout the colormap, and are clearly not monotonically increasing.
|
||||
These would not be good options for use as perceptual colormaps.
|
||||
|
||||
``` python
|
||||
cmaps['Qualitative'] = ['Pastel1', 'Pastel2', 'Paired', 'Accent',
|
||||
'Dark2', 'Set1', 'Set2', 'Set3',
|
||||
'tab10', 'tab20', 'tab20b', 'tab20c']
|
||||
```
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
Some of the miscellaneous colormaps have particular uses for which
|
||||
they have been created. For example, gist_earth, ocean, and terrain
|
||||
all seem to be created for plotting topography (green/brown) and water
|
||||
depths (blue) together. We would expect to see a divergence in these
|
||||
colormaps, then, but multiple kinks may not be ideal, such as in
|
||||
gist_earth and terrain. CMRmap was created to convert well to
|
||||
grayscale, though it does appear to have some small kinks in
|
||||
\(L^*\). cubehelix was created to vary smoothly in both lightness
|
||||
and hue, but appears to have a small hump in the green hue area.
|
||||
|
||||
The often-used jet colormap is included in this set of colormaps. We can see
|
||||
that the \(L^*\) values vary widely throughout the colormap, making it a
|
||||
poor choice for representing data for viewers to see perceptually. See an
|
||||
extension on this idea at [[mycarta-jet]](#mycarta-jet).
|
||||
|
||||
``` python
|
||||
cmaps['Miscellaneous'] = [
|
||||
'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern',
|
||||
'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg',
|
||||
'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar']
|
||||
```
|
||||
|
||||
First, we'll show the range of each colormap. Note that some seem
|
||||
to change more "quickly" than others.
|
||||
|
||||
``` python
|
||||
nrows = max(len(cmap_list) for cmap_category, cmap_list in cmaps.items())
|
||||
gradient = np.linspace(0, 1, 256)
|
||||
gradient = np.vstack((gradient, gradient))
|
||||
|
||||
|
||||
def plot_color_gradients(cmap_category, cmap_list, nrows):
|
||||
fig, axes = plt.subplots(nrows=nrows)
|
||||
fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99)
|
||||
axes[0].set_title(cmap_category + ' colormaps', fontsize=14)
|
||||
|
||||
for ax, name in zip(axes, cmap_list):
|
||||
ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name))
|
||||
pos = list(ax.get_position().bounds)
|
||||
x_text = pos[0] - 0.01
|
||||
y_text = pos[1] + pos[3]/2.
|
||||
fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10)
|
||||
|
||||
# Turn off *all* ticks & spines, not just the ones with colormaps.
|
||||
for ax in axes:
|
||||
ax.set_axis_off()
|
||||
|
||||
|
||||
for cmap_category, cmap_list in cmaps.items():
|
||||
plot_color_gradients(cmap_category, cmap_list, nrows)
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
||||
## Lightness of matplotlib colormaps
|
||||
|
||||
Here we examine the lightness values of the matplotlib colormaps.
|
||||
Note that some documentation on the colormaps is available
|
||||
([[list-colormaps]](#list-colormaps)).
|
||||
|
||||
``` python
|
||||
mpl.rcParams.update({'font.size': 12})
|
||||
|
||||
# Number of colormap per subplot for particular cmap categories
|
||||
_DSUBS = {'Perceptually Uniform Sequential': 5, 'Sequential': 6,
|
||||
'Sequential (2)': 6, 'Diverging': 6, 'Cyclic': 3,
|
||||
'Qualitative': 4, 'Miscellaneous': 6}
|
||||
|
||||
# Spacing between the colormaps of a subplot
|
||||
_DC = {'Perceptually Uniform Sequential': 1.4, 'Sequential': 0.7,
|
||||
'Sequential (2)': 1.4, 'Diverging': 1.4, 'Cyclic': 1.4,
|
||||
'Qualitative': 1.4, 'Miscellaneous': 1.4}
|
||||
|
||||
# Indices to step through colormap
|
||||
x = np.linspace(0.0, 1.0, 100)
|
||||
|
||||
# Do plot
|
||||
for cmap_category, cmap_list in cmaps.items():
|
||||
|
||||
# Do subplots so that colormaps have enough space.
|
||||
# Default is 6 colormaps per subplot.
|
||||
dsub = _DSUBS.get(cmap_category, 6)
|
||||
nsubplots = int(np.ceil(len(cmap_list) / dsub))
|
||||
|
||||
# squeeze=False to handle similarly the case of a single subplot
|
||||
fig, axes = plt.subplots(nrows=nsubplots, squeeze=False,
|
||||
figsize=(7, 2.6*nsubplots))
|
||||
|
||||
for i, ax in enumerate(axes.flat):
|
||||
|
||||
locs = [] # locations for text labels
|
||||
|
||||
for j, cmap in enumerate(cmap_list[i*dsub:(i+1)*dsub]):
|
||||
|
||||
# Get RGB values for colormap and convert the colormap in
|
||||
# CAM02-UCS colorspace. lab[0, :, 0] is the lightness.
|
||||
rgb = cm.get_cmap(cmap)(x)[np.newaxis, :, :3]
|
||||
lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb)
|
||||
|
||||
# Plot colormap L values. Do separately for each category
|
||||
# so each plot can be pretty. To make scatter markers change
|
||||
# color along plot:
|
||||
# http://stackoverflow.com/questions/8202605/
|
||||
|
||||
if cmap_category == 'Sequential':
|
||||
# These colormaps all start at high lightness but we want them
|
||||
# reversed to look nice in the plot, so reverse the order.
|
||||
y_ = lab[0, ::-1, 0]
|
||||
c_ = x[::-1]
|
||||
else:
|
||||
y_ = lab[0, :, 0]
|
||||
c_ = x
|
||||
|
||||
dc = _DC.get(cmap_category, 1.4) # cmaps horizontal spacing
|
||||
ax.scatter(x + j*dc, y_, c=c_, cmap=cmap, s=300, linewidths=0.0)
|
||||
|
||||
# Store locations for colormap labels
|
||||
if cmap_category in ('Perceptually Uniform Sequential',
|
||||
'Sequential'):
|
||||
locs.append(x[-1] + j*dc)
|
||||
elif cmap_category in ('Diverging', 'Qualitative', 'Cyclic',
|
||||
'Miscellaneous', 'Sequential (2)'):
|
||||
locs.append(x[int(x.size/2.)] + j*dc)
|
||||
|
||||
# Set up the axis limits:
|
||||
# * the 1st subplot is used as a reference for the x-axis limits
|
||||
# * lightness values goes from 0 to 100 (y-axis limits)
|
||||
ax.set_xlim(axes[0, 0].get_xlim())
|
||||
ax.set_ylim(0.0, 100.0)
|
||||
|
||||
# Set up labels for colormaps
|
||||
ax.xaxis.set_ticks_position('top')
|
||||
ticker = mpl.ticker.FixedLocator(locs)
|
||||
ax.xaxis.set_major_locator(ticker)
|
||||
formatter = mpl.ticker.FixedFormatter(cmap_list[i*dsub:(i+1)*dsub])
|
||||
ax.xaxis.set_major_formatter(formatter)
|
||||
ax.xaxis.set_tick_params(rotation=50)
|
||||
|
||||
ax.set_xlabel(cmap_category + ' colormaps', fontsize=14)
|
||||
fig.text(0.0, 0.55, 'Lightness $L^*$', fontsize=12,
|
||||
transform=fig.transFigure, rotation=90)
|
||||
|
||||
fig.tight_layout(h_pad=0.0, pad=1.5)
|
||||
plt.show()
|
||||
```
|
||||
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
||||
## Grayscale conversion
|
||||
|
||||
It is important to pay attention to conversion to grayscale for color
|
||||
plots, since they may be printed on black and white printers. If not
|
||||
carefully considered, your readers may end up with indecipherable
|
||||
plots because the grayscale changes unpredictably through the
|
||||
colormap.
|
||||
|
||||
Conversion to grayscale is done in many different ways [[bw]](#bw). Some of the
|
||||
better ones use a linear combination of the rgb values of a pixel, but
|
||||
weighted according to how we perceive color intensity. A nonlinear method of
|
||||
conversion to grayscale is to use the \(L^*\) values of the pixels. In
|
||||
general, similar principles apply for this question as they do for presenting
|
||||
one's information perceptually; that is, if a colormap is chosen that is
|
||||
monotonically increasing in \(L^*\) values, it will print in a reasonable
|
||||
manner to grayscale.
|
||||
|
||||
With this in mind, we see that the Sequential colormaps have reasonable
|
||||
representations in grayscale. Some of the Sequential2 colormaps have decent
|
||||
enough grayscale representations, though some (autumn, spring, summer,
|
||||
winter) have very little grayscale change. If a colormap like this was used
|
||||
in a plot and then the plot was printed to grayscale, a lot of the
|
||||
information may map to the same gray values. The Diverging colormaps mostly
|
||||
vary from darker gray on the outer edges to white in the middle. Some
|
||||
(PuOr and seismic) have noticeably darker gray on one side than the other
|
||||
and therefore are not very symmetric. coolwarm has little range of gray scale
|
||||
and would print to a more uniform plot, losing a lot of detail. Note that
|
||||
overlaid, labeled contours could help differentiate between one side of the
|
||||
colormap vs. the other since color cannot be used once a plot is printed to
|
||||
grayscale. Many of the Qualitative and Miscellaneous colormaps, such as
|
||||
Accent, hsv, and jet, change from darker to lighter and back to darker gray
|
||||
throughout the colormap. This would make it impossible for a viewer to
|
||||
interpret the information in a plot once it is printed in grayscale.
|
||||
|
||||
``` python
|
||||
mpl.rcParams.update({'font.size': 14})
|
||||
|
||||
# Indices to step through colormap.
|
||||
x = np.linspace(0.0, 1.0, 100)
|
||||
|
||||
gradient = np.linspace(0, 1, 256)
|
||||
gradient = np.vstack((gradient, gradient))
|
||||
|
||||
|
||||
def plot_color_gradients(cmap_category, cmap_list):
|
||||
fig, axes = plt.subplots(nrows=len(cmap_list), ncols=2)
|
||||
fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99,
|
||||
wspace=0.05)
|
||||
fig.suptitle(cmap_category + ' colormaps', fontsize=14, y=1.0, x=0.6)
|
||||
|
||||
for ax, name in zip(axes, cmap_list):
|
||||
|
||||
# Get RGB values for colormap.
|
||||
rgb = cm.get_cmap(plt.get_cmap(name))(x)[np.newaxis, :, :3]
|
||||
|
||||
# Get colormap in CAM02-UCS colorspace. We want the lightness.
|
||||
lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb)
|
||||
L = lab[0, :, 0]
|
||||
L = np.float32(np.vstack((L, L, L)))
|
||||
|
||||
ax[0].imshow(gradient, aspect='auto', cmap=plt.get_cmap(name))
|
||||
ax[1].imshow(L, aspect='auto', cmap='binary_r', vmin=0., vmax=100.)
|
||||
pos = list(ax[0].get_position().bounds)
|
||||
x_text = pos[0] - 0.01
|
||||
y_text = pos[1] + pos[3]/2.
|
||||
fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10)
|
||||
|
||||
# Turn off *all* ticks & spines, not just the ones with colormaps.
|
||||
for ax in axes.flat:
|
||||
ax.set_axis_off()
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
for cmap_category, cmap_list in cmaps.items():
|
||||
|
||||
plot_color_gradients(cmap_category, cmap_list)
|
||||
```
|
||||
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
||||
## Color vision deficiencies
|
||||
|
||||
There is a lot of information available about color blindness (*e.g.*,
|
||||
[[colorblindness]](#colorblindness)). Additionally, there are tools available to convert images
|
||||
to how they look for different types of color vision deficiencies.
|
||||
|
||||
The most common form of color vision deficiency involves differentiating
|
||||
between red and green. Thus, avoiding colormaps with both red and green will
|
||||
avoid many problems in general.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[colorcet]([1](#id[2](#id4)), 2) [https://colorcet.pyviz.org](https://colorcet.pyviz.org)
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[[Ware]](#id3)[http://ccom.unh.edu/sites/default/files/publications/Ware_1988_CGA_Color_sequences_univariate_maps.pdf](http://ccom.unh.edu/sites/default/files/publications/Ware_1988_CGA_Color_sequences_univariate_maps.pdf)
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[[Moreland]](#id6)[http://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf](http://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf)
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[[list-colormaps]](#id11)[https://gist.github.com/endolith/2719900#id7](https://gist.github.com/endolith/2719900#id7)
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[[mycarta-banding]](#id7)[https://mycarta.wordpress.com/2012/10/14/the-rainbow-is-deadlong-live-the-rainbow-part-4-cie-lab-heated-body/](https://mycarta.wordpress.com/2012/10/14/the-rainbow-is-deadlong-live-the-rainbow-part-4-cie-lab-heated-body/)
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[mycarta-jet]([1](#id9), [2](#id10)) [https://mycarta.wordpress.com/2012/10/06/the-rainbow-is-deadlong-live-the-rainbow-part-3/](https://mycarta.wordpress.com/2012/10/06/the-rainbow-is-deadlong-live-the-rainbow-part-3/)
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[[kovesi-colormaps]](#id8)[https://arxiv.org/abs/1509.03700](https://arxiv.org/abs/1509.03700)
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[[bw]](#id12)[http://www.tannerhelland.com/3643/grayscale-image-algorithm-vb6/](http://www.tannerhelland.com/3643/grayscale-image-algorithm-vb6/)
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[[colorblindness]](#id13)[http://www.color-blindness.com/](http://www.color-blindness.com/)
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[[IBM]](#id5)[https://doi.org/10.1109/VISUAL.1995.480803](https://doi.org/10.1109/VISUAL.1995.480803)
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
[[palettable]](#id1)[https://jiffyclub.github.io/palettable/](https://jiffyclub.github.io/palettable/)
|
||||
|
||||
|
||||
|
||||
**Total running time of the script:** ( 0 minutes 9.320 seconds)
|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: colormaps.py](https://matplotlib.org/_downloads/9df0748eeda573fbccab51a7272f7d81/colormaps.py)
|
||||
- [Download Jupyter notebook: colormaps.ipynb](https://matplotlib.org/_downloads/6024d841c77bf197ffe5612254186669/colormaps.ipynb)
|
||||
|
||||
135
Python/matplotlab/colors/colors.md
Normal file
135
Python/matplotlab/colors/colors.md
Normal file
@@ -0,0 +1,135 @@
|
||||
---
|
||||
sidebarDepth: 3
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# Specifying Colors
|
||||
|
||||
Matplotlib recognizes the following formats to specify a color:
|
||||
|
||||
- an RGB or RGBA (red, green, blue, alpha) tuple of float values in ``[0, 1]``
|
||||
(e.g., ``(0.1, 0.2, 0.5)`` or ``(0.1, 0.2, 0.5, 0.3)``);
|
||||
- a hex RGB or RGBA string (e.g., ``'#0f0f0f'`` or ``'#0f0f0f80'``;
|
||||
case-insensitive);
|
||||
- a string representation of a float value in ``[0, 1]`` inclusive for gray
|
||||
level (e.g., ``'0.5'``);
|
||||
- one of ``{'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}``;
|
||||
- a X11/CSS4 color name (case-insensitive);
|
||||
- a name from the [xkcd color survey](https://xkcd.com/color/rgb/), prefixed with ``'xkcd:'`` (e.g.,
|
||||
``'xkcd:sky blue'``; case insensitive);
|
||||
- one of the Tableau Colors from the 'T10' categorical palette (the default
|
||||
color cycle): ``{'tab:blue', 'tab:orange', 'tab:green', 'tab:red',
|
||||
'tab:purple', 'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan'}``
|
||||
(case-insensitive);
|
||||
- a "CN" color spec, i.e. ``'C'`` followed by a number, which is an index into
|
||||
the default property cycle (``matplotlib.rcParams['axes.prop_cycle']``); the
|
||||
indexing is intended to occur at rendering time, and defaults to black if the
|
||||
cycle does not include color.
|
||||
|
||||
"Red", "Green", and "Blue" are the intensities of those colors, the combination
|
||||
of which span the colorspace.
|
||||
|
||||
How "Alpha" behaves depends on the ``zorder`` of the Artist. Higher
|
||||
``zorder`` Artists are drawn on top of lower Artists, and "Alpha" determines
|
||||
whether the lower artist is covered by the higher.
|
||||
If the old RGB of a pixel is ``RGBold`` and the RGB of the
|
||||
pixel of the Artist being added is ``RGBnew`` with Alpha ``alpha``,
|
||||
then the RGB of the pixel is updated to:
|
||||
``RGB = RGBOld * (1 - Alpha) + RGBnew * Alpha``. Alpha
|
||||
of 1 means the old color is completely covered by the new Artist, Alpha of 0
|
||||
means that pixel of the Artist is transparent.
|
||||
|
||||
For more information on colors in matplotlib see
|
||||
|
||||
- the [Color Demo](https://matplotlib.orggallery/color/color_demo.html) example;
|
||||
- the [``matplotlib.colors``](https://matplotlib.orgapi/colors_api.html#module-matplotlib.colors) API;
|
||||
- the [List of named colors](https://matplotlib.orggallery/color/named_colors.html) example.
|
||||
|
||||
## "CN" color selection
|
||||
|
||||
"CN" colors are converted to RGBA as soon as the artist is created. For
|
||||
example,
|
||||
|
||||
``` python
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib as mpl
|
||||
|
||||
th = np.linspace(0, 2*np.pi, 128)
|
||||
|
||||
|
||||
def demo(sty):
|
||||
mpl.style.use(sty)
|
||||
fig, ax = plt.subplots(figsize=(3, 3))
|
||||
|
||||
ax.set_title('style: {!r}'.format(sty), color='C0')
|
||||
|
||||
ax.plot(th, np.cos(th), 'C1', label='C1')
|
||||
ax.plot(th, np.sin(th), 'C2', label='C2')
|
||||
ax.legend()
|
||||
|
||||
demo('default')
|
||||
demo('seaborn')
|
||||
```
|
||||
|
||||
- 
|
||||
- 
|
||||
|
||||
will use the first color for the title and then plot using the second
|
||||
and third colors of each style's ``mpl.rcParams['axes.prop_cycle']``.
|
||||
|
||||
## xkcd v X11/CSS4
|
||||
|
||||
The xkcd colors are derived from a user survey conducted by the
|
||||
webcomic xkcd. [Details of the survey are available on the xkcd blog](https://blog.xkcd.com/2010/05/03/color-survey-results/).
|
||||
|
||||
Out of 148 colors in the CSS color list, there are 95 name collisions
|
||||
between the X11/CSS4 names and the xkcd names, all but 3 of which have
|
||||
different hex values. For example ``'blue'`` maps to ``'#0000FF'``
|
||||
where as ``'xkcd:blue'`` maps to ``'#0343DF'``. Due to these name
|
||||
collisions all of the xkcd colors have ``'xkcd:'`` prefixed. As noted in
|
||||
the blog post, while it might be interesting to re-define the X11/CSS4 names
|
||||
based on such a survey, we do not do so unilaterally.
|
||||
|
||||
The name collisions are shown in the table below; the color names
|
||||
where the hex values agree are shown in bold.
|
||||
|
||||
``` python
|
||||
import matplotlib._color_data as mcd
|
||||
import matplotlib.patches as mpatch
|
||||
|
||||
overlap = {name for name in mcd.CSS4_COLORS
|
||||
if "xkcd:" + name in mcd.XKCD_COLORS}
|
||||
|
||||
fig = plt.figure(figsize=[4.8, 16])
|
||||
ax = fig.add_axes([0, 0, 1, 1])
|
||||
|
||||
for j, n in enumerate(sorted(overlap, reverse=True)):
|
||||
weight = None
|
||||
cn = mcd.CSS4_COLORS[n]
|
||||
xkcd = mcd.XKCD_COLORS["xkcd:" + n].upper()
|
||||
if cn == xkcd:
|
||||
weight = 'bold'
|
||||
|
||||
r1 = mpatch.Rectangle((0, j), 1, 1, color=cn)
|
||||
r2 = mpatch.Rectangle((1, j), 1, 1, color=xkcd)
|
||||
txt = ax.text(2, j+.5, ' ' + n, va='center', fontsize=10,
|
||||
weight=weight)
|
||||
ax.add_patch(r1)
|
||||
ax.add_patch(r2)
|
||||
ax.axhline(j, color='k')
|
||||
|
||||
ax.text(.5, j + 1.5, 'X11', ha='center', va='center')
|
||||
ax.text(1.5, j + 1.5, 'xkcd', ha='center', va='center')
|
||||
ax.set_xlim(0, 3)
|
||||
ax.set_ylim(0, j + 2)
|
||||
ax.axis('off')
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Download
|
||||
|
||||
- [Download Python source code: colors.py](https://matplotlib.org/_downloads/8fb6dfde0db5f6422a7627d0d4e328b2/colors.py)
|
||||
- [Download Jupyter notebook: colors.ipynb](https://matplotlib.org/_downloads/04907c28d4180c02e547778b9aaee05d/colors.ipynb)
|
||||
|
||||
Reference in New Issue
Block a user