Inspired by a question in the latex-community.org forum and on the basis of a solution to a similar question on tex.SX, I came up with some code to plot a heatmap
based on matrix in form of a standard tabular environment.
Single color example
I will first consider a more basic case where the heatmap
consist of a single color and values in the interval [0,1], with 0 being black and 1 red.
Example
\begin{table}[ht] \begin{center} \begin{tabular}{*{10}{R}} 0.03 & 0.34 & 0.41 & 0.25 & 0.89 & 0.49 & 0.79 & 0.83 & 0.82 & 0.94 \\ 0.49 & 0.25 & 0.80 & 0.83 & 0.93 & 0.25 & 0.82 & 0.80 & 0.35 & 0.00 \\ 0.21 & 0.13 & 0.53 & 0.07 & 1.00 & 0.66 & 0.07 & 0.18 & 0.73 & 0.05 \\ 0.81 & 0.49 & 0.27 & 0.07 & 0.57 & 0.12 & 0.26 & 0.39 & 0.57 & 0.80 \\ 0.23 & 0.31 & 0.90 & 0.03 & 0.18 & 0.60 & 0.49 & 0.02 & 0.44 & 0.50 \\ 0.40 & 0.91 & 0.84 & 0.88 & 0.04 & 0.59 & 0.75 & 0.91 & 0.69 & 0.88 \\ 0.36 & 0.12 & 0.92 & 0.73 & 0.12 & 0.58 & 0.42 & 0.89 & 0.15 & 0.50 \\ 0.93 & 0.07 & 0.57 & 0.43 & 0.12 & 0.70 & 0.54 & 0.25 & 0.06 & 0.57 \\ 0.09 & 0.98 & 0.46 & 0.83 & 0.69 & 0.56 & 0.08 & 0.77 & 0.40 & 0.88 \\ 0.07 & 0.44 & 0.65 & 0.46 & 0.65 & 0.75 & 0.80 & 0.56 & 0.89 & 0.50 \\ \end{tabular} \end{center} \end{table}
Complete sourcecode
\documentclass[12pt]{article} \usepackage{tikz} \usepackage{collcell} \newcommand*{\MinNumber}{0}% \newcommand*{\MaxNumber}{1}% \newcommand{\ApplyGradient}[1]{% \pgfmathsetmacro{\PercentColor}{100.0*(#1-\MinNumber)/(\MaxNumber-\MinNumber)} \hspace{-0.33em}\colorbox{red!\PercentColor!black}{} } \newcolumntype{R}{>{\collectcell\ApplyGradient}c<{\endcollectcell}} \renewcommand{\arraystretch}{0} \setlength{\fboxsep}{3mm} % box size \setlength{\tabcolsep}{0pt} \begin{document} \begin{table}[ht] \begin{center} \begin{tabular}{*{10}{R}} 0.03 & 0.34 & 0.41 & 0.25 & 0.89 & 0.49 & 0.79 & 0.83 & 0.82 & 0.94 \\ 0.49 & 0.25 & 0.80 & 0.83 & 0.93 & 0.25 & 0.82 & 0.80 & 0.35 & 0.00 \\ 0.21 & 0.13 & 0.53 & 0.07 & 1.00 & 0.66 & 0.07 & 0.18 & 0.73 & 0.05 \\ 0.81 & 0.49 & 0.27 & 0.07 & 0.57 & 0.12 & 0.26 & 0.39 & 0.57 & 0.80 \\ 0.23 & 0.31 & 0.90 & 0.03 & 0.18 & 0.60 & 0.49 & 0.02 & 0.44 & 0.50 \\ 0.40 & 0.91 & 0.84 & 0.88 & 0.04 & 0.59 & 0.75 & 0.91 & 0.69 & 0.88 \\ 0.36 & 0.12 & 0.92 & 0.73 & 0.12 & 0.58 & 0.42 & 0.89 & 0.15 & 0.50 \\ 0.93 & 0.07 & 0.57 & 0.43 & 0.12 & 0.70 & 0.54 & 0.25 & 0.06 & 0.57 \\ 0.09 & 0.98 & 0.46 & 0.83 & 0.69 & 0.56 & 0.08 & 0.77 & 0.40 & 0.88 \\ 0.07 & 0.44 & 0.65 & 0.46 & 0.65 & 0.75 & 0.80 & 0.56 & 0.89 & 0.50 \\ \end{tabular} \end{center} \end{table} \end{document}
The code in details
We load two packages, tikz and collcell. The first is needed to determine the color, it allows floating point calculations. We use the latter to pass values of table cells as input to a macro that we are going to define.
\usepackage{tikz} \usepackage{collcell}
Next, we need to set the minimum and a maximum value which will be set to black
and red
, here 0 and 1. The interval can contain zero, e.g. [-100,100].
\newcommand*{\MinNumber}{0}% \newcommand*{\MaxNumber}{1}
Now we define a macro that computes the percentage of red and black color based on the actual value in the table (passed as argument) and the min and max defined above. We also draw the square box in this macro and leave the box text parameter empty. LaTeX adds whitespace to tabular cells in front of text. We can remove this whitespace using a negative \hspace
. There probably exists a parameter controlling this whitespace, but I was not able to figure out its name.
\newcommand{\ApplyGradient}[1]{% \pgfmathsetmacro{\PercentColor}{100.0*(#1-\MinNumber)/(\MaxNumber-\MinNumber)} \hspace{-0.33em}\colorbox{red!\PercentColor!black}{} }
We define a custom column type R
. It calls the macro we just defined. As mentioned earlier, the collcell package passes the value of each cell in the column as argument to the macro. >{...}
and <{...}
is a way to add declarations before and after any content in each cell of a column. See the array/tabular package documentation for more details.
\newcolumntype{R}{>{\collectcell\ApplyGradient}c<{\endcollectcell}}
Finally, we want to completely remove whitespace from cells to get a grid without white lines between rows and columns, added by default. Here, \fboxsep
is used as an easy way to control the box size.
\setlength{\tabcolsep}{0pt} % whitespace between columns \renewcommand{\arraystretch}{0} % whitespace between rows (multiplicative factor) \setlength{\fboxsep}{3mm} % box size
With this we are ready to draw the table. Since each column is of the same type, R
, we use *{num_rep}{type}
as a shortcut for column repetition.
\begin{table}[ht] \begin{center} \begin{tabular}{*{10}{R}} ... \end{tabular} \end{center} \end{table}
Two-color example
To get two colors instead of just one, the code needs a minor modification in the macro that calculates colors and plots boxes.
For simplicity, I assume values in the interval [-1,1], with 1 bright red, 0 black and -1 bright green. However, this code is readily adapted to a different set of values. You might have to define a minimum and maximum for both colors should they cover a different range of values or change the condition. To distinguish between negative (green) and positive (red), we check whether the value is greater than zero (#1pt>\z@
). LaTeX knows how to handle floating point values only when a length unit is provided. This is why we use a little trick here and temporarily add points as unit (#1pt
). Besides that, the macro is almost exactly the same as what we saw in the single color case.
\makeatletter \newcommand{\ApplyGradient}[1]{% \ifdim\dimexpr#1pt>\z@ \pgfmathsetmacro{\PercentColor}{100.0*(#1-\MinNumber)/(\MaxNumber-\MinNumber)} \hspace{-0.33em}\colorbox{red!\PercentColor!black}{} \else \pgfmathsetmacro{\PercentColor}{100.0*(-#1-\MinNumber)/(\MaxNumber-\MinNumber)} \hspace{-0.33em}\colorbox{green!\PercentColor!black}{} \fi } \makeatother
Limitations
The limitation of this approach clearly is size. I tried 1000×1000 and LaTeX could not handle it due to the capacity (!TeX capacity exceeded, sorry [main memory size=3000000]
). You might be able to increase that value. 100×100 worked perfectly and here is what it looks like.
Generation of tables
Tables were generated using the following R code:
library(xtable) xtable(replicate(10, runif(10,0,1))) % 10x10 matrix uniform [0,1] xtable(replicate(10, runif(10,-1,1))) % 10x10 matrix uniform [-1,1] xtable(replicate(100, runif(100,0,1))) % 100x100 matrix uniform [0,1]
hnhuty
I want to add row and column names, a key (legend) and shrink the size of the plot. Can you help me?
tom
I’d add them manually to the table. You can control the plot size through the box size parameter:
Please provide a minimal working example in case this does not answer your question completely and I’ll be happy to give it a try.
winniex0412
I want to add multiple heat maps with different colors in my article. Could you help me?
tom
One possibility would be to extend the function \ApplyGradient so you can pass the colors to the function. HTH, Tom