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.

Excel has a cool feature called "data bar" conditional formatting that allows the user to partially fill a cell background to create histograms based on cell contents. Here is a screen clip example from a workbook:

example

I'm looking for a TiKZ, minipage, or some other solution that will allow me to only partially color the background of a cell. I've played around with a few options without success.

Here is a minimum working example using \cellcolor from the xcolor package.

\documentclass{article}
\usepackage[table]{xcolor}% http://ctan.org/pkg/xcolor
\begin{document}
\begin{tabular}{l|c|r}
  \hline
  91.41 & \cellcolor{green!25}90.81 & 38.76 \\
  \cellcolor{green!25}98.75 & 13.82 & 94.62 \\
  57.11 & \cellcolor{green!25}51.21 & \cellcolor{green!25}42.84 \\
  \hline
\end{tabular}
\end{document}

The code above produces:

mwe-example

In a perfect world, I'd have a macro like \partialcellcolor{green!25}{0.41\linewidth} or whatever to specify that the color extends across 41% of the cell. I use "R" to generate the table, so I'll let it do the math as it scripts the latex code.

Any suggestions?

share|improve this question
1  
I guess this question was asked before but right now I can only find gradient-color-in-one-cell-of-a-table –  Uwe Ziegenhagen Nov 8 '12 at 20:35

2 Answers 2

up vote 35 down vote accepted
+50

Update with minimal and maximal values

Minimal values are only usable when negative values are disabled (\negativeValuesfalse).

Code

