Numpy: “受控”创建对象数组

创建于 2019-12-04  ·  45评论  ·  资料来源: numpy/numpy

最近在 numpy 中不推荐自动创建对象数组。 我同意这种变化,但编写某些类型的通用代码来确定用户提供的参数是否可转换为非对象数组似乎有点困难。

重现代码示例:

Matplotlib 包含以下代码段:

    # <named ("string") colors are handled earlier>
    # tuple color.
    c = np.array(c)
    if not np.can_cast(c.dtype, float, "same_kind") or c.ndim != 1:
        # Test the dtype explicitly as `map(float, ...)`, `np.array(...,
        # float)` and `np.array(...).astype(float)` all convert "0.5" to 0.5.
        # Test dimensionality to reject single floats.
        raise ValueError(f"Invalid RGBA argument: {orig_c!r}")

但有时会使用各种格式的颜色数组调用该函数(例如["red", (0.5, 0.5, 0.5), "blue"] )——我们捕获 ValueError 并一次转换每个项目。

现在对 np.array(c) 的调用将发出 DeprecationWarning。 我们如何解决这个问题? 甚至像np.min_scalar_type(c)这样的东西也会发出警告(我猜它不应该?),所以我不清楚如何检查“如果我们把这个东西转换成一个数组,dtype 会是什么?”

Numpy/Python 版本信息:


1.19.0.dev0+bd1adc3 3.8.0(默认,2019 年 11 月 6 日,21:49:08)
[海湾合作委员会 7.3.0]

57 - Close?

最有用的评论

有人可以指出operator.mod示例吗?

至于==运算符,我看到的那个是在做类似np.array(vals, dtype=object) == vals where vals=[1, [2, 3]] (解释代码)的事情,所以解决方案是主动创建右侧的数组边。

许多scipy 失败似乎是np.array([0.25, np.array([0.3])]) ,其中将标量和 ndarray 与shape==(1,)将违反维度发现并创建对象数组。 外部参照 gh-15075

所有45条评论

一种选择是
```蟒蛇
尝试:
# 抢占先机,将弃用提升为一个错误,将替换它
使用 warnings.catch_warnings():
warnings.filterwarnings('raise', DeprecationWarning, message="...")
c_arr = np.asarray(c)
除了(弃用警告,值错误):
# 无论你现在为 ValueError 做什么

我猜想这一点,以及 gh-15045 中提到的失败测试是在几年内发出DeprecationWarning而不是直接发出ValueError导致更多代码流失的情况。

请注意, warnings.catch_warnings不是线程安全的。 这使得解决方法有点容易出现后续问题。

我认为代码改动值得弃用期。

Matplotlib 运行带有警告失败的测试套件,以尽早准确地捕捉到这种变化,所以这对我来说似乎是系统工作:)。

但是 AFAICT 甚至没有一个相当简单的修复(如上所述,建议的修复不是线程安全的):/

我想我在这里看到了@anntzer的观点。 我们陷入了混乱,下游库想要快速失败,以便他们可以尝试其他东西,而应该向用户显示更温和的消息。

今天的问题是,图书馆作者无法在不实际发出警告的情况下询问“这会发出警告吗”……发出警告,并且抑制它不是线程安全的。

关于警告线程安全: https :

AFAIK,弃用在发布分支中。 我们要还原它吗? 如果没有,修复程序将需要向后移植。 我仍然不清楚为什么在发布分支轮子中没有提出警告并且直到最后两次构建才出现在每晚构建中。 我在分支之后没有更改任何内容,从那时起在主分支中的提交中没有任何看起来非常可疑,除了可能是 #15040。

恕我直言(并且同意@mattip的上述观点)如果在没有弃用期的情况下切换到提高,那么这种更改会更容易在下游处理。 虽然不确定这是一个选项:/

或者可能 multibuild 对待分支与 master 不同。

FWIW 在这个变化上我总是至少是 -1,特别是作为一个不完整的数据结构的敏锐用户,但无论如何现在我需要弄清楚如何处理 SciPy 1.4.0rc2 prep in 的数百次测试失败https://github.com/scipy/scipy/pull/11161

现在我需要弄清楚如何处理数百次测试失败

一个简单的选择是:

  • 抑制 pytest 配置中的警告
  • 打开一个问题稍后修复它

我们使用DeprecationWarning而不是ValueError是给下游项目和用户一个宽限期来做到这一点。

AFAIK,弃用在发布分支中。 我们要还原它吗?

我想我们知道,下雨的问题。 我们现在有一个列表,列出了 Pandas、Matplotlib、SciPy、 numpy.testing和 NumPy ufuncs、 ==等中的问题。我认为我们现在应该恢复更改并评估/修复所有这些事情,然后重新引入弃用。

我们可以妥协一个pendingdeprecationwarning吗?

这样,下游项目可以将其添加到他们的忽略列表中,当我们切换回 DeprecationWarning 时,他们可以再次做出决定。

我们似乎已经偏离了最初的问题,这似乎是“给定一系列值,matplotlib 如何确定它们是单一颜色还是颜色列表”。 我认为应该有一个解决方案,不需要将值转换为 ndarray,并检查该数组的 dtype。 某种递归is_a_color()函数可能是更好的解决方案。

我已在 #15053 中恢复了 1.18.x 的更改。

情绪是破坏 scipy 和 pandas CI 很烦人,以至于暂时将其恢复到 master 中。 不过,我希望它在基本上预定的时间内返回(比如在一个月内)。 不过,我们可能需要找到解决方案。 熊猫正在做的修复也让我有点担心,因为它们使用catch_warnings

如果实在没有办法,就需要线程安全的警告抑制。 np.seterr可能会为它保留一个插槽:/。

我们似乎已经偏离了最初的问题,这似乎是“给定一系列值,matplotlib 如何确定它们是单一颜色还是颜色列表”。

我认为@anntzer提出的问题更普遍。 这是关于编写一个接受多种类型输入的函数,其逻辑如下:

  • 创建ndarray(flexible_input)
  • 如果`new_ndarray.dtype.kind == 'O':处理这个
  • 其他: use_the_array

