Making a macro for transition probability matrices (a la Pinsky & Karlin’s “Introduction to Stochastic Modeling”)

arraysformattingmatricesnicematrixpgfplotstable

In An Introduction to Stochastic Modeling by Mark Pinsky and Samuel Karlin, transition probability matrices for finite-state Markov chains take a particular formatting style:

enter image description here

Particular items of note:

  • The sides of the matrix (where we normally see brackets, parentheses, or single vertical bars) are double vertical bars here.
  • The rows and the columns are numbered, starting from zero.

A past post here on TeX.SE discusses how to make these, and the end result is a good one.

However, using the code there is somewhat cumbersome if you need to type many such matrices, rather than just doing it as a one-off thing, especially as the size of said matrices may vary.

Of course, then, this suggests the creation of some sort of macro to handle it. But I'm not sure how to go about doing so, being rather new to LaTeX.

Ideally, such a macro would just take in the contents of the matrix itself. So I might have, say,

\tpm{
0.3 & 0.2 & 0.5 \\
0.5 & 0.1 & 0.4 \\
0.5 & 0.2 & 0.3
}

or

\tpm{
0.4 & 0.3 & 0.2 & 0.1 \\
0.1 & 0.4 & 0.3 & 0.2 \\
0.3 & 0.2 & 0.1 & 0.4 \\
0.2 & 0.1 & 0.4 & 0.3
}

(to generate the sample matrices above) and the rest would be done for me: regardless of the size, the columns and rows would be numbered accordingly, the double bars produced, and so on.

Can someone make such a macro? Any help would be appreciated!

Best Answer

UPDATED <<<<<

It is done using the package nicematrix and defining a new environment.

It has the advantage that it mainly keeps the content of the array separate from the style that will be applied to it.

Thus allowing, as in this case, to have the raw matrix as the only input of an environment, and on the other hand, to adjust the style without interfering with the content.

Note that the array to be generated needs a first empty row, and all rows must start with &, to make room for column and row numbers, which are automatically filled.

The last four lines, following \CodeAfter redraws the four vertical lines, --replacing the separators inserted by VNiceMatrix (now hidedn)--, to put them a little further to the left of the row numbers.

x

\documentclass{article}

\usepackage{nicematrix} % needed <<<<<<<<<<<<<<<<<
\usepackage{tikz}% needed <<<<<<<<<<<<<<<<<

\newcounter{nrow}
\newcounter{ncol}   

\newenvironment{probability-matrices}
{\setcounter{nrow}{0}\setcounter{ncol}{0}%
    \NiceMatrixOptions{delimiters/color=white}
    $\begin{VNiceMatrix}%
        [cell-space-limits = 4pt,
        first-row,
        first-col,
        code-for-first-row =\arabic{ncol}\addtocounter{ncol}{1},
        code-for-first-col = \arabic{nrow}\addtocounter{nrow}{1},   
        margin=4pt, 
        ]
}
{
\CodeAfter
\tikz \draw [transform canvas={xshift=-1.0pt}] (1-|1) --(last-|1) ;
\tikz \draw [transform canvas={xshift=1.0pt}] (1-|1) --(last-|1) ;
\tikz \draw [transform canvas={xshift=1.0pt}] (1-|last) -- (last-|last) ;
\tikz \draw [transform canvas={xshift=-1.0pt}] (1-|last) -- (last-|last) ;
\end{VNiceMatrix}$}

\begin{document}    

$ \mathbf{P} =$
\begin{probability-matrices}
&    &     &    \\ % needed  <<<
&0.3 & 0.2 & 0.5 \\
&0.5 & 0.1 & 0.4 \\
&0.5 & 0.2 & 0.3    
\end{probability-matrices}
$ \mathbf{T} =$
\begin{probability-matrices}
&    &     &     &     \\ % needed <<<
&0.4 & 0.3 & 0.2 & 0.1 \\
&0.1 & 0.4 & 0.3 & 0.2 \\
&0.3 & 0.2 & 0.1 & 0.4 \\
&0.2 & 0.1 & 0.4 & 0.3  
\end{probability-matrices}  

\end{document}

UPDATE (10/01/21)

There are two alternatives to alleviate writing: they both rely on the pgfplotstable package to create and insert the first row and the first column that are outside the array.

The number of columns and rows is managed automatically. The separators are || as requested. Only the content of the array is needed.

The first one uses only pgfplotstable.

For the second I "ported" the nicematrix solution from the previous published solution.

nicematrix offers the possibility to insert tkiz commands to draw figures on the cells, highlight them, etc. because it defines the tikz nodes in the cells.

But perhaps these improved capabilities are not necessary in this simple case and it is always better to use fewer packages.

There are slight differences in the results: with nicematrix the baseline is better.

In both cases the compilation will be done twice the first time.

The data is read using \pgfplotstableread and stored in a macro. All the rows must end with the row separation character, defined here as \\.

The data can also be stored in a file to read later.

The command \tpm{<macro>} will typeset the stored array.

(1) only pgfplotstable

p

\documentclass[12pt,a4paper]{article}

\usepackage{pgfplotstable}
\pgfplotsset{compat=1.17}   

