4

I am drawing a large heatmap containing only colors using \pgfplotstabletypeset. The table contains 103 columns, and 111 rows, which I am trying to squeeze in the area of 13cm * 16cm. In order to save horizontal space, I have rotated the labels in the first row by 90 degrees, but the columns are still much too wide. Simultaneously, rows are too tall.

I would like to specify table cell width and height. I'm not worried about the labels in the 1st row/column becoming super-tiny as a result of this. How do I do that?

Below, you'll find my MWE. It's inspired by this question. The only thing I found regarding the adjustment of column widths in pgfplotstable can be found in Sec. 2.3 of the pgfplotstablr docs. I already applied that in my MWE, but it doesn't do much in my favor.

\documentclass{standalone}

\listfiles
\usepackage{colortbl}
\usepackage{array}
\usepackage{filecontents}
\usepackage{pgfplotstable}
\usetikzlibrary{pgfplots.colormaps}

\pgfplotsset{compat=1.9}
\pgfplotsset{
    colormap={myCustomColorMap}{
        color(0)=(blue);
        color(1)=(red);
    },
}

% https://tex.stackexchange.com/a/158985/31877
\pgfplotstableset{
    /color cells/min/.initial=0,
    /color cells/max/.initial=1000,
    /color cells/textcolor/.initial=,
    /color cells/colormap name/.initial=,% this was added
    %
    % Usage: 'color cells={min=<value which is mapped to lowest color>, 
    %   max = <value which is mapped to largest>}
    color cells/.code={%
        \pgfqkeys{/color cells}{#1}%
        \pgfkeysalso{%
            postproc cell content/.code={%
                \begingroup
                % acquire the value before any number printer changed it:
                \pgfkeysgetvalue{/pgfplots/table/@preprocessed cell content}\value
                \ifx\value\empty
                    \endgroup
                \else
                \pgfkeys{/pgf/fpu=true}%
                \pgfmathparse{log10(\value)}%
                \pgfmathfloattofixed{\pgfmathresult}%
                \let\value=\pgfmathresult
                \pgfkeys{/pgf/fpu=false}%
                % map that value:
                \pgfplotscolormapaccess
                    [\pgfkeysvalueof{/color cells/min}:\pgfkeysvalueof{/color cells/max}]
                    {\value}
                    {\pgfkeysvalueof{/color cells/colormap name}}% I CHANGED THIS
                % now, \pgfmathresult contains {<R>,<G>,<B>}
                % 
                % acquire the value AFTER any preprocessor or
                % typesetter (like number printer) worked on it:
                \pgfkeysgetvalue{/pgfplots/table/@cell content}\typesetvalue
                \pgfkeysgetvalue{/color cells/textcolor}\textcolorvalue
                % tex-expansion control
                \toks0=\expandafter{\empty}%\typesetvalue}%
                \xdef\temp{%
                    \noexpand\pgfkeysalso{%
                        @cell content={%
                            \noexpand\cellcolor[rgb]{\pgfmathresult}%
                            \noexpand\definecolor{mapped color}{rgb}{\pgfmathresult}%
                            \ifx\textcolorvalue\empty
                            \else
                                \noexpand\color{\textcolorvalue}%
                            \fi
                            \the\toks0 %
                        }%
                    }%
                }%
                \endgroup
                \temp
                \fi
            }%
        }%
    }
}