既然不能将dtype=object到这样的代码中,应该怎么做?

熊猫正在做的修复也让我有点担心,因为它们使用catch_warnings

@seberg不是suppress_warnings更好吗?

@rgommers不, suppress_warnings解决了警告抑制在不应该永久存在的问题。 不过,这已在较新的 python 版本上修复,因此我们不再真正需要它(它具有更好的属性,因为它支持嵌套,但它不支持线程安全。我不确定这在 python 之外是否可行,甚至如果是,那可能是不可取的)

不完全确定有问题的案例是否违背了它们的初衷(https://numpy.org/neps/nep-0034.html),我们只是没有预料到。

无论如何,一种出路是按照“感谢您的关注,但我们明确想要依赖于上下文的对象 dtype 并将自己处理有问题的输入”的方式明确启用旧行为。 像其中之一

~~~
np.array(data, dtype='allow_object')

np.array(数据,allow_object_dtype=True)

使用 np.array_create_allow_object_dtype():
np.array(数据)
~~~

一切都不是很漂亮,命名肯定会得到改进。 但这为依赖行为并希望保留它的库提供了一条干净的出路(至少目前是这样)。

实际上不是 matplotlib 案例:

with np.forbid_ragged_arrays_immediately():
    np.array(data)

因为您真的想捕获错误,而不是获取对象数据类型?

当前等待 master 的弃用没有恢复。 我不认为它应该像 1.18 那样批量恢复,因为这也删除了我认为我们想要保留的修复程序。 @mattip在我们决定长期做什么之前,我们将不胜感激更有针对性的回归。

FWIW 我认为 mpl 中遇到此问题的大多数地方都可以修复(或多或少重组 - 在一种情况下,如果在...之后速度更快,它就会证明代码)。
认为@timhoffm提出的 API 会比with np.forbid_ragged_arrays_immediately:更好,因为后者可以很容易地根据前者编写(如果np.array(..., allow_object=True).dtype == object则提高),而相反( try: with np.forbid: ... except ValueError: ... ) 毕竟如果我们仍然想创建一个对象数组,效率会降低。 但是一个 CM(只是“本地移动超过弃用期”)总比没有好。

(同样,我认为这个改变是好的,只是执​​行方式的问题。)

是的,我们只需要弄清楚 API 应该是什么样子。 正如许多人所指出的,目前有两个主要问题:

  1. 混淆object"allow ragged" 。 如果对象具有合理的类型(例如Decimal ),您实际上希望收到警告/错误,但也可能需要传递dtype=object
  2. 没有办法选择加入新行为继续使用旧行为(没有警告)。 似乎至少 Opt-In 可能是内部使用所必需的,如果我们不提供它,我们基本上假设(可能间接地)只有最终用户遇到这些情况?

最后,我们必须弄清楚如何把它塞进我们的代码中 :)。 ndmin可能是另一个目标,至少可以塞进控制不规则行为的旗帜中。

