1

在 Geant4 中使用输出.root文件的模拟,我很高兴发现了这个uproot包。

相信数据帧是我特定分析任务的最佳选择,我正在使用uproot.pandas.df()将 TTree 中的内容读取到这样的数据帧中。

不幸的是,结果证明这是一个瓶颈。虽然代码可以很好地处理所有数字输入,但处理字符串似乎是一个严重的问题。该文件很大,生成的帧有 2406703 行。

虽然此代码(Egammaz_eu两个数字):

df = uproot.open('rootFile.root')['seco_tuple;1].pandas.df( ['Egamma','z_eu'])

平均需要 430 毫秒,包括已经包含字符串的一列:

df = uproot.open('rootFile.root')['seco_tuple;1].pandas.df( ['Name','Egamma','z_eu'])

将时间增加到近 3.5 秒。有一个带有字符串的第二列会使时间加倍。我还尝试过读取字典中的数据,然后将其传递给数据框。读取数据相当快,但将其传递到数据帧然后又非常慢。

由于字符串显然会导致代码占用更多资源,所以我想知道字符串通常是否是数据帧的问题,或者这里的特定类型的字符串可能会有所不同?

我希望在这里得到一些进一步的了解,并可以尝试提供.root文件以及 MWE,以防需要。

提前致谢!

4

1 回答 1

0

部分问题在于,用纯 Python 编写的 uproot 在性能上在 NumPy 处理得非常快的数字工作和任何涉及 Python 对象(例如字符串)的工作之间存在严重分歧。在某些情况下,像这样,我们可以选择将字符串视为数字数组,这对性能有很大帮助。

另一部分是 Pandas 本身。NumPy 的字符串效率很低,因为它们必须被填充到一个共同的长度——数据集中最长字符串的长度——因为 NumPy 只能处理直线数组。因此,Pandas 选择了一种不同的低效率:它将字符串作为 Python 对象,通过一个 NumPy 数组(即指向 Python 对象的dtype指针object,而不是原始数字数据)。

>>> import pandas, numpy
>>> df = pandas.DataFrame({"column": ["one", "two", "three", "four", "five"]})
>>> df
  column
0    one
1    two
2  three
3   four
4   five
>>> df.values
array([['one'],
       ['two'],
       ['three'],
       ['four'],
       ['five']], dtype=object)

当字符串只是标签时,Pandas 有一个“分类” dtype(不是 NumPy 的一部分!),它将每个不同的字符串替换为一个整数,因此它实际上是一个带有元数据的整数数组。

>>> df["column"].astype("category")
0      one
1      two
2    three
3     four
4     five
Name: column, dtype: category
Categories (5, object): [five, four, one, three, two]

如果与字符串总数相比,您有少量不同的字符串(不是上面的情况),那么这会更快并且使用更少的内存。如果每个字符串都是唯一的,这只会使数据膨胀。

也许 uproot 的 DataFrame 转换应该将一些字符串值的分支读入 "categorical" dtype。这需要用户明确要求作为参数,因为它并不总是有用的。这样的事情会出现在uproot._connect._pandas.futures2df函数中——如果有人愿意贡献,我会为此接受 PR。

于 2019-11-19T16:01:18.957 回答