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 would like to be able to transpose a tabular(x) table like in the following example. It is not necessary to be completely automated, but I don't want to change the data order, which is a painful and stupid. By "not completely automated" I mean that e.g. the headers can be done seperately etc.

Let's see the MWE:

\documentclass{article}

\usepackage{booktabs}
\def\Midrule{\midrule[\heavyrulewidth]}

\begin{document}

\centering

\begin{tabular}{*6c}\toprule
\bfseries Name & Alice & Bob & Chuck & Dave & Eve\\\Midrule
\bfseries Sex & Female & Male & Male & Male & Female\\\midrule
\bfseries Age & 18 & 19 & 20 & 21 & 22\\\bottomrule
\end{tabular}

\bigskip

Which I want to look like

\bigskip

\begin{tabular}{*3c}\toprule
\bfseries Name & \bfseries Sex & \bfseries Age\\\Midrule
Alice & Female & 18\\\midrule
Bob & Male & 19\\\midrule
Chuck & Male & 20\\\midrule
Dave & Male & 21\\\midrule
Eve & Female & 22\\\bottomrule
\end{tabular}


\end{document}

MWE

Note: I know that there's an expl3 solution, but I would like to get it without expl3 (mostly because people would might use my code quite often have an old LaTeX installed and I cannot be convincing them to change this).

share|improve this question
add comment

3 Answers

up vote 14 down vote accepted

enter image description here

You can use \valign instead of \halign

\documentclass{article}

\usepackage{booktabs,array}
\def\Midrule{\midrule[\heavyrulewidth]}
\newcount\rowc

