# Simplest way to create a grid with colored squares and labels

I am trying to create a grid with colored squares, multi-line labels within the squares, and labels on the axes. Is there a simple and easy way to achieve this?

This question is the closest I could find to the answer, but I'm not sure how to add the second line of text in each box, or whether there is an easy way to add the rotated y-axis label (or the other labels for that matter). For this reason I am wondering whether a graph might be more suited.

This question is more along the lines of "is this easily possible and with what tool" rather than "what is the exact code to achieve this" (although that would be great as well). If the answer is "it's not easily possible" then that is fine, and I will look at alternative options.

The following image depicts what I am trying to produce:

-
Could you please add a fully compilable example of your efforts, if any? –  Malipivo Apr 21 at 7:56
@malipivo - I considered adding code to my question, but my efforts so far have been minimal and consisted mainly of using tabular and its variants (and none have included labelling the axes). I can post these, but my question is more along the lines of "is this easily possible" and "what is the best tool", rather than "how can I update my existing code". If the answer is that there is no easy and quick way, then I might simply produce the diagram in other software (as I have done above) and include it as an image in my document. Thanks. –  abeverley Apr 21 at 8:25
Wow, what a great selection of answers. It certainly confirmed my suspicion that there is more than one way to achieve the result! I've accepted the answer that I have actually used for now (it was a tough decision), but in the longer term I'd like to go through all of these in more detail. I have no need for it to be repeatable at the moment, but it would be nice to know that I can easily slot one of these into a document in the future. I haven't used LuaLaTeX before (I've been using XeLaTeX) but will certainly investigate (I didn't know that it had the same font flexibility). Thanks. –  abeverley Apr 22 at 9:42

Here, I do it with stacks. You can change the length of the box-edge to suit via the length \sqsz.

REVISED so that the color and category label are selected based on the box value (yellow-low <2, orange-med 3-7, red high >7)

\documentclass{article}
\usepackage[usestackEOL]{stackengine}
\usepackage{xcolor, graphicx}
\newlength\sqsz
\sqsz=1.4cm
\newcommand\sq[1]{\protect\sqhelper{#1}}
\newcommand\sqhelper[1]{\sffamily\bfseries\fboxsep=-\fboxrule%
\ifnum#1>7\def\sqcolor{red}\def\sqcat{High}\else%
\ifnum#1>2\def\sqcolor{orange}\def\sqcat{Med}\else%
\def\sqcolor{yellow}\def\sqcat{Low}\fi\fi%
\fcolorbox{black}{\sqcolor}{\makebox[\sqsz]{%
\rule[\dimexpr-.5\sqsz+.2\ht\strutbox]{0pt}{\sqsz}\stackanchor[2pt]{#1}{(\sqcat)}}}}
\newcommand\hsq[1]{\protect\hsqhelper{#1}}
\newcommand\hsqhelper[1]{\sffamily\bfseries%
\makebox[\sqsz]{\rule{0pt}{\dimexpr.5\sqsz+.2\ht\strutbox}#1}}
\newcommand\vsq[1]{\protect\vsqhelper{#1}}
\newcommand\vsqhelper[1]{\sffamily\bfseries%
\rule[\dimexpr-.5\sqsz+.2\ht\strutbox]{0pt}{\sqsz}#1}
\begin{document}
\raisebox{.5\sqsz}{\rotatebox{-90}{\sffamily\bfseries\makebox[4\sqsz]{Impact of Event}}}~
\setstackgap{S}{0pt}
\Shortunderstack{\vsq{4}\\ \vsq{3}\\ \vsq{2}\\ \vsq{1}}~~
\stackunder[9pt]{%
\Shortunderstack{
\sq{4}\sq{8}\sq{12}\sq{16}\\
\sq{3}\sq{6}\sq{9}\sq{12}\\
\sq{2}\sq{4}\sq{6}\sq{8}\\
\sq{1}\sq{2}\sq{3}\sq{4}\\
\hsq{1}\hsq{2}\hsq{3}\hsq{4}
}
}{\sffamily\bfseries Probability of event}
\end{document}


-

I think you'll be better off reproducing your image with PGF/TikZ and LuaLaTeX instead of tabular and tables.

Here is my solution:

I would recommend to use LuaLaTeX since you can program things like these and reuse the code to draw other matrices using the same Lua function (you would have to change the input matrix data, and only call print_data with that matrix as its argument).

This may not be the easiest solution but I would stick with this for the previously mentioned reasons.

\documentclass[a4paper]{article}

\usepackage[svgnames]{xcolor}
\usepackage{tikz}
\usepackage{luacode}

\usetikzlibrary{positioning}

\tikzset{%
cell/.style={%
font=\bfseries\sffamily\scriptsize,
draw,
minimum size=1cm,%
inner sep=0pt,%
outer sep=0pt,%
align=center%
},%
low/.style={%
fill=Gold!50!Yellow%
},%
med/.style={%
fill=DarkOrange%
},%
high/.style={%
fill=Crimson!40!Red%
},%
count/.style={%
minimum size=1cm,%
inner sep=0pt,%
outer sep=0pt,%
align=center%
},%
bottomlabel/.style={%
anchor=north,%
},%
leftlabel/.style={%
anchor=north,%
rotate=270%
}
}

\begin{luacode*}
data = {{4, 8, 12, 16},
{3, 6, 9, 12},
{2, 4, 6, 8},
{1, 2, 3, 4}}

function print_data(data)
local cols = #data[1]
local rows = #data

tex.sprint([[\begin{tikzpicture}[font=\sffamily] ]])
tex.sprint([[\begin{scope}[local bounding box=graph] ]])

for j = 1, cols do
for i = 1, rows do
if data[i][j] >= 1 and data[i][j] <= 2 then
tex.sprint([[\node[cell, low] at (]])
elseif data[i][j] >= 3 and data[i][j] <= 6 then
tex.sprint([[\node[cell, med] at (]])
else
tex.sprint([[\node[cell, high] at (]])
end
tex.sprint(j - 1)
tex.sprint([[cm,]])
tex.sprint(-i + 1)
tex.sprint([[cm){]])

if data[i][j] >= 1 and data[i][j] <= 2 then
tex.sprint(data[i][j])
tex.sprint([[\\(Low)]])
elseif data[i][j] >= 3 and data[i][j] <= 6 then
tex.sprint(data[i][j])
tex.sprint([[\\(Med)]])
else
tex.sprint(data[i][j])
tex.sprint([[\\(High)]])
end
tex.sprint([[};]])
end
end

tex.sprint([[\end{scope}]])

for i = rows, 1, -1 do
tex.sprint([[\node[count] at (]])
tex.sprint(-1)
tex.sprint([[cm,]])
tex.sprint(-i + 1)
tex.sprint([[cm){]])
tex.sprint(rows - i + 1)
tex.sprint([[};]])
end

for j = cols, 1, -1 do
tex.sprint([[\node[count] at (]])
tex.sprint(-j + cols)
tex.sprint([[cm,]])
tex.sprint(-cols)
tex.sprint([[cm){]])
tex.sprint(cols - j + 1)
tex.sprint([[};]])
end

tex.sprint([[\node[below=of graph, bottomlabel] {Probability of event};]])
tex.sprint([[\node[left=of graph, leftlabel] {Impact of event};]])
tex.sprint([[\end{tikzpicture}]])
end
\end{luacode*}

\begin{document}
\end{document}

-
Well, this seems to be the most popular ;-) I've used the other solution for the time being as it was the most succinct, but I like the way this creates the print_data function, so I'll look into it in the longer term. See also my comment to the question. Thanks. –  abeverley Apr 22 at 9:45

Doesn't generalize that well, but that didn't seem to be a requirement:

\documentclass[tikz,border=5]{standalone}
\renewcommand\familydefault\sfdefault
\tikzset{%
box/.style={
align=center, minimum size=1cm, inner sep=0pt, font=\tiny\bfseries,
draw=black, #1
},
Low/.style={fill=yellow},
Med/.style={fill=orange},
High/.style={fill=red},
}
\begin{document}
\begin{tikzpicture}
\foreach \i in {1,...,4}
\foreach \j [evaluate={\k=int(\j*\i);
\s=\k<3 ? "Low" : (\k>6 ? "High" : "Med");}] in {1,...,4}{
\node at (\j, \i) [box=\s]  {\k \\ (\s)};
\ifnum\i=1 \node at (\j, 0) {\j}; \fi
\ifnum\j=1 \node at (0, \i) {\i}; \fi
}
\node [rotate=-90] at (-.75,2.5) {Impact of Event};
\node              at (2.5,-.75) {Probability of Event};
\end{tikzpicture}
\end{document}


-
Nice and compact! –  Will Robertson Apr 21 at 22:55

Here's one LaTeX possibility producing the coloring and the labeling (Low, Med, High) automatically, depending on the numeric value of the entries. The collcell package was the main tool here:

\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{collcell}
\usepackage{hhline}
\usepackage{graphicx}
\usepackage{pgf}

\newcommand\mycolor{blue}
\newcommand\mylabel{a}
\def\Side{1cm}

\newcommand\ColCell[1]{%
\pgfmathparse{#1<3?1:0}%
\ifnum\pgfmathresult=0\relax
\gdef\mycolor{orange!80}
\gdef\mylabel{Med}
\else
\ifnum\pgfmathresult=1\relax
\gdef\mycolor{yellow}
\gdef\mylabel{Low}
\fi \fi
\pgfmathparse{#1>6?1:0}%
\ifnum\pgfmathresult=1\relax
\gdef\mycolor{red!95!black}
\gdef\mylabel{High}
\fi
\expandafter\expandafter\cellcolor{\mycolor}%
\parbox[c][\dimexpr\Side+1.5\tabcolsep\relax][c]{\Side}{%
\sffamily\centering #1 \\ (\mylabel)
}%
}

\newcolumntype{E}{>{\centering\arraybackslash\collectcell\ColCell}p{\Side}<{\endcollectcell}}

\begin{document}

\noindent\begin{tabular}{ll*{4}{|E}|}
\hhline{~~|*4{-}|}
\raisebox{-1.8cm}[0pt][0pt]{\rotatebox[origin=c]{-90}{%
\makebox[0pt]{\sffamily Impact of event}}}
& 4 & 4 & 8 & 12 & 16 \\
\hhline{~~|*4{-}|}
& 3 & 3 & 6 & 9 & 12 \\
\hhline{~~|*4{-}|}
& 2 & 2 & 4 & 6 & 8 \\
\hhline{~~|*4{-}|}
& 1 & 1 & 2 & 3 & 4 \\
\hhline{~~|*4{-}|} \\[-1ex]
& \multicolumn{1}{c}{} & \multicolumn{1}{c}{1} & \multicolumn{1}{c}{2}
& \multicolumn{1}{c}{3} & \multicolumn{1}{c}{4} \\
& \multicolumn{1}{c}{} & \multicolumn{4}{c}{\sffamily Probability of event}
\end{tabular}

\end{document}


-

I'll explain how to do this with ConTeXt + Lua. To keep the answer simple, I'll start by not considering the the "labels" of the rows and columns. I think that a good interface for this type of table is to be able to type:

\ProbabilityTable
{ { 4, 8, 12, 16 },
{ 3, 6,  9, 12 },
{ 2, 4,  6,  8 },
{ 1, 2,  3,  4 },
}


and get the desired result. So, lets start with the bare bones implementation so that this interface works. Since there is some flow-control and logic involved, I'll move all the calculations to Lua. So, at the TeX end, we simply need:

\unexpanded\def\ProbabilityTable#1%
{\ctxcommand{probability_table({#1})}}


where probability_table (actually commands.probability_table) is a Lua function that takes a 2-D Lua table and typesets it. The Lua part of the code is straight forward, so there are only a few comments in the code. Basically, we iterate over the lua-table, and then typeset it as a ConTeXt table.

\startluacode
-- Function that returns an index (low|medium|high) for an integer
local index = function(n)
local value = ""
if n <= 2 then
value = "low"
elseif n <= 7 then
value = "medium"
else
value = "high"
end
return value
end

-- Map values to colors
local colors = {
["low"]    = "yellow",
["medium"] = "orange",
["high"]   = "red",
}

-- Map values to labels
local labels = {
["low"]    = "(Low)",
["medium"] = "(Med)",
["high"]   = "(High)",
}

-- Print the probability table
probability_table = function(values)
-- Assume that values is a 2-D table:
context.bTABLE({"setups=probability:table"})
for row = 1,#values do
line = values[row]

context.bTR()
for col = 1, #line do
value = line[col]
idx   = index(value)

options = { string.format("backgroundcolor=%s", colors[idx]) }
context.bTD(options)
context("%s \\crlf %s",  value, labels[idx])
context.eTD()

end
context.eTR()
end
context.eTABLE()
end

-- Create a public interface
commands.probability_table = probability_table;

\stopluacode


Combining all this gives:

Now that the basic interface is working, we can take care of the formatting. For ConTeXt tables, this can be done by specifying a setup and adding an interface to it (which is already done by "{setups=probability:table}" line in the lua code).

\startsetups probability:table
\setupTABLE[background=color]
\stopsetups


which gives:

Finally, to fix the alignment and formatting, add:

\startsetups probability:table
\setupTABLE[background=color]
\setupTABLE[align=middle, style=\ssbf, offset=0.5em]
\stopsetups


which gives

Adding row and column labels is left as a simple exercise for the reader :-). The labels are simply a new row and column with slightly different formatting.

The complete ConTeXt code is:

\startluacode
-- Function that returns an index (low|medium|high) for an integer
local index = function(n)
local value = ""
if n <= 2 then
value = "low"
elseif n <= 7 then
value = "medium"
else
value = "high"
end
return value
end

-- Map values to colors
local colors = {
["low"]    = "yellow",
["medium"] = "orange",
["high"]   = "red",
}

-- Map values to labels
local labels = {
["low"]    = "(Low)",
["medium"] = "(Med)",
["high"]   = "(High)",
}

-- Print the probability table
probability_table = function(values)
-- Assume that values is a 2-D table:
context.bTABLE({"setups=probability:table"})
for row = 1,#values do
line = values[row]

context.bTR()
for col = 1, #line do
value = line[col]
idx   = index(value)

options = { string.format("backgroundcolor=%s", colors[idx]) }
context.bTD(options)
context("%s \\crlf %s",  value, labels[idx])
context.eTD()

end
context.eTR()
end
context.eTABLE()
end

-- Create a public interface
commands.probability_table = probability_table;

\stopluacode

\unexpanded\def\ProbabilityTable#1%
{\ctxcommand{probability_table({#1})}}

\startsetups probability:table
\setupTABLE[background=color]
\setupTABLE[align=middle, style=\ssbf, offset=0.5em]
\stopsetups

\starttext
\ProbabilityTable
{ { 4, 8, 12, 16 },
{ 3, 6,  9, 12 },
{ 2, 4,  6,  8 },
{ 1, 2,  3,  4 },
}
\stoptext


Note that:

1. There is a nice separation between content and presentation. You can easily change the formatting by changing the probability:table setup.

2. It is easy to change how the result is shown by changing the labels and colors tables. This can be made configurable at the TeX end as well.

3. I find it easier to program in Lua rather than in TeX.

4. ConTeXt provides a nice lua interface for the TeX macros. So, you can use context.bTABLE() rather than tex.print("\\bTABLE"), etc.

EDIT: Heiko Oberdiek mentioned that the cell background should not completely fill the cell. To achieve that in ConTeXt, add backgroundoffset=<dimen> to the table settings. For example, with:

\startsetups probability:table
\setupTABLE[background=color, backgroundoffset=-1pt]
\setupTABLE[align=middle, style=\ssbf, offset=0.5em]
\stopsetups


you get:

-
Excellent separation of content and presentation; thank for the detailed code. –  Will Robertson Apr 21 at 22:54

Here is my try with MetaPost and LuaLaTeX. I hope I've correctly understood the underlying logic of the picture. You can change the size of the squares with the u parameter, and their numbers with the n parameter.

I think this kind of picture can be better done with whatever drawing package devised for LaTeX (tikz, pstricks, mfpic, MetaPost, Asymptote and so on), provided that you have a reasonable knowledge of this package. This way it is easier to express the underlying mathematical logic of the picture in the program that draws it.

\documentclass{article}
\usepackage{unicode-math}
\usepackage{luamplib}
\mplibtextextlabel{enable}
\begin{document}
\begin{mplibcode}
input mpcolornames;
u = 1.25cm; n = 4; path mysquare;
vardef fill_square(expr i, j, str, clr) =
save mysquare; path mysquare;
mysquare = ((i-1, j-1) -- (i, j-1) -- (i, j) -- (i-1, j) -- cycle) scaled u;
fill mysquare withcolor clr;
label("\begin{tabular}{c}$" & decimal(i*j) & "$\\" & str & "\end{tabular}",
u*(i-.5, j-.5));
enddef;
beginfig(1);
for i = 1 upto n:
for j = 1 upto n:
if (i+j = n) or (i+j = n+1): fill_square(i, j, "(Mid)", Orange);
elseif i+j < n: fill_square(i, j, "(Low)", Yellow);
else: fill_square(i, j, "(High)", red);
fi;
endfor;
endfor;
for i = 1 upto n:
label.bot("$" & decimal i & "$", u*(i-.5, 0));
label.lft("$" & decimal i & "$", u*(0, i-.5));
endfor;
labeloffset := 6*labeloffset;
label.bot(btex Probability of event etex, u*(n/2, 0));
label.lft(btex Impact of event etex rotatedaround (u*(0, n/2), -90), u*(0, n/2));
for i = 0 upto n:
draw (i*u, 0) -- u*(i, n);
draw (0, i*u) -- u*(n, i);
endfor;
endfig;
\end{mplibcode}
\end{document}


-
You can avoid using tabluar environment by using label.top(decimal i*j, p) and label.bot("High)", p), etc. –  Aditya Apr 21 at 18:15
@Aditya True, but that would not work so easily if there were more than two lines. That's the reason why I chose a more general way. –  fpast Apr 21 at 18:17

The grid can also be drawn with the "good old" picture environment.

• So far it is the only answer, that respects that the cell color does not fill the cell entirely; it leaves a tiny space to the surrounding black lines. Probably the purpose of the space is the increase of the contrast for the lines.

• The rotated label at the left is done by \rotatebox of the graphicx package.

• Package multido helps with \multido for setting the cells. Also it calculates the values in the cells. And the cell value is used to select the color and text via macro \ClassifyValue.

\documentclass{article}

\usepackage{graphicx}
\usepackage{color}
\usepackage{multido}

\begin{document}
\setlength{\unitlength}{1cm}
\definecolor{med}{rgb}{1,.4,0}
\newcommand*{\ClassifyValue}[4]{%
\ifnum#1<3 %
#2%
\else
\ifnum#1<7 %
#3%
\else
#4%
\fi
\fi
}
\begin{picture}(5,5)(-1,-1)
\small
\sffamily
\multido{\ni=1+1,\ny=0+1}{4}{%
\multido{\nv=\ni+\ni,\nx=0+1}{4}{%
\put(\nx,\ny){%
\makebox(1,1){%
\textcolor{\ClassifyValue{\nv}{yellow}{med}{red}}{%
\rule{\unitlength}{\unitlength}%
}%
}%
}%
\put(\nx,\ny){%
\makebox(1,1){%
\scriptsize
\bfseries
\begin{tabular}{@{}c@{}}%
\nv\\
(\ClassifyValue{\nv}{Low}{Med}{High})%
\end{tabular}%
}%
}%
}%
}%
\multiput(0,0)(0,1){5}{\line(1,0){4}}%
\multiput(0,0)(1,0){5}{\line(0,1){4}}%
\multido{\nx=0+1,\nv=1+1}{4}{%
\put(\nx,-.35){\makebox(1,0){\nv}}%
}%
\put(2,-1){\makebox(0,0)[b]{Probability of event}}%
\multido{\ny=0+1,\nv=1+1}{4}{%
\put(-.35,\ny){\makebox(0,1){\nv}}%
}%
\put(-1,2){\makebox(0,0)[l]{\rotatebox{270}{Impact of event}}}%
\end{picture}%
\end{document}


-
I edited my answer so that the background color does not cover the complete cell. –  Aditya Apr 22 at 21:39