为什么numpy中没有'is' ufunc?
Why is there no 'is' ufunc in numpy?
我一定可以
a[a == 0] = something
将 a
中等于零的每个条目设置为 something
。等价地,我可以写
a[np.equal(a, 0)] = something
现在,假设 a
是 dtype=object
的数组。我不能写 a[a is None]
,因为 a
本身当然不是 None
。意图很明确:我希望比较 is
像任何其他 ufunc 一样被广播。 list from the docs 列出的内容与 is
-unfunc.
完全不同
为什么会有 none,而且对我来说更有趣的是:什么是高性能替代品?
这里有两件事在起作用。
第一个(也是更重要的)一个是 is
直接在 Python 解释器中实现,没有重定向到 dunder 方法的选项。与许多其他对象一样,Numpy 数组具有实现 ==
操作的 __eq__
方法。 a is None
大致被视为 id(a) == id(None)
,在任何情况下都无法追索元素实现。这就是 python 的工作原理。
第二个方面是 numpy 从根本上是为存储数字而设计的。对象数组是将对对象的引用存储为数字的特殊情况。这似乎与列表存储对象引用的方式相同,但仅在处理引用时才相似。例如,列表的元素始终是对对象的引用,即使列表包含齐次整数也是如此。 dtype int
的 numpy 数组 不 包含 python 个对象。数组的每个连续元素都是原始二进制整数,而不是对 python 对象包装器的引用。即使 python 允许您覆盖 is
运算符,按元素应用也是没有意义的。
所以如果你想比较对象,使用python列表:
mylist = [...]
mylist = [something if x is None else x for x in mylist]
如果您坚持使用 numpy 数组,要么 (a) 使用数字数组并用其他东西标记 None
元素,例如 np.nan
,要么 (b) 将数组视为列表.您将必须将 id
或 is
应用于每个元素,它们是 python 构造,因此此时没有“性能”方式来执行此操作,或者 (c) 仅使用==
,会触发python级的相等比较,对于单例None
.
相当于is
除了像 reshape
这样的操作和不依赖于 dtype
的索引(itemsize 除外),对象 dtype 数组上的操作以列表理解速度执行,迭代元素并对每个元素应用适当的方法。有时该方法不存在,例如在执行 np.sin
.
时
为了说明,请考虑来自评论之一的数组:
In [132]: a = np.array([1, None, 0, np.nan, ''])
In [133]: a
Out[133]: array([1, None, 0, nan, ''], dtype=object)
对象数组测试:
In [134]: a==None
Out[134]: array([False, True, False, False, False])
In [135]: timeit a==None
5.16 µs ± 73.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
一个等价的理解:
In [136]: [x is None for x in a]
Out[136]: [False, True, False, False, False]
In [137]: timeit [x is None for x in a]
1.52 µs ± 18.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
它更快,即使我们将结果转换回数组(这不是一个便宜的步骤):
In [138]: timeit np.array([x is None for x in a])
4.67 µs ± 95.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
数组列表版本的迭代速度更快:
In [139]: timeit np.array([x is None for x in a.tolist()])
2.52 µs ± 48.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
让我们看看完整的赋值动作:
In [141]: a[[x is None for x in a.tolist()]]
Out[141]: array([None], dtype=object)
In [142]: %%timeit a1=a.copy()
...: a1[[x is None for x in a1.tolist()]] = np.nan
...:
...:
4.03 µs ± 10 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [143]: %%timeit a1=a.copy()
...: a1[a1==None] = np.nan
...:
...:
6.18 µs ± 28.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
通常的注意事项可能会有所不同。
我一定可以
a[a == 0] = something
将 a
中等于零的每个条目设置为 something
。等价地,我可以写
a[np.equal(a, 0)] = something
现在,假设 a
是 dtype=object
的数组。我不能写 a[a is None]
,因为 a
本身当然不是 None
。意图很明确:我希望比较 is
像任何其他 ufunc 一样被广播。 list from the docs 列出的内容与 is
-unfunc.
为什么会有 none,而且对我来说更有趣的是:什么是高性能替代品?
这里有两件事在起作用。
第一个(也是更重要的)一个是 is
直接在 Python 解释器中实现,没有重定向到 dunder 方法的选项。与许多其他对象一样,Numpy 数组具有实现 ==
操作的 __eq__
方法。 a is None
大致被视为 id(a) == id(None)
,在任何情况下都无法追索元素实现。这就是 python 的工作原理。
第二个方面是 numpy 从根本上是为存储数字而设计的。对象数组是将对对象的引用存储为数字的特殊情况。这似乎与列表存储对象引用的方式相同,但仅在处理引用时才相似。例如,列表的元素始终是对对象的引用,即使列表包含齐次整数也是如此。 dtype int
的 numpy 数组 不 包含 python 个对象。数组的每个连续元素都是原始二进制整数,而不是对 python 对象包装器的引用。即使 python 允许您覆盖 is
运算符,按元素应用也是没有意义的。
所以如果你想比较对象,使用python列表:
mylist = [...]
mylist = [something if x is None else x for x in mylist]
如果您坚持使用 numpy 数组,要么 (a) 使用数字数组并用其他东西标记 None
元素,例如 np.nan
,要么 (b) 将数组视为列表.您将必须将 id
或 is
应用于每个元素,它们是 python 构造,因此此时没有“性能”方式来执行此操作,或者 (c) 仅使用==
,会触发python级的相等比较,对于单例None
.
is
除了像 reshape
这样的操作和不依赖于 dtype
的索引(itemsize 除外),对象 dtype 数组上的操作以列表理解速度执行,迭代元素并对每个元素应用适当的方法。有时该方法不存在,例如在执行 np.sin
.
为了说明,请考虑来自评论之一的数组:
In [132]: a = np.array([1, None, 0, np.nan, ''])
In [133]: a
Out[133]: array([1, None, 0, nan, ''], dtype=object)
对象数组测试:
In [134]: a==None
Out[134]: array([False, True, False, False, False])
In [135]: timeit a==None
5.16 µs ± 73.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
一个等价的理解:
In [136]: [x is None for x in a]
Out[136]: [False, True, False, False, False]
In [137]: timeit [x is None for x in a]
1.52 µs ± 18.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
它更快,即使我们将结果转换回数组(这不是一个便宜的步骤):
In [138]: timeit np.array([x is None for x in a])
4.67 µs ± 95.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
数组列表版本的迭代速度更快:
In [139]: timeit np.array([x is None for x in a.tolist()])
2.52 µs ± 48.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
让我们看看完整的赋值动作:
In [141]: a[[x is None for x in a.tolist()]]
Out[141]: array([None], dtype=object)
In [142]: %%timeit a1=a.copy()
...: a1[[x is None for x in a1.tolist()]] = np.nan
...:
...:
4.03 µs ± 10 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [143]: %%timeit a1=a.copy()
...: a1[a1==None] = np.nan
...:
...:
6.18 µs ± 28.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
通常的注意事项可能会有所不同。