我想要实现的目标

我想要实现的目标

我想要实现的目标

框架可以创建带有彩色框的二维直方图,表示计数率,如下所示:

TH2F 样式直方图的 ROOT 输出。

我的问题实际上只是:我可以通过 PGFPlots 生成这种二维直方图吗?这篇文章的其余部分描述了我目前的发现和尝试。


截至最近撰写本文时,ROOT 有一个名为 TikZ 输出引擎TTeXDump,进而生成:

通过 TTeXDump 类对上述 TH2F 直方图进行 ROOT 输出。

这接近良好的图形质量。轴标签文本很容易手动修改为 TeX 语法(或者可以在导出之前在 ROOT 中完成),但还存在其他问题:

  • 标签、刻度等的放置均由原始坐标完成(TTeXDump描述上述图片的TikZ 代码输出示例)。例如,将x标签置于轴下方的中心并不是一件容易的事,这使得符合与直接使用 PGFPlots 制作的其他图相一致的图形布局变得很棘手。
  • 由于所有图形实体都是静态定义的,因此缩放不会产生透明的结果。

或许还有其他东西。


自己的尝试

我尝试使用 PGFPlots 从导出的 ROOT 数据生成图表。但是,有几个细节我搞错了,也许有一个更明显的解决方案。

通过以以下形式转储直方图箱数据

xcenter ycenter weight

在文件scatter.csvPastebin 数据链接),以下代码给出以下结果:

\documentclass{article}

\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=newest}
\usepackage{siunitx}

\begin{document}

\begin{tikzpicture}
  \begin{axis}[
    xlabel={$\theta$ /($\pi$ rad)},
    ylabel={Energy /\si{\MeV}},
    enlarge x limits=.02,
    enlarge y limits=.02,
    minor tick num=4,
    xticklabel style={/pgf/number format/fixed},% exponential axis notation looks bad in this case
    colorbar,
    scatter/use mapped color={%
      draw=mapped color,
      fill=mapped color
    }]
    \addplot[
      scatter,
      scatter src=explicit,
      only marks,
      mark=square*,
    ] file{scatter.csv};
  \end{axis}
\end{tikzpicture}

\end{document}

当前 PGFPlots 的数据渲染。

此后,可以轻松对文本、刻度线、颜色图等进行微调,但数据呈现存在一些问题:

  1. “箱”尺寸由标记尺寸模拟,但最好直接输入箱号来设置标记尺寸。这也需要不对称地完成,因为和方向上的箱数量会有所不同xy我查看了mark=cube*cube/size xcube/size y但未能成功更改标记尺寸。目前,标记是对称的,乍一看可能还不错,但实际上标记以非平凡的方式重叠,这本身就是一个交易破坏者。
  2. enlarge x axis检查后插入和值enlarge y axis,以避免标记突出轴外。相反,轴距应该根据标记尺寸自动计算。
  3. 数据点标记位于轴和刻度标记的顶部,这在这里不是最佳的。强制 pgfplots 中的绘图标记“轴在顶部”有一些关于此问题的信息,但解决方案有些复杂。这里有更好的方法吗?

上述散点图方法是否是错误的?其他一些想法如下:

  • 也许我可以直接使用散点图数据,并使用 PGFPlots 执行分箱,而不是导出计算出的分箱。但我找不到这样做的方法,而且存在遇到内存限制的潜在风险。
  • 初始 ROOT 输出 PDF 可以去掉轴、刻度标记和标题,只保留图形表面。然后我可以使用\addplot figure包含这些内容并使用 PGFPlots 将轴重新绘制上去。数值轴限值可以从 ROOT 中提取,因此比例应该能够正确再现。我还没有研究过从最大/最小值校准 PGFPlots 中的颜色图比例,但这也应该是可能的。也许会有一些对齐问题需要解决。

    TTeXDump如果我可以使用输出,删除静态定义的轴、刻度等,并仅使用生成的 TikZ 命令来绘制图体,这将有助于实现自动化\addplot。但我看不出有什么简单的方法可以将其与此结合起来。

  • 数据输出可以用箱号来定义,而不是用明确的坐标来定义,即

    1 10 11.0
    

    对于xbin 1,ybin 10 的值为 11,而不是当前的:

    0.04580479262184749 0.0755985979686503 11.0
    

    由于我们还定义了轴限制和箱量,这实际上应该是我们构建直方图所需的全部信息,但我发现执行起来并不容易。


结论

这看起来似乎有很多问题,但正如最初提到的,内核实际上只有一个:我可以通过 PGFPlots 生成这种二维直方图吗?

答案1

在 Christian Feuersänger 的评论的帮助下,我写出了一个我满意的解决方案,所以我自己回答了这个问题。

问题使用 pgfplots,如何排列数据矩阵以绘制曲面图,以便矩阵中的每个单元格都绘制为正方形?Christian Feuersänger 在他的第一条评论中建议的 TeX.SE 非常有用。将数据视为矩阵而不是散点图使事情变得更容易。

首先,我需要以矩阵形式获取输入数据。除非您对 ROOT 感兴趣,否则请跳过此部分并直接转到下面的“工作示例”。


ROOT数据导出

之前,我通过以下方式生成散点图值ROOT 的 Python 绑定作为:

import csv

