我正在从数据文件生成 pgfplotstable。在该文件中,在列中Class
,具有相同值的后续行应自动合并/折叠/连接以形成\multirow{n}{*}{value}
。n
其中是列中具有相同值的后续行的数量Class
。
首次遇到某个值时无法预测该数字n
。如何实现自动化?
如果稍后再次遇到某个值(例如 Mammal),则该行应该不是向上移动到任何以前遇到的版本。
梅威瑟:
\documentclass{standalone}
\usepackage{filecontents}
\usepackage{pgfplotstable}
\usepackage{booktabs}
\usepackage{multirow}
\usepackage{color}
\begin{filecontents}{data.csv}
Class,Genus
Mammal,Dog
Mammal,Cat
Mammal,Mouse
Bird,Chicken
Bird,Duck
Insect,Fly
Mammal,Bear
\end{filecontents}
\begin{document}
\pgfplotstabletypeset[
col sep=comma,
string type,
every head row/.style={before row={
\multicolumn{2}{c}{\textcolor{red}{Not what I want:}}\\\toprule},
after row=\midrule},
every last row/.style={after row=\bottomrule},
]{data.csv}
\pgfplotstabletypeset[
col sep=comma,
string type,
every head row/.style={before row={
\multicolumn{2}{c}{\textcolor{green}{What I want to automate:}}\\\toprule},
after row=\midrule},
every last row/.style={after row=\bottomrule},
]{
Class,Genus
\multirow{3}{*}{Mammal},Dog
%multirow should be extended automatically with each new Mammal row that's printed
,Cat
,Mouse
\multirow{2}{*}{Bird},Chicken
%multirow should be extended automatically with each new Bird row that's printed
,Duck
Insect,Fly
Mammal,Bear
}
\end{document}
答案1
这种方法不使用tabular
和multirow
,而是逐行检查输入文件并保存与给定类相关的属字符串。完成后,它使用堆栈构建与相关类相邻的属。
这将输入文件作为空格分隔的数据(请参阅本答案的附录以获取与 csv 兼容的代码)。如果您的条目是单个单词,则这种空格分隔方法可以正常工作。如果条目不止一个单词,您可以~
在条目内使用它作为硬空格分隔符,或者您可以转到本答案末尾的附录。
因为这种方法不使用tabular
环境,所以如果将答案放在table
浮动环境之外,则当数据列表太长时将允许分页。
修改后的解决方案(符合OP的分组规范):
这个解决方案实际上比我原来的方法更简单,原来的方法经过了额外的排序步骤。
\documentclass{article}
\usepackage{filecontents,readarray,stackengine}
\begin{filecontents*}{data.ssv}
Class Genus
Mammal Dog
Mammal Cat
Mammal Mouse
Bird Chicken
Bird Duck
Insect Fly
Mammal Bear
\end{filecontents*}
\newcounter{entryindex}
\newcounter{classes}
\setstackEOL{\&}
\setstackgap{L}{12pt}
\def\doubleexpandedCS{\expandafter\expandafter\expandafter%
\Centerstack\expandafter\expandafter\expandafter}
\def\processfile#1{%
\readdef{#1}{\tmpa}%
\readArrayij{\tmpa}{mydata}{2}%
\setcounter{entryindex}{0}%
\whiledo{\value{entryindex} < \mydataROWS}{%
\stepcounter{entryindex}%
\edef\theClass{\arrayij{mydata}{\arabic{entryindex}}{1}}%
\edef\theGenus{\arrayij{mydata}{\arabic{entryindex}}{2}}%
\def\classexists{F}%
\ifnum\value{classes}>0
\ifthenelse{\equal{%
\csname Class\romannumeral\value{classes}\endcsname}{\theClass}}%
{\def\classexists{T}}{}%
\fi%
\if F\classexists%
\stepcounter{classes}%
\expandafter\edef\csname Class%
\romannumeral\value{classes}\endcsname{\theClass}%
\expandafter\edef\csname theGenuses%
\romannumeral\value{classes}\endcsname{\theGenus}%
\else
\expandafter\edef\csname theGenuses%
\romannumeral\value{classes}\endcsname{%
\csname theGenuses\romannumeral\value{classes}\endcsname%
\&\theGenus}%
\fi%
}%
}
\def\showresults{%
\noindent\hbox to 2in{\hrulefill}\par\noindent
\makebox[1in]{\Classi}\makebox[1in]%
{\doubleexpandedCS{\csname theGenusesi\endcsname}}\\
\raisebox{.5\baselineskip}{\hbox to 2in{\hrulefill}}\par\noindent
\setcounter{entryindex}{1}%
\whiledo{\value{entryindex}<\value{classes}}{%
\stepcounter{entryindex}%
\makebox[1in]{\csname Class\romannumeral\value{entryindex}\endcsname}%
\makebox[1in]{\doubleexpandedCS{\csname theGenuses%
\romannumeral\value{entryindex}\endcsname}}\smallskip\\
}%
}
\begin{document}
\processfile{data.ssv}
Not counting the header, there are \the\numexpr\theclasses-1\relax\ class
entries:
\begin{table}[ht]
{\centering\showresults\par}
\end{table}
And here is the following text
\end{document}
如果希望“类”与“属”列表顶部对齐,则重新定义
\def\doubleexpandedCS{\strutlongstacks{T}\expandafter\expandafter\expandafter%
\Longunderstack\expandafter\expandafter\expandafter}
会产生
如果不喜欢相邻类之间的小跳跃,则消除定义\smallskip
末尾的结果\showresults
:
附录:
如果需要以逗号分隔而不是以空格分隔的数据,则使用新的readarray
包功能,可以\readarraysepchar{,}
在调用之前设置解析分隔符\processfile
。
答案2
为了补充解决方案multirow
中的答案pgfplotstable
,我们可以检查下一行条目并将该重复项保存在计数器中。当下一行不同(与相比\ifx
)时,我们就会分支到多行条目。这是可能的,因为多行巧妙地允许覆盖的总行数为负数。然后剩下的步骤是在表中创建另一行,以保存我们刚刚过滤的唯一条目。
一个缺点是,我们无法直接从文件中读取,而是需要先将其读入宏,然后创建唯一列(保存在名称下u-<colname>
),然后使用原始列名声明对其进行排版。以下是完整的示例
\documentclass[border=5mm]{standalone}
\usepackage{filecontents}
\usepackage{pgfplotstable}
\usepackage{booktabs}
\usepackage{multirow}
\pgfplotsset{compat=1.12}
\begin{filecontents*}{data.csv}
Class,Genus
Mammal,Dog
Mammal,Cat
Mammal,Mouse
Bird,Chicken
Bird,Duck
Insect,Fly
Mammal,Bear
\end{filecontents*}
\newcount\pgfplotstableuniqueentry
\pgfplotstableuniqueentry=0
\def\pgfplotstableadduniquecol#1#2{%
\pgfplotstablecreatecol[
create col/assign/.code={%
\getthisrow{#1}\entry\getnextrow{#1}\nextentry% This and next for comparison
\ifx\entry\nextentry\relax% Compare
\xdef\tempentry{\entry}% Save it to some macro
\pgfkeyssetvalue{/pgfplots/table/create col/next content}{}% Empty cell
\global\advance\pgfplotstableuniqueentry1\relax%Increment the dup count
\else%
\ifnum\the\pgfplotstableuniqueentry>0\relax% If there is a duplicate
\advance\pgfplotstableuniqueentry1% increment for multirow
\edef\temp{\noexpand%Selectively expand some macros
\pgfkeyssetvalue{/pgfplots/table/create col/next content}{%
\noexpand%
\multirow{-\the\pgfplotstableuniqueentry}{*}{\tempentry}
}}\temp% Execute the expanded version
\global\pgfplotstableuniqueentry=0%Reset dup counter
\else%
\pgfkeyslet{/pgfplots/table/create col/next content}{\entry}%Do nothing
\fi%
\fi%
}
]{u-#1}{#2}%
}
\begin{document}
\pgfplotstableread[col sep=comma]{data.csv}\mytable %Read the table
\pgfplotstableadduniquecol{Class}{\mytable} % Add the unique column
\pgfplotstabletypeset[string type,
every head row/.style={before row=\toprule},
every last row/.style={after row=\bottomrule},
columns={u-Class,Genus},
columns/u-Class/.style={column name=Class}
]{\mytable}
\end{document}