38

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).

27

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}
  • Now I need to understand what this is doing. Is it possible to avoid user-specified column width? – yo' 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
19
+100

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.

17

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
%% https://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}
  • 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. – yo' Nov 23 '12 at 7:38
1

I had to invent such a technique when answering this question: How to create a table macro that takes more than 10 arguments. However, that will never match up in a search for table transposition, so I adapt the answer to this more relevant question, and supply compatibility with booktabs. It further allows customized input separators for column and row separators. It uses the listofitems to parse the data and perform the transposition into a token register.

\documentclass{article}
\usepackage{listofitems,booktabs}
\newtoks\transposedtoks
\newcommand\transposedata[2][\\/&]{%
  \setsepchar{#1}%
  \transposedtoks{\toprule}%
  \greadlist*\rawdata{#2}
  \foreachitem\y\in\rawdata[1]{%
    \foreachitem\x\in\rawdata[]{%
      \ifnum\xcnt>1\transposedtoks\expandafter{\the\transposedtoks&}\fi%
      \expandafter\expandafter\expandafter\def%
        \expandafter\expandafter\expandafter\tmp%
        \expandafter\expandafter\expandafter{%
        \rawdata[\xcnt,\ycnt]}%
      \expandafter\transposedtoks\expandafter\expandafter\expandafter{%
        \expandafter\the\expandafter\transposedtoks\tmp}%
    }%
  \transposedtoks\expandafter{\the\transposedtoks\\}%
  \ifnum\ycnt=\listlen\rawdata[1]\relax\def\tmp{\bottomrule}%
    \else\def\tmp{\midrule}\fi%
  \expandafter\transposedtoks\expandafter\expandafter\expandafter{%
    \expandafter\the\expandafter\transposedtoks\tmp}%
  }%
}
\begin{document}
\transposedata{
\bfseries Name & Alice & Bob & Chuck & Dave & Eve\\
\bfseries Sex & Female & Male & Male & Male & Female\\
\bfseries Age & 18 & 19 & 20 & 21 & 22
}%
\begin{tabular}{*3c}
\the\transposedtoks
\end{tabular}
%
or
%
\transposedata[:/,]{a,b,c:d,e,f}%
\begin{tabular}{*3c}
\the\transposedtoks
\end{tabular}
\end{document}

enter image description here

0

Another solution would be to use an online table generator for LaTeX, which has a feature to transpose a table.

Seems to me the easiest solution in case a single table needs to be transposed.

  1. File -> From LaTeX code...
  2. Add a LaTeX code for a table
  3. Table -> Transpose table
  4. "Generate" button

This way you get a transposed table.

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.