当前等待 master 的弃用没有恢复。 我不认为它应该像 1.18 那样批量恢复,因为这也删除了我认为我们想要保留的修复程序。 @mattip在我们决定长期做什么之前,我们将不胜感激更有针对性的回归。

我认为完全还原没有问题,然后重新引入现在有意义的任何部分。 同样,恢复某些东西不是对好坏的价值判断,它只是一种实用的方式来解开我们刚刚通过按下合并按钮破坏的一堆东西。 显然存在 NEP 中没有预见到的影响和未解决的问题,因此首先恢复是正确的做法。

尚未恢复的论据 - 虽然更改在 master 中,但我们可以利用下游 CI 运行来尝试找出他们的解决方法的样子

下游 CI 是红色的,这_非常_没有帮助。 我们现在有他们的失败列表,我们不需要保持他们的 CI 的红色来让我们的生活更轻松一些。

并且至少 Matplotlib 的 CI 是针对pip install --pre而不是 master 分支运行的

并且至少 Matplotlib 的 CI 是针对pip install --pre而不是 master 分支运行的

那是从夜间轮子中拉出来的样子。 1.18.0rc1 的更改已经恢复,因此如果您将使用来自 PyPI 的--pre进行安装,您应该不会看到它。

上面的一些评论相当于重新考虑 NEP 34 中提议的更改。我不确定这个线程是否是继续讨论的合适地方,但这里是。 (如果应该在别处讨论也无妨——复制和粘贴评论很容易。:smile:另外,你们中的一些人在关于 slack 的讨论中看到了这些评论的变化。)

最近考虑过这个问题后,我最终得出了与arraydtype参数允许函数通过创建一维对象数组来处理参差不齐的输入。 实际上,这启用了dtype=None的 pre-NEP-34 行为,其中不规则形状的输入会自动转换为对象数组。 如果给出dtype任何其他值(包括Noneobject ),如果输入是参差不齐的形状,则会给出弃用警告。 在 NumPy 的未来版本中,该警告将转换为错误。

我认为现在很明显,使用dtype=object来处理不规则形状的输入并不是解决问题的好方法。 理想情况下,我们会将“对象数组”的概念与“参差不齐的数组”分开。 但是我们不能完全解耦它们,因为当我们想要处理一个参差不齐的数组时,我们唯一的选择就是创建一个对象数组。 另一方面,有时我们想要一个对象数组,但我们不希望不规则形状的输入自动转换为序列的对象数组。

例如(参见@seberg上一条评论中的第 1 项),假设f1f2f3f4Fraction对象,我正在处理Fraction的对象数组。 我对创建一个参差不齐的数组不感兴趣。 如果我不小心写了a = np.array([f1, f2, [f3, f4]], dtype=object) ,我_希望_产生错误,因为我们有 NEP 34 的所有原因。但是,使用 NEP 34,这将创建一个长度为 3 的一维数组。

添加新关键字参数的替代方案,例如@timhoffm的第二个建议,似乎比必要的更复杂。 我们试图解决的问题是“脚枪”,其中参差不齐的输入会自动转换为一维对象数组。 只有当dtype=None传递给array时才会出现问题。 要求用户将dtype=None替换dtype=<special-value-that-enables-ragged-handling>以维持旧的麻烦行为,这是对 API 的简单更改,易于解释。 我们真的需要更多吗?

我认为现在很明显,使用dtype=object来处理不规则形状的输入并不是解决问题的好方法。 理想情况下,我们会将“对象数组”的概念与“参差不齐的数组”分开。

听起来很合理,也许吧。 指出NumPy 中没有真正的“参差不齐的数组”概念也很好。 这是我们基本上不支持的东西(在文档、问题跟踪器或邮件列表中搜索“ragged”以确认您是否需要),这是 DyND 和 XND 支持的东西,我们只是开始谈论简洁短语来讨论“我们想要删除绊倒用户的np.array([1, [2, 3]])行为”。 因此,将“参差不齐的数组”作为一种新的 API 进行烘焙应该非常谨慎,这绝对不是我们想要推广的东西。 因此,最好在我们可能添加的任何dtype=some_workaround的命名中明确说明这一点。

似乎普遍的意见是通过允许np.array(vals, dtype=special)来扩展弃用(可能无限期)的解决方案,这将与 NEP 34 之前的行为类似。我更喜欢单例而不是字符串,因为这意味着库使用可以做special = getattr(np.special, None)和他们的代码将跨版本工作。

现在我们需要决定名称以及它应该公开的位置。 也许never_failguess_dimensions ? 至于在哪里公开它,我不想把它挂在np而是其他一些内部模块上,也许用_来表明它确实是一个私有接口。

