Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I recently stumbled upon this post, that explains how to make polar histogramms with R. An improved version of R-package can be found here. Even though it looks unfamiliar, the result of is quite nice in my opinion.

I thought that I would be great to directly generate such a graph within LaTeX. Unfortunately pgfplots does not contain such a function yet, but maybe somebody here does know how a polar histogram could be achieved.

This is an example histogram that is provided on the page.

enter image description here

share|improve this question
5  
It's a nice pgfplots problem alright but I don't think it's pretty and I doubt that it's useful. You can include the same info in the same space by just using straight stacked bar plot. That's probably why you have to introduce white arcs to support the information otherwise it would be impossible to compare something above with something below. Also the central parts of the plot is lost due to the scaling. –  percusse Dec 19 '12 at 17:02
2  
This sort of chart can be useful if the positioning of the items round the outside is meaningful -- then you can get a radar chart like overview of the data that may tell you more than a straight bar chart. It's the sort of thing that I would do directly in PostScript, or possibly with Metapost or something else low level. –  Thruston Dec 19 '12 at 17:13
    
related: question search for 'ring chart' –  cmhughes Dec 19 '12 at 19:37
add comment

1 Answer 1

up vote 39 down vote accepted

Fourth Version

Some corrections from third version: automatic orientation for labels of groups, correct lengths for bended arrows, better endings for ticks (did you remark 666? ;-)), factorization of styles (there are two global styles: dartstyle and orig).

Here are two results (with dartstyle then with orig):

enter image description here

enter image description here

The code:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.text}
\usepackage{ifthen}
\tikzset{
  orig/.style={
    hist 1/.style={fill=red!70!gray},
    hist 2/.style={fill=blue!60!white},
    hist 3/.style={fill=green!50!gray},
    arrow group/.style={draw,color=black,very thick,latex-latex},
    target/.style={fill=pink!60!black,draw=black,
      line width=1pt,double distance=1pt,double=white},
    rev text on arc/.style={
      decorate,decoration={text along path,
        text={##1},text align={align=center},
        text color=black,reverse path}
    },
    text on arc/.style={
      decorate,decoration={text along path,
        text={##1},text align={align=center},
        text color=black,
      },
    },
    major tick/.style={draw=white,thick},
    minor tick/.style={draw=white,thin,draw opacity=.5},
    tick label/.style={font=\tiny\bfseries},
    text=black,
    font=\bfseries\sffamily,
  },
  dartstyle/.style={
    hist 1/.style={fill=red!80!white},
    hist 2/.style={fill=yellow!60!white},
    hist 3/.style={fill=green!70!black},
    arrow group/.style={draw=white,white,very thick,latex-latex},
    target/.style={fill=black,draw=black,
      line width=1pt,double distance=1pt,double=white},
    rev text on arc/.style={
      decorate,decoration={text along path,
        text={##1},text align={align=center},
        text color=white,reverse path}
    },
    text on arc/.style={
      decorate,decoration={text along path,
        text={##1},text align={align=center},
        text color=white}
    },
    major tick/.style={draw=white,thick},
    minor tick/.style={draw=white,thin,draw opacity=.5},
    tick label/.style={font=\tiny\bfseries},
    text=white,
    font=\bfseries\sffamily,
  },
}

\def\astep{9} % step (degree) between sectors
\def\mstep{3} % half width (degree) of each sector
\def\min{8mm} % min distance from center
\def\max{4cm} % max distance from center

\def\mydata{%
  First Group/{%
    xxx/{10,30,60},%
    aab/{20,30,50},%
    aba/{30,30,40},%
    aad/{40,30,30},%
    aca/{50,30,20},%
    zcb/{60,10,30},%
    aaa/{10,30,60},%
    aab/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60}%
  },%
  Second Group/{%
    yyy/{60,10,30},%
    zcb/{60,10,30},%
    aaa/{10,30,60},%
    aab/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60},%
    zcb/{60,10,30},%
    zcb/{75,10,15},%
    aaa/{10,30,60}%
  },%
  Third Group/{%
    zzz/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60},%
    zcb/{60,10,30},%
    zcb/{60,10,30},%
    aaa/{10,30,60},%
    aab/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60},%
    zcb/{60,30,10}% 
  }%
}

