19

I have a pretty big binary matrix of maybe 70 rows and 64 columns. For ease of viewing, I would like to draw this matrix in TikZ where e.g. a 1 is represtented by a red square and a 0 is a white square. Does anyone have any idea how I could obtain this?

Ideally, I would have something where I can just load the matrix from a file and TikZ would handle everything else. Otherwise, I am also happy with a solution having to do find/replace every 0 by some string, and replace every 1 by another string.

Thank you in advance!

Example matrix:

10110101010
10010101010
01010111010
11110010100
01100011001
11101010111
10101010111

6 Answers 6

21

The answer provides two solutions: by means of the first one it is possible to customize the dimension of the squares and perhaps suits better your needs. Both are based on answers given here.

Solution n.1 is based on Create square and custom size cells in a table using pgfplotstable:

\documentclass{article}
\usepackage{filecontents}
\usepackage[table]{xcolor}
\usepackage{pgfplotstable}
\usetikzlibrary{calc}
\pgfplotsset{compat=1.8}

\begin{filecontents}{matrix.cvs}
1 0 1 1 0 1 0 1 0 1 0
1 0 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 1 1 0 1 0
1 1 1 1 0 0 1 0 1 0 0
0 1 1 0 0 0 1 1 0 0 1
1 1 1 0 1 0 1 0 1 1 1
1 0 1 0 1 0 1 0 1 1 1
\end{filecontents}

\makeatletter
\tikzset{
    zero color/.initial=white,
    zero color/.get=\zerocol,
    zero color/.store in=\zerocol,
    one color/.initial=red,
    one color/.get=\onecol,
    one color/.store in=\onecol,
    cell wd/.initial=1ex,
    cell wd/.get=\cellwd,
    cell wd/.store in=\cellwd,
    cell ht/.initial=1ex,
    cell ht/.get=\cellht,
    cell ht/.store in=\cellht,
}

\newcommand{\drawgrid}[2][]{
\medskip
\begin{tikzpicture}[#1]
  \pgfplotstableforeachcolumn#2\as\col{
    \pgfplotstableforeachcolumnelement{\col}\of#2\as\colcnt{%
      \ifnum\colcnt=0
        \fill[\zerocol]($ -\pgfplotstablerow*(0,\cellht) + \col*(\cellwd,0) $) rectangle+(\cellwd,\cellht);
      \fi
      \ifnum\colcnt=1
        \fill[\onecol]($ -\pgfplotstablerow*(0,\cellht) + \col*(\cellwd,0) $) rectangle+(\cellwd,\cellht);
      \fi
    }
  }
\end{tikzpicture}
\medskip
}
\makeatother

\begin{document}
% read the file
\pgfplotstableread{matrix.cvs}{\matrixfile}

\drawgrid{\matrixfile}

\drawgrid[zero color=green, one color=cyan]{\matrixfile}

\drawgrid[zero color=orange, 
  one color=violet,
  cell ht=2em,
  cell wd=2em]{\matrixfile}
\end{document}

The result:

enter image description here

Solution n.2 is based on Parametrize shading in table through TikZ:

\documentclass{article}
\usepackage{filecontents}
\usepackage[table]{xcolor}
\usepackage{pgfplotstable}
\pgfplotsset{compat=1.8}

\begin{filecontents}{matrix.cvs}
1 0 1 1 0 1 0 1 0 1 0
1 0 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 1 1 0 1 0
1 1 1 1 0 0 1 0 1 0 0
0 1 1 0 0 0 1 1 0 0 1
1 1 1 0 1 0 1 0 1 1 1
1 0 1 0 1 0 1 0 1 1 1
\end{filecontents}

