请问这种表格带颜色,且色条长度反映了表格数据长度,这类表格该如何绘制

发布于 2025-08-28 10:22:41

请问这种表格带颜色,且色条长度反映了表格数据长度,这类表格该如何绘制
跪求大神解答,或者绘制思路也行,谢谢谢谢谢谢谢谢

Snipaste_2025-08-28_10-18-01.png

查看更多

关注者
1
被浏览
133
3 个回答
远方不远
远方不远 18小时前
Hello, LuaLaTeX!

请不要LaTeX当成万能的工具!

shadow
shadow 16小时前
这个人懒得不得了,竟然啥也没写

tikz的matrix可以实现,就是比较麻烦

Sagittarius Rover
Sagittarius Rover 7小时前
这家伙很懒,什么也没写!

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}

image.png

由于我没想好关于「同一行内既有正数也有负数」应该如何绘制,因此\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}

image.png

嗯...但他目前也有缺点,似乎暂时缺少对填充的数字为「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}

image.png

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览