解释

解释

我一直在为一个朋友编写一个 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 互操作性”。

在我们的例子中,这些所需的设置是:

  • 在和 中使用相同的xy单位值。在本例中,我使用了、。这些值会影响图形的最终大小(也会影响 Y 轴上显示的数字,因为单位值越大,显示数字的空间就越大)。tikzpictureaxisx=1cmy=0.2mmy
  • anchor=origin, disabledatascaling已添加到环境中的选项axis

一旦使用这些设置,您就可以使用标准 TikZ 命令,例如\draw,等,使用与您的数据相同的坐标系。

为了绘制连接线,我使用了两个嵌套循环。

  • 外循环是标准的 TikZ循环,它\foreach遍历字符串值one并选择数据表中的适当列。twothree
  • 对于上述每一项,内循环都会绘制连接该列中所有标记的线。此内循环是一个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 解析数据并检测它是否为naninf等,然后使用\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
    }
}

现在的结果是:

新结果

相关内容