2

I am given a correspondence of letters (e.g. cities) with numbers (e.g. facilities), and the numbers are associated with a weight (e.g. price of the facility). I think of defining such correspondences by something like

\myfunction{<facility>}{<price-of-facility>}{<cities-in-which-the-facility-is>}

I would like to generate a table with the cities as rows, the facilities as columns, and the price in the intersection of both (if any). Of course, the table will be sparse and it will be sadly cumbersome to do it by hand.

Is there a way to generate such a table automatically?

Example of data

  • facility A (price **): cities 1, 3, 5
  • B (*): 1, 4
  • C (**): 3, 4, 5
  • D (***): 2, 3
  • E (*): 1, 2, 5

Or, with the function I suggested above (and writing the prices with stars):

  • \myfunction{A}{**}{1,3,5}
  • \myfunction{B}{*}{1,4}
  • \myfunction{C}{**}{3,4,5}
  • \myfunction{D}{***}{2,3}
  • \myfunction{E}{*}{1,2,5}
  • 1
    The answer is of course yes but you will make your question much more attractive if you add some number of sample calls such that others do not have to invent the data. – Schrödinger's cat Jan 15 at 1:44
  • 1
    @Schrödinger'scat Absolutely, thanks for the advice, I will do that – TheStudent Jan 15 at 1:53
1

Here is something that I think does what you are asking.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{plotmarks}
\usepgfmodule{parser}
\newcounter{stars}
\newif\ifdrawstars
\pgfparserdef{starparser}{initial}{the letter o}% 
{\ifdrawstars
\path (\starshift*1pt+\number\value{stars}*6pt,0pt) node{\pgfuseplotmark{o}};
\fi
\stepcounter{stars}}% 
\pgfparserdef{starparser}{initial}{the character *}% 
{\ifdrawstars
\path (\starshift*1pt+\number\value{stars}*6pt,0pt) node{\pgfuseplotmark{star}};
\fi
\stepcounter{stars}}% 
\pgfparserdef{starparser}{initial}{the character ;}% 
{\pgfparserswitch{final}}
\pgfmathtruncatemacro\ovalshift{0}
\tikzset{pics/stars/.style={code={
   \setcounter{stars}{0}
   \drawstarsfalse
   \pgfparserparse{starparser}#1;
   \pgfmathtruncatemacro\starshift{3-3*\number\value{stars}}
   \setcounter{stars}{0}
   \drawstarstrue
   \pgfparserparse{starparser}#1;
   }},pics/stars/.default=*}
