3

I am new to latex and this is my first question of tex.sx, and I apologise in advance for being unable to make a good reproducable example.

I am trying to colour cells of a table according to a 'vector' of colour codes.

I have seen some questions about colouring cells based on their value (e.g. Are there an easy way to coloring tables depending on the value in each cell?). I am trying to specify a vector of colours of the same length as the number of rows in the table, and then use it to define the colours of one of the columns.

I'm not even sure if you can create a 'vector' in this way in latex!

Many thanks.

1
  • 3
    Welcome to TeX.SX! What do you mean by a "vector of colours" and how do you want to use them to colour the table cells? It is currently not clear to me, at least, what you are trying to do. A good minimal working example in this case might be an example table and a description of the output that you are looking for. Perhaps something like this: tex.stackexchange.com/questions/401370/…?
    – user30471
    May 12, 2019 at 11:11

1 Answer 1

4

Welcome to TeX.SX! You can do like this (coloring cells in the second column, for instance):

\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{xparse}

\ExplSyntaxOn

\msg_new:nnn { mytblvectorcolor } { not-enough-elements-in-color-vector }
  { Not~enough~elements~in~color~vector. }

\seq_new:N \l_my_color_vector_seq
\seq_new:N \g__my_work_color_vector_seq

\cs_new_protected:Npn \my_set_local_color_vector:n #1
  {
    \seq_set_from_clist:Nn \l_my_color_vector_seq {#1}
  }

\cs_new_protected:Nn \my_use_current_color_vector:
  {
    \seq_gset_eq:NN \g__my_work_color_vector_seq \l_my_color_vector_seq
  }

\cs_new_protected:Npn \my_set_color_vector:n #1
  {
    \my_set_local_color_vector:n {#1}
    \my_use_current_color_vector:
  }

\cs_generate_variant:Nn \my_set_color_vector:n { x }

\NewDocumentCommand \myUseCurrentColorVector { }
  {
    \my_use_current_color_vector:
  }

\NewDocumentCommand \mySetColorVector { m }
  {
    \my_set_color_vector:n {#1}
  }

\cs_new_protected:Npn \my_apply_current_vector_color:N #1
  {
    \seq_gpop_left:NN \g__my_work_color_vector_seq \l_tmpa_tl

    \tl_if_eq:NNTF \l_tmpa_tl \q_no_value
      {
        \msg_error:nn { mytblvectorcolor }
          { not-enough-elements-in-color-vector }
      }
      { \exp_args:NV #1 \l_tmpa_tl }
  }

\cs_new_protected:Nn \my_apply_current_color_to_cell:
  {
    \my_apply_current_vector_color:N \cellcolor
  }

\NewDocumentCommand \myApplyCurrentColorToCell { }
  {
    \my_apply_current_color_to_cell:
  }

% #1: color model
% #2: stem for color names
% #3: color specs (seq)
\cs_new_protected:Npn \my_define_color_series:nnN #1#2#3
  {
    \seq_set_eq:NN \l_tmpa_seq #3
    \int_step_inline:nnn { 1 } { \seq_count:N #3 }
      {
        \seq_gpop_left:NN \l_tmpa_seq \l_tmpa_tl
        \exp_args:Nnnx
          \definecolor { #2 ##1 } {#1} { \tl_use:N \l_tmpa_tl }
      }
  }

\seq_new:N \l__my_batch_define_colors_seq

% #1: color model
% #2: stem for color names
% #3: color specs (clist)
\cs_new_protected:Npn \my_batch_define_colors:nnn #1#2#3
  {
    \seq_set_from_clist:Nn \l__my_batch_define_colors_seq {#3}
    \my_define_color_series:nnN {#1} {#2} \l__my_batch_define_colors_seq
  }

\NewDocumentCommand \myBatchDefineColors { m m m }
  {
    \my_batch_define_colors:nnn {#1} {#2} {#3}
  }

% #1: first color index (default: 1)
% #2: last color index
% #3: stem for color names
\cs_new_protected:Npn \my_batch_set_color_vector:nnn #1#2#3
  {
    \seq_clear:N \l_tmpa_seq
    \int_step_inline:nnn {#1} {#2}
      { \seq_gput_right:Nn \l_tmpa_seq { #3 ##1 } }
    \my_set_color_vector:x { \seq_use:Nn \l_tmpa_seq { , } }
  }

\NewDocumentCommand \myBatchSetColorVector { O{1} m m }
  {
    \my_batch_set_color_vector:nnn {#1} {#2} {#3}
  }

\ExplSyntaxOff

\begin{document}
\setlength{\parskip}{\baselineskip}

\mySetColorVector{red, green, yellow}% set color vector at global scope
\begin{tabular}{c>{\myApplyCurrentColorToCell}cc}
  a0 & a1 & a2\\
  b0 & b1 & b2\\
  c0 & c1 & c2
\end{tabular}

{% Use different colors in this group (local scope)
  \mySetColorVector{red!20, green!20, blue!20}%
  \begin{tabular}{c>{\myApplyCurrentColorToCell}cc}
    A0 & A1 & A2\\
    B0 & B1 & B2\\
    C0 & C1 & C2
  \end{tabular}%
}%

% Reuse the color vector set at global scope
\myUseCurrentColorVector
\begin{tabular}{c>{\myApplyCurrentColorToCell}cc}
  a0 & a1 & a2\\
  b0 & b1 & b2\\
  c0 & c1 & c2
\end{tabular}

% Example of batch-definition for colors and the color vector.
% Note: \myBatchDefineColors can be called in the preamble.
\myBatchDefineColors{rgb}{MyColor}{%
  {0.8,0.9,1.0},
  {0.7,0.6,0.5},
  {0.6,0.5,0.7},
  {0.5,0.4,0.5},
  {0.4,0.6,0.5},
  {0.6,0.8,0.7}
}%
% \myBatchDefineColors{HTML}{MyColor}{
%   00AA00,
%   00BB11,
%   11DDEE,
%   5547FE,
%   AD0245,
%   14CB1A
% }%
%
% What follows is a shortcut for
% \mySetColorVector{MyColor1,MyColor2,MyColor3,MyColor4,MyColor5,MyColor6}%
\myBatchSetColorVector{6}{MyColor}%
% The table has 5 lines and the color vector 6 elements; that's okay.
\begin{tabular}{c>{\myApplyCurrentColorToCell}cc}
  a0 & a1 & a2\\
  b0 & b1 & b2\\
  c0 & c1 & c2\\
  d0 & d1 & d2\\
  e0 & e1 & e2
\end{tabular}

\end{document}

Screenshot for simple example

Of course, the color vector you set with either \mySetColorVector or \myUseCurrentColorVector before starting a tabular or array that uses \myApplyCurrentColorToCell, must have at least as many elements as the number of times \myApplyCurrentColorToCell is called in said tabular or array. The macro \myApplyCurrentColorToCell doesn't have to be always called in the same column, by the way; this will be shown in the next example.

Here is a more sophisticated example (who said bloated? :-) with generalizations of the previous macros that can conditionally loop over the color vector. As you can see, the macros can be used in other contexts than a tabular or array environment.

\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{tcolorbox}
\usepackage{xparse}

\ExplSyntaxOn

\msg_new:nnn { mytblvectorcolor } { not-enough-elements-in-color-vector }
  { Not~enough~elements~in~color~vector. }

\seq_new:N \l_my_color_vector_seq
% We are going to copy \g__my_saved_color_vector_seq to this one before
% starting a set of color operations, then from pop from it as we need to get
% each next color.
\seq_new:N \g__my_work_color_vector_seq
\seq_new:N \g__my_saved_color_vector_seq

\cs_new_protected:Npn \my_set_local_color_vector:n #1
  {
    \seq_set_from_clist:Nn \l_my_color_vector_seq {#1}
  }

\cs_new_protected:Nn \my_use_current_color_vector:
  {
    \seq_gset_eq:NN \g__my_work_color_vector_seq \l_my_color_vector_seq
    \seq_gset_eq:NN \g__my_saved_color_vector_seq \l_my_color_vector_seq
  }

\cs_new_protected:Npn \my_set_color_vector:n #1
  {
    \my_set_local_color_vector:n {#1}
    \my_use_current_color_vector:
  }

\cs_generate_variant:Nn \my_set_color_vector:n { x }

\NewDocumentCommand \myUseCurrentColorVector { }
  {
    \my_use_current_color_vector:
  }

\NewDocumentCommand \mySetColorVector { m }
  {
    \my_set_color_vector:n {#1}
  }

% #1: a token (typically a control sequence)
\cs_new_protected:Npn \my_apply_current_vector_color:N #1
  {
    \seq_gpop_left:NN \g__my_work_color_vector_seq \l_tmpa_tl

    \tl_if_eq:NNTF \l_tmpa_tl \q_no_value
      {
        \msg_error:nn { mytblvectorcolor }
          { not-enough-elements-in-color-vector }
      }
      { \exp_args:NV #1 \l_tmpa_tl }
  }

% #1: a <boolean expression> indicating whether looping is enabled
% #2: a token (typically a control sequence) passed to
%     \my_apply_current_vector_color:N
\cs_new_protected:Npn \my_apply_current_vector_color:nN #1#2
  {
    \bool_if:nT {#1}
      {                         % looping allowed -> restore the work vector
        \seq_if_empty:NT \g__my_work_color_vector_seq
          {
            \seq_gset_eq:NN \g__my_work_color_vector_seq
                            \g__my_saved_color_vector_seq
          }
      }

    \my_apply_current_vector_color:N #2
  }

% #1: an xparse boolean that can be tested with \IfBooleanTF and says whether
%     looping is allowed
% #2: a token (typically a control sequence) passed to
%     \my_apply_current_vector_color:N
\cs_new_protected:Npn \my_apply_current_vector_color_usrlevel:nN #1#2
  {
    \exp_args:Nx \my_apply_current_vector_color:nN
      { \IfBooleanTF {#1} { \c_true_bool } { \c_false_bool } }
    #2
  }

% #2: a token (typically a control sequence) passed to
%     \my_apply_current_vector_color:N
%
% Star version: looping is allowed
\NewDocumentCommand \myApplyCurVecColor { s m }
  {
    \my_apply_current_vector_color_usrlevel:nN {#1} #2
  }

\cs_new_protected:Npn \my_list_and_process_elements:nNn #1#2#3
  {
    \seq_set_split:Nnn \l_tmpa_seq { } {#3}
    \seq_clear:N \l_tmpb_seq
    \seq_map_inline:Nn \l_tmpa_seq
      {
        \seq_gput_right:Nn \l_tmpb_seq
          { \my_apply_current_vector_color:nN {#1} #2 {##1} }
      }
    \seq_use:Nnnn \l_tmpb_seq { ~and~ } { ,~ } { ,~and~ }
  }

\NewDocumentCommand \myListAndProcessElements { s m m }
  {
    \exp_args:Nx \my_list_and_process_elements:nNn
      { \IfBooleanTF {#1} { \c_true_bool } { \c_false_bool } }
    #2 {#3}
  }

\NewDocumentCommand \myApplyCurrentColorToCell { s }
  {
    \my_apply_current_vector_color_usrlevel:nN {#1} \cellcolor
  }

% It's ugly to have to define these, but it happens that using a starred
% command such as \myApplyCurrentColorToCell in the >{decl.} syntactic
% element of a tabular preamble causes headaches when the star isn't there
% and LaTeX looks for it... Therefore, we provide the two versions of
% \myApplyCurrentColorToCell in a way that doesn't require to check for the
% presence of the star. These commands are safe to use in the >{decl.} parts
% of a tabular preamble.
\NewDocumentCommand \myApplyCurrentColorToCellNoLoop { }
  {
    \my_apply_current_vector_color:nN { \c_false_bool } \cellcolor
  }

\NewDocumentCommand \myApplyCurrentColorToCellLoop { }
  {
    \my_apply_current_vector_color:nN { \c_true_bool } \cellcolor
  }

% #1: color model
% #2: stem for color names
% #3: color specs (seq)
\cs_new_protected:Npn \my_define_color_series:nnN #1#2#3
  {
    \seq_set_eq:NN \l_tmpa_seq #3
    \int_step_inline:nnn { 1 } { \seq_count:N #3 }
      {
        \seq_gpop_left:NN \l_tmpa_seq \l_tmpa_tl
        \exp_args:Nnnx
          \definecolor { #2 ##1 } {#1} { \tl_use:N \l_tmpa_tl }
      }
  }

\seq_new:N \l__my_batch_define_colors_seq

% #1: color model
% #2: stem for color names
% #3: color specs (clist)
\cs_new_protected:Npn \my_batch_define_colors:nnn #1#2#3
  {
    \seq_set_from_clist:Nn \l__my_batch_define_colors_seq {#3}
    \my_define_color_series:nnN {#1} {#2} \l__my_batch_define_colors_seq
  }

\NewDocumentCommand \myBatchDefineColors { m m m }
  {
    \my_batch_define_colors:nnn {#1} {#2} {#3}
  }

% #1: first color index (default: 1)
% #2: last color index
% #3: stem for color names
\cs_new_protected:Npn \my_batch_set_color_vector:nnn #1#2#3
  {
    \seq_clear:N \l_tmpa_seq
    \int_step_inline:nnn {#1} {#2}
      { \seq_gput_right:Nn \l_tmpa_seq { #3 ##1 } }
    \my_set_color_vector:x { \seq_use:Nn \l_tmpa_seq { , } }
  }

\NewDocumentCommand \myBatchSetColorVector { O{1} m m }
  {
    \my_batch_set_color_vector:nnn {#1} {#2} {#3}
  }

% Macros for saving the last element popped from the color vector, in order
% to potentially use it several times (per tabular row, for instance).
%
% The 'Loop' suffix doesn't mean we store a color loop; it means the macro is
% the variant that is *allowed to loop* over the color vector if it has less
% elements than what is used.
\NewDocumentCommand \myStoreCurrentColorLoop { }
  {
    \my_apply_current_vector_color:nN { \c_true_bool } \myDoStoreColor
  }

\tl_new:N \g_my_current_color_tl % token list for storing a “current color”

\NewDocumentCommand \myDoStoreColor { m }
  {
    \tl_gset:Nn \g_my_current_color_tl {#1}
  }

\NewDocumentCommand \myApplyStoredColorToCell { }
  {
    \exp_args:Nx \cellcolor { \tl_use:N \g_my_current_color_tl }
  }

\ExplSyntaxOff

% Box what follows using a tcolorbox. #1 is the base color; it is blended with
% others by \xymybox. The \colorlet is necessary when #1 itself contains
% exclamation marks.
\newcommand{\myApplyCurrentColorToTCBox}[1]{%
  \colorlet{tmpcolor}{#1}%
  \xmybox[tmpcolor]%
}

% From the tcolorbox manual
\newtcbox{\xmybox}[1][red]{%
  on line, arc=7pt,colback=#1!10!white,colframe=#1!50!black,
  before upper={\rule[-3pt]{0pt}{10pt}},boxrule=1pt,
  boxsep=0pt,left=6pt,right=6pt,top=2pt,bottom=2pt
}

\begin{document}
\setlength{\parskip}{\baselineskip}

\mySetColorVector{red, green, yellow}% set color vector at global scope
\begin{tabular}{c>{\myApplyCurrentColorToCellLoop}cc}
  a0 & a1 & a2\\
  b0 & b1 & b2\\
  c0 & c1 & c2\\
  d0 & d1 & d2\\
  e0 & e1 & e2
\end{tabular}

{% Use different colors in this group (local scope)
  \mySetColorVector{red!20, green!20, blue!20}%
  % 3 colors and 3 lines -> the “Loop” variant would work as well but is not
  % required here.
  \begin{tabular}{c>{\myApplyCurrentColorToCellNoLoop}cc}
    A0 & A1 & A2\\
    B0 & B1 & B2\\
    C0 & C1 & C2
  \end{tabular}%
}%

% Reuse the color vector set at global scope
\myUseCurrentColorVector
\begin{tabular}{c>{\myApplyCurrentColorToCellLoop}cc}
  a0 & a1 & a2\\
  b0 & b1 & b2\\
  c0 & c1 & c2\\
  d0 & d1 & d2\\
  e0 & e1 & e2
\end{tabular}

\myUseCurrentColorVector
{%
  \let\applyColor=\myApplyCurrentColorToCell % abbreviation
  \begin{tabular}{ccc}
    \applyColor a0 & a1 & a2\\
    b0                            & \applyColor b1 & b2\\
    c0                            & c1             & \applyColor c2
  \end{tabular}%
}

\mySetColorVector{red, green, blue!20, yellow!50}%
\myListAndProcessElements*{\myApplyCurrentColorToTCBox}{%
  {I love cats}%
  {small cats}%
  {fat cats}%
  {smart cats}%
  {all kinds of cats}%
  {other cats}%
  {also cats}%
}.

% Example of batch-definition for colors and the color vector.
% Note: \myBatchDefineColors can be called in the preamble.
\myBatchDefineColors{rgb}{MyColor}{%
  {0.4,0.2,0.6},
  {0.2,1.0,0.1},
  {0.3,0.5,0.8},
  {0.9,0.4,0.5},
  {1.0,0.9,0.1},
  {0.1,0.8,0.7}
}%
% What follows is a shortcut for
% \mySetColorVector{MyColor1,MyColor2,MyColor3,MyColor4,MyColor5,MyColor6}%
\myBatchSetColorVector{6}{MyColor}%
\myListAndProcessElements*{\myApplyCurrentColorToTCBox}{%
  {\LaTeX\ is cool}%
  {rather cool}%
  {very cool}%
  {quite cool}%
  {\"{u}ber cool}%
  {most definitely cool}%
}!

\myUseCurrentColorVector
\begin{tabular}{>{\myStoreCurrentColorLoop}c
                >{\myApplyStoredColorToCell}c
                >{\myApplyStoredColorToCell}c}
  a0 & a1 & a2\\
  b0 & b1 & b2\\
  c0 & c1 & c2\\
  d0 & d1 & d2\\
  e0 & e1 & e2
\end{tabular}

\end{document}

Sophisticated example

Concise way to define many custom colors

In case you want to define a vector containing many custom colors (that aren't already available by name), you would normally have to write:

\definecolor{MyColor1}{rgb}{0.5,0.6,0.8}
\definecolor{MyColor2}{rgb}{0.7,0.6,0.9}
...
\mySetColorVector{MyColor1,MyColor2,...}

Since this is a bit tedious, I've included macros in both examples that allow you to simply do instead:

\myBatchDefineColors{rgb}{MyColor}{%
  {0.8,0.9,1.0},
  {0.7,0.6,0.5},
  {0.6,0.5,0.7},
  {0.5,0.4,0.5},
  {0.4,0.6,0.5},
  {0.6,0.8,0.7}
}%
% shortcut for \mySetColorVector{MyColor1,MyColor2,MyColor3,MyColor4,MyColor5,MyColor6}%
\myBatchSetColorVector{6}{MyColor}%
% Use the newly-set color vector here, for instance:
\begin{tabular}{...}
  ...
\end{tabular}

I used the rgb color model in this example, but you can use any color model that is supported by xcolor. For example, with the HTML color model, the following definition (of six custom colors) is valid:

\myBatchDefineColors{HTML}{MyColor}{
  00AA00,
  00BB11,
  11DDEE,
  5547FE,
  AD0245,
  14CB1A
}%

(you can put all on the same line, even without spaces if you wish)

Note: the calls to \myBatchDefineColors may be done in the document preamble. It is often of good idea, as for commands you define yourself.

6
  • Thank you, this works brilliantly! It solves my problem and I will look carefully through what you've done for my own learning. Do you know how to make it work with HEX colours (e.g. #E9F0F4) rather than words like 'red'?
    – Dan Lewer
    May 12, 2019 at 20:51
  • 1
    Maybe there is a shorter way, but the following works: \definecolor{SomeColor}{HTML}{E9F0F4} then as usual, you can have stuff like \mySetColorVector{SomeColor, SomeColor!50, SomeColor!30}. You can also use \definecolor{SomeColor}{RGB}{37,29,118} (range 0-255), \definecolor{SomeColor}{rgb}{0.2,0.4,1.0} (range [0,1]), etc. See table 3 (Supported color models) page 10 of the xcolor Manual.
    – frougon
    May 12, 2019 at 21:20
  • 1
    You can also use \usepackage[table,dvipsnames]{xcolor} or \usepackage[table,svgnames]{xcolor} or \usepackage[table,x11names]{xcolor} and refer to colors by their names then. The choice is more limited, but x11names has 317 colors, which is not so bad... (see xcolor manual p. 39).
    – frougon
    May 12, 2019 at 21:29
  • 1
    See again the examples and answer: I've added macros for batch-definition of custom colors and use for the current color vector. I've harmonized command and variable names between the two examples; the API layer is a bit better too.
    – frougon
    May 13, 2019 at 7:30
  • 1
    If you want to do this with only one current color vector, this means the same color is used inside a given line (some columns may not be colored, though). This is now implemented in the sophisticated example (trivial to adapt to the simple one: just replace \my_apply_current_vector_color:nN { \c_true_bool } in \myStoreCurrentColorLoop with \my_apply_current_vector_color:N, and remove the “Loop” suffix from the new macro name, since it wouldn't loop anymore). If, OTOH, you need different color vectors for different columns, that would require some architectural changes.
    – frougon
    May 13, 2019 at 8:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .