Graphics, Figures & TablesMacros for piechart, barchart, spiderweb

Information and discussion about graphics, figures & tables in LaTeX documents.
Post Reply
koubi
Posts: 2
Joined: Fri Sep 16, 2016 4:39 pm

Macros for piechart, barchart, spiderweb

Post by koubi »

Hi,

I spent few days trying to "macrofy" the rendering of piechart, barchart, and spiderweb from raw data input wihtin the document. I wanted some high level API like

Code: Select all

\barchart
  {5cm} %%label width
  { %% data (label/value)
    apples/432,
    banana/34,
    pineapples/1223
  }%%barchart

\spiderweb %% 0 to 100 grid
  {4,5cm} %% radius for labels
  {3,5cm} %% radius for spiderweb
  {70} %% baseline score
  { %% data (label/score)
    Fifty/10,
    Sixty/20,
    Seventy/50
  }%%spiderweb

or

\def\colorCycleList{{"blue","red","yellow","green", "orange", "gray" }}
\piechart{
  (Very Low,37),
  (Low,23),
  (Average,34),
  (High,5),
  (Very High,1)
}%%piechart
The above is how I currently use the macros I implemented. I had to use xintForPair for the last one, maybe foreach can be use instead... well although the spiderweb rendering isn't that short (few seconds...), it's fine for me, and I just wanted to share.

Macro for piechart with example :

Code: Select all

\documentclass{standalone}

\usepackage{tikz}
\usepackage{fp}
\usepackage{pgfplots}
\usepackage{xintexpr}
\usepackage{xstring}

%% originaly used with babel french, which causes some issues
%% wihout the following line
\usetikzlibrary{babel}


\def\colorCycleList{{"blue","red","yellow","green", "orange", "gray" }}