我认为前进的道路是修改 NEP 34,然后在邮件列表上公开讨论。

请注意,有一些报告也涉及使用运算符的问题(至少==operator.mod )。 您是打算忽略它,还是以某种方式将该状态存储在数组中?

在几乎所有情况下,可能都知道其中一个操作数是一个 numpy 数组。 因此,应该可以通过手动转换为 numpy 数组来获得明确定义的行为。

有人可以指出operator.mod示例吗?

至于==运算符,我看到的那个是在做类似np.array(vals, dtype=object) == vals where vals=[1, [2, 3]] (解释代码)的事情,所以解决方案是主动创建右侧的数组边。

许多scipy 失败似乎是np.array([0.25, np.array([0.3])]) ,其中将标量和 ndarray 与shape==(1,)将违反维度发现并创建对象数组。 外部参照 gh-15075

有人可以指出operator.mod示例吗?

@jbrockmendel的 Pandas PR 中看到了这operator.mod )。

至于==运算符,我看到的那个是在做类似np.array(vals, dtype=object) == vals where vals=[1, [2, 3]] (解释代码)的操作,所以解决方案是主动创建右侧的数组边。

那时它变成np.array(vals, dtype=object) == np.array(vals, dtype=object) ,所以最好删除测试:)

@mattip写道:

我更喜欢单例而不是字符串,因为这意味着库使用可以做 special = getattr(np.special, None) 并且他们的代码可以跨版本工作。

这对我来说听起来不错。

现在我们需要决定名称以及它应该公开的位置。 也许never_failguess_dimensions ? 至于在哪里公开它,我宁愿不把它挂在 np 上,而是挂在其他一些内部模块上,也许用 _ 表示它确实是一个私有接口。

我目前的工作名称是legacy_auto_dtype ,但可能还有许多其他名称我不会抱怨。

我不确定这个名字应该是私人的。 根据 _private_ 和 _public_ 的任何实际定义,这将是一个 _public_ 对象。 它为用户提供了保留旧行为的方法,例如通过将array(data)重写为array(data, dtype=legacy_auto_dtype) 。 我想更新的 NEP 将解释这就是应该如何修改代码以维护遗留行为(对于那些必须这样做的人)。 如果是这样,该对象肯定不是私有的。 事实上,它似乎是一个将无限期保留在 NumPy 中的公共对象。 但也许我对修改后的 NEP 34 将如何发挥作用的理解是错误的。

同意@WarrenWeckesser对公共/私人的描述; 它要么是公开的,要么不应该被 NumPy 之外的任何人使用。

重新命名:请选择一个描述功能的名称。 像“遗产”这样的东西几乎从来都不是一个好主意。

请选择一个描述功能的名称。

auto_object , auto_dtype , auto ?

想了一会儿...

这个对象有什么作用?

目前,当给 NumPy 一个包含长度与常规 nd 数组不一致的子序列的 Python 对象时,NumPy 将创建一个object数据类型的数组,对象位于发生形状不一致的第一级保留为 Python 对象。 例如, array([[1, 2], [1, 2, 3]])形状(2,)np.array([[1, 2], [3, [99]]])形状(2, 2)等。在 NEP 34 中,我们正在弃用这种行为,因此尝试创建一个具有“参差不齐”输入的数组最终将导致错误,除非明确启用。 我们正在谈论的特殊值启用旧行为。

什么是一个好名字? ragged_as_object ? inconsistent_shapes_as_object ?

那时它变成np.array(vals, dtype=object) == np.array(vals, dtype=object) ,所以最好删除测试:)

嗯,我是在转述。 实际测试更像是my_func(vals) == vals应该变成my_func(vals) == np.array(vals, dtype=object)

我将提议对 NEP 34 进行扩展,以允许 dtype 的特殊值。

请注意,scipy 似乎不需要这个哨兵来通过 scipy/scipy#11310 和 scipy/scipy#11308 的测试

gh-15119 被合并,重新实现了 NEP。 如果没有恢复,我们可以关闭这个问题

我将关闭它,因为我们在 1.19 版本之前没有跟进它。 我至少希望这是因为讨论已经平息,因为所有重大项目都能够找到合理的解决方案来解决它带来的问题。
如果我错了,请纠正我,特别是如果这仍然容易出现 Pandas、matplotlib 等问题。但我想我们会在 1.19.x 候选版本周期中听说过。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

mrava87 picture mrava87  ·  53评论

jakirkham picture jakirkham  ·  55评论

sturlamolden picture sturlamolden  ·  68评论

khinsen picture khinsen  ·  88评论

valentinstn picture valentinstn  ·  61评论