Это дополнительный вопрос кмой предыдущий.
В этой статье я хотел бы рассмотреть два случая:
Первый, загрузить таблицу как есть, просто наложив собственные заголовки столбцов и строк.
Второй, загрузить таблицу и повернуть ее, затем наложить собственные заголовки столбцов и строк.
В обоих случаях, упомянутых выше, необходимо обрабатывать два различных типа баз данных, как показано ниже.
\documentclass{article}
\usepackage{datatool,filecontents,booktabs}
% 1st dataset
\begin{filecontents*}{data1.csv}
, col1 , col2 , col3
row1 , 11 , 12 , 13
row2 , 21 , 22 , 23
\end{filecontents*}
% 2nd dataset
\begin{filecontents*}{data2.csv}
11 , 12 , 13
21 , 22 , 23
\end{filecontents*}
\begin{document}
\section{First case: without rotation}
The desired output for both datasets should be
\begin{table}[h]
\centering
\caption{without rotation}
\begin{tabular}{@{}lccc@{}}
\toprule
& First Column & Second Column & Third Column\\
\midrule
First Row & 11 & 12 & 13 \\
\midrule
Second Row & 21 & 22 & 23 \\
\bottomrule
\end{tabular}
\end{table}
\section{Second case: with rotation}
The desired output for both datasets should be
\begin{table}[h]
\centering
\caption{with rotation}
\begin{tabular}{@{}lcc@{}}
\toprule
& First Column & Second Column\\
\midrule
First Row & 21 & 11 \\
\midrule
Second Row & 22 & 12 \\
\midrule
Third Row & 23 & 13 \\
\bottomrule
\end{tabular}
\end{table}
\end{document}
решение1
Лучше всего убедиться, что обеим базам данных назначены одинаковые ключи (через опцию keys
в \DTLloaddb
). Это означает, что ключи могут использоваться для ссылки на один и тот же столбец в обеих базах данных, даже если у них разные индексы столбцов:
\DTLloaddb[keys={rowheader,col1,col2,col3}]{database1}{database1.csv}
\DTLloaddb[noheader,keys={col1,col2,col3}]{database2}{database2.csv}
Заголовки столбцов можно задать для обеих баз данных с помощью следующих ключей:
\newcommand{\setdatabaseheaders}[1]{%
\DTLsetheader{#1}{col1}{First Column}%
\DTLsetheader{#1}{col2}{Second Column}%
\DTLsetheader{#1}{col3}{Third Column}%
}
\setdatabaseheaders{database1}
\setdatabaseheaders{database2}
Однако если заголовки столбцов являются общими для всех таблиц, то проще сохранить их в легкодоступном виде с помощью etoolbox
команды \csdef
:
\csdef{columnheader1}{First Column}
\csdef{columnheader2}{Second Column}
\csdef{columnheader3}{Third Column}
Понятие заголовков строк отсутствует, datatool
но их достаточно просто предоставить на основе индекса строки аналогичным образом:
\csdef{rowheader1}{First Row}
\csdef{rowheader2}{Second Row}
Столбец, идентифицированный как rowheader
необходимо игнорировать при отображении данных. Наличие этого столбца можно проверить с помощью \DTLifhaskey
. Пользовательские команды для отображения данных могут вставлять заголовки строк, ссылаясь на команду, заданную как\csname rowheader
idx\endcsname
(или\csuse{rowheader
idx}
) в начале каждой строки. Аналогично для заголовков столбцов.
Полная МВЭ:
\documentclass{article}
\usepackage{booktabs}
\usepackage{datatool}
% Case 1
\begin{filecontents*}{database1.csv}
,col1 , col2 , col3
row1 , 11 , 12 , 13
row2 , 21 , 22 , 23
\end{filecontents*}
% Case 2
\begin{filecontents*}{database2.csv}
11 , 12 , 13
21 , 22 , 23
\end{filecontents*}
\DTLloaddb[keys={rowheader,col1,col2,col3}]{database1}{database1.csv}
\DTLloaddb[noheader,keys={col1,col2,col3}]{database2}{database2.csv}
\csdef{rowheader1}{First Row}
\csdef{rowheader2}{Second Row}
\csdef{rowheader3}{Third Row}
\csdef{columnheader1}{First Column}
\csdef{columnheader2}{Second Column}
\csdef{columnheader3}{Third Column}
\newcount\columnidx
\newcount\rowidx
\newcount\columncount
\newcommand{\displaydatabase}[1]{%
\DTLifhaskey{#1}{rowheader}%
{%
\columncount=\numexpr\DTLcolumncount{#1}-1\relax
\def\columnoffset{1}%
}%
{%
\columncount=\DTLcolumncount{#1}%
\def\columnoffset{0}%
}%
% construct \begin{tabular} arguments
\def\columnargs{}%
\columnidx=0
\loop
\advance\columnidx by 1\relax
\appto\columnargs{c}%
\ifnum\columnidx<\columncount
\repeat
\edef\tabularcontents{\noexpand\begin{tabular}{l\columnargs}}%
\appto\tabularcontents{\toprule}%
% add table header
\dtlforeachkey(\thiskey,\thiscol,\thistype,\thisheader)\in#1\do
{%
\ifdefstring{\thiskey}{rowheader}%
{}%
{%
\eappto\tabularcontents{\noexpand&
\expandonce{\csname columnheader\number\numexpr\thiscol-\columnoffset\endcsname}}%
}%
}%
% iterate over all rows
\rowidx=0
\loop
\advance\rowidx by 1\relax
% header
\eappto\tabularcontents{\noexpand\\\noexpand\midrule
\expandonce{\csname rowheader\the\rowidx\endcsname}}%
% columns
\dtlforeachkey(\thiskey,\thiscol,\thistype,\thisheader)\in#1\do
{%
\ifdefstring{\thiskey}{rowheader}%
{}%
{%
\DTLgetvalue{\thisvalue}{#1}{\rowidx}{\thiscol}%
\eappto\tabularcontents{\noexpand& \expandonce\thisvalue}%
}%
}%
\ifnum\rowidx<\DTLrowcount{#1}
\repeat
\appto\tabularcontents{\\\bottomrule\end{tabular}}%
\tabularcontents
}
\newcommand{\displayrotateddatabase}[1]{%
\DTLifhaskey{#1}{rowheader}%
{%
\def\columnoffset{1}%
}%
{%
\def\columnoffset{0}%
}%
\columncount=\DTLrowcount{#1}%
% construct \begin{tabular} arguments
\def\columnargs{}%
\columnidx=0
\loop
\advance\columnidx by 1\relax
\appto\columnargs{c}%
\ifnum\columnidx<\columncount
\repeat
\edef\tabularcontents{\noexpand\begin{tabular}{l\columnargs}}%
\appto\tabularcontents{\toprule}%
% add table header
\columnidx=0
\loop
\advance\columnidx by 1\relax
\eappto\tabularcontents{\noexpand&
\expandonce{\csname columnheader\the\columnidx\endcsname}}%
\ifnum\columnidx<\columncount
\repeat
% iterate through all columns omitting rowheader
\dtlforeachkey(\thiskey,\thiscol,\thistype,\thisheader)\in#1\do
{%
\ifdefstring{\thiskey}{rowheader}%
{}%
{%
% header title
\eappto\tabularcontents{%
\noexpand\\\noexpand\midrule
\expandonce{\csname rowheader\number\numexpr\thiscol-\columnoffset\endcsname}}%
% row loop in reverse order
\rowidx=\DTLrowcount{#1}\relax
\loop
\DTLgetvalue{\thisvalue}{#1}{\rowidx}{\thiscol}%
\eappto\tabularcontents{\noexpand&\expandonce\thisvalue}%
\advance\rowidx by -1\relax
\ifnum\rowidx>0
\repeat
}%
}%
\appto\tabularcontents{\\\bottomrule\end{tabular}}%
\tabularcontents
}
\newcommand{\displaytransposedatabase}[1]{%
\DTLifhaskey{#1}{rowheader}%
{%
\def\columnoffset{1}%
}%
{%
\def\columnoffset{0}%
}%
\columncount=\DTLrowcount{#1}%
% construct \begin{tabular} arguments
\def\columnargs{}%
\columnidx=0
\loop
\advance\columnidx by 1\relax
\appto\columnargs{c}%
\ifnum\columnidx<\columncount
\repeat
\edef\tabularcontents{\noexpand\begin{tabular}{l\columnargs}}%
\appto\tabularcontents{\toprule}%
% add table header
\columnidx=0
\loop
\advance\columnidx by 1\relax
\eappto\tabularcontents{\noexpand&
\expandonce{\csname columnheader\the\columnidx\endcsname}}%
\ifnum\columnidx<\columncount
\repeat
% iterate through all columns omitting rowheader
\dtlforeachkey(\thiskey,\thiscol,\thistype,\thisheader)\in#1\do
{%
\ifdefstring{\thiskey}{rowheader}%
{}%
{%
% header title
\eappto\tabularcontents{%
\noexpand\\\noexpand\midrule
\expandonce{\csname rowheader\number\numexpr\thiscol-\columnoffset\endcsname}}%
% row loop
\rowidx=0\relax
\loop
\advance\rowidx by 1\relax
\DTLgetvalue{\thisvalue}{#1}{\rowidx}{\thiscol}%
\eappto\tabularcontents{\noexpand&\expandonce\thisvalue}%
\ifnum\rowidx<\DTLrowcount{#1}
\repeat
}%
}%
\appto\tabularcontents{\\\bottomrule\end{tabular}}%
\tabularcontents
}
\begin{document}
\section{No rotation}
\subsection{database1}
\displaydatabase{database1}
\subsection{database2}
\displaydatabase{database2}
\subsection{Desired Output}
\begin{table}[h]
\centering
\caption{without rotation}
\begin{tabular}{@{}lccc@{}}
\toprule
& First Column & Second Column & Third Column\\
\midrule
First Row & 11 & 12 & 13 \\
\midrule
Second Row & 21 & 22 & 23 \\
\bottomrule
\end{tabular}
\end{table}
\newpage
\section{Rotation}
\subsection{database1}
\displayrotateddatabase{database1}
\subsection{database2}
\displayrotateddatabase{database2}
\subsection{Desired Output}
\begin{table}[h]
\centering
\caption{with rotation}
\begin{tabular}{@{}lcc@{}}
\toprule
& First Column & Second Column\\
\midrule
First Row & 21 & 11 \\
\midrule
Second Row & 22 & 12 \\
\midrule
Third Row & 23 & 13 \\
\bottomrule
\end{tabular}
\end{table}
\newpage
\section{Transpose}
\subsection{database1}
\displaytransposedatabase{database1}
\subsection{database2}
\displaytransposedatabase{database2}
\end{document}
Неповернутые таблицы:
Повернутые таблицы:
Транспонированные таблицы:
Примечания:
\DTLloaddb
не обрезает конечные пробелы, поэтому если бы я не использовалkeys={rowheader,col1,col2,col3}
, ключи для второго, третьего и четвертого столбцов включали бы конечный пробел. Следующий MWE не работает:\documentclass{article} \usepackage{datatool} \begin{filecontents*}{database1.csv} ,col1 , col2 , col3 row1 , 11 , 12 , 13 row2 , 21 , 22 , 23 \end{filecontents*} \DTLloaddb{database1}{database1.csv} \DTLsetheader{database1}{col1}{First Column} \begin{document} \end{document}
Это приводит к ошибке
! Package datatool Error: Database `database1' doesn't contain key `col1'.
Метка столбца включает завершающий пробел, поэтому назначение должно быть следующим:
\DTLsetheader{database1}{col1 }{First Column}
Начальные пробелы игнорируются, поэтому
\DTLsetheader{database1}{ col2 }{Second Column}
выдает ошибку, хотя в CSV-файле есть начальный пробел. Должно быть:
\DTLsetheader{database1}{col2 }{Second Column}
Аналогично для каждой записи. Например:
\documentclass{article}
\usepackage{datatool}
\begin{filecontents*}{database1.csv}
,col1 , col2 , col3
row1 , 11 , 12 , 13
row2 , 21 , 22 , 23
\end{filecontents*}
\DTLloaddb{database1}{database1.csv}
\begin{document}
\DTLgetvalue{\thisvalue}{database1}{1}{2}`\thisvalue'.
\end{document}
Это производит:
Начальный пробел игнорируется, но конечный пробел сохраняется.
В этом примере это не имеет большого значения, так как ключи назначаются с использованием, keys
которое не содержит пробелов. Записи находятся в tabular
среде, поэтому ложный пробел не заметен, но с этим следует быть осторожным.