\pgfplotstableset{col sep =&, row sep =\\}

\newcommand{\tpm}[1]{% display a stored matrix <<<<<<<<<<<<<<<<<<<
\renewcommand{\arraystretch}{1.3}
\pgfplotstablegetcolsof{#1}
\pgfmathsetmacro{\finex}{\pgfmathresult}
\pgfmathsetmacro{\fine}{\pgfmathresult-1}
\pgfplotstableset{%
    create on use/z/.style={
        create col/set list={0,1,...,20}
    },  
    string type,    
    header=false,
}
\pgfplotstabletypeset[      
    every col no 0/.style={string type, 
        assign cell content/.code={% 
            \pgfkeyssetvalue{/pgfplots/table/@cell content}%
            {\multicolumn{1}{c||}{####1}}%
        },
    },  
    every col no \finex/.style={string type,    
    assign cell content/.code={% 
            \pgfkeyssetvalue{/pgfplots/table/@cell content}%
            {\multicolumn{1}{c||}{####1}}%
        },
    },
    columns={z,0,1,...,\fine},
    columns/z/.style={column name={}}
    ]{#1}   
                }% end \tpm
    
\begin{document}
        
\pgfplotstableread{% store table in macro \mP
    0.3 & 0.2 & 0.5 \\
    0.2 & 0.6 & 0.2 \\
    0.5 & 0.2 & 0.3 \\ % needed a last end of row
}{\mP}

\pgfplotstableread{%% store table in macro \mT
    0.4 & 0.3 & 0.2 & 0.1 \\
    0.1 & 0.4 & 0.3 & 0.2 \\
    0.3 & 0.2 & 0.1 & 0.4 \\
    0.2 & 0.1 & 0.4 & 0.3 \\% needed a last end of row
}{\mT}
    
$\mathbf{P} =$\tpm{\mP}

\bigskip

$\mathbf{T} =$\tpm{\mT} 

\pgfplotstableread{% store table in macro \mMx
0             &$\frac{1}{2}$  & $\frac{1}{2}$ \\
$\frac{1}{2}$ & 0             & $\frac{1}{2}$ \\
$\frac{1}{2}$ & $\frac{1}{2}$ & 0 \\ % needed a last end of row
}{\mMx}

\bigskip    

$\mathbf{M} =$\tpm{\mMx}    

\end{document}

(2) pgfplotstable + nicematrix

nn

\documentclass[12pt,a4paper]{article}

\usepackage{pgfplotstable}
\pgfplotsset{compat=1.17} 
\usepackage{nicematrix}
\usepackage{tikz}% 

\pgfplotstableset{col sep =&, row sep =\\}

\newcommand{\tpm}[1]{% display a stored matrix <<<<<<<<<<<<<<<<<
\NiceMatrixOptions{first-row,  first-col,margin=0.8ex, delimiters/color=white, cell-space-limits = 0.8ex}       
\pgfplotstablegetcolsof{#1}
\pgfmathsetmacro{\fine}{\pgfmathresult-1} % number of columns
\pgfplotstableset{% add first column 
    create on use/z/.style={create col/set list={0,1,...,20}},  
    string type,
    header=false,
                }
$\pgfplotstabletypeset[%
    begin table=\begin{VNiceMatrix},
    end table={\CodeAfter % better || lines
            \tikz \draw [transform canvas={xshift=-0.20ex}] (1-|1) --(last-|1) ;
            \tikz \draw [transform canvas={xshift=0.20ex}] (1-|1) --(last-|1) ;
            \tikz \draw [transform canvas={xshift=0.20ex}] (1-|last) -- (last-|last) ;
            \tikz \draw [transform canvas={xshift=-0.20ex}] (1-|last) -- (last-|last) ;
        \end{VNiceMatrix}},
    skip coltypes,
    columns={z,0,1,...,\fine},
    columns/z/.style={column name={}}
    ]{#1}$
                        }% end \tpm

\begin{document}

\pgfplotstableread{% store table in macro \mP
    0.3 & 0.2 & 0.5 \\
    0.2 & 0.6 & 0.2 \\
    0.5 & 0.2 & 0.3 \\ % needed a last end of row
}{\mP}

\pgfplotstableread{%% store table in macro \mT
0.4 & 0.3 & 0.2 & 0.1 \\
0.1 & 0.4 & 0.3 & 0.2 \\
0.3 & 0.2 & 0.1 & 0.4 \\
0.2 & 0.1 & 0.4 & 0.3 \\% needed a last end of row
}{\mT}  

$\mathbf{P} =$\tpm{\mP}

\bigskip

$\mathbf{T} =$\tpm{\mT} 

\pgfplotstableread{% store table in macro \mMx
0           &\frac{1}{2}  &\frac{1}{2} \\
\frac{1}{2} & 0           & \frac{1}{2} \\
\frac{1}{2} & \frac{1}{2} & 0 \\ % needed a last end of row
}{\mMx}
    
\bigskip    

\NiceMatrixOptions{columns-width=15pt} % expand the columns
$\mathbf{M} =$\tpm{\mMx}    

\end{document}
Related Question