\documentclass{article}
\usepackage[table]{xcolor}% http://ctan.org/pkg/xcolor
\usepackage{tikz,array,collcell}
\usetikzlibrary{calc}
\pgfdeclarelayer{background}
\pgfdeclarelayer{main}
\pgfsetlayers{background,main}
\newif\ifnegativeValues
\newcommand{\tikzMe}[1]{%
  \tikz[baseline]{
    \node[anchor=base,text width=\minWidth,align=\alignment,inner sep=0pt,inner xsep=\tabcolsep,outer sep=0pt] (n) {\strut$#1$};
    \begin{pgfonlayer}{background}
        \ifnegativeValues
            \pgfmathparse{#1<0?"red!50":"green!25"}
            \edef\color{\pgfmathresult}
        \else
            \def\color{green!25}
        \fi
       \pgfmathparse{abs((#1-\minValue)/(\maxValue-\minValue))}
       \fill[color=\color] (n.north west) rectangle ($(n.south west)!\pgfmathresult!(n.south east)$);
    \end{pgfonlayer}
  }
}
%\negativeValuestrue % inserts a minus sign for the minWidth and use red for negative values
\negativeValuesfalse % doesn't insert a minus sign, uses only green

\newcolumntype{H}[3]{%
  @{}
  >{%
    \ifx\\#1\\\def\alignment{right}\else\def\alignment{#1}\fi%
    \ifnegativeValues\def\minValue{0.}\else\def\minValue{#2}\fi%
    \def\maxValue{#3}%
    \def\minWidth{\widthof{$\ifnegativeValues-\fi#3$}}%
    \collectcell\tikzMe%
  }c<{\endcollectcell}
  @{}}
\begin{document}
\begin{tabular}{|H{}{10.}{100.00}|H{}{2.}{105.00}|H{}{0.}{150.00}|}
    \hline
    10.0   & 2.00   & 0.00   \\
    15.49  & 13.82  & 100.00 \\
    100.00 & 105.00 & 150.00 \\ \hline
\end{tabular}
\end{document}

Output

Update

Old answer

Code

\documentclass{article}
\usepackage[table]{xcolor}% http://ctan.org/pkg/xcolor
\usepackage{tikz,array,collcell}
\usetikzlibrary{calc}
\pgfdeclarelayer{background}
\pgfdeclarelayer{main}
\pgfsetlayers{background,main}
\newcommand*{\minWidth}{\widthof{$-100.00$}}
\newcommand*{\maxValue}{100}
\newcommand{\tikzMeL}[1]{\tikzMe{#1}{left}}
\newcommand{\tikzMeC}[1]{\tikzMe{#1}{center}}
\newcommand{\tikzMeR}[1]{\tikzMe{#1}{right}}
\newcommand{\tikzMe}[2]{%
  \tikz[baseline]{
    \node[anchor=base,text width=\minWidth,align=#2,inner sep=0pt,inner xsep=\tabcolsep,outer sep=0pt] (n) {\strut$#1$};
    \begin{pgfonlayer}{background}
       \pgfmathparse{#1<0?"red!50":"green!25"}
       \edef\color{\pgfmathresult}
       \pgfmathparse{abs(#1/\maxValue)}
       \fill[color=\color] (n.north west) rectangle ($(n.south west)!\pgfmathresult!(n.south east)$);
    \end{pgfonlayer}
  }
}
\newcolumntype{L}{@{}>{\collectcell\tikzMeL}c<{\endcollectcell}@{}}
\newcolumntype{C}{@{}>{\collectcell\tikzMeC}c<{\endcollectcell}@{}}
\newcolumntype{R}{@{}>{\collectcell\tikzMeR}c<{\endcollectcell}@{}}
\begin{document}
\begin{tabular}{|L|C|R|}        \hline
    91.41  & 100.00 & 38.76   \\
    -15.49 & -13.82 & -100.00 \\
    57.11  & 51.21  & -42.84  \\ \hline
\end{tabular}
\end{document}

Output

Output

share|improve this answer
3  
This is a great example of how to keep the data section of the table clean by using the array package embed all the logic in the column types. Thanks! –  John Leonard Nov 8 '12 at 22:44
    
I've been thinking about how to encode \maxValue on a column-by-column basis. For example, \begin{tabular}{|L|C|R|} might look like: `\begin{tabular}{|L{0}{100}|C{-20}{120}|R{0}{100}|} where 2 arguments (min and max column range) are included on a column-by-column basis. –  John Leonard Nov 9 '12 at 12:16
    
@JohnLeonard I've updated my answer. The column H takes three arguments: #1 → alignment (default right), #2 → minimal value, #3 → maximal value. –  Qrrbrbirlbel Nov 9 '12 at 12:38
    
I switched my "answered" to your solution. THANKS! This is really, really cool! –  John Leonard Nov 9 '12 at 12:42
    
@Qrrbrbirlbel How can column header row be used with text in it...? –  Nicholas Hamilton Jun 18 '13 at 10:27

Another sans-TikZ alterantive:

enter image description here

\documentclass{article}
\usepackage[table]{xcolor}% http://ctan.org/pkg/xcolor
\usepackage[nomessages]{fp}% http://ctan.org/pkg/fp
\newcommand{\maxnum}{100.00}
\newlength{\maxlen}
\newcommand{\databar}[2][green!25]{%
  \settowidth{\maxlen}{\maxnum}%
  \addtolength{\maxlen}{\tabcolsep}%
  \FPeval\result{round(#2/\maxnum:4)}%
  \rlap{\color{green!25}\hspace*{-.5\tabcolsep}\rule[-.05\ht\strutbox]{\result\maxlen}{.95\ht\strutbox}}%
  \makebox[\dimexpr\maxlen-\tabcolsep][r]{#2}%
}
\begin{document}

\begin{tabular}{*{3}{|l}|}
  \hline
  \databar{91.41} & \databar {90.81} & \databar{38.76} \\
  \databar{98.75} & \databar {13.82} & \databar{94.62} \\
  \databar{57.11} & \databar {51.21} & \databar{42.84} \\
  \databar{20.00} & \databar{100.00} & \databar{80.00} \\
  \hline
\end{tabular}
\end{document}

You provide the maximum number (as a macro \maxnum) that is used to calculate the percentage fill. Calculations are performed using fp.


Using this variation of \databar provides a fill that leaves a (barely visible) .5\arrayrulewidth on either side of the table cell. This is just to avoid any kind of artefacts caused by the viewer. The TikZ solution doesn't show this since it draws the coloured boxes in the background (almost as an underlay) of the entire table, so the rules overdraw any overlap:

enter image description here

%...
\newcommand{\databar}[2][green!25]{%
  \settowidth{\maxlen}{\maxnum}%
  \addtolength{\maxlen}{\dimexpr2\tabcolsep-\arrayrulewidth}%
  \FPeval\result{round(#2/\maxnum:4)}%
  \rlap{\color{green!25}\hspace*{\dimexpr-\tabcolsep+.5\arrayrulewidth}\rule[-.05\ht\strutbox]{\result\maxlen}{.95\ht\strutbox}}%
  \makebox[\dimexpr\maxlen-2\tabcolsep+\arrayrulewidth][r]{#2}%
}
%...
share|improve this answer
    
Can this answer be modified so that the 100% bars stretch the full width of the cell (and others proportionally)? –  Sparr Nov 8 '12 at 21:07
    
@Sparr: I've added that to my answer. I've still kept a small (.5\arrayrulewidth) gap between the box and the tabular rules, since a reader may show an "overlap", although this is actually not the case. If you really don't want that, you can remove any reference to \arrayrulewidth in the updated version of \databar. –  Werner Nov 8 '12 at 21:19
    
@Werner: It was hard to pick, but I have to go with your non-Tikz option. Thanks!!!! I'm going to play with this base code so that the maximum value in each column is used to scale the histograms. This version hard codes \maxnum to 100, but in my working code I'll need to use \maxnum over all the values in a given column. –  John Leonard Nov 8 '12 at 21:20
    
@JohnLeonard: That can be achieved using the array package and specifying a new column type that takes the "maximum value" as an argument. –  Werner Nov 8 '12 at 21:22
    
I have adapted this solution to use a custom maximum width as my column header was quite a bit wider than the maximum number; works like a charm :) –  O. R. Mapper 14 hours ago

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.