\pgfmathdeclarefunction{lg10}{1}{%
    \pgfmathparse{ln(#1)/ln(10)}%
}

\begin{filecontents}{data.csv}
item,Xa,Xb,Ya,Yb,Za,Zb
a,2,,2,,783764,783764
b,,2,,,500000,500000
c,,5,,,100000,100000
d,2,,8,,20000,20000
e,,,,,10000,10000
f,,,,3,5000,5000
g,,2,,,1000,1000
h,6,,,4,500,500
i,,,4,,100,100
j,,,,,50,50
k,,2,,,10,10
l,,,7,,5,5
m,,6,,3,2,2
n,,,,,1,1
\end{filecontents}

\begin{document}
\newcolumntype{C}{>{\centering\arraybackslash}p{1mm}}

\pgfplotstabletypeset[
    font=\tiny,
    every head row/.style={
        typeset cell/.code={
        \ifnum\pgfplotstablecol=1
            \pgfkeyssetvalue{/pgfplots/table/@cell content}{&}%
        \else
            \ifnum\pgfplotstablecol=\pgfplotstablecols
                \pgfkeyssetvalue{/pgfplots/table/@cell content}{\rotatebox{90}{##1}\\}%
            \else
                \pgfkeyssetvalue{/pgfplots/table/@cell content}{\rotatebox{90}{##1}&}%
            \fi
        \fi
        }
    },
    %max=lg10(783764)
    colorCell/.style={
        column type=C,
        color cells={min=0,max=5.9,colormap name=myCustomColorMap}
    },
    itemCell/.style={
        string type,
        column name={},
    },
    col sep=comma,
    columns={item,Xa,Xb,Ya,Yb,Za,Zb},
    columns/item/.style={itemCell},
    columns/Xa/.style={colorCell},
    columns/Xb/.style={colorCell},
    columns/Ya/.style={colorCell},
    columns/Yb/.style={colorCell},
    columns/Za/.style={colorCell},
    columns/Zb/.style={colorCell},
]{data.csv}

\end{document}

rendering

3

The easiest solution may be to put the pgfplotstable into the \resizebox command. From Wikibooks:

The graphicx packages features the command \resizebox{width}{height}{object} which can be used with tabular to specify the height and width of a table.

\documentclass{standalone}

\listfiles
\usepackage{colortbl}
\usepackage{array}
\usepackage{filecontents}
\usepackage{pgfplotstable}
\usepackage{graphicx}
\usetikzlibrary{pgfplots.colormaps}

\pgfplotsset{compat=1.9}
\pgfplotsset{
    colormap={myCustomColorMap}{
        color(0)=(blue);
        color(1)=(red);
    },
}

% http://tex.stackexchange.com/a/158985/31877
\pgfplotstableset{
    /color cells/min/.initial=0,
    /color cells/max/.initial=1000,
    /color cells/textcolor/.initial=,
    /color cells/colormap name/.initial=,% this was added
    %
    % Usage: 'color cells={min=<value which is mapped to lowest color>, 
    %   max = <value which is mapped to largest>}
    color cells/.code={%
        \pgfqkeys{/color cells}{#1}%
        \pgfkeysalso{%
            postproc cell content/.code={%
                \begingroup
                % acquire the value before any number printer changed it:
                \pgfkeysgetvalue{/pgfplots/table/@preprocessed cell content}\value
                \ifx\value\empty
                    \endgroup
                \else
                \pgfkeys{/pgf/fpu=true}%
                \pgfmathparse{log10(\value)}%
                \pgfmathfloattofixed{\pgfmathresult}%
                \let\value=\pgfmathresult
                \pgfkeys{/pgf/fpu=false}%
                % map that value:
                \pgfplotscolormapaccess
                    [\pgfkeysvalueof{/color cells/min}:\pgfkeysvalueof{/color cells/max}]
                    {\value}
                    {\pgfkeysvalueof{/color cells/colormap name}}% I CHANGED THIS
                % now, \pgfmathresult contains {<R>,<G>,<B>}
                % 
                % acquire the value AFTER any preprocessor or
                % typesetter (like number printer) worked on it:
                \pgfkeysgetvalue{/pgfplots/table/@cell content}\typesetvalue
                \pgfkeysgetvalue{/color cells/textcolor}\textcolorvalue
                % tex-expansion control
                \toks0=\expandafter{\empty}%\typesetvalue}%
                \xdef\temp{%
                    \noexpand\pgfkeysalso{%
                        @cell content={%
                            \noexpand\cellcolor[rgb]{\pgfmathresult}%
                            \noexpand\definecolor{mapped color}{rgb}{\pgfmathresult}%
                            \ifx\textcolorvalue\empty
                            \else
                                \noexpand\color{\textcolorvalue}%
                            \fi
                            \the\toks0 %
                        }%
                    }%
                }%
                \endgroup
                \temp
                \fi
            }%
        }%
    }
}

\pgfmathdeclarefunction{lg10}{1}{%
    \pgfmathparse{ln(#1)/ln(10)}%
}

\begin{filecontents}{data.csv}
item,Xa,Xb,Ya,Yb,Za,Zb
a,2,,2,,783764,783764
b,,2,,,500000,500000
c,,5,,,100000,100000
d,2,,8,,20000,20000
e,,,,,10000,10000
f,,,,3,5000,5000
g,,2,,,1000,1000
h,6,,,4,500,500
i,,,4,,100,100
j,,,,,50,50
k,,2,,,10,10
l,,,7,,5,5
m,,6,,3,2,2
n,,,,,1,1
\end{filecontents}

\newcolumntype{C}{>{\centering\arraybackslash}p{1mm}}



\begin{document}
\resizebox{3in}{2in}{\pgfplotstabletypeset[
    font=\tiny,
    every head row/.style={
        typeset cell/.code={
        \ifnum\pgfplotstablecol=1
            \pgfkeyssetvalue{/pgfplots/table/@cell content}{&}%
        \else
            \ifnum\pgfplotstablecol=\pgfplotstablecols
                \pgfkeyssetvalue{/pgfplots/table/@cell content}{\rotatebox{90}{##1}\\}%
            \else
                \pgfkeyssetvalue{/pgfplots/table/@cell content}{\rotatebox{90}{##1}&}%
            \fi
        \fi
        }
    },
    %max=lg10(783764)
    colorCell/.style={
        column type=C,
        color cells={min=0,max=5.9,colormap name=myCustomColorMap}
    },
    itemCell/.style={
        string type,
        column name={},
    },
    col sep=comma,
    columns={item,Xa,Xb,Ya,Yb,Za,Zb},
    columns/item/.style={itemCell},
    columns/Xa/.style={colorCell},
    columns/Xb/.style={colorCell},
    columns/Ya/.style={colorCell},
    columns/Yb/.style={colorCell},
    columns/Za/.style={colorCell},
    columns/Zb/.style={colorCell},
]{data.csv}}
\end{document}

enter image description here

One strange thing about this though is that even though I specify the options width=3in and height=2in to \resizebox, for some reason my output has width 3.01 in and height 3.9 in. I don't know why \resizebox gets the height wrong by a multiplicative factor.

  • 1
    I was considering that. But I'm a bit reluctant, since this destroys the aesthetically pleasing proportions of the font used; Even if the text becomes insanely tiny. – derabbink Feb 7 '14 at 18:22
2

Based on what I learned in How to map value to color using TikZ?, I am able to do what you want by using the R programming language to pre-process the input file, then to draw each rectangle using TikZ and a \foreach loop. (The answer in Need some help getting started with TikZ, groupplots and color mapping of matrix uses a similar approach where instead of using pgfplotstable, the answerer draws his own "table" using TikZ). Unfortunately I don't know enough TeX to do all the processing in TeX, so I did the processing in R. (But you could use your favorite programming language instead of R).

First, I save the input file as a text file 2014-02-09.txt.

Next, I run an R script to compute the base-10 logs, and output the entries in the input file into a LaTeX list. (Note: R is a widely used statistical programming language.)

temp <- read.table('2014-02-09.txt', sep=',', header = TRUE)

sink('2014-02-09-converted.tex')
prestring <- ''
cat('\\newcommand*{\\ListOfRectangles}{')
cat('%x/y/value\n')
for (i in 1:nrow(temp)) {
  for (j in 2:ncol(temp)) {
    value <- log(temp[i,j]) / log(10)
    if (!is.na(value)) {
      cat(sprintf('%s%d/%d/%.2f', prestring, j, i, value))
      prestring <- ',\n'
    }
  }
}
cat('%\n}')
sink()

The output is a file 2014-02-09-converted.tex whose contents are:

\newcommand*{\ListOfRectangles}{%x/y/value
2/1/0.30,
4/1/0.30,
6/1/5.89,
7/1/5.89,
3/2/0.30,
6/2/5.70,
7/2/5.70,
3/3/0.70,
6/3/5.00,
7/3/5.00,
2/4/0.30,
4/4/0.90,
6/4/4.30,
7/4/4.30,
6/5/4.00,
7/5/4.00,
5/6/0.48,
6/6/3.70,
7/6/3.70,
3/7/0.30,
6/7/3.00,
7/7/3.00,
2/8/0.78,
5/8/0.60,
6/8/2.70,
7/8/2.70,
4/9/0.60,
6/9/2.00,
7/9/2.00,
6/10/1.70,
7/10/1.70,
3/11/0.30,
6/11/1.00,
7/11/1.00,
4/12/0.85,
6/12/0.70,
7/12/0.70,
3/13/0.78,
5/13/0.48,
6/13/0.30,
7/13/0.30,
6/14/0.00,
7/14/0.00%
}

Finally, I compile the LaTeX code which \inputs the previously computed list. Notice that by varying the xscale and yscale options you can change the size of the PDF output.

\documentclass{standalone}

\usepackage{tikz}

\input{2014-02-09-converted}

\begin{document}
% to change the output size of the PDF, change the xscale and yscale values.
\begin{tikzpicture}[xscale=0.5,yscale=0.3]
  \colorlet{color min}[rgb]{blue}
  \colorlet{color max}[rgb]{red}

  \def\min{0}
  \def\max{5.89}

  \foreach \sX/\sY/\myvalue in \ListOfRectangles {
    \pgfmathtruncatemacro\lambda{(\max-\myvalue)/(\max-\min)*100}
    \colorlet{my color}[rgb]{color min!\lambda!color max}
    \fill[my color] (\sX, -\sY) rectangle +(1cm,1cm);
  }
\end{tikzpicture}
\end{document}

Output:

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.