\begin{document}
\begin{tikzpicture}[orig]
  \tikzset{
    declare function={
      secttoangle(\sect)=(\sect)*\astep;
      percenttodist(\percent)=\min+(\max-\min)/100*\percent;
    },
  }

  \path[target]
  circle(\max+1.6cm);

  \def\cursectinit{-.666}
  \foreach \curgroup/\curdata in \mydata {
    \foreach \curlabel/\values [count=\cp] in \curdata {
      % angle for this current label
      \pgfmathsetmacro{\angle}{secttoangle(\cursectinit+\cp)}
      % percent
      \xdef\total{0}
      % histogram
      \foreach \val [count=\cv] in \values {
        \pgfmathsetmacro{\nexttotal}{\total+\val}
        \pgfmathsetmacro{\dmin}{percenttodist(\total)}
        \pgfmathsetmacro{\dmax}{percenttodist(\nexttotal)}
        % sector
        \path[hist \cv=\angle] (\angle+\mstep:\dmin pt)
        arc(\angle+\mstep:\angle-\mstep:\dmin pt) -- (\angle-\mstep:\dmax pt)
        arc(\angle-\mstep:\angle+\mstep:\dmax pt) -- cycle;
        % iteration
        \xdef\total{\nexttotal}
      }
      % label (with autorotation)
      \pgfmathtruncatemacro{\revlab}{and(\angle>90,\angle<270)?1:0}
      \ifthenelse{\equal{\revlab}{1}}{ 
        \node[rotate=180+\angle,anchor=east] at (\angle:\max) {\curlabel};
      }{
        \node[rotate=\angle,anchor=west] at (\angle:\max) {\curlabel};
      }
    }
    % group limits
    \pgfmathsetmacro{\newsectinit}{\cursectinit+\cp}
    \pgfmathsetmacro{\angleinit}{secttoangle(\cursectinit + 1)-\mstep}
    \pgfmathsetmacro{\anglefinal}{secttoangle(\newsectinit)+\mstep}
    % group label
    {
      \Large\bfseries\sffamily
      \pgfmathtruncatemacro{\anglem}{(\angleinit+\anglefinal)/2}
      \pgfmathtruncatemacro{\revtext}{and(\anglem>0,\anglem<180)?1:0}
      \ifthenelse{\equal{\revtext}{1}}{ 
        \draw[rev text on arc=\curgroup] (\angleinit:\max+1.1cm)
        arc(\angleinit:\anglefinal:\max+1.1cm);
      }{
        \draw[text on arc=\curgroup] (\angleinit:\max+1.1cm+.5em)
        arc(\angleinit:\anglefinal:\max+1.1cm+.5em);
      }
    }
    % group arrow
    \path[arrow group]
    (\angleinit:\max+9mm) arc(\angleinit:\anglefinal:\max+9mm);
    % iteration
    \pgfmathsetmacro{\newsectinit}{\newsectinit+1}
    \xdef\cursectinit{\newsectinit}
  }

  % level ticks
  \pgfmathsetmacro{\angleinit}{secttoangle(0)}
  \pgfmathsetmacro{\anglefinal}{secttoangle(\cursectinit-1)+\mstep}
  % major ticks with labels
  \foreach \percent in {0,50,100}{
    \pgfmathsetmacro{\dist}{percenttodist(\percent)}
    % tick
    \path[major tick] (\angleinit:\dist pt)
    arc(\angleinit:\anglefinal:\dist pt);
    % label
    \node[tick label,below,rotate=secttoangle(0)]
    at ({secttoangle(0)}:\dist pt) {\percent\%};
  }
  % minor ticks
  \foreach \percent in {10,20,30,40,60,70,80,90}{
    \pgfmathsetmacro{\dist}{percenttodist(\percent)}
    % tick
    \path[minor tick] (\angleinit:\dist pt)
    arc(\angleinit:\anglefinal:\dist pt);
  }

  % legend
  \foreach \mycat [count=\c] in {Bad,Mediocre,Good}{
    \path[hist \c=0] (2.75,-.5-.5*\c) rectangle ++(.2,.2) ++(0,-.1)
    node[right]{\mycat};
  }
\end{tikzpicture}
\end{document}

Third Version

Here is my third version: dartboard style, better ticks for the levels, legend...

enter image description here

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.text,shadows}
\usepackage{ifthen}
\tikzset{
  hist 1/.style={fill=red!80!white},
  hist 2/.style={fill=yellow!60!white},
  hist 3/.style={fill=green!70!black},
}

\def\astep{9} % step (degree) between sectors
\def\mstep{3} % half width (degree) of each sector
\def\min{8mm} % min distance from center
\def\max{4cm} % max distance from center

