From 3220bb2048ba98b984673c1e7fe4aac686cedf47 Mon Sep 17 00:00:00 2001 From: ImportanceOfBeingErnest Date: Mon, 3 Sep 2018 04:19:33 +0200 Subject: [PATCH] Introduce new Tableau colors --- .../introduce-new-tableau-colors.rst | 24 ++++ examples/color/color_demo.py | 31 +++-- examples/color/colormap_reference.py | 2 +- examples/color/named_colors.py | 106 ++++++++++-------- lib/matplotlib/_cm.py | 49 ++++++++ lib/matplotlib/_color_data.py | 17 +++ lib/matplotlib/colors.py | 28 +++-- tutorials/colors/colormaps.py | 3 +- tutorials/colors/colors.py | 21 +++- 9 files changed, 212 insertions(+), 69 deletions(-) create mode 100644 doc/users/next_whats_new/introduce-new-tableau-colors.rst diff --git a/doc/users/next_whats_new/introduce-new-tableau-colors.rst b/doc/users/next_whats_new/introduce-new-tableau-colors.rst new file mode 100644 index 000000000000..72737cb07607 --- /dev/null +++ b/doc/users/next_whats_new/introduce-new-tableau-colors.rst @@ -0,0 +1,24 @@ +:orphan: + +Introduce new Tableau colors +---------------------------- + +In its version 10, Tableau `introduced a new palette of categorical colors +`__. +Those are now available in matplotlib with the prefix ``tabx:``: +``{'tabx:blue', 'tabx:orange', 'tabx:red', 'tabx:cyan', 'tabx:green', +'tabx:yellow', 'tabx:purple', 'tabx:pink', 'tabx:brown', 'tabx:grey'}`` + +Those colors are also provided as a new ``tabx10`` colormap. An additional +``tabx20`` colormap with is added. + +In general those colors are a little less saturated than those from the default +color cycle. Replacing the default color cycler with those colors can e.g. be +achieved via + +:: + + cols = plt.cm.tabx10.colors + plt.rcParams["axes.prop_cycle"] = plt.cycler("color", cols)} + + diff --git a/examples/color/color_demo.py b/examples/color/color_demo.py index d366f0a1b959..b3d4d85fdd20 100644 --- a/examples/color/color_demo.py +++ b/examples/color/color_demo.py @@ -3,7 +3,7 @@ Color Demo ========== -Matplotlib gives you 8 ways to specify colors, +Matplotlib gives you 7 ways to specify colors, 1) an RGB or RGBA tuple of float values in ``[0, 1]`` (e.g. ``(0.1, 0.2, 0.5)`` or ``(0.1, 0.2, 0.5, 0.3)``). RGBA is short for Red, Green, Blue, Alpha; @@ -13,16 +13,25 @@ 4) a single letter string, i.e. one of ``{'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}``; 5) a X11/CSS4 ("html") color name, e.g. ``"blue"``; -6) a name from the `xkcd color survey `__, - prefixed with ``'xkcd:'`` (e.g., ``'xkcd:sky blue'``); +6) a color name from a palette, prefixed with the palette's name: + + a. a name from the `xkcd color survey `__; + prefixed with ``'xkcd:'`` (e.g., ``'xkcd:sky blue'``); + b. one of ``{'tab:blue', 'tab:orange', 'tab:green', + 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', + 'tab:gray', 'tab:olive', 'tab:cyan'}`` which are the Tableau Colors from + the 'T10' categorical palette (which is the default color cycle); + c. one of ``{'tabx:blue', 'tabx:orange', 'tabx:red', 'tabx:cyan', + 'tabx:green', 'tabx:yellow', 'tabx:purple', 'tabx:pink', 'tabx:brown', + 'tabx:gray' }`` which are the colors from the + `new Tableau10 categorical palette + `__; + 7) a "Cn" color spec, i.e. `'C'` followed by a single digit, which is an index into the default property cycle (``matplotlib.rcParams['axes.prop_cycle']``); the indexing occurs at artist creation time and defaults to black if the cycle does not include color. -8) one of ``{'tab:blue', 'tab:orange', 'tab:green', - 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', - 'tab:gray', 'tab:olive', 'tab:cyan'}`` which are the Tableau Colors from the - 'tab10' categorical palette (which is the default color cycle); For more information on colors in matplotlib see @@ -47,12 +56,14 @@ ax.set_xlabel('time (s)', color='c') # 5) a named color: ax.set_ylabel('voltage (mV)', color='peachpuff') -# 6) a named xkcd color: +# 6a) a named xkcd color: ax.plot(t, s, 'xkcd:crimson') +# 6b) tab notation: +ax.tick_params(axis="x", labelcolor='tab:orange') +# 6c) tabx notation: +ax.tick_params(axis="y", labelcolor='tabx:yellow') # 7) Cn notation: ax.plot(t, .7*s, color='C4', linestyle='--') -# 8) tab notation: -ax.tick_params(labelcolor='tab:orange') plt.show() diff --git a/examples/color/colormap_reference.py b/examples/color/colormap_reference.py index afb578ffad1b..a41bb97d8a09 100644 --- a/examples/color/colormap_reference.py +++ b/examples/color/colormap_reference.py @@ -33,7 +33,7 @@ ('Qualitative', [ 'Pastel1', 'Pastel2', 'Paired', 'Accent', 'Dark2', 'Set1', 'Set2', 'Set3', - 'tab10', 'tab20', 'tab20b', 'tab20c']), + 'tab10', 'tab20', 'tab20b', 'tab20c', 'tabx10', 'tabx20']), ('Miscellaneous', [ 'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern', 'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg', diff --git a/examples/color/named_colors.py b/examples/color/named_colors.py index 5a16c2d813f1..b35ed4a1e160 100644 --- a/examples/color/named_colors.py +++ b/examples/color/named_colors.py @@ -12,52 +12,70 @@ * the :doc:`/gallery/color/color_demo`. """ -import matplotlib.pyplot as plt -from matplotlib import colors as mcolors - - -colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS) - -# Sort colors by hue, saturation, value and name. -by_hsv = sorted((tuple(mcolors.rgb_to_hsv(mcolors.to_rgba(color)[:3])), name) - for name, color in colors.items()) -sorted_names = [name for hsv, name in by_hsv] - -n = len(sorted_names) -ncols = 4 -nrows = n // ncols - -fig, ax = plt.subplots(figsize=(9, 8)) -# Get height and width -X, Y = fig.get_dpi() * fig.get_size_inches() -h = Y / (nrows + 1) -w = X / ncols - -for i, name in enumerate(sorted_names): - row = i % nrows - col = i // nrows - y = Y - (row * h) - h - - xi_line = w * (col + 0.05) - xf_line = w * (col + 0.25) - xi_text = w * (col + 0.3) - - ax.text(xi_text, y, name, fontsize=(h * 0.5), - horizontalalignment='left', - verticalalignment='center') - - ax.hlines(y + h * 0.1, xi_line, xf_line, - color=colors[name], linewidth=(h * 0.6)) - -ax.set_xlim(0, X) -ax.set_ylim(0, Y) -ax.set_axis_off() +import matplotlib.pyplot as plt +import matplotlib.colors as mcolors + + +def plot_colortable(colors, title, sort_colors=True, emptycols=0): + + cell_width = 185 + cell_height = 24 + swatch_width = 38 + margin = 10 + topmargin = 30 + + # Sort colors by hue, saturation, value and name. + by_hsv = ((tuple(mcolors.rgb_to_hsv(mcolors.to_rgba(color)[:3])), name) + for name, color in colors.items()) + if sort_colors is True: + by_hsv = sorted(by_hsv) + names = [name for hsv, name in by_hsv] + + n = len(names) + ncols = 4 - emptycols + nrows = n // ncols + int(n % ncols > 0) + + width = cell_width * 4 + 2 * margin + height = cell_height * nrows + margin + topmargin + dpi = 72 + + fig, ax = plt.subplots(figsize=(width / dpi, height / dpi), dpi=dpi) + fig.subplots_adjust(margin/width, margin/height, + (width-margin)/width, (height-topmargin)/height) + ax.set_xlim(0, cell_width * 4) + ax.set_ylim(cell_height * (nrows-0.5), -cell_height/2.) + ax.yaxis.set_visible(False) + ax.xaxis.set_visible(False) + ax.set_axis_off() + ax.set_title(title, fontsize=20, loc="left") + + for i, name in enumerate(names): + row = i % nrows + col = i // nrows + y = row * cell_height + + swatch_start_x = cell_width * col + swatch_end_x = cell_width * col + swatch_width + text_pos_x = cell_width * col + swatch_width + 5 + + ax.text(text_pos_x, y, name, fontsize=12, + horizontalalignment='left', + verticalalignment='center') + + ax.hlines(y, swatch_start_x, swatch_end_x, + color=colors[name], linewidth=18) + plt.show() + +plot_colortable(mcolors.BASE_COLORS, "Base Colors", + sort_colors=False, emptycols=1) +plot_colortable(mcolors.TABLEAU_COLORS, "Tableau Palette", + sort_colors=False, emptycols=1) +plot_colortable(mcolors.TABLEAUX_COLORS, "New Tableau Palette", + sort_colors=False, emptycols=1) +#sphinx_gallery_thumbnail_number = 4 +plot_colortable(mcolors.CSS4_COLORS, "CSS Colors") -fig.subplots_adjust(left=0, right=1, - top=1, bottom=0, - hspace=0, wspace=0) -plt.show() ############################################################################# # diff --git a/lib/matplotlib/_cm.py b/lib/matplotlib/_cm.py index bfc1d64d312a..6b079452749e 100644 --- a/lib/matplotlib/_cm.py +++ b/lib/matplotlib/_cm.py @@ -1346,6 +1346,53 @@ def _gist_yarg(x): return 1 - x ) +_tabx10colors = [ + "#4e79a7", # blue + "#f28e2b", # orange + "#e15759", # red + "#76b7b2", # cyan + "#59a14f", # green + "#edc948", # yellow + "#b07aa1", # purple + "#ff9da7", # pink + "#9c755f", # brown + "#bab0ac", # grey + ] + +_tabx20colors = [ + "#4e79a7", # blue + "#a0cbe8", + "#f28e2b", # orange + "#ffbe7d", + "#59a14f", # green + "#8cd17d", + "#b6992d", # yellow # is different from tabx10 + "#f1ce63", + "#499894", # cyan # is different from tabx10 + "#86bcb6", + "#e15759", # red + "#ff9d9a", + "#79706e", # grey # is different from tabx10 + "#bab0ac", + "#d37295", # pink # is different from tabx10 + "#fabfd2", + "#b07aa1", # purple # is different from tabx10 + "#d4a6c8", + "#9d7660", # brown # is different from tabx10 + "#d7b5a6", + ] + +_tabx10_data = [] +for c in _tabx10colors: + d = [int(s, 16)/255. for s in list(map(''.join, zip(*[iter(c[1:])]*2)))] + _tabx10_data.append(d) + +_tabx20_data = [] +for c in _tabx20colors: + d = [int(s, 16)/255. for s in list(map(''.join, zip(*[iter(c[1:])]*2)))] + _tabx20_data.append(d) + + datad = { 'Blues': _Blues_data, 'BrBG': _BrBG_data, @@ -1423,4 +1470,6 @@ def _gist_yarg(x): return 1 - x 'tab20': {'listed': _tab20_data}, 'tab20b': {'listed': _tab20b_data}, 'tab20c': {'listed': _tab20c_data}, + 'tabx10': {'listed': _tabx10_data}, + 'tabx20': {'listed': _tabx20_data}, } diff --git a/lib/matplotlib/_color_data.py b/lib/matplotlib/_color_data.py index 973f4a2f2435..b0e70d2bb81d 100644 --- a/lib/matplotlib/_color_data.py +++ b/lib/matplotlib/_color_data.py @@ -30,6 +30,23 @@ TABLEAU_COLORS = OrderedDict( ('tab:' + name, value) for name, value in TABLEAU_COLORS) +# These colors are from Tableau Version 10 +TABLEAUX_COLORS = ( + ("blue", "#4e79a7"), + ("orange", "#f28e2b"), + ("red", "#e15759"), + ("cyan", "#76b7b2"), + ("green", "#59a14f"), + ("yellow", "#edc948"), + ("purple", "#b07aa1"), + ("pink", "#ff9da7"), + ("brown", "#9c755f"), + ("gray", "#bab0ac") +) +# Normalize name to "tabx:" to avoid name collisions. +TABLEAUX_COLORS = OrderedDict( + ('tabx:' + name, value) for name, value in TABLEAUX_COLORS) + # This mapping of color names -> hex values is taken from # a survey run by Randel Monroe see: # http://blog.xkcd.com/2010/05/03/color-survey-results/ diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 75ac1a85e098..6d8aa6916a71 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -41,12 +41,21 @@ level (e.g., ``'0.5'``); * one of ``{'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}``; * a X11/CSS4 color name; -* a name from the `xkcd color survey `__; - prefixed with ``'xkcd:'`` (e.g., ``'xkcd:sky blue'``); -* one of ``{'tab:blue', 'tab:orange', 'tab:green', - 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', - 'tab:gray', 'tab:olive', 'tab:cyan'}`` which are the Tableau Colors from the - 'T10' categorical palette (which is the default color cycle); +* a color name from a palette, prefixed with the palette's name: + + * a name from the `xkcd color survey `__; + prefixed with ``'xkcd:'`` (e.g., ``'xkcd:sky blue'``); + * one of ``{'tab:blue', 'tab:orange', 'tab:green', + 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', + 'tab:gray', 'tab:olive', 'tab:cyan'}`` which are the Tableau Colors from + the 'T10' categorical palette (which is the default color cycle); + * one of ``{'tabx:blue', 'tabx:orange', 'tabx:red', 'tabx:cyan', + 'tabx:green', 'tabx:yellow', 'tabx:purple', 'tabx:pink', 'tabx:brown', + 'tabx:gray' }`` which are the colors from the + `new Tableau10 categorical palette + `__; + * a "CN" color spec, i.e. `'C'` followed by a single digit, which is an index into the default property cycle (``matplotlib.rcParams['axes.prop_cycle']``); the indexing occurs at artist creation time and defaults to black if the @@ -61,7 +70,8 @@ import numpy as np import matplotlib.cbook as cbook -from ._color_data import BASE_COLORS, TABLEAU_COLORS, CSS4_COLORS, XKCD_COLORS +from ._color_data import (BASE_COLORS, TABLEAU_COLORS, TABLEAUX_COLORS, + CSS4_COLORS, XKCD_COLORS) class _ColorMapping(dict): @@ -89,6 +99,10 @@ def __delitem__(self, key): _colors_full_map.update({k.replace('gray', 'grey'): v for k, v in TABLEAU_COLORS.items() if 'gray' in k}) +_colors_full_map.update(TABLEAUX_COLORS) +_colors_full_map.update({k.replace('gray', 'grey'): v + for k, v in TABLEAUX_COLORS.items() + if 'gray' in k}) _colors_full_map.update(BASE_COLORS) _colors_full_map = _ColorMapping(_colors_full_map) diff --git a/tutorials/colors/colormaps.py b/tutorials/colors/colormaps.py index 4a04c5eaf595..c923187768ab 100644 --- a/tutorials/colors/colormaps.py +++ b/tutorials/colors/colormaps.py @@ -160,7 +160,8 @@ cmaps['Qualitative'] = ['Pastel1', 'Pastel2', 'Paired', 'Accent', 'Dark2', 'Set1', 'Set2', 'Set3', - 'tab10', 'tab20', 'tab20b', 'tab20c'] + 'tab10', 'tab20', 'tab20b', 'tab20c', + 'tabx10', 'tabx20'] ############################################################################### # Miscellaneous diff --git a/tutorials/colors/colors.py b/tutorials/colors/colors.py index 5721cb6b0b5a..83be76c100b9 100644 --- a/tutorials/colors/colors.py +++ b/tutorials/colors/colors.py @@ -12,12 +12,21 @@ level (e.g., ``'0.5'``); * one of ``{'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}``; * a X11/CSS4 color name; -* a name from the `xkcd color survey `__; - prefixed with ``'xkcd:'`` (e.g., ``'xkcd:sky blue'``); -* one of ``{'tab:blue', 'tab:orange', 'tab:green', - 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', - 'tab:gray', 'tab:olive', 'tab:cyan'}`` which are the Tableau Colors from the - 'T10' categorical palette (which is the default color cycle); +* a color name from a palette, prefixed with the palette's name: + + * a name from the `xkcd color survey `__; + prefixed with ``'xkcd:'`` (e.g., ``'xkcd:sky blue'``); + * one of ``{'tab:blue', 'tab:orange', 'tab:green', + 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', + 'tab:gray', 'tab:olive', 'tab:cyan'}`` which are the Tableau Colors from + the 'T10' categorical palette (which is the default color cycle); + * one of ``{'tabx:blue', 'tabx:orange', 'tabx:red', 'tabx:cyan', + 'tabx:green', 'tabx:yellow', 'tabx:purple', 'tabx:pink', 'tabx:brown', + 'tabx:gray'}`` which are the colors from the + `new Tableau10 categorical palette + `__; + * a "CN" color spec, i.e. `'C'` followed by a single digit, which is an index into the default property cycle (``matplotlib.rcParams['axes.prop_cycle']``); the indexing occurs at artist creation time and defaults to black if the