1

我有一个看起来像这样的多索引熊猫数据框(称为 p_z):

                     p_z
entry subentry
0     0         0.338738
      1         0.636035
      2        -0.307365
      3        -0.167779
      4         0.243284
...                  ...
26692 891      -0.459227
      892       0.055993
      893      -0.469857
      894       0.192554
      895       0.155738

[11742280 rows x 1 columns]

我希望能够根据另一个多维数据框(或 numpy 数组)选择某些行。它看起来像一个 pandas 数据框(称为 tofpid):

                tofpid
entry subentry
0     0              0
      1              2
      2              4
      3              5
      4              7
...                ...
26692 193          649
      194          670
      195          690
      196          725
      197          737

[2006548 rows x 1 columns]

我也把它当作一个尴尬的数组,它是一个 (26692, ) 数组(每个条目都有非标准数量的子条目)。这是一个选择 df/array,它告诉 p_z df 要保留哪些行。所以在 p_z 的条目 0 中,它应该保留子条目 0、2、4、5、7 等。

我找不到在熊猫中完成这项工作的方法。我是 pandas 的新手,甚至是 multiindex 的新手;但我觉得应该有办法做到这一点。如果它能够更好地广播,我将在大约 1500 个类似大小的数据帧上执行此操作。如果有帮助,这些数据框来自使用 uproot 导入的 *.root 文件(如果有另一种方法可以在没有 pandas 的情况下做到这一点,我会接受它;但我很想使用 pandas 来保持井井有条)。

编辑:这是一个可重复的示例(由 Jim Pavinski 的回答提供;谢谢!)。

import awkward as ak
import pandas as pd

>>> p_z = ak.Array([[ 0.338738, 0.636035, -0.307365, -0.167779, 0.243284,  
                      0.338738, 0.636035],
                    [-0.459227, 0.055993, -0.469857,  0.192554, 0.155738, 
                     -0.459227]])
>>> p_z = ak.to_pandas(p_z)
>>> tofpid = ak.Array([[0, 2, 4, 5], [1, 2, 4]])
>>> tofpid = ak.to_pandas(tofpid)

这两个数据帧都是在 uproot 中本地生成的,但这将重现与 uproot 相同的数据帧(使用笨拙的库)。

4

2 回答 2

1

这是一个具有足够结构来表示问题的可重现示例(使用笨拙的库):

>>> import awkward as ak
>>> 
>>> p_z = ak.Array([
...     [ 0.338738, 0.636035, -0.307365, -0.167779, 0.243284,  0.338738, 0.636035],
...     [-0.459227, 0.055993, -0.469857,  0.192554, 0.155738, -0.459227],
... ])
>>> p_z
<Array [[0.339, 0.636, ... 0.156, -0.459]] type='2 * var * float64'>
>>> 
>>> tofpid = ak.Array([[0, 2, 4, 5], [1, 2, 4]])
>>> tofpid
<Array [[0, 2, 4, 5], [1, 2, 4]] type='2 * var * int64'>

在 Pandas 形式中,这是:

>>> df_p_z = ak.to_pandas(p_z)
>>> df_p_z
                  values
entry subentry          
0     0         0.338738
      1         0.636035
      2        -0.307365
      3        -0.167779
      4         0.243284
      5         0.338738
      6         0.636035
1     0        -0.459227
      1         0.055993
      2        -0.469857
      3         0.192554
      4         0.155738
      5        -0.459227
>>> df_tofpid = ak.to_pandas(tofpid)
>>> df_tofpid
                values
entry subentry        
0     0              0
      1              2
      2              4
      3              5
1     0              1
      1              2
      2              4

作为一个尴尬的数组,你想要做的是将第一个数组切片第二个。也就是说,你想要p_z[tofpid]

>>> p_z[tofpid]
<Array [[0.339, -0.307, ... -0.47, 0.156]] type='2 * var * float64'>
>>> p_z[tofpid].tolist()
[[0.338738, -0.307365, 0.243284, 0.338738], [0.055993, -0.469857, 0.155738]]

使用 Pandas,我设法做到了这一点:

>>> df_p_z.loc[df_tofpid.reset_index(level=0).apply(lambda x: tuple(x.values), axis=1).tolist()]
                  values
entry subentry          
0     0         0.338738
      2        -0.307365
      4         0.243284
      5         0.338738
1     1         0.055993
      2        -0.469857
      4         0.155738

这里发生的事情是将 MultiIndexdf_tofpid.reset_index(level=0)"entry"一部分变成一列,然后apply在每一行上执行一个 Python 函数,如果axis=1,每一行是x.values,并将tolist()结果变成一个元组列表,如

>>> df_tofpid.reset_index(level=0).apply(lambda x: tuple(x.values), axis=1).tolist()
[(0, 0), (0, 2), (0, 4), (0, 5), (1, 1), (1, 2), (1, 4)]

这就是loc需要从其 MultiIndex 中选择条目/子条目对的内容。

我的 Pandas 解决方案有两个缺点:它很复杂,而且它通过 Python 迭代和对象,它的扩展性不如数组。Pandas 专家很有可能找到比我更好的解决方案。关于 Pandas,我有很多不知道的地方。

于 2021-08-04T13:18:42.073 回答
1

国际大学联盟:

输入数据:

>>> p_z
                     p_z
entry subentry
0     0         0.338738
      1         0.636035
      2        -0.307365
      3        -0.167779
      4         0.243284

>>> tofpid
                tofpid
entry subentry
0     0              0
      1              2
      2              4
      3              5
      4              7

从第二个数据框的列(条目,tofpid)创建一个新的多索引:

mi = pd.MultiIndex.from_frame(tofpid.reset_index(level='subentry', drop=True)
                                    .reset_index())

输出结果:

>>> p_z.loc[mi.intersection(p_z.index)]
              p_z
entry
0     0  0.338738
      2 -0.307365
      4  0.243284
于 2021-08-04T07:30:10.083 回答