2

I have a .csv table containing both categorical and numeric data.

  • The first column contains the categories, which are expressed as strings.
  • The second column contains the number of observations that fall in each category. This is the only numeric data in the table.
  • The remaining columns contain the various occurrences of each observation, and these occurrences are strings.

To have a visual glimpse on the structure of the file:

CATEGORIES  NOBS    OCCURRENCE1     OCCURRENCE2     OCCURRENCE3
Street      3       Street A        Street XXXXX    Street YY 
Square      2       Square ZZZZ     Square B        ''
Lane        1       Lane XXXXX      ''              ''
Park        1       Park A          ''              ''

Notice that some strings may well be empty. Moreover, the length of the strings is variable.

Starting from this data, I would like to obtain a figure like the one below.

What I call a "bar graph containing boxed strings" in the title

Ideally, I would be interested in having the overall length of each row be coherent with NOBS. This might require to reduce font size in some rows, and I am ready to do this by hand if needed. However the width of the various boxes must be determined so as to generate an overall size of the bar that is coherent with NOBS. An alternative could also be to use a fixed text box size of width equal to the longest string: the overall length of each bar would then be coherent with NOBS by construction.

I have tried to do some research on how to implement this type of figure, but I have so far failed miserably. Have I entirely missed some package or other useful solution? Any help would be greatly appreciated.

1

Does this go in the right direction?

\documentclass{article}
\usepackage{filecontents}
\begin{filecontents*}{datatable.csv}
CATEGORIES;NOBS;OCCURRENCE1;OCCURRENCE2;OCCURRENCE3
Street;3;Street A;Street XXXXX;Street YY 
Square;2;Square ZZZZ;Square B;''
Lane;1;Lane XXXXX;'';''
Park;1;Park A;'';''
\end{filecontents*}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usepackage{pgfplotstable}
\pgfplotsset{compat=1.16}
\newcounter{realitems}
\begin{document}
\begin{tikzpicture}[font=\sffamily,header/.style={text
    width=2.4cm,align=left,fill=gray!20,minimum height=8mm,draw,inner xsep=1ex},
    funky bar/.cd,inner sep/.initial=1ex,width/.initial=2.1cm]
 % your empty cell indicator
 \edef\none{''}
 % load table and figure out how many rows and columns it has
 \pgfplotstableread[col sep=semicolon]{datatable.csv}\loadedtable
 \pgfplotstablegetrowsof{\loadedtable} 
 \pgfmathtruncatemacro{\NumRows}{\pgfplotsretval-1} 
 \pgfplotstablegetcolsof{\loadedtable} 
 \pgfmathtruncatemacro{\NumCols}{\pgfplotsretval-1}
 % loop 
  \foreach \Row in {0,...,\NumRows}
  {\setcounter{realitems}{0} %measure the items
   \foreach \Col in {2,...,\NumCols}
    {\pgfplotstablegetelem{\Row}{[index]\Col}\of{\loadedtable}
      \ifx\pgfplotsretval\none
      \else
       \stepcounter{realitems}
       \ifnum\number\value{realitems}=1
        \xdef\CurLst{"\pgfplotsretval"}
        \pgfmathsetmacro{\tmpwd}{width("\pgfplotsretval")}
        \xdef\tmpwd{\tmpwd}
       \else
        \xdef\CurLst{\CurLst,"\pgfplotsretval"}
        \pgfmathsetmacro{\tmpwd}{\tmpwd+width("\pgfplotsretval")}
        \xdef\tmpwd{\tmpwd}
       \fi
      \fi
    }
   \typeout{\Row,\CurLst}   
   \pgfmathsetmacro{\ratio}{\number\value{realitems}*
    (\pgfkeysvalueof{/tikz/funky bar/width}-2*\pgfkeysvalueof{/tikz/funky bar/inner sep})/\tmpwd}   
   \pgfplotstablegetelem{\Row}{[index]0}\of{\loadedtable}
   \ifnum\Row=0 
    \node[header,alias=n-\Row-0] (H\Row) {\pgfplotsretval};
   \else
    \node[header,alias=n-\Row-0,below=-\pgflinewidth\space of H\the\numexpr\Row-1] (H\Row) {\pgfplotsretval};
   \fi
   \foreach \X [evaluate=\X as \PrevX using {int(\X-1)}] in {1,...,\number\value{realitems}}
   {\ifnum\number\value{realitems}=1
    \pgfmathsetmacro{\myitem}{\CurLst}
    \typeout{\myitem}
    \pgfmathsetmacro{\mywidth}{\pgfkeysvalueof{/tikz/funky bar/width}}
    \node[anchor=center,inner sep=0pt,scale=\ratio] (n-\Row-\X)
    at ([xshift=\mywidth/2]n-\Row-\PrevX.east)  {\myitem};
    \draw ([yshift=-\pgflinewidth/2]n-\Row-0.north -| n-\Row-\PrevX.east)
    -- ++ (\mywidth pt,0) |- ([yshift=\pgflinewidth/2]n-\Row-0.south -| n-\Row-\PrevX.east);;
   \else
    \pgfmathsetmacro{\myitem}{{\CurLst}[\X-1]}
    \pgfmathsetmacro{\mywidth}{2*\pgfkeysvalueof{/tikz/funky bar/inner sep}+\ratio*width("\myitem")}
    \node[anchor=center,inner sep=0pt,scale=\ratio,
    right={ifthenelse(\X==1,1,2)*\pgfkeysvalueof{/tikz/funky bar/inner sep}}
    of n-\Row-\PrevX] (n-\Row-\X) {\myitem};
    \draw ([yshift=-\pgflinewidth/2]n-\Row-0.north -| n-\Row-\PrevX.east)
    -- ++ (\mywidth pt,0) |- ([yshift=\pgflinewidth/2]n-\Row-0.south -| n-\Row-\PrevX.east);;
    \fi
   }
   }
\end{tikzpicture}
\end{document}

enter image description here

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.