Random sample of an array in TikZ

Is there a way to extract a random sample from an array in TikZ?

For instance, if \def\myarray{{1, "two", 2+1, "IV", "cinq", "sechs", sin(\i*5)*14}} defines an array, a random, length-3 sample may be "sechs", 2+1, "IV".

Ideally, it should be possible to specify sample size and whether sampling must be done with or without replacement.

I'm looking for something similar to the sample function in R: https://stat.ethz.ch/R-manual/R-devel/library/base/html/sample.html.

1 Answer

There are some nice tools in the pgf package for playing with random sequences and, in particular, the command \pgfmathrandomitem does exactly what you want.

To give a little more detail, the MWE:

\documentclass{article}
\usepackage{pgf}

\pgfmathsetseed{\number\time}% set a "random" seed based on time
\pgfmathdeclarerandomlist{myarray}{{1}{two}{2+1}{IV}{cinq}{sechs}{sin(\i*5)*14}}

\begin{document}
\pgfmathrandomitem\myitem{myarray}\myitem

\pgfmathrandomitem\myitem{myarray}\myitem

\pgfmathrandomitem\myitem{myarray}\myitem

\pgfmathrandomitem\myitem{myarray}\myitem
\end{document}


produces:

One caveat: this will give a random list with repeats. If you want to avoid repetitions then see How do I generate in LaTeX a list of random questions avoiding repetitions?.

EDIT (adding random sequences without repeats)

As I said above, to allow random sequences to be generated without repeats we can tap into Mark Wibrow's code from How do I generate in LaTeX a list of random questions avoiding repetitions?. The code below wraps everything into a pair of macros:

• \DefineRandomSequence is used to define, or set, the sequence that we want to choose randomly from. By default, this will let you generate random elements of this sequence with repeats but there is *-form, \DefineRandomSequence*, that generates random elements without repeats.

• \myitem print a random element of the list. If the sequence is being generated without repeats then an error will be returned if there are no more elements to choose from.

To define the *-form of \DefineRandomSequence I have used \NewDocumentCommand from the xparse package.

Here is the full code:

\documentclass{article}
\usepackage{pgf}
\usepackage{pgffor}% needed only for the examples
\usepackage{xparse}

% \DefineRandomSequence{<sequence>}: random elements with repeats
% \DefineRandomSequence*{<sequence>}: random elements without repeats
\NewDocumentCommand\DefineRandomSequence{sm}{%
\pgfmathsetseed{\number\time}% set a "random" seed based on time
\pgfmathdeclarerandomlist{myrandomlist}{#2}
\IfBooleanTF{#1}{\let\pgfsequencegenerator\pgfmathrandomitemwithoutreplacement}
{\let\pgfsequencegenerator\pgfmathrandomitem}
}
% \myitem: print random element of sequence
\newcommand\myitem{\pgfsequencegenerator\randomitem{myrandomlist}\randomitem}

\makeatletter
% Mark Wibrow: https://tex.stackexchange.com/questions/113987/how-do-i-generate-in-latex-a-list-of-random-questions-avoiding-repetitions
\def\pgfmathrandomitemwithoutreplacement#1#2{%
\pgfmath@ifundefined{pgfmath@randomlist@#2}{\pgfmath@error{Unknown random list #2'}}{%
\edef\pgfmath@randomlistlength{\csname pgfmath@randomlist@#2\endcsname}%
\ifnum\pgfmath@randomlistlength>0\relax%
\pgfmathrandominteger{\pgfmath@randomtemp}{1}{\pgfmath@randomlistlength}%
\def\pgfmath@marshal{\def#1}%
\expandafter\expandafter\expandafter\pgfmath@marshal\expandafter\expandafter\expandafter{\csname pgfmath@randomlist@#2@\pgfmath@randomtemp\endcsname}%
% Now prune.
\c@pgfmath@counta=\pgfmath@randomtemp\relax%
\c@pgfmath@countb=\c@pgfmath@counta%
\advance\c@pgfmath@countb by1\relax%
\pgfmathloop%
\ifnum\c@pgfmath@counta=\pgfmath@randomlistlength\relax%
\else%
\def\pgfmath@marshal{\expandafter\global\expandafter\let\csname pgfmath@randomlist@#2@\the\c@pgfmath@counta\endcsname=}%
\expandafter\pgfmath@marshal\csname pgfmath@randomlist@#2@\the\c@pgfmath@countb\endcsname%
\advance\c@pgfmath@counta by1\relax%
\advance\c@pgfmath@countb by1\relax%
\repeatpgfmathloop%
\advance\c@pgfmath@counta by-1\relax%
\expandafter\xdef\csname pgfmath@randomlist@#2\endcsname{\the\c@pgfmath@counta}%
\else%
\pgfmath@error{Random list #2' is empty}%
\fi%
}}
\makeatother

\begin{document}

With repeats:
\DefineRandomSequence{{1}{two}{2+1}{IV}{cinq}{sechs}{sin(\i*5)*14}}
\foreach \x in {1,...,5} { \myitem\space }

Without repeats:
\DefineRandomSequence*{{1}{two}{2+1}{IV}{cinq}{sechs}{sin(\i*5)*14}}
\foreach \x in {1,...,5} { \myitem\space }

\end{document}


This produces the output:

• This solution works great for the case when repetitions are allowed. For the case without repetitions, I find using R's sample function to create a text file with index permutations, which is then read into LaTeX, to be easier than using the code in the link, which, to me at least, looks as impenetrable as the author says. – nemarona Dec 6 '16 at 17:04
• @nemarona It's easy enough to use the code in the link if you treat it as a black box. I have added some code. – Andrew Dec 6 '16 at 23:19