0

I would like to make bar plot which has items grouped in categories (see sketch below). Each category can consist of any combination of kind of the data specified in the legend. I would like to read all data from table, but I'm not sure if some kind of "cell array" or "structures" is not better.

enter image description here

Already I tried to use bchart package following the answer here (but it occurred that I can't hide y-axis):

\documentclass{standalone}
\usepackage{bchart}
\usetikzlibrary{patterns}

% Bars:
\newcommand{\bccbar}[3][]{
    % Set defaults:
    \renewcommand{\bcbarcolor}{blue!20}
    \renewcommand{\bcbartext}{}
    \renewcommand{\bcbarlabel}{}
    \renewcommand{\bcbarvalue}{#2\bcunit--#3\bcunit}
    \renewcommand{\bcplainbar}{false}
    % Read parameters:
    \setkeys{bcbar}{#1}
    % Draw bar:
    \draw [pattern=crosshatch dots]
          ([xshift=(#2-\bcmin)*(\bcwidth/\bcrange)]0,\bcpos) coordinate (tlbar) rectangle
          ([xshift=(#3-\bcmin)*(\bcwidth/\bcrange)]0,\bcpos-5mm) coordinate (brbar);
    \ifthenelse{\equal{\bcplainbar}{true}}{}{
        % Write value:
        \node [fill=white,inner sep=1.5pt,anchor=west] at ($(tlbar-|brbar)!0.5!(brbar)$) {\bcfontstyle{\bcbarvalue, \bcbartext}};
    }
    % Write text:
    %\node[anchor=west] at ([xshift=#3*(\bcwidth/\bcrange)]0,\bcpos-2.5mm) {\bcfontstyle\bcbartext};
    % Write label:
    %\node[anchor=east] at ([xshift=#2*(\bcwidth/\bcrange)]0,\bcpos-2.5mm) {\bcfontstyle\bcbarlabel};
    % Move vertical position downward:
    \addtolength{\bcpos}{-5mm}
}%

\begin{document}
\begin{bchart}[min=-10,max=100,step=10]
\bccbar[text=A]{20}{45}
\smallskip
\bccbar[text=B]{40}{55}
\smallskip
\bccbar[text=C]{0}{31}
%\smallskip
%\bcbar[text=D]{31}
\end{bchart}
\end{document}

I also abandoned an idea of application of error bars because I don't see how to fill them with patterns. My MWE is based on the rearangement of @esdd answer to the question, how to make Bar graph with positive min max:

\documentclass{standalone}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
%\input{./plotssettings.tex}
\usepackage{ifthen}
\usetikzlibrary{matrix}
\pgfplotsset{compat=1.15}

\pgfplotstableread[col sep=comma]{
Year,   FarMin, FarMax, NearMin, NearMax, HereMin, HereMax, bMin, bMax 
1930,   20,     50,     10,      30,      15,      40,      20,   50
1940,   10,     60,     15,      60,      20,      70,      10,   60
1950,   15,     78,     nan,     nan,     32,      42,      15,   78
1960,   20,     30,     15,      40,      20,      10,      20,   30
1970,   5,      30,     30,      40,      15,      20,      5,    30
}\datatable


\begin{document}%
\begin{tikzpicture}
\pgfplotsset{
    every axis/.style={
        xbar stacked,
        enlarge y limits=0.15,
        bar width=6pt,
        xmin=0,xmax=80,
    },
    compat/bar nodes=1.8,
    minimum/.style={forget plot,draw=none,fill=none}
}

\pgfplotstablegetcolsof{\datatable}
\pgfmathparse{\pgfplotsretval-1}
\edef\noofcolumns{\pgfmathresult}   % number of columns with numeric data in table
\pgfmathparse{int(\noofcolumns-1)}
\edef\penultimate{\pgfmathresult}

\foreach \datacolno in {1,3,...,\penultimate}{
    \pgfmathparse{int(\datacolno+1)}
    \edef\datanextcolno{\pgfmathresult}
    \pgfmathparse{int((\datanextcolno)/2)}
    \edef\barno{\pgfmathresult}     % number of bar
    \pgfmathparse{8pt*(\barno-(\noofcolumns/2+1)/2)}
    \edef\barshift{\pgfmathresult pt}   % offset within one group of bars (local)
    \pgfmathparse{int((\datacolno-1)/2)}
    \edef\cyclelistshift{\pgfmathresult}    % number of painting
    \ifthenelse{\barno=1}%
        {\def\mystyle{%
            xmajorgrids,
            xlabel=Population,
            %yticklabels from table={\datatable}{Year}
        }}%
        {\def\mystyle{axis lines=none}}
    \begin{axis}[
        bar shift=\barshift,
        cycle list shift=\cyclelistshift,
        \mystyle,
    ]
        \addplot [minimum] table [x index=\datacolno,y=Year] {\datatable};
        \addplot table [x expr=\thisrowno{\datanextcolno}-\thisrowno{\datacolno},y=Year] {\datatable};
        \label{plot:\barno}
    \end{axis}
}

\matrix[
    matrix of nodes,
    draw,
    anchor=north east,
    nodes={inner xsep=.1em},
    column 1/.style={nodes={anchor=center}},
    column 2/.style={nodes={anchor=west}}
] at([shift={(-5pt,-5pt)}]current axis.north east)
{
    \ref{plot:1}&Far\\
    \ref{plot:2}&Near\\
    \ref{plot:3}&Here\\
    \ref{plot:4}&beyond\\
};
\end{tikzpicture}
\end{document}

But here also I have a problems - with reading of non-numerical data (after replacement of numbers 1930 etc. by strings), with making palette of patterns, with bar localization (the answer given by @Christian Feuersänger here is not satisfying for me, because tick labels are kept in constant spacings).

Plot with unwanted gap within one group of bars

1 Answer 1

1

I am still not totally sure I understood your question, but you will see below an example with some resemblance to boh of your pictures. Feel free to ask for precisions or comment if something is not to your taste.

\documentclass{standalone}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepgfplotslibrary{colorbrewer}
\pgfplotsset{compat=1.16}

\pgfplotsset{
/pgfplots/bar shift auto/.style={
/pgf/bar shift={%
(.5+\plotnumofactualtype)*\pgfplotbarwidth + \plotnumofactualtype*(#1)},
},
}

\pgfplotstableread[col sep=comma]{
Year,   FarMin, FarMax, NearMin, NearMax, HereMin, HereMax, bMin, bMax 
1930,   20,     50,     10,      30,      15,      40,      20,   50
1940,   10,     60,     15,      60,      20,      70,      10,   60
1950,   15,     78,     0,     0,     32,      42,      15,   78
1960,   20,     30,     15,      40,      20,      10,      20,   30
1970,   5,      30,     30,      40,      15,      20,      5,    30
}\datatable

\pgfplotsset{
    every axis/.style={
        xbar stacked,
        bar shift auto=0pt,
        bar width={10/8},
        ymin=1930,ymax=1980,
        ytick={1930,1940,...,1980},
        height=10cm, scale only axis,
        y tick label as interval,
        yticklabel={$\pgfmathprintnumber[1000 sep={}]{\tick}$},
        ymajorgrids
    },
        /pgfplots/bar cycle list/.style={/pgfplots/cycle list={
            {Set1-A,fill=Set1-A!50!white,mark=none},
            {Set1-B,fill=Set1-B!50!white,mark=none},
            {Set1-C,fill=Set1-C!50!white,mark=none},
            {Set1-D,fill=Set1-D!50!white,mark=none},
            {Set1-E,fill=Set1-E!50!white,mark=none},
            {Set1-F,fill=Set1-F!50!white,mark=none},
            {Set1-G,fill=Set1-G!50!white,mark=none},
            {Set1-H,fill=Set1-H!50!white,mark=none},
            },
            },
}

\begin{document}%
\begin{tikzpicture}
   \begin{axis}
    \pgfplotsinvokeforeach{FarMin,FarMax,NearMin,NearMax,HereMin,HereMax,bMin,bMax}{
        \addplot+[] table [y=Year,x=#1] {\datatable};
        \addlegendentry{#1}
        }
    \end{axis}
\end{tikzpicture}
\end{document}

enter image description here

EDIT Another proposal based on cheating a scatter plot as used by @Jake or @Schrödinger's cat. It could require a bit of cleanup and simplification, but this is the idea.

\documentclass{standalone}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepgfplotslibrary{colorbrewer}
\pgfplotsset{compat=1.16}

\pgfplotsset{
/pgfplots/bar shift auto/.style={
/pgf/bar shift={%
(.5+\plotnumofactualtype)*\pgfplotbarwidth + \plotnumofactualtype*(#1)},
},
}

\pgfplotstableread[col sep=comma]{
Year,   FarMin, FarMax, NearMin, NearMax, HereMin, HereMax, bMin, bMax 
1930,   20,     50,     10,      30,      15,      40,      20,   50
1940,   10,     60,     15,      60,      20,      70,      10,   60
1950,   15,     78,     0,       0,       32,      42,      15,   78
1960,   20,     30,     15,      40,      20,      10,      20,   30
1970,   5,      30,     30,      40,      15,      20,      5,    30
}\datatable

\pgfplotsset{
    customaxis1/.style={
        xbar stacked,
        stack negative=separate,
        bar shift auto=0pt,
        bar width={10/8},
        ymin=1930,ymax=1980,
        ytick={1930,1940,...,1980},
        height=10cm, scale only axis,
        y tick label as interval,
        yticklabel={$\pgfmathprintnumber[1000 sep={}]{\tick}$},
        ymajorgrids
    },
        /pgfplots/bar cycle list/.style={/pgfplots/cycle list={
            {Set1-A,fill=Set1-A!50!white,mark=none},
            {Set1-B,fill=Set1-B!50!white,mark=none},
            {Set1-C,fill=Set1-C!50!white,mark=none},
            {Set1-D,fill=Set1-D!50!white,mark=none},
            {Set1-E,fill=Set1-E!50!white,mark=none},
            {Set1-F,fill=Set1-F!50!white,mark=none},
            {Set1-G,fill=Set1-G!50!white,mark=none},
            {Set1-H,fill=Set1-H!50!white,mark=none},
            },
            },
}

\begin{document}%
\begin{tikzpicture}
    \begin{axis}[customaxis1,
            scatter,
            only marks,
            clip mode=individual,
            xmax=150,
            %colormap/Set1,
            cycle list/Set1-4,
        ]
        \addplot[Set1-A,
            visualization depends on={\thisrow{FarMin} \as \startvalue},
            visualization depends on={\thisrow{FarMax} \as \endvalue},
            scatter/@pre marker code/.append code={\fill [draw=black,fill=Set1-A] (axis direction cs:0,0) rectangle (axis direction cs:\endvalue,2.5);
                    \pgfplotsset{mark=none}}
        ] table [y expr={\thisrow{Year}+0*2.5},x=FarMin] {\datatable};
        \addplot[Set1-B,
            visualization depends on={\thisrow{NearMin} \as \startvalue},
            visualization depends on={\thisrow{NearMax} \as \endvalue},
            scatter/@pre marker code/.append code={\fill [draw=black,fill=Set1-B] (axis direction cs:0,0) rectangle (axis direction cs:\endvalue,2.5);
                    \pgfplotsset{mark=none}}
        ] table [y expr={\thisrow{Year}+1*2.5},x=NearMin] {\datatable};
        \addplot[Set1-C,
            visualization depends on={\thisrow{HereMin} \as \startvalue},
            visualization depends on={\thisrow{HereMax} \as \endvalue},
            scatter/@pre marker code/.append code={\fill [draw=black,fill=Set1-C] (axis direction cs:0,0) rectangle (axis direction cs:\endvalue,2.5);
                    \pgfplotsset{mark=none}}
        ] table [y expr={\thisrow{Year}+2*2.5},x=HereMin] {\datatable};
        \addplot[Set1-D,
            visualization depends on={\thisrow{bMin} \as \startvalue},
            visualization depends on={\thisrow{bMax} \as \endvalue},
            scatter/@pre marker code/.append code={\fill [draw=black,fill=Set1-D] (axis direction cs:0,0) rectangle (axis direction cs:\endvalue,2.5);
                    \pgfplotsset{mark=none}}
        ] table [y expr={\thisrow{Year}+3*2.5},x=bMin] {\datatable};
    \end{axis}
\end{tikzpicture}

\end{document}

enter image description here

5
  • Thx for the answer. It is not, what I'd like. In the first my sketch I drawn what I would like (it depicts what I exactly would like to have). I have in this example the table of data shown at the bottom of picture. I would like to read x_min and x_max for each bar, read label for this bar, read entry for legend, and read (from the first column) the category of group of bars. In this example are four groups of bars (separated by greater spacing). In general each group has different number of bars.
    – forrest
    May 5, 2020 at 12:21
  • @forrest, just to ensure I understand well : you want bars to start at the value indicated by xMin and end at the value of xMax ?
    – BambOo
    May 5, 2020 at 14:21
  • yes, exactly. But solutions linked in my question are not satisfying for me. In the single row of the table at the bottom of my sketch are listed pairs of values (for horizontal positions of bar ends). After each pair of numbers given in the table there is a label for this bar (denoted by orange arrows).
    – forrest
    May 5, 2020 at 17:30
  • the input data is all collected in the one table - I proposed the structure of this table at the bottom of my sketch. While x_min and x_max are "nan", then the group of bars should be squeezed (to not left an empty space in the place of absent bar). In the picture are 4 groups: "Category A", "Category B", "Category C " and "Category D" - these are also read from the first column of the table an put as yticks. I use black and white scale for filling bars.
    – forrest
    May 5, 2020 at 17:44
  • 1
    @forrest, as far as I understand your demand, squeezing the plot won't be possible I think. Regarding the categories, it is just a matter of symbolic y coords. Finally, in my last proposal, it is really easy to fill the bars with some pattern
    – BambOo
    May 5, 2020 at 18:03

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.