我希望能够像以下示例一样转置tabular
( x
) 表。这不需要完全自动化,但我不想更改数据顺序,这很痛苦而且很愚蠢。我说的“不是完全自动化”是指,例如,标题可以单独完成等等。
让我们看看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}
注意:我知道有一个expl3
解决方案,但我想在没有它的情况下得到它expl3
(主要是因为人们可能会经常使用我的代码并安装旧的 LaTeX 并且我无法说服他们改变这一点)。
答案1
你可以\valign
使用\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}
答案2
Apgfplotstable
解决方法也很方便。
\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}
如果你实在不想触碰数据,那么你可以col sep=&,row sep=\\
向中添加键\pgfplotstableread
。
答案3
这是一个替代解决方案,使用
包裹collcell
和这个结合
包裹datatool
存储每个数据元素以供后续处理。因此,以下代码:
\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}
产量:
包裹environ
用于以便我们可以将原始表格排版成这样\savebox
它就不会在输出中产生不需要的空间。
笔记:
- 原始列规范应用于转置表。因此,原始表中的列数必须大于行数。否则,只需在源表中过度指定表格列(因为它应用于行数)。在此示例中,前三列规范应用于转置表,其他列被忽略。
进一步增强:
- 标题行后面的行是
\midrule
。我尝试了不少方法来实现\Midrule
这一点,但都无法让它工作。我认为结合了\DTliflasrow 的等效项可以解决这个问题,但无法让它工作。我开始意识到这tabular
比我想象的还要棘手。
代码:
\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}
答案4
在回答这个问题时我必须发明这样一种技巧:如何创建接受超过 10 个参数的表宏。但是,这在搜索表转置时永远不会匹配,因此我调整了答案以适应这个更相关的问题,并提供与的兼容性booktabs
。它还允许为列和行分隔符自定义输入分隔符。它使用来listofitems
解析数据并执行转置到令牌寄存器中。
\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}