\def\mydata{%
  First Group/{%
    xxx/{10,30,60},%
    aab/{20,30,50},%
    aba/{30,30,40},%
    aad/{40,30,30},%
    aca/{50,30,20},%
    zcb/{60,10,30},%
    aaa/{10,30,60},%
    aab/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60}%
  },%
  Second Group/{%
    yyy/{60,10,30},%
    zcb/{60,10,30},%
    aaa/{10,30,60},%
    aab/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60},%
    zcb/{60,10,30},%
    zcb/{60,10,30},%
    aaa/{10,30,60}%
  },%
  Third Group/{%
    zzz/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60},%
    zcb/{60,10,30},%
    zcb/{60,10,30},%
    aaa/{10,30,60},%
    aab/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60},%
    zcb/{60,10,30}% 
  }%
}

\begin{document}
\begin{tikzpicture}%
  [text=white,font=\bfseries\sffamily,
  declare function={
    secttoangle(\sect)=(\sect)*\astep;
    percenttodist(\percent)=\min+(\max-\min)/100*\percent;
  }]

  \path[fill=black] circle(\max+1.6cm);

  \def\cursectinit{0}
  \foreach \curgroup/\curdata in \mydata {
    \foreach \curlabel/\values [count=\cp] in \curdata {
      % angle for this current label
      \pgfmathsetmacro{\angle}{secttoangle(\cursectinit+\cp)}
      % percent
      \xdef\total{0}
      % histogram
      \foreach \val [count=\cv] in \values {
        \pgfmathsetmacro{\nexttotal}{\total+\val}
        \pgfmathsetmacro{\dmin}{percenttodist(\total)}
        \pgfmathsetmacro{\dmax}{percenttodist(\nexttotal)}
        % sector
        \path[hist \cv=\angle] (\angle+\mstep:\dmin pt)
        arc(\angle+\mstep:\angle-\mstep:\dmin pt) -- (\angle-\mstep:\dmax pt)
        arc(\angle-\mstep:\angle+\mstep:\dmax pt) -- cycle;
        % iteration
        \xdef\total{\nexttotal}
      }
      % label (with autorotation)
      \pgfmathtruncatemacro{\anglenode}{\angle}
      \ifthenelse{\( \anglenode > 90 \) \AND \( \anglenode < 270 \)}{ 
        \node[rotate=180+\anglenode,anchor=east] at (\angle:\max) {\curlabel};
      }{
        \node[rotate=\anglenode,anchor=west] at (\angle:\max) {\curlabel};
      }
      \typeout{label:\curlabel}
    }
    % group limits
    \pgfmathsetmacro{\newsectinit}{\cursectinit+\cp}
    \pgfmathsetmacro{\angleinit}{secttoangle(\cursectinit + 1)}
    \pgfmathsetmacro{\anglefinal}{secttoangle(\newsectinit)}
    \typeout{from:\angleinit,to:\anglefinal}
    % group label
    {
      \Large\bfseries\sffamily
      \draw[decorate,decoration={text along path,
        text=\curgroup,text align={align=center},
        text color=white,reverse path}]
      (\angleinit:\max+1.1cm) arc(\angleinit:\anglefinal:\max+1.1cm);
    }
    % group arrow
    \draw[white,very thick,latex-latex]
    (\angleinit:\max+9mm) arc(\angleinit:\anglefinal:\max+9mm);
    % iteration
    \pgfmathsetmacro{\newsectinit}{\newsectinit+1}
    \xdef\cursectinit{\newsectinit}
  }

  % levels
  \pgfmathsetmacro{\angleinit}{secttoangle(0)}
  \pgfmathsetmacro{\anglefinal}{secttoangle(\cursectinit-1)+\astep/2}
  % thick ticks with labels
  \foreach \percent in {0,50,100}{
    \pgfmathsetmacro{\dist}{percenttodist(\percent)}
    % tick
    \draw[white,thick] (\angleinit:\dist pt)
    arc(\angleinit:\anglefinal:\dist pt);
    % label
    \node[below,font=\tiny\bfseries,rotate=secttoangle(0)]
    at ({secttoangle(0)}:\dist pt) {\percent\%};
  }
  % thin ticks
  \foreach \percent in {10,20,30,40,60,70,80,90}{
    \pgfmathsetmacro{\dist}{percenttodist(\percent)}
    % tick
    \draw[white,draw opacity=.5] (\angleinit:\dist pt)
    arc(\angleinit:\anglefinal:\dist pt);
  }

  % legend
  \foreach \mycat [count=\c] in {Bad,Mediocre,Good}{
    \path[hist \c] (3,-.25+-.5*\c) rectangle ++(.2,.2) ++(0,-.1)
    node[right]{\mycat};
  }