\newcommand\myfunction[3]{\path foreach \X in {#1} {
foreach \Y in {#3} {(\X|-\Y) pic{stars=#2}}};}
\begin{document}
\begin{center}
\begin{tikzpicture}[xscale=1,yscale=0.8]
 \path foreach \X [count=\Y] in {A,...,F} 
  {(\Y,0) node (\X){\X}}
   foreach \X in {1,...,5} {(0,-\X) node (\X){\X}};
 \myfunction{B}{*}{1,4}
 \myfunction{C}{**}{3,4,5}
 \myfunction{D}{***}{2,3}
 \myfunction{E}{*}{1,2,5}
\end{tikzpicture}
\end{center}
\end{document}

enter image description here

In this first version I keep things basic. You can make it more fancy, and one could scan over the input to set up the "axes" and so on and so forth. Please let me know if there are things that are to be changed.

Here is a more customizable version with numbers.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{plotmarks}
\usepgfmodule{parser}
\newcounter{stars}
\makeatletter
\pgfmathdeclarefunction{Dim}{1}{%
\begingroup%
\pgfutil@tempcnta0%
\@for\pgfutil@tempa:=#1\do{\advance\pgfutil@tempcnta1}%
\edef\pgfmathresult{\the\pgfutil@tempcnta}%
\pgfmathsmuggle\pgfmathresult\endgroup%
}
\makeatletter
\newif\ifdrawstars
\pgfparserdef{starparser}{initial}{the letter o}% 
{\stepcounter{stars}}% 
\pgfparserdef{starparser}{initial}{the character *}% 
{\stepcounter{stars}}% 
\pgfparserdef{starparser}{initial}{the character ;}% 
{\pgfparserswitch{final}}
\pgfmathtruncatemacro\ovalshift{0}
\tikzset{pics/stars/.style={code={
   \setcounter{stars}{0}%
   \pgfparserparse{starparser}#1;%
   \node{\number\value{stars}};%
   }},pics/stars/.default=*,
   pics/adjacency table/.style={code={
   \tikzset{adjacency table/.cd,#1}
   \def\pv##1{\pgfkeysvalueof{/tikz/adjacency table/##1}}%
   \begin{scope}[local bounding box=tab]
    \draw [thick] (-0.5,0.5) rectangle 
    ({2*Dim("\pv{list X}")-0.5},{-2*Dim("\pv{list Y}")-0.5});
    \edef\temp{\pv{list X}}
    \path foreach \X [count=\Y] in \temp
        {(\Y,0) node (X-\X) {\X}};
    \edef\temp{\pv{list Y}}
    \path foreach \X [count=\Y] in \temp
        {(0,-\Y) node (Y-\X) {\X}
        \ifnum\Y=0
         (0,-\Y+0.5-|tab.west) edge[thick] (0,-\Y+0.5-|tab.east)
         \else
         (0,-\Y+0.5-|tab.west) edge (0,-\Y+0.5-|tab.east)
        \fi};
    \edef\temp{\pv{list Y}}
    \path foreach \X [count=\Y] in \temp    
     {\ifnum\Y=1
         (\Y-0.5,0|-tab.north) edge[thick] (\Y-0.5,0|-tab.south)
      \else
         (\Y-0.5,0|-tab.north) edge (\Y-0.5,0|-tab.south)
    \fi};
   \end{scope}  
   }},
   adjacency table/.cd,list X/.initial={1,...,5},list Y/.initial={A,...,F}}
\newcommand\myfunction[3]{\path foreach \X in {#1} {
foreach \Y in {#3} {(Y-\X-|X-\Y) pic{stars=#2}}};}
\begin{document}
\begin{center}
\begin{tikzpicture}
 \pic[xscale=0.9,yscale=0.7]{adjacency table={list X={1,...,5},list Y={A,...,F}}};
 \myfunction{B}{*}{1,4}
 \myfunction{C}{**}{3,4,5}
 \myfunction{D}{***}{2,3}
 \myfunction{E}{*}{1,2,5}
\end{tikzpicture}
\end{center}
\end{document}

enter image description here

|improve this answer|||||
  • Thank you very much, that is indeed almost perfectly what I need. Is there any way to put axes and lines to separate the rows and columns in the table? – TheStudent Jan 15 at 3:41
  • Moreover, here is the issue: in fact I don't want labels as simple as 1, 2, 3, 4, etc. but rather other reference numbers or names. Your code seems to use the label itself for the position of the elements. What can I do with, instead, 190, 191, 240, 241 (that should give a table of four rows, without different spacing between them)? – TheStudent Jan 15 at 3:46
  • Sorry for all this mess: I understood how to toy with the "count" instead of the label itself, so it works for any label now. I have two more issues: 1/ how to replace *'s by other things? (numbers or $\star$ instead typically) and 2/ How can I do to write the labels A, B, C, etc. vertically (Since in fact it will be the name of the cities) – TheStudent Jan 15 at 4:00
  • @TheStudent I added a version with numbers and lines. – Schrödinger's cat Jan 15 at 4:43
  • I do not understand how to to with the adjacency labels: for instance if instead of *'s I want to put $\star$'s, and make so that when there are three they are not aligned but in a triangle (for the sake of space and balance, say)? Is that also possible to put the entries of the row/columns in bold? If I should rather ask for another question for this, I will be glad to do so – TheStudent Mar 5 at 5:56

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.