使用不同长度的锯齿状数组进行绘图

Plotting with different length of jagged arrays

我在尝试绘制具有不同长度的锯齿状数组的二维直方图或图形时遇到问题。

这是一个简单的例子。假设gen级pT及其Et有7个事件

pT = [ [46.8], [31.7], [21], [29.9], [13.9], [41.2], [15.7] ]
Et = [ [41.4], [25.5, 20], [19.6], [27.4], [12, 3.47], [37.8], [10] ]

这里,一些事件(第 2、5 个)有两个 y 值对应一个 x 值。 我想制作图形或二维直方图,将 x = pt 和 y = et,并将两点放在一起。 即 (31.7, 25.5) 和 (31.7, 20)

如何对齐这些值以进行绘图?

您要做的是“广播”两个数组:

Awkward broadcasting 是 NumPy 广播的泛化,包括 variable-length 个列表。

广播通常在您执行数学计算时自动发生:

>>> import awkward1 as ak
>>> ak.Array([[1, 2, 3], [], [4, 5]]) + ak.Array([100, 200, 300])
<Array [[101, 102, 103], [], [304, 305]] type='3 * var * int64'>

但您也可以手动完成:

>>> ak.broadcast_arrays(ak.Array([[1, 2, 3], [], [4, 5]]),
...                     ak.Array([100, 200, 300]))
[<Array [[1, 2, 3], [], [4, 5]] type='3 * var * int64'>,
 <Array [[100, 100, 100], [], [300, 300]] type='3 * var * int64'>]

当两个数组具有不同的深度(NumPy 术语中的不同“维度”)时,一个数组的标量将被复制以与另一个数组中列表的所有元素对齐。

您有两个深度相同的列表:

>>> pT = ak.Array([ [46.8], [31.7], [21], [29.9], [13.9], [41.2], [15.7] ])
>>> Et = ak.Array([ [41.4], [25.5, 20], [19.6], [27.4], [12, 3.47], [37.8], [10] ])

要手动广播它们,您可以通过从每个列表中取出第一个元素来减少 pT 的深度。

>>> pT[:, 0]
<Array [46.8, 31.7, 21, ... 13.9, 41.2, 15.7] type='7 * float64'>

然后您可以将 pT 的每个标量广播到 Et 的每个列表中。

>> ak.broadcast_arrays(pT[:, 0], Et)
[<Array [[46.8], [31.7, 31.7, ... 41.2], [15.7]] type='7 * var * float64'>,
 <Array [[41.4], [25.5, 20], ... [37.8], [10]] type='7 * var * float64'>]

如果我把它们全部打印成 Python 列表,这会更清楚:

>>> pT_broadcasted, Et = ak.broadcast_arrays(pT[:, 0], Et)
>>> pT_broadcasted.tolist()
[[46.8], [31.7, 31.7], [21.0], [29.9], [13.9, 13.9], [41.2], [15.7]]
>>> Et.tolist()
[[41.4], [25.5, 20.0], [19.6], [27.4], [12.0, 3.47], [37.8], [10.0]]

现在您看到 31.7 已被复制以与 [25.5, 20.0] 中的每个值对齐。

在 NumPy 中,您经常会看到广播长度为 1 的维度而不是创建维度的示例,如下所示:

>>> import numpy as np
>>> np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + np.array([[100], [200], [300]])
array([[101, 102, 103],
       [204, 205, 206],
       [307, 308, 309]])

Awkward Array 遵循此规则,但前提是维度的长度“恰好为 1”,而不是“一堆 variable-length 列表恰好每个的长度都为 1”。我写的方式pT,它有后者:

>>> ak.type(pT)     # 7 lists with variable length
7 * var * float64
>>> ak.num(pT)      # they happen to each have length 1... this time...
<Array [1, 1, 1, 1, 1, 1, 1] type='7 * int64'>

由于这些列表是 in-principle 变量,它们不会像长度为 1 的 NumPy 数组那样广播。

>>> ak.broadcast_arrays(pT, Et)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jpivarski/irishep/awkward-1.0/awkward1/operations/structure.py", line 699, in broadcast_arrays
    out = awkward1._util.broadcast_and_apply(inputs, getfunction, behavior)
  File "/home/jpivarski/irishep/awkward-1.0/awkward1/_util.py", line 972, in broadcast_and_apply
    out = apply(broadcast_pack(inputs, isscalar), 0)
  File "/home/jpivarski/irishep/awkward-1.0/awkward1/_util.py", line 745, in apply
    outcontent = apply(nextinputs, depth + 1)
  File "/home/jpivarski/irishep/awkward-1.0/awkward1/_util.py", line 786, in apply
    nextinputs.append(x.broadcast_tooffsets64(offsets).content)
ValueError: in ListOffsetArray64, cannot broadcast nested list

(https://github.com/scikit-hep/awkward-1.0/blob/0.3.2/src/cpu-kernels/operations.cpp#L778)

如果您将数组显式转换为 NumPy,它将具有常规类型。 (自我注意:如果有一种方法可以将 variable-length 维度变为常规或 vice-versa 而无需将整个数组转换为 NumPy。)

>>> ak.type(pT)
7 * var * float64
>>> ak.type(ak.to_numpy(pT))
7 * 1 * float64

因此,获得相同广播的另一种方法是将 pT 转换为 NumPy,而不是使用 pT[:, 0].

挑选每个列表的第一个元素
>>> ak.broadcast_arrays(ak.to_numpy(pT), Et)
[<Array [[46.8], [31.7, 31.7, ... 41.2], [15.7]] type='7 * var * float64'>,
 <Array [[41.4], [25.5, 20], ... [37.8], [10]] type='7 * var * float64'>]

无论哪种方式,都假设 pT 由长度为 1 的列表组成。pT[:, 0] 表达式假设了这一点,因为它要求某些东西在每个列表中都有索引 0 (因此长度至少为 1)并且它忽略了可能存在的任何其他内容。如果 pT 数组不是规则的,则 ak.to_numpy(pT) 表达式将引发异常,这种形状可以用 NumPy 表示。

现在您已将 pT_broadcastedEt 与同一结构对齐,您必须将它们都展平以将它们传递给绘图例程(需要 non-jagged 数据).

>>> ak.flatten(pT_broadcasted), ak.flatten(Et)
(<Array [46.8, 31.7, 31.7, ... 13.9, 41.2, 15.7] type='9 * float64'>,
 <Array [41.4, 25.5, 20, ... 3.47, 37.8, 10] type='9 * float64'>)

绘图例程可能会尝试对其中的每一个进行 np.asarray,这与 ak.to_numpy 相同,这将起作用,因为这些展平数组是规则的。如果您有双重锯齿状数据或更复杂的数据,则必须进一步展平。