16

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

  • 1
    It might be easier in this case to use straight Tikz instead of pgfplots. Start by stacking and naming all the nodes of the far left. Or perhaps the faint color bars. – John Kormylo Jun 27 '14 at 2:59
  • Instead of a graph you could try with a tabular with pgfplotstable help. – Ignasi Jun 27 '14 at 8:45
  • 1
    I'am almost there!! = ) – leonardo cesar Jun 27 '14 at 12:39
  • Waiting for answer!!! – MYaseen208 Jun 27 '14 at 12:54
  • @leonardocesar: Just out of interest: Which country is it? – Martin Thoma Jul 2 '14 at 19:45
14

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.

  • I should mention that I stole the symbols from leonardo cesar's solution. – John Kormylo Jun 29 '14 at 1:10
  • wow! amazing solution!!! but there is a little problem with logic! you must to divide each group age by total population. thanks!!! – leonardo cesar Jun 29 '14 at 15:30
  • Okay, change made. – John Kormylo Jun 29 '14 at 18:24
  • This solution is nice, but the age ranges use a hyphen with spaces, which is typographically poor. Correct would be an en dash without spaces. – lblb May 13 '17 at 14:05
10

this look better = )

\documentclass[10pt]{article}
\usepackage{pgfplots}\pgfplotsset{compat=1.10}
\usepackage{pgfplotstable}
\usepackage{lmodern}
\usepackage{wasysym}
\begin{document}

\pgfplotstableread[col sep=comma, header=true]{
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}[
    name=popaxis,
    scale only axis,
    xbar,
    xmin=0,
    xmax=5,
    %xbar=2pt,
    %xlabel={\%},
    width=0.5*\textwidth, height= 0.5*\textheight,
   y dir = reverse,
   nodes near coords = {\pgfmathprintnumber\pgfplotspointmeta\%},
   every node near coord/.append style={font=\scriptsize, color=black},
   xticklabel= {\pgfmathprintnumber\tick\%},
    y tick label style={text width=3cm,align=center},
    axis x line=left,
    axis y line=none,
%
    enlarge x limits = {value=0.15,upper},
    axis line style={-},
    clip=false
]
\addplot[magenta,fill=magenta!50] table[y expr =\coordindex, x expr={\thisrow{woman}/31395*100}] \loadedtable;%total pop = 31395 
%
\pgfplotstablegetrowsof{\loadedtable}
\pgfmathsetmacro{\lastrow}{\pgfplotsretval-1}  
\node[xshift=-1.5cm,yshift=-0.5cm,align=center] at (axis cs:0,\lastrow) {\textcolor{cyan}{\huge\mars} Man}; 
\node[xshift= 1.5cm,yshift=-0.5cm,align=center] at (axis cs:0,\lastrow) {\textcolor{magenta}{\huge\venus} Woman}; 
\node[xshift= 0.0cm,yshift=-2.0cm,align=center] at (axis cs:0,\lastrow) {\%}; 
%
\end{axis}
\begin{axis}[ 
at={(popaxis.north west)},anchor=north east, %xshift=-3cm,
scale only axis,
    xbar = 0,
    xmin = 0,
    xmax = 5,
    width=0.5*\textwidth,
    height= 0.5*\textheight,
    x dir=reverse,
    y dir=reverse,
    nodes near coords = {\pgfmathprintnumber\pgfplotspointmeta\%},
    every node near coord/.append style={rotate = 0, anchor = east, font=\scriptsize, color=black},
    xticklabel= {\pgfmathprintnumber\tick\%},
    axis x line=left,
    axis y line*=left,
%
    ytick = data,
    yticklabels from table = {\loadedtable}{age},
    ytick align=center,
    ytick pos=left,
    enlarge x limits = {value=0.15,upper},
    axis line style={-}
]
\addplot[cyan,fill=cyan!50] table[y expr =\coordindex, x expr={\thisrow{man}/31395*100}] \loadedtable; %total pop = 31395 
\end{axis}
\end{tikzpicture}
\end{document}

prettier

  • This solution is nice, but the age ranges use a hyphen with spaces, which is typographically poor. Correct would be an en dash without spaces – lblb May 13 '17 at 14:05

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.