ak.add 功能类似于 np.add

ak.add function similar to np.add

在笨拙的数组中我们是否已经有了类似于 np.add 的函数?

我处于需要添加它们的情况,“+”运算符适用于简单数组但不适用于嵌套数组。

例如 >>> ak.to_list(c1)

[[], [], [], [], [0.944607075944902]]

>>> ak.to_list(c2)

[[0.9800207661211596], [], [], [], []]

>>> c1+c2

回溯(最近调用最后): 文件“”,第 1 行,位于 文件“/afs/cern.ch/work/k/khurana/EXOANALYSIS/CMSSW_11_0_2/src/bbDMNanoAOD/analyzer/dependencies/lib/python3.6/site-packages/numpy/lib/mixins.py”,第 21 行,在 func return ufunc(自己,其他) 文件“/afs/cern.ch/work/k/khurana/EXOANALYSIS/CMSSW_11_0_2/src/bbDMNanoAOD/analyzer/dependencies/lib/python3.6/site-packages/awkward1/highlevel.py”,第 1380 行,在 array_ufunc 中 return awkward1._connect._numpy.array_ufunc(ufunc、方法、输入、kwargs) 文件“/afs/cern.ch/work/k/khurana/EXOANALYSIS/CMSSW_11_0_2/src/bbDMNanoAOD/analyzer/dependencies/lib/python3.6/site-packages/awkward1/_connect/_numpy.py”,第 107 行,在 array_ufunc 中 out = awkward1._util.broadcast_and_apply(输入,getfunction,行为) 文件“/afs/cern.ch/work/k/khurana/EXOANALYSIS/CMSSW_11_0_2/src/bbDMNanoAOD/analyzer/dependencies/lib/python3.6/site-packages/awkward1/_util.py”,第 972 行,在 broadcast_and_apply 中 输出 = 应用(broadcast_pack(输入,标量),0) 文件“/afs/cern.ch/work/k/khurana/EXOANALYSIS/CMSSW_11_0_2/src/bbDMNanoAOD/analyzer/dependencies/lib/python3.6/site-packages/awkward1/_util.py”,第 745 行,在应用中 outcontent = apply(nextinputs, depth + 1) 文件“/afs/cern.ch/work/k/khurana/EXOANALYSIS/CMSSW_11_0_2/src/bbDMNanoAOD/analyzer/dependencies/lib/python3.6/site-packages/awkward1/_util.py”,第 786 行,在应用中 nextinputs.append(x.broadcast_tooffsets64(偏移量).内容) ValueError: 在 ListOffsetArray64 中,无法广播嵌套列表

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

我添加它们的唯一方法是使用第一个,然后用 0 替换 None。

>>> z1=ak.fill_none(ak.firsts(c1),0.)

>>> z2=ak.fill_none(ak.firsts(c2),0.)

>>> z1

<Array [0, 0, 0, 0, 0.945] type='5 * float64'>

>>> z2

<Array [0.98, 0, 0, 0, 0] type='5 * float64'>

>>> z1+z2

<Array [0.98, 0, 0, 0, 0.945] type='5 * float64'>

是否可以为 ak 设计类似 np.add 的东西,即使 scope/functionality 有限。我所说的有限范围是指如果它只能在相同维度的 ak 数组上工作,那么它至少可以满足我目前的目的。

谢谢。

您为

看到的异常
>>> ak.to_list(c1)
[[], [], [], [], [0.944607075944902]]

>>> ak.to_list(c2)
[[0.9800207661211596], [], [], [], []]

>>> c1+c2

是正确的:你不能添加这两个数组。这不是因为 Awkward 缺少 ak.add 功能。这样的事情将等同于 np.add:

>>> c1 + c2          # this actually calls np.add
<Array [[], [], [], [], [1.89]] type='5 * var * float64'>
>>> np.add(c1, c1)
<Array [[], [], [], [], [1.89]] type='5 * var * float64'>

它不起作用,因为数组在每个位置都有不同数量的元素。这就像尝试添加两个不同形状的 NumPy 数组。 (您可以添加具有 某些 不同形状的 NumPy 数组,就像您可以添加具有 某些 不同形状的尴尬数组一样,如果它们 broadcast .这些没有。)

如果您希望空列表的行为类似于其中包含零的列表,那么您做对了:ak.firsts and ak.singletons 在表示缺失数据的两种方式之间进行转换:

  • as None 与另一个值
  • 作为空列表与长度为 1 的列表中的值。

在某些语言中,缺失值或可能缺失值被视为长度为 0 或长度为 1 的列表,例如 Scala's Option type。因此,

>>> ak.firsts(c1)
<Array [None, None, None, None, 0.945] type='5 * ?float64'>

假定您从 empty-or-singleton 开始(在您的示例中似乎是正确的)并将其转换为 option-type 数组,深度减少一级。然后执行 ak.fill_none 意味着您希望这些缺失值(来自空列表)像零一样进行加法运算,并且您得到了想要的结果。

>>> ak.fill_none(ak.firsts(c1), 0) + ak.fill_none(ak.firsts(c2), 0)
<Array [0.98, 0, 0, 0, 0.945] type='5 * float64'>

从您的数据中不清楚的一件事是,您是否总是期望列表最多只有一个项目——ak.firsts 只会从每个列表中提取第一个项目。如果你有

>>> c1 = ak.Array([[], [], [], [], [0.999, 0.123]])
>>> c2 = ak.Array([[0.98], [], [], [], []])

然后

>>> ak.fill_none(ak.firsts(c1), 0) + ak.fill_none(ak.firsts(c2), 0)
<Array [0.98, 0, 0, 0, 0.999] type='5 * float64'>

可能不是您想要的,因为它删除了 0.123。您可能实际上希望 ak.pad_none 每个列表至少有一个元素,如下所示:

>>> ak.pad_none(c1, 1)
<Array [[None], [None], ... [0.999, 0.123]] type='5 * var * ?float64'>
>>> ak.fill_none(ak.pad_none(c1, 1), 0)
<Array [[0], [0], [0], [0], [0.999, 0.123]] type='5 * var * float64'>

这维护了结构,区分除 0 和 1 之外的所有长度的列表长度,因为空列表已被转换为 [0]。除非这些较长的列表匹配长度(回到你原来的问题),否则你不能使用它来添加,但你也可以安排它。

>>> ak.fill_none(ak.pad_none(c1, 2), 0) + ak.fill_none(ak.pad_none(c2, 2), 0)
<Array [[0.98, 0], [0, ... 0], [0.999, 0.123]] type='5 * var * float64'>

这完全取决于您拥有什么结构以及您想要什么结构。创建一个执行上述两件事之一的新函数不是一个好主意,特别是如果它的名称与 NumPy 函数的名称非常接近,例如 np.add,因为它以不同的方式工作任何人都必须对此进行解释才能安全地使用它。如果你想做一件专门的事情,用更简单的原语构建它会更安全(即使你把它包装成你自己工作中的一个方便的函数),因为那样你就知道它遵循什么规则了。