\makeatletter
\def\ttabular{%
\hbox\bgroup
\let\\\cr
\def\rulea{\ifnum\rowc=\@ne \hrule height 1.3pt \fi}
\def\ruleb{
\ifnum\rowc=1\hrule height 1.3pt \else
\ifnum\rowc=6\hrule height \heavyrulewidth 
   \else \hrule height \lightrulewidth\fi\fi}
\valign\bgroup
\global\rowc\@ne
\rulea
\hbox to 10em{\strut \hfill##\hfill}%
\ruleb
&&%
\global\advance\rowc\@ne
\hbox to 10em{\strut\hfill##\hfill}%
\ruleb
\cr}
\def\endttabular{%
\crcr\egroup\egroup}


\begin{document}

\centering


\begin{ttabular}
\bfseries Name & Alice & Bob & Chuck & Dave & Eve\\
\bfseries Sex & Female & Male & Male & Male & Female\\
\bfseries Age & 18 & 19 & 20 & 21 & 22\\
\end{ttabular}



\bigskip

Which I want to look like

\bigskip

\begin{tabular}{*3c}\toprule
\bfseries Name & \bfseries Sex & \bfseries Age\\\Midrule
Alice & Female & 18\\\midrule
Bob & Male & 19\\\midrule
Chuck & Male & 20\\\midrule
Dave & Male & 21\\\midrule
Eve & Female & 22\\\bottomrule
\end{tabular}


\end{document}
share|improve this answer
    
Now I need to understand what this is doing. Is it possible to avoid user-specified column width? –  tohecz Oct 8 '12 at 10:01
    
You need a user specified width there for same reason you need a user specified width in a tabular p column (everything is transposed:-) so the answer is yes or no, depending if you want to rely on on a single pass tex primitive alignment or a multi-pass X column trial that works out a good width, and how much time you have to program the latter:-) –  David Carlisle Oct 8 '12 at 10:04
add comment

Here is an alternate solution that uses the collcell package in conjunction with the datatool package to store each of the data elements for later processing. So, the following code:

\begin{Ttabular}{cccccc}
    \bfseries Name & Alice & Bob & Chuck & Dave & Eve\\
    \bfseries Sex & Female & Male & Male & Male & Female\\
    \bfseries Age & 18 & 19 & 20 & 21 & 22\\
\end{Ttabular}

yields:

enter image description here

The environ package is used so that we can typeset the original table into a \savebox so it does not produce unwanted space in the output.

Notes:

  • The original column specification is applied to the transposed table. Hence, the number of columns in the original table must be greater than the number of rows. Otherwise just need to over specify the tabular columns in the source table (since it gets applied to the number of rows). In this example the first three column specification are applied to the transposed table, and the others are ignored.

Further Enhancements:

  • The line after the header row is \midrule. I attempted quite few things to be able to use \Midrule for that but was not able to get it to work. It thought that incorporating the solution from Equivalent of \DTLiflastrow would solve this but was not able to get it to work. I am starting to realize that tabular is even more finicky than I thought.

Code:

\documentclass{article}
\usepackage{collcell}
\usepackage{xstring}
\usepackage{datatool}
\usepackage{booktabs}
\usepackage{environ}
%\usepackage{showframe}

\def\Midrule{\midrule[\heavyrulewidth]}

\newcounter{CurrentRow}% = column of transposed table
\newcounter{CurrentColumn}
\setcounter{CurrentColumn}{0}
\newtoggle{DoneWithFirstRow}

\newlength{\WidthAdjustment}
\newcommand*{\FirstColumn}[1]{%
    \IfEq{\arabic{CurrentColumn}}{0}{%
        % This is the start of the very first data entry in first row.
        \global\togglefalse{DoneWithFirstRow}%
        \setcounter{CurrentRow}{1}% initial value
    }{%
        % We have already completed a row. Now starting a new row.
        \global\toggletrue{DoneWithFirstRow}%
        \stepcounter{CurrentRow}%
    }%
    \setcounter{CurrentColumn}{0}%
    \NewData{#1}%
}
\newcommand*{\NewData}[1]{%
    \dtlexpandnewvalue%
    \stepcounter{CurrentColumn}%
    \iftoggle{DoneWithFirstRow}{%
        \dtlgetrow{TransposedTabularDB}{\arabic{CurrentColumn}}%
        \dtlappendentrytocurrentrow{\Alph{CurrentRow}}{#1}%
        \dtlrecombine%
    }{%
        \DTLnewrow{TransposedTabularDB}%
        \DTLnewdbentry{TransposedTabularDB}{\Alph{CurrentRow}}{#1}%
    }%
}%

\newcolumntype{F}{>{\collectcell\FirstColumn}c<{\endcollectcell}}
\newcolumntype{C}{>{\collectcell\NewData}{c}<{\endcollectcell}}

%% No longer needed since we switched to NewEnviron
%% http://tex.stackexchange.com/questions/12234/how-do-i-expand-a-macro-into-a-tabular-head
%\newcommand{\SaveColumnSpecificationAsZ}[1]{\newcolumntype{Z}{#1}}


\newtoggle{EncounteredDataRow}

\newsavebox{\TempBox}
\DTLnewdb{TransposedTabularDB}

\NewEnviron{Ttabular}[1]{%
    %\SaveColumnSpecificationAsZ{#1}%
    % Initialize in case of multiple uses
    \setcounter{CurrentColumn}{0}%
    \global\togglefalse{EncounteredDataRow}%
    \savebox{\TempBox}{%
        \begin{tabular}{FCCCCCC}% over speced tabular
            \BODY%
        \end{tabular}%
    }%
    \begin{tabular}{#1}\toprule%
    % This could be made smarter to detect number of columns
    \DTLforeach*{TransposedTabularDB}{\Aa=A, \Ba=B, \Ca=C}{%
      \DTLiffirstrow{}{\\\midrule}% 
        \Aa & \Ba & \Ca %
    }\\\bottomrule%
    \end{tabular}%
}%

\begin{document}
\noindent
\begin{Ttabular}{cccccc}
\bfseries Name & Alice & Bob & Chuck & Dave & Eve\\
\bfseries Sex & Female & Male & Male & Male & Female\\
\bfseries Age & 18 & 19 & 20 & 21 & 22\\
\end{Ttabular}

\bigskip\noindent
Which I want to look like
\bigskip

\noindent
\begin{tabular}{*3c}\toprule
\bfseries Name & \bfseries Sex & \bfseries Age\\\Midrule
Alice & Female & 18\\\midrule
Bob & Male & 19\\\midrule
Chuck & Male & 20\\\midrule
Dave & Male & 21\\\midrule
Eve & Female & 22\\\bottomrule
\end{tabular}
\end{document}
share|improve this answer
    
This looks great! I'll have to test it and probably add some small features. (I prefer the line after the header to be \heavyrulewidth, but I think I'm able to do this myself). But this is something I thought must be doable. –  tohecz Nov 23 '12 at 7:38
add comment

A pgfplotstable solution is also convenient.

\documentclass{article}
\usepackage{booktabs}
\usepackage{pgfplotstable}
\begin{document}
\pgfplotstableread{
Name   Alice   Bob   Chuck   Dave   Eve
Sex   Female   Male   Male   Male   Female
Age  18   19   20   21   22
}\mywidetable

\pgfplotstabletranspose[string type,
                        colnames from=Name,
                        input colnames to=Name
                        ]\mytalltable{\mywidetable}

\pgfplotstabletypeset[string type,
        every head row/.style={before row=\toprule,after row=\midrule},
        every last row/.style={after row=\bottomrule},
        after row=\midrule
]{\mytalltable}


\end{document}

enter image description here

If you really don't want to touch the data, then you can add col sep=&,row sep=\\ keys to the \pgfplotstableread.

share|improve this answer
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.