tabular input by columns (i.e. transpose a table)
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}
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}
If you really don't want to touch the data, then you can add col sep=&,row sep=\\
keys to the \pgfplotstableread
.
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:
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 thattabular
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}