\end{tikzpicture}
\end{document}

Second Version

Here is a new version with some embellishments : groups, shaded backgrounds, levels...

enter image description here

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.text,shadows}
\usepackage{ifthen}
\tikzset{
  shaded/.style 2 args={
    draw=black,
    thin,rounded corners=1pt,
    top color=#1!50!black,
    bottom color=#1!30!black,
    middle color=#1,
    shading angle=#2-90,
  },
  hist 1/.style={shaded={cyan}{#1}},
  hist 2/.style={shaded={orange}{#1}},
  hist 3/.style={shaded={yellow}{#1}},
}

\def\astep{9} % width (degree) of each sector
\def\min{1cm} % min distance from center
\def\max{3cm} % max distance from center

\def\mydata{%
  First Group/{%
    xxx/{10,30,60},%
    aab/{20,30,50},%
    aba/{30,30,40},%
    aad/{40,30,30},%
    aca/{50,30,20},%
    zcb/{60,10,30},%
    aaa/{10,30,60},%
    aab/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60}%
  },%
  Second Group/{%
    yyy/{60,10,30},%
    zcb/{60,10,30},%
    aaa/{10,30,60},%
    aab/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60},%
    zcb/{60,10,30},%
    zcb/{60,10,30},%
    aaa/{10,30,60}%
  },%
  Third Group/{%
    zzz/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60},%
    zcb/{60,10,30},%
    zcb/{60,10,30},%
    aaa/{10,30,60},%
    aab/{30,30,40},%
    aba/{10,80,10},%
    aad/{30,30,40},%
    aca/{10,30,60},%
    zcb/{60,10,30}% 
  }%
}

\begin{document}
\begin{tikzpicture}[text=white,font=\bfseries,
  declare function={secttoangle(\sect)=(\sect)*\astep+\astep);},
  ]
  \sffamily
  \shade[top color=gray,bottom color=gray,middle color=black,shading angle=45]
  circle(\max+2cm);
  %\pgfmathsermacro{\angle}{-270}
  \def\cursectinit{0}
  \foreach \curgroup/\curdata in \mydata {
    \foreach \curlabel/\values [count=\cp] in \curdata {
      % angle for this current label
      \pgfmathsetmacro{\angle}{secttoangle(\cursectinit+\cp)}
      % distance from center
      \pgfmathsetmacro{\total}{\min}
      \pgfmathsetmacro{\am}{\max-\min}
      \xdef\total{\total}
      % histogram
      \foreach \val [count=\cv] in \values {
        \pgfmathsetmacro{\nexttotal}{\total pt+\am/100*\val}
        % sector
        \path[hist \cv=\angle]
        (\angle+\astep/2:\total pt)
        arc(\angle+\astep/2:\angle-\astep/2:\total pt)
        -- (\angle-\astep/2:\nexttotal pt)
        arc(\angle-\astep/2:\angle+\astep/2:\nexttotal pt)
        -- cycle;
        % iteration
        \xdef\total{\nexttotal}
      }
      % label (with autorotation)
      \pgfmathtruncatemacro{\anglenode}{\angle}
      \ifthenelse{\( \anglenode > 90 \) \AND \( \anglenode < 270 \)}{ 
        \node[rotate=180+\anglenode,anchor=east] at (\angle:\max) {\curlabel};
      }{
        \node[rotate=\anglenode,anchor=west] at (\angle:\max) {\curlabel};
      }
      \typeout{label:\curlabel}
    }
    \pgfmathsetmacro{\newsectinit}{\cursectinit+\cp}
    \pgfmathsetmacro{\angleinit}{secttoangle(\cursectinit + 1)}
    \pgfmathsetmacro{\anglefinal}{secttoangle(\newsectinit)}
    \typeout{from:\angleinit,to:\anglefinal}
    {
      \LARGE\bfseries
      \draw[decorate,
      font=\LARGE\bfseries,
      decoration={text along path,
        text=\curgroup,
        text align={align=center},
        text color=white,
        reverse path,
      }]
      (\angleinit:\max+1.2cm) arc(\angleinit:\anglefinal:\max+1.2cm);
    }

    \draw[white,very thick,latex-latex]
    (\angleinit:\max+1cm) arc(\angleinit:\anglefinal:\max+1cm);
    \pgfmathsetmacro{\newsectinit}{\newsectinit+1}
    \xdef\cursectinit{\newsectinit}
  }

  \pgfmathsetmacro{\angleinit}{secttoangle(0)}
  \pgfmathsetmacro{\anglefinal}{secttoangle(\cursectinit-1)+\astep/2}

  \foreach \percent in {25,50,75}{
    \pgfmathsetmacro{\dist}{\min+(\max-\min)/100*\percent}
    \draw[white,thick] (\angleinit:\dist pt)
    arc(\angleinit:\anglefinal:\dist pt);
    \node[font=\tiny\bfseries,rotate=secttoangle(-1)]
    at ({secttoangle(-1)}:\dist pt) {\percent\%};
  }

\end{tikzpicture}
\end{document}

First Version

Here is an example of polar histogram with TikZ.

enter image description here

\documentclass{standalone}
\usepackage{tikz}
\usepackage{ifthen}
\tikzset{
  hist 1/.style={fill=orange},
  hist 2/.style={fill=lime},
  hist 3/.style={fill=cyan},
}

\def\astep{6} % width (degree) of each sector
\def\min{5mm} % min distance from center
\def\max{3cm} % max distance from center

\begin{document}
\begin{tikzpicture}[text=white,font=\bfseries]
  \fill[green!30!black] circle(\max+1cm);
  \foreach \curlabel/\values [count=\cp] in {%
    aaa/{10,30,60},
    aab/{20,30,50},
    aba/{30,30,40},
    aad/{40,30,30},
    aca/{50,30,20},
    zcb/{60,10,30},
    aaa/{10,30,60},
    aab/{30,30,40},
    aba/{10,80,10},
    aad/{30,30,40},
    aca/{10,30,60},
    /,
    zcb/{60,10,30},
    zcb/{60,10,30},
    aaa/{10,30,60},
    aab/{30,30,40},
    aba/{10,80,10},
    aad/{30,30,40},
    aca/{10,30,60},
    zcb/{60,10,30}, 
    zcb/{60,10,30},
    aaa/{10,30,60},
    /,
    aab/{30,30,40},
    aba/{10,80,10},
    aad/{30,30,40},
    aca/{10,30,60},
    zcb/{60,10,30}, 
    zcb/{60,10,30},
    aaa/{10,30,60},
    aab/{30,30,40},
    aba/{10,80,10},
    aad/{30,30,40},
    aca/{10,30,60},
    zcb/{60,10,30}% 
  }{
    \ifthenelse{\equal{\curlabel}{}}{}{
      % angle for this current label
      \pgfmathsetmacro{\angle}{(\cp-1)*\astep-90+\astep/2}
      % distance from center
      \pgfmathsetmacro{\total}{\min}
      \pgfmathsetmacro{\am}{\max-\min}
      \xdef\total{\total}
      % histogram
      \foreach \val [count=\cv] in \values {
        \pgfmathsetmacro{\nexttotal}{\total pt+\am/100*\val}
        % sector
        \filldraw[hist \cv]
        (\angle+\astep/2:\total pt)
        arc(\angle+\astep/2:\angle-\astep/2:\total pt)
        -- (\angle-\astep/2:\nexttotal pt)
        arc(\angle-\astep/2:\angle+\astep/2:\nexttotal pt)
        -- cycle;
        % iteration
        \xdef\total{\nexttotal}
        \typeout{\val:\total}
      }
      % label (with autorotation)
      \pgfmathtruncatemacro{\anglenode}{\angle}
      \ifthenelse{\( \anglenode > 90 \) \AND \( \anglenode < 270 \)}{ 
        \node[rotate=180+\anglenode,anchor=east] at (\angle:\max) {\curlabel};
      }{
        \node[rotate=\anglenode,anchor=west] at (\angle:\max) {\curlabel};
      }
    }
  }
\end{tikzpicture}
\end{document}
share|improve this answer
    
that already looks good. Is it be possible though to add white lines that indicate the different levels? –  maetra Dec 21 '12 at 11:44
    
In the second and third example, I think you should swap Third Group, in order that you could read it easily. –  Manuel Dec 22 '12 at 2:07
    
very impressive! Have you also tried drawing the white lines only on the bar plots as in the example? And I think the bended arrows for the groups are a bit too short. –  maetra Dec 22 '12 at 11:05
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.