I'd like to use data stored in an external CSV file to fill nodes inside a tikzpicture matrix. If my data is stored as x/y/z, I can write the following loop (put together from this and this):

\documentclass{standalone}
\usepackage{tikz}
\usepackage{xcolor}
\usepackage{catchfile}
\newcommand\loaddata[1]{\CatchFileDef\loadeddata{#1}{\endlinechar=-1}}

\begin{filecontents*}{\jobname.dat}
1/1/20, 1/2/80, 1/3/20,
2/1/80, 2/2/100, 2/3/80,
3/1/20, 3/2/80, 3/3/20
\end{filecontents*}

\begin{document}
\begin{tikzpicture}
  \loaddata{\jobname.dat}
  \foreach \x/\y/\c in \loadeddata {
      \pgfmathsetmacro\k{\c*1} % \c values could be transformed here
      \fill[color=red!\k] (\x,\y) rectangle +(1,1);
   }
\end{tikzpicture}
\end{document}

My data, instead, is stored as a matrix of z values (color intensities), e.g.

\begin{filecontents*}{\jobname.csv}
20, 80, 20
80, 100, 80
20, 80, 20
\end{filecontents*}

Please help me adapt the code so that I can read the colors from the file and that the x and y coordinates are inferred from the cell position inside the matrix.

The tikzpicture looks like this:

enter image description here

  • Do you want to read the actual color from your dat file (red!20), or do you prefer it as you have it? Also not all these data needed in dat file.... just the color could be enough. – koleygr Sep 14 at 8:48
  • Ok... but it could be a matrix of integers with just the color value like z_i but without 1/1/z_1 1/2/z_2 etc... Would this be accepted or you prefer your own dat file format? – koleygr Sep 14 at 8:53
  • 1
    Thanks, got it. – koleygr Sep 14 at 8:55
  • When I posted my question, I expected that the answer would be straightforward, but as the answers of koleygr and marmot show, it involves quite a bit of trickery! On second thoughts, I could have edited my CSV file to put it in the x/y/z format and use the code that I present in the OP. In R, this is one way to do it: If m is my matrix of color intensities, then matrix(mapply(function(x, i, j) paste0(i,"/",j,"/",x), m, row(m), col(m)), nrow = nrow(m)) returns the matrix in the x/y/z format. But I'm glad to be learning how to do this stuff in LaTeX! – PatrickT Sep 14 at 16:13
  • If the loaded data is quoted, e.g. "1/2/100", then sticking \catcode`"=9 at the start of the tikzpicture will remove the quote marks (there may be undesirable side effects to this bulldozer approach, but it worked in this example). – PatrickT Sep 14 at 16:56
up vote 5 down vote accepted

First version: The size of the data matrix is known and hard-coded (here a 3 x 3 matrix):

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{xcolor}
\usepackage{catchfile}
\usepackage{filecontents}
\newcommand\loaddata[1]{\CatchFileDef\loadeddata{#1}{\endlinechar=-1}}

\begin{filecontents*}{\jobname.csv}
2, 8, 2,
8, 10, 8,
2, 8, 2
\end{filecontents*}

\begin{document}
\def\MySpaceLength{3}
\begin{tikzpicture}
   \loaddata{\jobname.csv}
   \foreach \k[count=\i from 0] in \loadeddata{
   \pgfmathsetmacro\myx{-3*int(\i/3)+\i}
   \pgfmathsetmacro\myy{int(\i/3)}
   \fill[color=red!\k] ({\MySpaceLength*\myx},-{\MySpaceLength*\myy}) rectangle+ ($(\MySpaceLength,\MySpaceLength)$);
   }
\end{tikzpicture}
\end{document}

enter image description here

Second version: the size of the matrix is known and set by the user, but not hard-coded. It is stored in the variables \XMatrixDimension and \YMatrixDimension:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{xcolor}
\usepackage{catchfile}
\usepackage{filecontents}
\newcommand\loaddata[1]{\CatchFileDef\loadeddata{#1}{\endlinechar=-1}}

\begin{filecontents*}{\jobname.csv}
2, 8, 2, 5,
8, 10, 8, 90,
2, 8, 2,60,
20, 2 , 56, 40,
30, 20, 50, 70
\end{filecontents*}

\begin{document}
\def\MySpaceLength{3}
\def\XMatrixDimension{4}
\def\YMatrixDimension{5}
\begin{tikzpicture}
   \loaddata{\jobname.csv}
   \foreach \k[count=\i from 0] in \loadeddata{
   \pgfmathsetmacro\myx{-\XMatrixDimension*int(\i/\XMatrixDimension)+\i}
   \pgfmathsetmacro\myy{int(\i/\XMatrixDimension)}
   \fill[color=red!\k] ({\MySpaceLength*\myx},-{\MySpaceLength*\myy}) rectangle+ ($(\MySpaceLength,\MySpaceLength)$);
   }
\end{tikzpicture}
\end{document}

Output:

enter image description here

  • Thanks for your answer koleygr. I think I understand the steps. I'll play around with this a little and get back to you if I get stumped. Perhaps I should have considered making a CSV of my data (obtained with R) in the 1/2/3 format. Might have been easier. That said, it's good to learn how to do this with LaTeX! Thanks. – PatrickT Sep 14 at 12:33
  • 1
    For your second version, I think that \pgfplotstablegetelem can be used to calculate the dimension of the matrix, such that it would not have to be set by the user at all, something similar to what marmot does in their answer and/or similar to this answer: tex.stackexchange.com/questions/343770/… – PatrickT Sep 14 at 16:10
  • 1
    @PatrickT, Nice addition and very nice edit. But instead of using a huge package (pgfplots) just to avoid one or two copy pastes is not recommended. If you want to use this functionality many times and you dont want just to copy paste the matrix and change any // with comma, then the way is what you already mentioned. So, the comment is useful – koleygr Sep 14 at 16:24
  • indeed good point about not loading a package that is not absolutely needed. My purpose has been served, so I thank you and shall accept your answer, as it came before marmot's, whose answer relies on the pgfplots package and is also very commendable! :-) – PatrickT Sep 14 at 16:35
  • Thanks. marmot has my vote now because I just learned some pgf – koleygr Sep 14 at 16:40

Here is a solution based on matrix plot that comes with pgfplots. It is taken from this answer, whose second part has some similarity with koleygr's answer. The advantage of the pgfplots solution is that you can use color maps for that. Of course, you can drop the colorbar.

\documentclass[border=3.14mm,tikz]{standalone}
\usepackage{pgfplots}
\usetikzlibrary{pgfplots.colormaps}
\pgfplotsset{compat=1.16}
\usepackage{pgfplotstable}
\usepackage{filecontents}

\begin{filecontents*}{\jobname.csv}
2, 8, 2
8, 10, 8
2, 8, 2
\end{filecontents*}
\newcommand*{\ReadOutElement}[4]{%
    \pgfplotstablegetelem{#2}{[index]#3}\of{#1}%
    \let#4\pgfplotsretval
}
\begin{document}
\pgfplotstableread[header=false,col sep=comma]{\jobname.csv}\datatable
\pgfplotstablegetrowsof{\datatable}
\pgfmathtruncatemacro{\numrows}{\pgfplotsretval}
\pgfplotstablegetcolsof{\datatable}
\pgfmathtruncatemacro{\numcols}{\pgfplotsretval}
\xdef\LstX{}
\xdef\LstY{}
\xdef\LstC{}
\foreach \Y [evaluate=\Y as \PrevY using {int(\Y-1)},count=\nY] in {1,...,\numrows}
{\pgfmathtruncatemacro{\newY}{\numrows-\Y}
\foreach \X  [evaluate=\X as \PrevX using {int(\X-1)},count=\nX] in {1,...,\numcols}
{
\ReadOutElement{\datatable}{\PrevY}{\PrevX}{\Current}
\pgfmathtruncatemacro{\nZ}{\nX+\nY}
\ifnum\nZ=2
\xdef\LstX{\PrevX}
\xdef\LstY{\PrevY}
\xdef\LstC{\Current}
\else
\xdef\LstX{\LstX,\PrevX}
\xdef\LstY{\LstY,\PrevY}
\xdef\LstC{\LstC,\Current}
\fi
}
}
\edef\temp{\noexpand\pgfplotstableset{
 create on use/x/.style={create col/set list={\LstX}},
 create on use/y/.style={create col/set list={\LstY}},
 create on use/color/.style={create col/set list={\LstC}},}}
\temp
\pgfmathtruncatemacro{\strangenum}{\numrows*\numcols}
\pgfplotstablenew[columns={x,y,color}]{\strangenum}\strangetable

%\pgfplotstabletypeset[empty cells with={---}]\strangetable
\begin{tikzpicture}
\pgfplotsset{%
    colormap={WhiteRedBlack}{%
        rgb255=(255,255,255)
        rgb255=(255,0,0)
        rgb255=(0,0,0)
    },
}
\begin{axis}[%
    small,
    every tick label/.append style={font=\tiny},
    tick align=outside,
    minor tick num=5,
    %
    xlabel=$\beta$,
    xticklabel pos=right,
    xlabel near ticks,
    xmin=-1, xmax=\numcols,
    xtick={0, 5, ..., 4},
    %
    ylabel=$\alpha$,
    ylabel style={rotate=-90},
    ymin=-1, ymax=\numrows,
    ytick={0, 5, ..., 4},
    %
    point meta min=0,
    point meta max=32,
    point meta=explicit,
    %
    %colorbar sampled,
    colorbar as palette,
    colorbar style={samples=3},
    colormap name=WhiteRedBlack,
    scale mode=scale uniformly,
]
\draw (axis description cs:0,0) -- (axis description cs:1,0);
 \addplot [
        matrix plot,
        %mesh/cols=4,
        point meta=explicit,
] table [meta=color,col sep=comma] \strangetable;
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

  • Thanks for your answer marmot. I'm going to need to play around with this a little to digest it. I've started reading the links, very useful. Quite frankly, I did not expect that changing the format of the data would induce so much extra work. Thanks! – PatrickT Sep 14 at 12:29
  • 1
    @PatrickT It could very well be that I missed a simple possibility... In case you find out, please ping me. ;-) – marmot Sep 14 at 12:32
  • Yours is an excellent answer and I have learned (and am still learning) quite a lot about the pgfplots package. I like how all the computations are automated. It was a difficult decision, but I selected koleygr's answer as it came first and is a little simpler (though not quite as general as yours). Thanks again! – PatrickT Sep 14 at 16:38
  • 1
    @PatrickT Yes, you should accept koleygr's answer. I can't help to note that the second part of my old answer is rather similar to his, and I would have wished he would have acknowledged this. Don't get me wrong, I think it is fine to reuse existing answers, but it would be nice to mention them when doing that. – marmot Sep 14 at 16:42

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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