我一直在为一个朋友编写一个 LaTeX 代码,用于自动绘制能量转换图。代码如下:
\documentclass{standalone}
\usepackage{pgfplots,pgfplotstable}
\usepgflibrary{plotmarks}
\usetikzlibrary{calc}
%%% Example data file
\pgfplotstableread{
one two three
0.0 0.0 0.0
-44.2 -42.8 -58.6
150.4 155.8 217.6
84.4 83.9 167.4
146.2 150.7 231.5
250.8 234.5 276.7
nan 61.4 62.0
nan 119.0 162.7
86.1 49.0 105.5
213.9 212.4 325.4
}\datatable
\begin{document}
\begin{tikzpicture}
\begin{axis}[
%only marks,
every axis plot post/.style={mark=-,thick,mark size=7pt},
ylabel=Energy (kJmol$^{-1}$),
xtick=\empty,
legend pos=outer north east,
xmin=-1,
%xmax=10,
ymin=-100,
%ymax=350,
axis lines=left,
xtick=\empty,
hide x axis,
legend entries={\small one, \small two,\small three},
legend style={draw=none},
title=Insert better title here,]
\pgfplotstablegetcolsof{\datatable}
\pgfmathsetmacro\numberofycols{\pgfplotsretval-1}
\pgfplotsinvokeforeach {0,...,\numberofycols}{
\addplot table[x expr=\coordindex, y index=#1] {\datatable};
}
\end{axis}
\end{tikzpicture}
\end{document}
这产生了一个还算不错的情节:
我遇到问题的部分是自动将各个级别连接在一起。在图片中,级别由一条实线连接,实线将线的中心连接在一起。需要的是将一个级别的结尾连接到下一个级别的开头,依此类推,直到所有级别。例如:
我使用了“仅标记”选项,该选项会将线条全部删除,并尝试插入一个单独的图,仅处理线条;但这种方法没有奏效。有没有一种简单的方法可以实现我所缺少的这个功能?
提前谢谢了。
答案1
这段代码产生了想要的输出(我认为),但由于某些值,也产生了错误(应该跳过)nan
。解释如下。更新:最后的无错误代码。
\documentclass{standalone}
\usepackage{pgfplots,pgfplotstable}
\usepgflibrary{plotmarks}
\usetikzlibrary{calc}
%%% Example data file
\pgfplotstableread{
one two three
0.0 0.0 0.0
-44.2 -42.8 -58.6
150.4 155.8 217.6
84.4 83.9 167.4
146.2 150.7 231.5
250.8 234.5 276.7
nan 61.4 62.0
nan 119.0 162.7
86.1 49.0 105.5
213.9 212.4 325.4
}\datatable
\begin{document}
\begin{tikzpicture}[x=1cm, y=0.2mm]
\begin{axis}[
%only marks,
every axis plot post/.style={mark=-,thick,mark size=2mm},
ylabel=Energy (kJmol$^{-1}$),
xtick=\empty,
legend pos=outer north east,
xmin=-1,
%xmax=10,
ymin=-100,
%ymax=350,
axis lines=left,
xtick=\empty,
hide x axis,
legend entries={\small one, \small two,\small three},
legend style={draw=none},
title=Insert better title here,
% Extra options added
anchor=origin,
disabledatascaling,
only marks,
x=1cm, y=0.2mm,]
\pgfplotstablegetcolsof{\datatable}
\pgfmathsetmacro\numberofycols{\pgfplotsretval-1}
\pgfplotsinvokeforeach {0,...,\numberofycols}{
\addplot table[x expr=\coordindex, y index=#1] {\datatable};
}
\end{axis}
% Extra code added
\foreach \case in {one,two,three} {
\xdef\previndex{0}
\xdef\prevlevel{0}
\pgfplotstableforeachcolumnelement{\case}\of\datatable\as\level{%
\draw[densely dotted] ($(\previndex,\prevlevel)+(0.2,0)$) --
($(\pgfplotstablerow,\level)+(-0.2,0)$);
\xdef\previndex{\pgfplotstablerow}
\xdef\prevlevel{\level}
}
}
\end{tikzpicture}
\end{document}
解释
上述代码背后的主要思想是使用pgfplots
绘制标记(水平线),然后使用“纯 tikz”用虚线连接它们。为了能够在 pgfplots 图上使用“纯 tikz”,需要进行一些其他设置,如中所述pgfplots 手册,第 4.27 节“Tikz 互操作性”。
在我们的例子中,这些所需的设置是:
- 在和 中使用相同的
x
和y
单位值。在本例中,我使用了、。这些值会影响图形的最终大小(也会影响 Y 轴上显示的数字,因为单位值越大,显示数字的空间就越大)。tikzpicture
axis
x=1cm
y=0.2mm
y
anchor=origin, disabledatascaling
已添加到环境中的选项axis
。
一旦使用这些设置,您就可以使用标准 TikZ 命令,例如\draw
,等,使用与您的数据相同的坐标系。
为了绘制连接线,我使用了两个嵌套循环。
- 外循环是标准的 TikZ循环,它
\foreach
遍历字符串值one
并选择数据表中的适当列。two
three
- 对于上述每一项,内循环都会绘制连接该列中所有标记的线。此内循环是一个
pgftables
宏 (\pgfplotstableforeachcolumnelement
),它会迭代一列中的值。对于每个值,我都会从前一个值到当前值绘制一条线。x 坐标只是行的索引,y 坐标是从该行的表中读取的值。
请注意,一些 tikz calc 用于考虑标记的大小(我将其固定为 2mm every axis plot post/.style={mark=-,thick,mark size=2mm}
)
现在,“一”列中的数据存在问题。两行包含文本nan
而不是数字。当这些值用作 y 坐标时,TikZ 会报错。如果我们忽略这些错误,结果就像要绘制的 y 坐标等于最后绘制的 y 坐标(因此水平虚线有间隙)。
我不确定这些nan
值的预期结果是什么。应该跳过它们吗?我的意思是,应该将第 5 行的条与第 8 行的条连接起来,跳过第 6 行和第 7 行吗?
更新:跳过 NaN
要跳过“纯 tikz”部分中的 NaN 值,解决方案很简单:我们必须检测哪些值是“nan”,并且不对它们执行任何操作(甚至不更新“先前”的辅助变量)。这将产生所需的结果。
检测 NaN 有点复杂,但很容易,如下所示在 pgfplots 中使用 nan 值。我们必须使用\pgfmathfloatparsenumber
让 pgf 解析数据并检测它是否为nan
、inf
等,然后使用\pgfmathfloatgetflagstomacro
提取结果的“标志”。此标志是一个整数,在本nan
例中为 3。
因此,新的“纯 tikz”部分应该是:
\foreach \case in {one,two,three} {
\xdef\previndex{0}
\xdef\prevlevel{0}
\pgfplotstableforeachcolumnelement{\case}\of\datatable\as\level{%
\pgfmathfloatparsenumber{\level}
\pgfmathfloatgetflagstomacro\pgfmathresult\flags
\ifnum\flags=3\relax\else
\draw[densely dotted] ($(\previndex,\prevlevel)+(0.2,0)$) -- ($(\pgfplotstablerow,\level)+(-0.2,0)$);
\xdef\previndex{\pgfplotstablerow}
\xdef\prevlevel{\level}
\fi
}
}
现在的结果是: