Thanks to Jasper Habicht!
最初的尝试 version1
一个可能的答案如下:
\documentclass{article}
\usepackage{libertinus-otf}
\usepackage[landscape]{geometry}
\usepackage{tabularray}
\UseTblrLibrary{functional, tikz}
\IgnoreSpacesOn
\tlNew \gTikzPathsTlpositive
\tlNew \gTikzPathsTlnegative
\clistNew \lCurrentRowDataClist
\prgNewFunction \RowMax {m} {%
\prgReturn {\fpEval {max(#1)}}
}
\prgNewFunction \RowMin {m} {%
\prgReturn {\fpEval {min(#1)}}
}
\prgNewFunction \DrawDataBar { m m } {%
\tlClear \gTikzPathsTl%
\intStepOneInline {#1} {\arabic{rowcount}} {%
\clistSet \lCurrentRowDataClist {}
\intStepOneInline {#2} {\arabic{colcount}} {%
\clistPutRight \lCurrentRowDataClist {\cellGetText {##1} {####1} }
}
\intStepOneInline {#2} {\arabic{colcount}} {%
\fpSet \lTmpaFp {\RowMax{\expValue \lCurrentRowDataClist}}
\fpSet \lTmpbFp {\RowMin{\expValue \lCurrentRowDataClist}}
\fpSet \lTmpcFp {\cellGetText {##1} {####1} }
\fpSet \lTmpiFp {%
\fpMathDiv {\fpUse \lTmpcFp} {\fpUse \lTmpaFp}
}%
\fpSet \lTmpjFp {%
\fpMathDiv {\fpUse \lTmpcFp} {\fpUse \lTmpbFp}
}%
\fpCompareTF {\lTmpaFp} > {\cZeroFp}{
\tlPutRight \gTikzPathsTlpositive {%
\evalWhole {%
([shift={(.5pt,.5pt)}]##1-####1.south~west) rectangle
([shift={(-.5pt,-.5pt)}]$(##1-####1.north~west)!{\fpUse \lTmpiFp}!(##1-####1.north~east)$)
}%
}%
}{%
\tlPutRight \gTikzPathsTlnegative {%
\evalWhole {%
([shift={(-.5pt,-.5pt)}]##1-####1.north~east) rectangle
([shift={(.5pt,.5pt)}]$(##1-####1.south~east)!{\fpUse \lTmpjFp}!(##1-####1.south~west)$)
}%
}%
}
}
}
}
\IgnoreSpacesOff
\begin{document}
\begin{tblrtikzbelow}
\fill [violet!80] \gTikzPathsTlpositive;
\fill [magenta!80] \gTikzPathsTlnegative;
\end{tblrtikzbelow}
\begin{tblr}{
colspec={*{5}{Q[c,3cm]}},
hlines, vlines,
row{1} = {bg=lightgray},
process=\DrawDataBar{2}{1}
}
Item1 & Item2 & Item3 & Item4 & Item5 \\
1 & 2 & 3 & 4 & 5 \\
6.5 & 5.4 & 4.3 & 3.2 & 2.1 \\
-1 & -2 & -3 & -4 & -5 \\
-6.5 & -5.4 & -4.3 & -3.2 & -2.1 \\
0.5 & 1.5 & 2.5 & 3.5 & 4.5 \\
-0.5 & -1.5 & -2.5 & -3.5 & -4.5 \\
-5.5 & -6.5 & -7.5 & -8.5 & -9.5 \\
-3.1 & -2.1 & -1.1 & -0.1 & -1.1 \\
1 & 4 & 2 & 8 & 5 \\
5.5 & 6.5 & 7.5 & 8.5 & 9.5 \\
\end{tblr}
\end{document}
由于我没想好关于「同一行内既有正数也有负数」应该如何绘制,因此\fpCompareTF
的判断应该也有一定的修改空间...目前只支持全正/全负的同一行数字,且个人认为用户接口并不太友好...
Edit: 直接增加功能version2
增加了对于行内数字有正有负形式的支持:
\documentclass{article}
\usepackage{libertinus-otf}
\usepackage[landscape]{geometry}
\usepackage{tabularray}
\UseTblrLibrary{functional, tikz}
\IgnoreSpacesOn
\tlNew \gTikzPathsTlpositive
\tlNew \gTikzPathsTlnegative
\tlNew \gTikzPathsTlcrosspositive
\tlNew \gTikzPathsTlcrossnegative
\clistNew \lCurrentRowDataClist
\prgNewFunction \RowMax {m} {%
\prgReturn {\fpEval {max(#1)}}
}
\prgNewFunction \RowMin {m} {%
\prgReturn {\fpEval {min(#1)}}
}
\prgNewFunction \DrawDataBar { m m } {%
\tlClear \gTikzPathsTl%
\intStepOneInline {#1} {\arabic{rowcount}} {%
\clistSet \lCurrentRowDataClist {}
\intStepOneInline {#2} {\arabic{colcount}} {%
\clistPutRight \lCurrentRowDataClist {\cellGetText {##1} {####1} }
}
\intStepOneInline {#2} {\arabic{colcount}} {%
\fpSet \lTmpaFp {\RowMax{\expValue \lCurrentRowDataClist}}
\fpSet \lTmpbFp {\RowMin{\expValue \lCurrentRowDataClist}}
\fpSet \lTmpcFp {\cellGetText {##1} {####1} }
\fpSet \lTmpiFp {%
\fpMathDiv {\fpUse \lTmpcFp} {\fpUse \lTmpaFp}
}%
\fpSet \lTmpjFp {%
\fpMathDiv {\fpUse \lTmpcFp} {\fpUse \lTmpbFp}
}%
\fpSet \lTmpkFp {%
\fpEval {( 0 - \fpUse \lTmpbFp) / (\fpUse \lTmpaFp - \fpUse \lTmpbFp)}
}%
\fpSet \lTmppFp {%
\fpEval {(\fpUse \lTmpcFp - \fpUse \lTmpbFp) / (\fpUse \lTmpaFp - \fpUse \lTmpbFp)}
}%
\fpCompareTF {\lTmpaFp} > {\cZeroFp}{
\fpCompareTF {\lTmpbFp} > {\cZeroFp}{%
% case1: max>0 min>0
\tlPutRight \gTikzPathsTlpositive {%
\evalWhole {%
([shift={(.5pt,.5pt)}]##1-####1.south~west) rectangle
([shift={(-.5pt,-.5pt)}]$(##1-####1.north~west)!{\fpUse \lTmpiFp}!(##1-####1.north~east)$)
}%
}%
}{
% case2: max>0 min<0
\fpCompareTF {\lTmpcFp} < {\cZeroFp}{%
% 如果当前值比0小,则从「\lTmppFp到\lTmpkFp」
\tlPutRight \gTikzPathsTlcrossnegative {%
\evalWhole {%
([shift={(.5pt,.5pt)}]$(##1-####1.south~west)!{\fpUse \lTmppFp}!(##1-####1.south~east)$)
rectangle
([shift={(-.5pt,-.5pt)}]$(##1-####1.north~west)!{\fpUse \lTmpkFp}!(##1-####1.north~east)$)
}
}
}{%% 如果当前值比0大,则从「\lTmpkFp到\lTmppFp」
\tlPutRight \gTikzPathsTlcrosspositive {%
\evalWhole {%
([shift={(.5pt,.5pt)}]$(##1-####1.south~west)!{\fpUse \lTmpkFp}!(##1-####1.south~east)$)
rectangle
([shift={(-.5pt,.5pt)}]$(##1-####1.north~west)!{\fpUse \lTmppFp}!(##1-####1.north~east)$)
}
}
}
}
}{%
% case3: max<0 min<0
\tlPutRight \gTikzPathsTlnegative {%
\evalWhole {%
([shift={(-.5pt,-.5pt)}]##1-####1.north~east) rectangle
([shift={(.5pt,.5pt)}]$(##1-####1.south~east)!{\fpUse \lTmpjFp}!(##1-####1.south~west)$)
}%
}%
}
}
}
}
\IgnoreSpacesOff
\begin{document}
\begin{tblrtikzbelow}
\fill [violet!80] \gTikzPathsTlpositive;
\fill [magenta!80] \gTikzPathsTlnegative;
\fill [green!80] \gTikzPathsTlcrosspositive;
\fill [cyan!80] \gTikzPathsTlcrossnegative;
\end{tblrtikzbelow}
\begin{tblr}{
colspec={*{5}{Q[c,3cm]}},
hlines, vlines,
row{1} = {bg=lightgray},
process=\DrawDataBar{2}{1}
}
Item1 & Item2 & Item3 & Item4 & Item5 \\
0.5 & 1.5 & 2.5 & 3.5 & 4.5 \\
-1 & -2 & -3 & -4 & -5 \\
-6.5 & -5.4 & -4.3 & -3.2 & -2.1 \\
6.5 & -5.4 & 4.3 & -3.2 & 2.1 \\
-0.5 & -1.5 & -2.5 & -3.5 & -4.5 \\
-5.5 & -6.5 & -7.5 & -8.5 & -9.5 \\
-1 & 2 & -3 & 4 & -5 \\
-3.1 & -2.1 & -1.1 & 1 & -1.1 \\
1 & 4 & 2 & 8 & 5 \\
5.5 & 6.5 & 7.5 & 8.5 & 9.5 \\
\end{tblr}
\end{document}
嗯...但他目前也有缺点,似乎暂时缺少对填充的数字为「0」时的支持,同时个人觉得条件判断的逻辑写复杂了,代码仍然有很大的改进空间...
Re-Edit:version3
完善了条件逻辑...但好像也并不完善,优化了坐标点位的命名,补充对「0」这一边界点的支持:
\documentclass{article}
\usepackage{libertinus-otf}
\usepackage[landscape]{geometry}
\usepackage{tabularray}
\UseTblrLibrary{functional, tikz}
\IgnoreSpacesOn
\tlNew \gTikzPathsTlpositive
\tlNew \gTikzPathsTlnegative
\tlNew \gTikzPathsTlcrosspositive
\tlNew \gTikzPathsTlcrossnegative
\clistNew \lCurrentRowDataClist
\prgNewFunction \RowMax {m} {%
\prgReturn {\fpEval {max(#1)}}
}
\prgNewFunction \RowMin {m} {%
\prgReturn {\fpEval {min(#1)}}
}
\prgNewFunction \DrawDataBar { m m } {%
\tlClear \gTikzPathsTl%
\intStepOneInline {#1} {\arabic{rowcount}} {%
\clistSet \lCurrentRowDataClist {}
\intStepOneInline {#2} {\arabic{colcount}} {%
\clistPutRight \lCurrentRowDataClist {\cellGetText {##1} {####1} }
}
\fpSet \lTmpmaxFp {\RowMax{\expValue \lCurrentRowDataClist}}
\fpSet \lTmpminFp {\RowMin{\expValue \lCurrentRowDataClist}}
\intStepOneInline {#2} {\arabic{colcount}} {%
\fpSet \lTmpnowFp {\cellGetText {##1} {####1} }
% cond1: min>0
\fpCompareT {\lTmpminFp} > {\cZeroFp} {
\fpSet \lTmpstartFp { \cZeroFp}
\fpSet \lTmpendFp { \fpEval {( \lTmpnowFp - \cZeroFp ) / ( \lTmpmaxFp - \cZeroFp ) }}
\tlPutRight \gTikzPathsTlpositive {%
\evalWhole {%
([shift={(.5pt,.5pt)}]$(##1-####1.south~west)!{\fpUse \lTmpstartFp}!(##1-####1.south~east)$)
rectangle
([shift={(-.5pt,-.5pt)}]$(##1-####1.north~west)!{\fpUse \lTmpendFp}!(##1-####1.north~east)$)
}
}
}
% cond2: max<0
\fpCompareT {\lTmpmaxFp} < {\cZeroFp} {
% \fpSet \lTmpstartFp {\fpEval {( \cZeroFp - \lTmpnowFp ) / ( \cZeroFp - \lTmpminFp ) }}% 好像有点不对称...
\fpSet \lTmpstartFp {\fpEval {\cOneFp - ( \lTmpnowFp - \cZeroFp ) / ( \lTmpminFp - \cZeroFp ) }}
\fpSet \lTmpendFp {\cOneFp}
\tlPutRight \gTikzPathsTlnegative {%
\evalWhole {%
([shift={(.5pt,.5pt)}]$(##1-####1.south~west)!{\fpUse \lTmpstartFp}!(##1-####1.south~east)$)
rectangle
([shift={(-.5pt,-.5pt)}]$(##1-####1.north~west)!{\fpUse \lTmpendFp}!(##1-####1.north~east)$)
}
}
}
% cond3: min < 0 < max
\fpCompareT {\lTmpminFp} < {\cZeroFp} {
\fpCompareT {\lTmpmaxFp} > {\cZeroFp} {
%% cond3进入判断
\fpSet \lTmpzeroFp { \fpEval {( \cZeroFp - \lTmpminFp ) / ( \lTmpmaxFp - \lTmpminFp )}}
\fpSet \lTmppointFp {
\fpEval { ( \lTmpnowFp - \lTmpminFp ) / ( \lTmpmaxFp - \lTmpminFp ) }
}
\fpCompareTF {\lTmpnowFp} < {\cZeroFp} {
% 分类绘制
\tlPutRight \gTikzPathsTlcrossnegative {%
\evalWhole {%
([shift={(.5pt,.5pt)}]$(##1-####1.south~west)!{\fpUse \lTmppointFp}!(##1-####1.south~east)$)
rectangle
([shift={(-.5pt,-.5pt)}]$(##1-####1.north~west)!{\fpUse \lTmpzeroFp}!(##1-####1.north~east)$)
}
}
} {
\tlPutRight \gTikzPathsTlcrosspositive {%
\evalWhole {%
([shift={(.5pt,.5pt)}]$(##1-####1.south~west)!{\fpUse \lTmpzeroFp}!(##1-####1.south~east)$)
rectangle
([shift={(-.5pt,-.5pt)}]$(##1-####1.north~west)!{\fpUse \lTmppointFp}!(##1-####1.north~east)$)
}
}
}
}
}
}
}
}
\IgnoreSpacesOff
\begin{document}
\begin{tblrtikzbelow}
\fill [violet!80] \gTikzPathsTlpositive;
\fill [magenta!80] \gTikzPathsTlnegative;
\fill [green!80] \gTikzPathsTlcrosspositive;
\fill [cyan!80] \gTikzPathsTlcrossnegative;
\end{tblrtikzbelow}
\begin{tblr}{
colspec={*{5}{Q[c,3cm]}},
hlines, vlines, %stretch = 0,
row{1} = {bg=lightgray,font=\bfseries\large},
process=\DrawDataBar{2}{1}
}
Item1 & Item2 & Item3 & Item4 & Item5 \\
0.5 & 1.5 & 2.5 & 3.5 & 4.5 \\
-1 & -2 & -3 & -4 & -5 \\
-6.5 & -5.4 & -4.3 & -3.2 & -2.1 \\
6.5 & -5.4 & 0 & -3.2 & 2.1 \\
-0.5 & -1.5 & -8.5 & -3.5 & -4.5 \\
-5.5 & -9.5 & -2.5 & -7.5 & -6.5\\
-1 & 2 & -3 & 4 & -5 \\
-3.1 & -2.1 & -1.1 & 1 & -1.1 \\
1 & 4 & 2 & 8 & 5 \\
5.5 & 6.5 & 7.5 & 8.5 & 9.5 \\
\end{tblr}
\end{document}
问 请问这种表格带颜色,且色条长度反映了表格数据长度,这类表格该如何绘制