%%
%% #1 = list of titles and values (not percents)
%%
%%  example : 
%%
%%	\piechart{
%%		(Very Low,37),
%%		(Low,23),
%%		(Average,34),
%%		(High,5),
%%		(Very High,1)
%%	}
%%
\newcounter{totalPieChart}
\newcommand{\piechart}[1]{
	%compute total in order to compute percentages
	\setcounter{totalPieChart}{0}
	\xintForpair ##1##2 in{#1} \do {\addtocounter{totalPieChart}{##2}}
	
	%get the number of colors in the \colorCycleList
	\pgfmathparse{int(dim(\colorCycleList)-1)}
	\edef\cycleColorMax{\pgfmathresult}
	
	\begin{tikzpicture}[nodes = {font=\sffamily}, scale=1]
		\def\angle{0}
		\def\radius{3}
		\newcount\cyclecount \cyclecount=-1
		\newcount\ind \ind=-1
		\xintForpair ##1##2 in{#1} \do {
			\def\currentLoc{##2}
			\def\name{##1}
			\FPeval{\percent}{round(\currentLoc*100/\thetotalPieChart,2)}
			\ifx\percent\empty\else               % If \percent is empty, do nothing
				\global\advance\cyclecount by 1     % Advance cyclecount
				\global\advance\ind by 1            % Advance list index
				\ifnum\cycleColorMax<\cyclecount                 % If cyclecount is larger than list
					\global\cyclecount=0              %   reset cyclecount and
					\global\ind=0                     %   reset list index
				\fi
				\pgfmathparse{\colorCycleList[\the\ind]} % Get color from cycle list
				\edef\color{\pgfmathresult}         %   and store as \color
				% Draw angle and set labels
				\draw[fill={\color!50},draw={\color!50!black!50}] (0,0) -- (\angle:\radius)
				arc (\angle:\angle+\percent*3.6:\radius) -- cycle;
				% do not print the percentage within the pie if the slice is too small
				\ifnum5<\percent
					\node at (\angle+0.5*\percent*3.6:0.7*\radius) {\percent\,\%};
					\node[pin=\angle+0.5*\percent*3.6:\name] at (\angle+0.5*\percent*3.6:\radius) {};
				\else
					\node at (\angle+0.5*\percent*3.6:0.7*\radius) {};
					\node[pin=\angle+0.5*\percent*3.6:\name~(\percent\,\%)] at (\angle+0.5*\percent*3.6:\radius) {};
				\fi
				%at (\angle+0.5*\percent*3.6:{\radius+1.3}) {\percent\%};
				\pgfmathparse{\angle+\percent*3.6}  % Advance angle
				\xdef\angle{\pgfmathresult}         %   and store in \angle
			\fi
		};
	\end{tikzpicture}
}


\begin{document}
	
\piechart{
	(Very Low,873)
}
\piechart{
	(Very Low,873),
	(Low,1890)
}
\piechart{
	(Very Low,873),
	(Low,1890),
	(Average,3755)
}
\piechart{
	(Very Low,873),
	(Low,1890),
	(Average,3755),
	(High,1165),
	(Very High,325),
	(Low,1890),
	(Average,3755),
	(Low,1890),
	(Average,3755),
	(Very Very High,5)
}

\end{document}
pie-chart-1.png
pie-chart-1.png (17.6 KiB) Viewed 4067 times
pie-chart-2.png
pie-chart-2.png (35.81 KiB) Viewed 4067 times
Macro for spiderweb with example:

Code: Select all

\documentclass{standalone}

\usepackage{tikz}
\usepackage{pgfplots}

%% originaly used with babel french, which causes some issues
%% wihout the following line
\usetikzlibrary{babel}

%%
%% #1 = radius (4cm is good)
%% #2 = radius for labels (3.5cm)
%% #3 = score baseline (blue line)
%% #4 = list of titles and values
%%
%% example 
%%  
%%  \spiderweb{4,5cm}{3,5cm}{70}{
%%	  Sécurité/10,
%%	  Maintenabilité/30,
%%	  Fiabilité/50,
%%	  Portabilité/70,
%%	  Performance/90
%%	}
%%
\newcommand{\spiderweb}[4]{
	
	\newcount\numberOfDimensionsForSpiderWebCounter \numberOfDimensionsForSpiderWebCounter=0
	\foreach \Title/\Score in {#4} {
		\global\advance\numberOfDimensionsForSpiderWebCounter by 1     % Advance cyclecount
	}
	
	\def\numberOfDimensionsForSpiderWeb{\the\numberOfDimensionsForSpiderWebCounter} % number of dimensions (config option)
	\def\numberOfScaleUnits{100} % number of scale units (config option)
	\def\calculatedAngleBetweenDimAxes{360/\numberOfDimensionsForSpiderWeb} % calculated angle between dimension axes
	
	\newdimen\R % maximal diagram radius (config option)
	\newdimen\L % radius to put dimension labels (config option)
	
	\L=#1
	\R=#2
	
	\begin{tikzpicture}[nodes = {font=\sffamily}, scale=1]
	\path (0:0cm) coordinate (O); % define coordinate for origin
	% draw the spiderweb
	\foreach \Title/\Value[count=\X] in {#4} {
		\draw (\X*\calculatedAngleBetweenDimAxes+90:0) -- (\X*\calculatedAngleBetweenDimAxes+90:\R);
		\node[pin=\X*\calculatedAngleBetweenDimAxes+90:{\Title}] at (\X*\calculatedAngleBetweenDimAxes+90:\R) {};
	}
	\foreach \Y in {0,...,10}{
		\draw [opacity=0.3] (90:\Y*\R*10/\numberOfScaleUnits) 
		\foreach \X in {1,...,\numberOfDimensionsForSpiderWeb}{
			-- (\X*\calculatedAngleBetweenDimAxes+90:\Y*\R*10/\numberOfScaleUnits)
		} -- cycle;
	}
	% create paths
	\foreach \Y in {0,...,\numberOfScaleUnits}{
		\foreach \X in {1,...,\numberOfDimensionsForSpiderWeb}{
			\path (\X*\calculatedAngleBetweenDimAxes+90:\Y*\R/\numberOfScaleUnits) coordinate (D\X-\Y);
			\fill (D\X-\Y) circle (1pt);
		}
	}
	
	\newcount\cyclecountDeux \cyclecountDeux=0
	\foreach \Title/\Score in {#4} {
		\global\advance\cyclecountDeux by 1     % Advance cyclecount
		\def\X{\the\cyclecountDeux}
		\def\Y{\Score}
		\path (\X*\calculatedAngleBetweenDimAxes+90:\Y*\R/\numberOfScaleUnits) coordinate (S\X);
		\fill (S\X) circle (1pt);
	}
	
	\newcount\cyclecount \cyclecount=1
	\foreach \Title/\Score in {#4} {
		\global\advance\cyclecount by 1     % Advance cyclecount
		\ifnum\numberOfDimensionsForSpiderWebCounter<\cyclecount
		\global\cyclecount=1
		\fi
		
		\newcount\nextcyclecount \nextcyclecount=\the\cyclecount;
		\global\advance\nextcyclecount by 1 
		\ifnum\numberOfDimensionsForSpiderWebCounter<\nextcyclecount
		\global\nextcyclecount=1
		\fi
		\draw  [color=blue,line width=4pt,opacity=0.5] (D\the\cyclecount-#3) -- (D\the\nextcyclecount-#3);
		\draw  [color=red,line width=4pt,opacity=0.5] (S\the\cyclecount) -- (S\the\nextcyclecount);
		
	}
	
	\end{tikzpicture}
}

\begin{document}

\spiderweb{4,5cm}{3,5cm}{70}{
	Chaos/10,
	Order/30,
	Neutral/50
}

\spiderweb{4,5cm}{3,5cm}{70}{
	Security/10,
	Maintenability/30,
	Reliability/50,
	Portability/70,
	Efficiency/10
}


\spiderweb{4,5cm}{3,5cm}{70}{
	Security/10,
	Maintenability/30,
	Reliability/50,
	Portability/70,
	Maintenability/30,
	Reliability/50,
	Portability/70,
	Maintenability/30,
	Reliability/50,
	Portability/70,
	Maintenability/30,
	Reliability/50,
	Portability/70,
	Maintenability/30,
	Reliability/50,
	Portability/70,
	Efficiency/10
}

\end{document}
spiderweb-chart-1.png
spiderweb-chart-1.png (39.83 KiB) Viewed 4067 times
spiderweb-chart-2.png
spiderweb-chart-2.png (43.08 KiB) Viewed 4067 times
Macro for barchart with example:

Code: Select all

\documentclass{report}

\usepackage[left=20mm,right=20mm, top=30mm, bottom=30mm,a4paper]{geometry}
\usepackage[usenames,dvipsnames,table,xcdraw]{xcolor}

\usepackage{pgfplots}
\usepackage{setspace}

\usepackage{siunitx}
\usepackage{tikz}
\usepackage{wallpaper}
\usepackage{xintexpr}
\usetikzlibrary{shadows}

%% originaly used with babel french, which causes some issues
%% wihout the following line
\usetikzlibrary{babel}

\makeatletter
\newcommand*{\getlengthincm}[1]{\strip@pt\dimexpr0.035146\dimexpr#1\relax\relax}
\makeatother


\newcommand{\nume}[1]{\num[group-separator=\text{~},output-decimal-marker={,},group-digits=integer,group-minimum-digits=4]{#1}}

%%
%% #1 = label width
%% #2 = list of titles and values
%%
%% example:
%%
%%	\barchart{6cm}{
%%		apples/2047,
%%		pears/1244,
%%		banana/1208,
%%		{a fruit name with slashes / well / ...}/1055,
%%		{a fruit name}/9009
%%	}
%%
\newcommand{\barchart}[2]{
	%% reorder list (thanks \foreach...)
	\foreach  \l/\x[count=\y] in {#2} {
		\ifthenelse{\equal{\y}{1}}{
			\xdef\orderedList{{\l}/\x}
		}{
			\xdef\orderedList{{\l}/\x, \orderedList}
		}
	}

	%% compute bar length to adjust to textwidth
	\newdimen\barlength
	\setlength{\barlength}{\textwidth}
	\addtolength{\barlength}{-#1}
	\addtolength{\barlength}{-2cm}
	
	\def\maxval{0} %% stores the maximum value of input data
	\newcount\barcount \barcount=0
	\foreach  \l/\x[count=\y] in \orderedList {
		\global\edef\maxval{\xinttheiiexpr max(\x,\maxval)\relax}
		\global\advance\barcount by 1
	}
	\edef\maxval{\xintthefloatexpr (\maxval*110)/100\relax} %% add 10% to the max value for dimensionning the graph
	
	\def\xstep{\xinttheiexpr [10] \getlengthincm{\barlength}/\maxval \relax} %% compute the x steps
	
	{ %% add group to avoid spreading stretch value
		\setstretch{0.6}
		\begin{tikzpicture}[x={(\xstep,0)}]
			\foreach  \l/\x[count=\y] in \orderedList { 
				%% \l = label name
				%% \x = input data
				\node[left] at (0,\y) {
					%% surround the data within a tabular
					\begin{tabular}{>{
							%\columncolor{white}
							\raggedleft\arraybackslash}p{#1}} \small \l \end{tabular}
				};
				%% draw bar
				\filldraw[blue!50, draw=blue!50!black] (0,\y-.3) rectangle (\x,\y+.3);
				%% draw value as a node
				\node[right] at (\x, \y) {\small\nume{\x}};
			}
			%% draw axis bar
			\draw (0,0.5) -- (0,\xintthefloatexpr{\the\barcount + 0.5}\relax);
			
		\end{tikzpicture}
	}
}


\begin{document}

\barchart{3cm}{
    {a line with slashes / and multiline}/666
}

\begin{figure}
    \barchart{4cm}{
        TITI/25000,
        TATA/15000,
        TUTU/3900
    }
    \caption{test bar chart}
\end{figure}

\end{document}
Image

Feel free to use and comment.

Best regards.
Last edited by koubi on Mon Sep 19, 2016 9:59 am, edited 2 times in total.

Recommended reading 2024:

LaTeXguide.org • LaTeX-Cookbook.net • TikZ.org

NEW: TikZ book now 40% off at Amazon.com for a short time.

And: Currently, Packt sells ebooks for $4.99 each if you buy 5 of their over 1000 ebooks. If you choose only a single one, $9.99. How about combining 3 LaTeX books with Python, gnuplot, mathplotlib, Matlab, ChatGPT or other AI books? Epub and PDF. Bundle (3 books, add more for higher discount): https://packt.link/MDH5p

Stefan Kottwitz
Site Admin
Posts: 10324
Joined: Mon Mar 10, 2008 9:44 pm

Macros for piechart, barchart, spiderweb

Post by Stefan Kottwitz »

Hi koubi,

welcome to the forum!

Very interesting macros. Thank you for sharing! I added images to your post, so readers can immediately see the examples as compiled images too.

When I tested the bar chart example, I got

! Undefined control sequence.
\XINT_expr_scanfunc_panic ...rror:bigtroubleahead
(0\relax
l.94 }


but I must admit that I tested it on a PC with a TeX installation of 2014. I will test it again on TeX Live 2016 at home.

Stefan
LaTeX.org admin
koubi
Posts: 2
Joined: Fri Sep 16, 2016 4:39 pm

Re: Macros for piechart, barchart, spiderweb

Post by koubi »

I compiled that on archlinux with texlive from repo. It also works with latest stable miktex on windows.
Post Reply