def th2f_to_csv(hist, csv_file):
    """Print TH2F bin data to CSV file."""
    xbins, ybins = hist.GetNbinsX(), hist.GetNbinsY()
    xaxis, yaxis = hist.GetXaxis(), hist.GetYaxis()
    with open(csv_file, 'w') as f:
        c = csv.writer(f, delimiter=' ', lineterminator='\n')
        for xbin in xrange(xbins+2):
            xcenter = xaxis.GetBinCenter(xbin)
            for ybin in xrange(ybins+2):
                ycenter = yaxis.GetBinCenter(ybin)
                weight = hist.GetBinContent(xbin, ybin)
                if weight > 0:
                    c.writerow((xcenter, ycenter, weight))

输入参数hist是一个TH2F对象。有关更多信息,请参阅 ROOT 和 PyROOT 文档。

中的“ +2xrange表示 ROOT 在实际绘制的箱体顶部保存了一个下溢箱体和一个溢出箱体。我在此阶段明确丢弃了没有内容的点,以保持 PGFPlots 中的“散点图”整洁。

但现在我想输出一个完整的矩阵,我这样做如下:

import csv

def th2f_to_csv(hist, csv_file):
    """Print TH2F bin data to CSV file."""
    xbins, ybins = hist.GetNbinsX(), hist.GetNbinsY()
    xaxis, yaxis = hist.GetXaxis(), hist.GetYaxis()
    with open(csv_file, 'w') as f:
        c = csv.writer(f, delimiter=' ', lineterminator='\n')
        for ybin in xrange(1, ybins+2):
            y_lowedge = yaxis.GetBinLowEdge(ybin)
            for xbin in xrange(1, xbins+2):
                x_lowedge = xaxis.GetBinLowEdge(xbin)
                weight = hist.GetBinContent(xbin, ybin)
                c.writerow((x_lowedge, y_lowedge, weight))

我现在通过从 开始范围来丢弃下溢箱1,并且我还丢弃了溢出箱,因为稍后在 PFDPlots 中选择时不会显示最后一个箱shader=flat corner。我本可以给出一个虚拟值而不是实际的溢出值,但这并不重要(编辑:实际上,如果溢出值大于/小于最大/最小“真实”值,则可能会有影响 - 它会影响彩色图比例,所以要小心)。

我现在感兴趣的不是提取箱子的中心,而是提取箱子的下边缘。

x我还更改了和循环的顺序,y以便以 PGFPlots 更高效处理的形式获取矩阵数据,如手册第 7.2.1 节所述:“将网格数据从 Matlab 导入 PGFPlots”。这在编译时间上产生了明显的差异。


工作示例

现在我有了矩阵,这是绘制此数据的最小工作示例(matrix.csv)作为二维直方图为:

\documentclass{article}

\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=newest}

\begin{document}

\begin{tikzpicture}
  \begin{axis}[
    view={0}{90},
    colorbar,
  ]
    \addplot3[
      surf,
      shader=flat corner,
      mesh/cols=51,
      mesh/ordering=rowwise,
    ] file {matrix.csv};
  \end{axis}
\end{tikzpicture}

\end{document}

最小工作示例。

手册中的第 7.2.1 节和之前链接的问题解释了这些参数。mesh/cols=51来自一个已知事实,即直方图包含 50 个水平箱,额外的一个代表了上面链接的 TeX.SE 问题中提到的“虚拟箱”。如果需要更多自动化,可以将箱数与数据一起输出到 CSV 特定的配置文件中。

一个问题是,编译器(xelatex在本例中)向终端抛出了 5000 行内容:

pgfplotsplothandlermesh@get@flat@color

图像中总共应该有 50⨯100 = 5000 个“单元格”需要渲染。过多的消息本身可能是一个错误,或者我可以通过某种方式抑制它。

另一个问题是“背景”,即表示零值的图形部分,是彩色的,这不是最佳选择。我发现最明显的解决方案是创建一个“开始”为白色的彩色图,这对这些类型的图形来说是有意义的。

除一些其他小的格式修复外,还产生了以下结果:

\documentclass{article}

\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=newest}
\usepackage{siunitx}

\begin{document}

\pgfplotsset{
  /pgfplots/colormap={coldredux}{
    [1cm]
    rgb255(0cm)=(255,255,255)
    rgb255(2cm)=(0,192,255)
    rgb255(4cm)=(0,0,255)
    rgb255(6cm)=(0,0,0)
  }
}

\begin{tikzpicture}
  \begin{axis}[
    view={0}{90},
    xlabel={$\theta$ /degrees},
    ylabel={Energy /\si{\MeV}},
    minor tick num=4,
    colorbar,
    colorbar style={ylabel={Counts}},
  ]
    \addplot3[
      surf,
      shader=flat corner,
      mesh/cols=51,
      mesh/ordering=rowwise,
      x filter/.code={\pgfmathparse{#1*180}\pgfmathresult},
      y filter/.code={\pgfmathparse{#1/1000}\pgfmathresult},
    ] file {matrix.csv};
  \end{axis}
\end{tikzpicture}

\end{document}

直方图的图形更新版本。

我将在分析阶段而不是绘图阶段实现x filter和转换。y filter

可能还不是一个完成的“产品”,但现在我可以自由地应用样式,并且它是用绝对可管理的代码量来制作的。

相关内容