请问这种表格带颜色,且色条长度反映了表格数据长度,这类表格该如何绘制
跪求大神解答,或者绘制思路也行,谢谢谢谢谢谢谢谢
用 tikz 的 matrix 实现。思路是先画顶部两行表格,再画左边绿色区域的表格,最后画中间的带彩色数据条的表格,中间表格分块画,每一块都是一个 matrix. 中间表格先输入内容,然后用 foreach 加 fill 填充彩色数据条,最后给表格整体用绿色背景,然后在中间区域覆盖白色背景。中间表格我做了一部分,剩余内容和前两行的同理,有兴趣可以自己补充。
\documentclass[border=1cm,tikz]{standalone}
\usepackage{xcolor}
\usetikzlibrary{scopes,matrix,calc,backgrounds,fit}
\definecolor{blue1}{RGB}{51,204,255}
\definecolor{blue2}{RGB}{51,153,255}
\begin{document}
\begin{tikzpicture}[thick]
% 顶部表格
{[thick]
\matrix(m)[matrix of nodes,nodes={minimum width=5.4cm, minimum height=0.7cm}, ]{
\hskip-1.4cm Parameter & Ep1 $(b_p=0.04)$ & Ep2 $(b_p=0.02)$ & Ep3 $(b_p=0.01)$\\
\hskip-1.4cm Strategy & S1 \hskip3.6em S2 \hskip3.6em S3 & S1 \hskip3.6em S2 \hskip3.6em S3 & S1 \hskip3.6em S2 \hskip3.6em S3\\
};
\draw(m-1-4.south east)--++(-23.2,0);
\draw[line width=1.3pt](m-2-4.south east)--++(-23.2,0);
\draw[line width=1pt,double](m-1-4.north east)--++(-23.2,0);
\draw(m-1-1.north east)--(m-2-1.south east)--++(0,-13.6) (m-1-2.north east)--(m-2-2.south east)--++(0,-13.6) (m-1-3.north east)--(m-2-3.south east)--++(0,-13.6);
}
% 左边表格
{[shift={(-8.2,-7.4)}]
\matrix(n) [matrix of nodes,nodes={text width=2.5cm,text height=0.5cm,execute at begin node={\setlength{\baselineskip}{4ex}}, anchor=center, },align=center] {
{Efficiency \\ change [pu]} & {Avg-HPP 1\\Min-HPP 1\\Avg-Hpp 2\\Min-HPP 2} \\
{GV movement\\ {[/]}} & {Distance\\Amount} \\
{RB movement\\ {[pu]}} & {Dist.-HPP 1\\Dist.-HPP 2\\Amount-HPP 1\\Amount-HPP 2} \\
{Frequency \\ quanlity [pu]} & {HPP 1\\HPP 2} \\
{Mileage\\ {[MW]}} & {HPP 1\\HPP 2} \\
Strength [pu] & {HPP 1\\HPP 2} \\
Mileage [pu] & {HPP 1\\HPP 2} \\
Contributiuon [pu] & {HPP 1\\HPP 2} \\
};
\draw[line width=1pt,double](n-8-1.south west) ++(0,-2mm)--++(21.775,0) (n-8-1.south west) ++(0,-2mm)--++(-1.5,0);
\draw[line width=1.3pt] (n-8-1.south west) ++(0,-2mm)--(n-1-1.north west)--++(0,0.5);
\draw(n-2-1.north west)++(0,-2mm)--++(21.775,0) (n-2-1.south west)++(0,-1mm)--++(21.775,0)
(n-5-1.north west)++(0,-2mm)--++(21.775,0) (n-6-1.north west)++(0,1mm)--++(21.775,0) (n-6-1.north west)++(0,1mm)--++(-1.5,0);
\draw[line width=1.3pt](n-4-1.north west)++(0,-2mm)--++(21.775,0) (n-4-1.north west)++(0,-2mm)--++(-1.5,0);
\node[rotate=90]at ($(n-2-1.west)+(-8mm,-1.5mm)$){\large Burden};
\node[rotate=90,align=center,font=\large]at ($(n-4-1.west)+(-8mm,-9mm)$){Regulation\\performance};
\node[rotate=90]at ($(n-7-1.west)+(-8mm,-1.5mm)$){\large Payment};
}
% 中间第一行表格
{[shift={(m-2-2.south)}, yshift=-1.35cm]
\matrix(p1)[matrix of nodes, nodes={minimum width=1.8cm, minimum height=0.6cm,}, column sep=0.cm]{
-0.079\% & -0.097\% & -0.144\%\\
-0.268\% & -0.297\% & -0.304\%\\
-0.030\% & -0.061\% & -0.196\%\\
-0.435\% & -0.472\% & -0.638\%\\
};
\foreach \x/\y in{%
1-1/0.079, 1-2/0.087, 1-3/0.12,
2-1/0.19, 2-2/0.19, 2-3/0.21,
3-1/0.030, 3-2/0.041, 3-3/0.13,
4-1/0.28, 4-2/0.3, 4-3/0.35
}
{\fill[red, opacity=0.55](p1-\x.north east) rectangle ++ (-\y,-0.5);
}
}
{[shift={(m-2-3.south)}, yshift=-1.35cm]
\matrix(p1)[matrix of nodes, nodes={minimum width=1.8cm, minimum height=0.6cm,}, column sep=0.cm]{
-0.217\% & -0.249\% & -0.535\%\\
-0.493\% & -0.535\% & -0.729\%\\
-0.125\% & -0.153\% & -0.743\%\\
-0.627\% & -0.653\% & -1.369\%\\
};
\foreach \x/\y in{%
1-1/0.199, 1-2/0.207, 1-3/0.37,
2-1/0.35, 2-2/0.35, 2-3/0.43,
3-1/0.070, 3-2/0.071, 3-3/0.44,
4-1/0.38, 4-2/0.4, 4-3/0.75
}
{\fill[red, opacity=0.55](p1-\x.north east) rectangle ++ (-\y,-0.5);
}
}
{[shift={(m-2-4.south)}, yshift=-1.35cm]
\matrix(p1)[matrix of nodes, nodes={minimum width=1.8cm, minimum height=0.6cm,}, column sep=0.cm]{
-0.0726\% & -0.771\% & -1.941\%\\
-1.034 \% & -1.069\% & -2.232\%\\
-0.528\% & -0.555\% & -2.505\%\\
-1.276\% & -1.300\% & -3.182\%\\
};
\foreach \x/\y in{%
1-1/0.4, 1-2/0.42, 1-3/1.1,
2-1/0.6, 2-2/0.6, 2-3/1.3,
3-1/0.32, 3-2/0.35, 3-3/1.42,
4-1/0.68, 4-2/0.7, 4-3/1.65
}
{\fill[red, opacity=0.55](p1-\x.north east) rectangle ++ (-\y,-0.5);
}
}
% 中间第二行表格
{[shift={(m-2-2.south)}, yshift=-3.35cm]
\matrix(p1)[matrix of nodes, nodes={minimum width=1.8cm, minimum height=0.6cm,}, column sep=0.cm]{
7.610 & 7.610 & 7.610\\
2066 & 2066 & 2066\\
};
\foreach \x/\y in{%
1-1/0.4, 1-2/0.4, 1-3/0.4
}
{\fill[blue1,opacity=0.4](p1-\x.north west)++(0.04,0.05) rectangle ++ (\y,-0.6);}
\foreach \x/\y in{%
2-1/1.3, 2-2/1.3, 2-3/1.3
}
{\fill[blue!80!green, opacity=0.4](p1-\x.north west)++(0.01,-0.05) rectangle ++ (\y,-0.5);}
}
{[shift={(m-2-3.south)}, yshift=-3.35cm]
\matrix(p1)[matrix of nodes, nodes={minimum width=1.8cm, minimum height=0.6cm,}, column sep=0.cm]{
15.634 & 15.634 & 15.634\\
2343 & 2343 & 2343\\
};
\foreach \x/\y in{%
1-1/0.7, 1-2/0.7, 1-3/0.7
}
{\fill[blue1,opacity=0.4](p1-\x.north west)++(0.04,0.05) rectangle ++ (\y,-0.6);}
\foreach \x/\y in{%
2-1/1.5, 2-2/1.5, 2-3/1.5
}
{\fill[blue!80!green, opacity=0.4](p1-\x.north west)++(0.01,-0.05) rectangle ++ (\y,-0.5);}
}
{[shift={(m-2-4.south)}, yshift=-3.35cm]
\matrix(p1)[matrix of nodes, nodes={minimum width=1.8cm, minimum height=0.6cm,}, column sep=0.cm]{
31.829 & 31.829 & 31.829\\
2676 & 2676 & 2676\\
};
\foreach \x/\y in{%
1-1/1.6, 1-2/1.6, 1-3/1.6
}
{\fill[blue1,opacity=0.4](p1-\x.north west)++(0.04,0.05) rectangle ++ (\y,-0.6);}
\foreach \x/\y in{%
2-1/1.75, 2-2/1.75, 2-3/1.75
}
{\fill[blue!80!green, opacity=0.4](p1-\x.north west)++(0.01,-0.05) rectangle ++ (\y,-0.5);}
}
% 背景
{[on background layer]
\fill[green!40,opacity=0.3](current bounding box.south east)++(0.2,-0.2) rectangle +(-23.8,15.6);
\fill[white](current bounding box.south east)++(-0.3,0.25) rectangle +(-16.2,13.6);
}
\end{tikzpicture}
\end{document}
以上过程是平凡但较繁琐的。
Thanks to Jasper Habicht!
一个可能的答案如下:
\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
的判断应该也有一定的修改空间...目前只支持全正/全负的同一行数字,且个人认为用户接口并不太友好...
增加了对于行内数字有正有负形式的支持:
\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」时的支持,同时个人觉得条件判断的逻辑写复杂了,代码仍然有很大的改进空间...
完善了条件逻辑...但好像也并不完善,优化了坐标点位的命名,补充对「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}
嗯嗯谢谢大神
@u156710 说难也不难,可以分步绘制,先画表格,每个区块可以看作一个 node,然后填内容,数据条可以目测,如果不要求太精确是可以行得通的。有空我会试试完成一部分。
@u26254 感觉这样的话可拓展性会相对比较差...