1

我知道有类似问题的解决方案:如何快速获得像 root_numpy root2array() 输出一样的 uproot.iterate() 输出 但据我所知,它仅适用于平面 ROOT TTrees。我想为以下问题提供通用解决方案:

  1. 尺寸固定但嵌套的数据,如粒子动量 (px, py, pz),在根 TTree 中表示为vector<double>
  2. 任意大小的维度数据

我所有的申请asjagged都失败了。jaggedarray情况(1)是否可以避免?

4

1 回答 1

1

如果数据是固定大小但存储为vector<double>,那么它们将被视为不是固定大小的。Uproot 总是将它们读取为锯齿状数组,因此asarray另一个问题中描述的方法不可用。

也就是说,如果您拥有比文件元数据更多的知识并且愿意尝试不受支持的破解,您可以强制对您的解释vector<double>始终包含三个元素。这是一个示例——首先,我们需要一个合适的 ROOT 文件:

TFile *f = new TFile("tmp.root", "recreate");
TTree *t = new TTree("t", "t");
std::vector<double> x;
t->Branch("x", &x);
x = {101, 102, 103};
t->Fill();
x = {201, 202, 203};
t->Fill();
x = {301, 302, 303};
t->Fill();
x = {401, 402, 403};
t->Fill();
x = {501, 502, 503};
t->Fill();
x = {601, 602, 603};
t->Fill();
t->Write();
f->Close();

现在,在 Uproot 中读取它的正常方法是使用 JaggedArray:

>>> import uproot
>>> branch = uproot.open("tmp.root")["t"]["x"]
>>> branch.array()
<JaggedArray [[101.0 102.0 103.0] [201.0 202.0 203.0] [301.0 302.0 303.0] [401.0 402.0 403.0] [501.0 502.0 503.0] [601.0 602.0 603.0]] at 0x7f325ab4eb90>
>>> branch.interpretation
asjagged(asdtype('>f8'), 10)

但这会在您每次阅读时分配新数组并进行一些操作以找到边界,您知道这些边界是常规的。(Uproot 不知道这一点。)

STL 向量的标头恰好有 10 个字节长。之后,它的内容按顺序从头到尾进行序列化,然后再转到下一个 STL 向量。对于三个 8 字节浮点数,即 10 + 8 + 8 + 8 = 34 字节,大小始终相同。它总是恰好是相同大小的事实对于以下内容至关重要。

NumPy 结构化数组可以将固定大小结构的数组表示为 dtype:

>>> array = branch.array(uproot.asdtype([("header", "S10"), ("x", ">f8"), ("y", ">f8"), ("z", ">f8")]))
>>> array
array([(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 101., 102., 103.),
       (b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 201., 202., 203.),
       (b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 301., 302., 303.),
       (b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 401., 402., 403.),
       (b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 501., 502., 503.),
       (b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 601., 602., 603.)],
      dtype=[('header', 'S10'), ('x', '<f8'), ('y', '<f8'), ('z', '<f8')])

我们不喜欢看标题,所以我们可以使用普通的 NumPy 切片将其剥离:

>>> array[["x", "y", "z"]]
array([(101., 102., 103.), (201., 202., 203.), (301., 302., 303.),
       (401., 402., 403.), (501., 502., 503.), (601., 602., 603.)],
      dtype={'names':['x','y','z'], 'formats':['<f8','<f8','<f8'], 'offsets':[10,18,26], 'itemsize':34})

(或者只是要求array["x"]获得一个非结构化数组)。因为我们可以用一个普通uproot.asdtype的来做到这一点,我们可以用一个预先分配的来做到这一点uproot.asarray

>>> import numpy as np
>>> big = np.empty(1000, dtype=[("header", "S10"), ("x", ">f8"), ("y", ">f8"), ("z", ">f8")])
>>> branch.array(uproot.asarray(big.dtype, big))
array([(b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 101., 102., 103.),
       (b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 201., 202., 203.),
       (b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 301., 302., 303.),
       (b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 401., 402., 403.),
       (b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 501., 502., 503.),
       (b'@\x00\x00\x1e\x00\t\x00\x00\x00\x03', 601., 602., 603.)],
      dtype=[('header', 'S10'), ('x', '>f8'), ('y', '>f8'), ('z', '>f8')])

上面,big必须至少与您要读取的数据集一样大,并且读取它会返回该数组的修剪视图。它不分配新数组——数据存储在big

>>> big[["x", "y", "z"]][:10]
array([( 1.01000000e+002,  1.02000000e+002,  1.03000000e+002),
       ( 2.01000000e+002,  2.02000000e+002,  2.03000000e+002),
       ( 3.01000000e+002,  3.02000000e+002,  3.03000000e+002),
       ( 4.01000000e+002,  4.02000000e+002,  4.03000000e+002),
       ( 5.01000000e+002,  5.02000000e+002,  5.03000000e+002),
       ( 6.01000000e+002,  6.02000000e+002,  6.03000000e+002),
       ( 1.22164945e-309,  5.26335088e-310,  1.22167067e-309),
       (-5.70498984e+158,  5.97958175e+158, -5.97958175e+158),
       (-4.92033505e+032, -4.92033505e+032, -4.92033505e+032),
       ( 3.77957352e+011,  3.77957221e+011,  3.77957320e+011)],
      dtype={'names':['x','y','z'], 'formats':['>f8','>f8','>f8'], 'offsets':[10,18,26], 'itemsize':34})

读取的 6 个事件之外的所有内容都是未初始化的垃圾,因此我建议使用该branch.array函数的修剪输出;这只是为了表明它big正在被填充——你没有得到一个新数组。

根据您的问题(2),如果数据不规则(每个条目的字节数),那么您无法做到这一点。另外,请记住,上述技术不受官方支持:您必须知道您的数据是常规的,并且您必须知道 STL 向量有一个 10 字节的标头,我们不希望大多数用户知道这些内容。

于 2020-03-14T13:12:03.447 回答