\makeatletter
\pgfplotstableset{
    zero color/.initial=white,
    zero color/.get=\zerocol,
    zero color/.store in=\zerocol,
    one color/.initial=red,
    one color/.get=\onecol,
    one color/.store in=\onecol,
    color cells/.style={
        every head row/.style={output empty row},
        string type,
        postproc cell content/.code={%
           \pgfkeysalso{@cell content=\rule{0cm}{2.4ex}\cellcolor{\zerocol}
           \pgfmathtruncatemacro\number{##1}
           \ifnum\number>0\cellcolor{\onecol}\fi}%
        },
        columns/x/.style={
            column name={},
            postproc cell content/.code={}
        }
    }
}
\makeatother

\begin{document}
% read the file
\pgfplotstableread{matrix.cvs}{\matrixfile}

\begin{table}
\centering
\pgfplotstabletypeset[color cells]\matrixfile
\end{table}

\begin{table}
\centering
\pgfplotstabletypeset[color cells, zero color=green, one color=cyan]\matrixfile
\end{table}
\end{document}

The result:

enter image description here

5
  • Is it possible to remove the lines between the cells?
    – Tom
    Commented Dec 19, 2013 at 22:24
  • @Tom: I think they depend on the viewer; did you try to increase the resolution? If the problem persist, likely we can remove them taking into account the \pgflinewidth. Commented Dec 20, 2013 at 7:59
  • Your solution requires, that the digits are separated by space. Can yoiu adjust the code to visualize the original matrix?
    – user15941
    Commented Jul 7, 2014 at 6:43
  • @user15941: you're right. Do you mean you would like to remove the spaces? They are used as delimiter, so it's not easy to get rid of them. You may want to use another delimiter, but still it does not answer your question. Commented Jul 7, 2014 at 6:46
  • Yes, I tried to tinker with this code, but It interprets only whole numbers not the single digits.
    – user15941
    Commented Jul 7, 2014 at 6:50
7

Just for fun and for comparison, I implemented a lualatex solution.

In this solution I use lua to transform the matrix from its textual form as a sequence of 0 and 1 into another string containing a lot of tikz commands which draw the figure.

The lua based solution seems to be slower than the pure tikz solutions, but perhaps this is due to the lualatex startup. The execution of the lua functions is very fast, but the compilation of the resulting tikz code is apparently the bottleneck. Probably my lua code could generate a more efficient tikz code.

To compile my example you'll require:

  • A working lualatex
  • A copy of the file matrix2tikz.lua (below)
  • The document sample.tex (below)

sample.tex

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{fit}
\directlua{dofile("matrix2tikz.lua")}  
\newcommand{\bitmapmatrix}[2][10]{%
\begin{scope}[bitmapmatrix]
\directlua{matrix2tikz("#2",#1)}%
\end{scope}
}
\tikzset{
  bitmapmatrix/.style = {line width = 2sp},
  pixel on/.style = {red},
  pixel off/.style = {white},
  pixel err/.style = {pink}
}

\def\mydata{
10110101010
10010101010
01010111010
11110010100
01100011001
11101010111
10101010111
}

\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}[node distance=2mm]
  \bitmapmatrix[5]{\mydata}
  \node[below=of matrix] {This is a test};
\end{tikzpicture}    
\end{document}

matrix2tikz.lua

local function matrix_to_tikz(tab , size)
  local width = size
  if (size == nil) then size=10 end
  local pixel_width = size / #tab[1]
  local height = pixel_width * #tab
  local pixel_cmd = string.format("\\filldraw[%%s] (%%f, %%f) +(-%f, %f)         → rectangle +(%f, -%f);",
          pixel_width/2, pixel_width/2, pixel_width/2, pixel_width/2)
  local str_tab = {}

  for y=1,#tab do
    row = {}
    for x=1,#tab[y] do
      if tab[y][x] == 1 then
          style = "pixel on"
      elseif tab[y][x] == 0 then
          style = "pixel off"
      else
          style = "pixel err"
      end
      row[x] = string.format(pixel_cmd, style, x*pixel_width, -y*pixel_width)
    end
    str_tab[y] = table.concat(row, "\n")
  end
  local extra = {}
  extra[1] = string.format("\\coordinate (aux1) at (%f,-%f);", pixel_width/2,    → height+pixel_width/2)
  extra[2] = string.format("\\coordinate (aux2) at (%f, %f);", width+            → pixel_width/2, -pixel_width/2)
  extra[3] = "\\node[inner sep=0pt, fit=(aux1) (aux2)] (matrix) {};"
  str_tab[#tab+1] =  table.concat(extra, "\n")
  return table.concat(str_tab,"\n")
end

function justWords(str)
  local t = {}
  local function helper(word) table.insert(t, word) return "" end
  if not str:gsub("%w+", helper):find"%S" then return t end
end

local function text_to_matrix(txt)
  local m = {}
  local l = justWords(txt)
  for i=1,#l do
    if (l[i]~= nil and #l[i]>1) then
        j = 1; row = {}
        for c in l[i]:gmatch(".") do
          row[j] = tonumber(c)
          j = j + 1
        end
        m[i] = row
    end
  end
  return m
end

function matrix2tikz(data, size)
  local t = text_to_matrix(data)
  local s = matrix_to_tikz(t, size)
  tex.print(s)
end

Compiling

Put both in the same folder and run lualatex sample.tex. This is the result:

Result

6

run with xelatex

\documentclass{article}
\usepackage{filecontents}
\usepackage{pstricks-add}

\begin{filecontents*}{matrix.data}
/dotmatrix [
1 0 1 1 0 1 0 1 0 1 0
1 0 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 1 1 0 1 0
1 1 1 1 0 0 1 0 1 0 0
0 1 1 0 0 0 1 1 0 0 1
1 1 1 0 1 0 1 0 1 1 1
1 0 1 0 1 0 1 0 1 1 1
] def
\end{filecontents*}

\begin{document}

 \begin{pspicture}(-0.5,-0.75)(7,11)
 \psframe*[linecolor=blue!60](0.5,0.5)(11.5,7.5)
 \psMatrixPlot[dotsize=1.1cm,dotstyle=square*,linecolor=red!60]{7}{11}{matrix.data}
\end{pspicture}

\end{document}

enter image description here

1
  • Compiles but no figure is drawn.
    – scribe
    Commented Feb 16, 2023 at 6:48
5

For comparison, here is a solution that uses ConTeXt (MkIV) and Metapost that loads data from an external file.

\startluacode
  local draw = function (x,y, size, color) 
     context("fill fullsquare xyscaled (%s, %s) shifted (%d*%s, -%d*%s) withcolor \\MPcolor{%s}; ",
              size, size, x, size, y, size, color)
  end

  function commands.visualize_data(filename, zero_color, one_color, size) 
    size = size or "5mm" -- default

    local data = ""
    local f = io.open(filename,'r') 

    if not f then
      print("Error:", "Cannot load file " .. filename)
    else 
      data = f:read("*all")
      f:close()

      local lines = string.splitlines(data)
      context.startMPcode()

      for i = 1, #lines do
        local current_line = lines[i]
        for j = 1, #current_line do
          local current_character = current_line:sub(j,j)
          local color 
          if current_character == "0" then 
             color = zero_color 
          else 
             color = one_color 
          end
          draw(j, i, size, color)
        end
      end

      context.stopMPcode()
    end
  end
\stopluacode

\starttext

\ctxcommand{visualize_data("data.txt", "red", "blue")}

\stoptext

which gives

enter image description here

1
  • 2
    Bah, showing off.... :P
    – percusse
    Commented Jul 12, 2013 at 18:39
5

Just for the fun of it, directly from the provided data. Probably L3 version is on the way :)

\documentclass{standalone}
\usepackage{tikz,xstring,etoolbox,catchfile}
\usetikzlibrary{matrix}

%=== Remove these later, it creates a dummy data file
\usepackage{filecontents}
\begin{filecontents*}{testdata.txt}
101101010101011010101010110101010
100101010101001010101010010101010
010101110100101011101001010111010
111100101001111001010011110010100
011000110010110001100101100011001
111010101111110101011111101010111
101010101111010101011110101010111
101101010101011010101010110101010
100101010101001010101010010101010
010101110100101011101001010111010
111100101001111001010011110010100
011000110010110001100101100011001
111010101111110101011111101010111
101010101111010101011110101010111
\end{filecontents*}
%================================

%Replace your file name 
\CatchFileDef{\mydata}{testdata.txt}{}

\def\doit#1{%
\let\mymatrixcontent\empty%
\StrCount{#1}{ }[\myrownumber]%
\xdef\mormatrix{#1}%
    \foreach \m in {1,...,\myrownumber}{%
    \StrCut{\mormatrix}{ }\myextractedrow\myremainingmatrix%
    \xdef\mormatrix{\myremainingmatrix}%
    \StrLen{\myextractedrow}[\mycolnum]%
    \foreach \k in {1,...,\mycolnum}{%
         \StrChar{\myextractedrow}{\k}[\kk]%
             \begingroup\edef\x{\endgroup%
             \noexpand\gappto\noexpand\mymatrixcontent{\ifnum\kk>0|[fill=red]|\fi\noexpand\&}}\x%
    }\gappto\mymatrixcontent{\\}}%
\begin{tikzpicture}
\matrix[matrix of nodes,ampersand replacement=\&,inner sep=0pt,
    nodes={minimum size=4mm,outer sep=0pt}] {\mymatrixcontent};
\end{tikzpicture}%
\let\mymatrixcontent=\empty
}

\begin{document}
\doit{\mydata}
\end{document}

enter image description here

2
  • I used this example, however when any of the border columns or rows contains only 0 values, it disappears. In latex file matrices of the same dimensions are not of equal size. Maybe we can output some phantom squares.
    – user15941
    Commented Jul 7, 2014 at 6:46
  • @user15941 If you add nodes in empty cells key to the matrix, phantom cells can be created.
    – percusse
    Commented Jul 7, 2014 at 7:10
0

Here is an improvement over a nice answer from @herbert that took advantage of pstricks-add and xelatex.

The earlier answer however did not create a proper grid for every cell in the matrix.It only displayed the grid structure for values set to 1. This enables a more generic solution beyond [0,1] valued matrices.

What we would like to see however is something like this —

Alternative color scheme, highlights grid over all cells

Here is a code snippet to achieve this:

\newcommand{\brickred}{0.7 0.25 0.33}
\newcommand{\lightorange}{0.95 0.6 0.33}
begin{pspicture}(-0.5,-.75)(12,8)
\psframe[linecolor=black](0.5,0.5)(11.5,7.5)
  \psMatrixPlot[colorType=5,
    colorTypeDef={
     dup /value exch def % save value and leave one on the stack
     value Min sub dMaxMin div neg 1 add 300 mul 400 add 
     tx@addDict begin wavelengthToRGB Red Green Blue end
     value 0 eq \pslbrace \lightorange \psrbrace if %
     value 1 eq \pslbrace \brickred \psrbrace if
     setrgbcolor
    },
   dotsize=1.05cm,
   dotstyle=square*,
   xStep=1,yStep=1,
   linecolor=black,
   ChangeOrder]{7}{11}{matrix.data}
\end{pspicture}

You can get the whole standalone code here

You must log in to answer this question.

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