[Tex/LaTex] How to draw a pretty population pyramid graph with pgfplots

pgfplotspgfplotstable

I'm trying to plot a graph population pyramid.

I wrote this MWE.

\documentclass{article}

\usepackage{pgfplots}
\usepackage{pgfplotstable}

\begin{document}
\pgfplotstableread[col sep=comma]{
age,man,woman
+100 years,0,4
95 - 99 years,3,6
90 - 94 years,10,16
85 - 89 years,43,57
80 - 84 years,103,111
75 - 79 years,175,224
70 - 74 years,274,322
65 - 69 years,421,427
60 - 64 years,514,524
55 - 59 years,578,606
50 - 54 years,732,785
45 - 49 years,885,911
40 - 44 years,1044,1120
35 - 39 years,1192,1289
30 - 34 years,1315,1306
25 - 29 years,1214,1362
20 - 24 years,1360,1336
15 - 19 years,1471,1406
10 - 14 years,1495,1446
5 - 9 years,1375,1351
0 - 4 years,1325,1257
}\loadedtable


\begin{tikzpicture}
\begin{axis}[
    width =\textwidth,
    height = 0.6*\textheight,
    enlarge x limits=0.6,
    xbar = 0pt,
    xmin = -5,
    xmax = 5,
    y dir = reverse,
    nodes near coords,
    bar width=8pt,
    bar shift=0pt,
    axis x line= left,
    nodes near coords = {\pgfmathprintnumber\pgfplotspointmeta\%},
    every node near coord/.append style={font=\scriptsize, color=black},
    xticklabel= {\pgfmathprintnumber\tick\%},
    symbolic y coords={+100 years,95 - 99 years,90 - 94 years,85 - 89 years,80 - 84 years,75 - 79 years,70 - 74 years,65 - 69 years,60 - 64 years,55 - 59 years,50 - 54 years,45 - 49 years,40 - 44 years,35 - 39 years,30 - 34 years,25 - 29 years,20 - 24 years,15 - 19 years,10 - 14 years,5 - 9 years,0 - 4 years},
   ytick={+100 years,95 - 99 years,90 - 94 years,85 - 89 years,80 - 84 years,75 - 79 years,70 - 74 years,65 - 69 years,60 - 64 years,55 - 59 years,50 - 54 years,45 - 49 years,40 - 44 years,35 - 39 years,30 - 34 years,25 - 29 years,20 - 24 years,15 - 19 years,10 - 14 years,5 - 9 years,0 - 4 years}
            ]
        \addplot [fill = cyan!50]    table[y=age,x expr={-\thisrow{man}/31395*100}] \loadedtable;%31395 is the total population
        \addlegendentry{Man}
        \addplot [fill = magenta!50] table[y=age,x expr={\thisrow{woman}/31395*100}] \loadedtable;%31395 is the total population
        \addlegendentry{Woman}
    \end{axis}
\end{tikzpicture}
\end{document}

But the result is an ugly graph:

enter image description here

How can I improve it to obtain something like this?

enter image description here

Best Answer

It takes a lot of work to be pretty!

\documentclass{standalone}
\usepackage[svgnames]{xcolor}
\usepackage{tikz}
\usepackage{pgfplotstable}
\usepackage{wasysym}

\begin{document}
\pgfplotstableread[col sep=comma]{
age,man,woman
+100 years,0,4
95 - 99 years,3,6
90 - 94 years,10,16
85 - 89 years,43,57
80 - 84 years,103,111
75 - 79 years,175,224
70 - 74 years,274,322
65 - 69 years,421,427
60 - 64 years,514,524
55 - 59 years,578,606
50 - 54 years,732,785
45 - 49 years,885,911
40 - 44 years,1044,1120
35 - 39 years,1192,1289
30 - 34 years,1315,1306
25 - 29 years,1214,1362
20 - 24 years,1360,1336
15 - 19 years,1471,1406
10 - 14 years,1495,1446
5 - 9 years,1375,1351
0 - 4 years,1325,1257
}\loadedtable

\newlength{\dy}\setlength{\dy}{\baselineskip}
\newlength{\dx}\setlength{\dx}{0.007em}
\newlength{\temp}

\pgfplotstablegetrowsof{\loadedtable}
\pgfmathparse{\pgfplotsretval-1}
\edef\rows{\pgfmathresult}
% compute total for percentages
\setlength{\temp}{0pt}
\foreach \y in {0,1,...,\rows}{%
  \pgfplotstablegetelem{\y}{man}\of\loadedtable
  \global\advance\temp by \pgfplotsretval pt
  \pgfplotstablegetelem{\y}{woman}\of\loadedtable
  \global\advance\temp by \pgfplotsretval pt
}
\pgfmathparse{0.01\temp}
\edef\total{\pgfmathresult}

\noindent\begin{tikzpicture}

\node at (0,2\dy) {Population Pyramid - 2010};
\node[below,text=Navy] at (-8em,0) {\huge\mars};
\node[below,text=HotPink] at (8em,0) {\huge\venus};

\foreach \y in {0,1,...,\rows}{%
  \pgfplotstablegetelem{\y}{age}\of\loadedtable
  \node[left] at (-19em,-\y\dy) {\strut\pgfplotsretval};
  \pgfplotstablegetelem{\y}{man}\of\loadedtable
  \node[left] at (-15em,-\y\dy) {\strut\pgfplotsretval};
  \node[left,fill=Navy,text width=\pgfplotsretval\dx,text height=.8\dy,inner sep=0]
    at (0,-\y\dy+0.1\dy) {};
  \pgfmathparse{\pgfplotsretval/\total}
  \pgfmathprintnumberto[fixed,precision=2]{\pgfmathresult}{\per}
  \node[left] at (-\pgfplotsretval\dx,-\y\dy) {\small\strut\per\%};
  \pgfplotstablegetelem{\y}{woman}\of\loadedtable
  \node[left] at (17em,-\y\dy) {\strut\pgfplotsretval};
  \node[right,fill=HotPink,text width=\pgfplotsretval\dx,text height=.8\dy,inner sep=0]
    at (0,-\y\dy+0.1\dy) {};
  \pgfmathparse{\pgfplotsretval/\total}
  \pgfmathprintnumberto[fixed,precision=2]{\pgfmathresult}{\per}
  \node[right] at (\pgfplotsretval\dx,-\y\dy) {\small\strut\per\%};
}
\node[left] at (-3em,-\rows\dy-.9\dy) {Man};
\node[left,fill=Navy,text width=2em,text height=.8\dy,inner sep=0]
  at (-1mm,-\rows\dy-.9\dy) {};
\node[right,fill=HotPink,text width=2em,text height=.8\dy,inner sep=0]
  at (+1mm,-\rows\dy-.9\dy) {};
\node[right] at (3em,-\rows\dy-.9\dy) {Woman};

\end{tikzpicture}
\end{document}

pyramid

You can adjust the size by adjusting the font, or with more precision using \resizebox.

Related Question