将 += 与 numpy.array 和 numpy.ma.array 一起使用的奇怪行为
Odd behavior of using += with numpy.array and numpy.ma.array
任何人都可以向我解释以下结果吗?
我知道这不是通常执行此操作的方式,但我发现此结果很奇怪。
import numpy as np
a = np.ma.masked_where(np.arange(20)>10,np.arange(20))
b = np.ma.masked_where(np.arange(20)>-1,np.arange(20))
c = np.zeros(a.shape)
d = np.zeros(a.shape)
c[~a.mask] += b[~a.mask]
print(b[~a.mask])
#masked_array(data=[--, --, --, --, --, --, --, --,--, --, --],
# mask=[ True, True, True, True, True, True, True, True, True, True, True],
# fill_value=999999,
# dtype=int64)
print(c)
#[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
d[~a.mask] = d[~a.mask] + b[~a.mask]
print(d)
#[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
我预计 c
不会改变,但我想这里发生了一些与内存中的对象相关的事情。此外,+=
保留原始对象,而 =
和 +
创建新的 d
.
我只是不太明白添加到 c
的数据来自哪里。
我将从一个更简单的示例开始,以便更好地理解:
b = np.ma.masked_where(np.arange(20)>-1,np.arange(20))
#b: [-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --]
#b.data: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
c = np.zeros(b.shape)
#c: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
d = np.zeros(b.shape)
#d: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
c += b
#c: [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]
d = d + b
#d: [-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --]
#d.data: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
第一个操作 c += b
是一个 in-place 操作。换句话说,它等同于 c = type(c).__iadd__(c, b)
根据 c
的类型进行加法运算,而 c
不是掩码数组,因此 b
的数据用作未掩码。
另一方面,d = d + b
等同于 d = np.MaskedArray.__add__(d, b)
(更具体地说,由于掩码数组是 ndarrays 的子类,它使用 __radd__
)并且是 不是 in-place 赋值。这意味着它创建一个新对象并在添加时使用等式右侧的更宽类型,因此将 d(这是一个未屏蔽数组)转换为屏蔽数组(因为 b
是一个屏蔽数组),因此,加法仅使用有效值(在本例中有 none,因为 b
的所有元素都被屏蔽且无效)。这导致掩码数组 d
与 b
具有相同的掩码,而 d
的数据保持不变。
这种行为差异不是 Numpy 特有的,也适用于 python 本身。 OP 问题中提到的案例具有类似的行为,正如@alaniwi 在评论中提到的那样,带有掩码 a
的布尔索引并不是该行为的基础。使用 a
来屏蔽 b
、c
和 d
的元素只是通过 a
限制对屏蔽元素的赋值(而不是数组的所有元素)仅此而已。
为了让事情更有趣,实际上更清楚,让我们交换 b
和 d
在右侧的位置:
e = np.zeros(b.shape)
#e: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
e = b + e
#e: [-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --]
#e.data: [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]
请注意,与 d = d + b
类似,右侧使用掩码数组 __add__
函数,因此输出是一个掩码数组,但由于您将 e
添加到 b
(a.k.a e = np.MaskedArray.__add__(b, e)
),返回 b
的屏蔽数据,而在 d = d + b
中,您将 b
添加到 d
] 返回 d
的数据。
任何人都可以向我解释以下结果吗? 我知道这不是通常执行此操作的方式,但我发现此结果很奇怪。
import numpy as np
a = np.ma.masked_where(np.arange(20)>10,np.arange(20))
b = np.ma.masked_where(np.arange(20)>-1,np.arange(20))
c = np.zeros(a.shape)
d = np.zeros(a.shape)
c[~a.mask] += b[~a.mask]
print(b[~a.mask])
#masked_array(data=[--, --, --, --, --, --, --, --,--, --, --],
# mask=[ True, True, True, True, True, True, True, True, True, True, True],
# fill_value=999999,
# dtype=int64)
print(c)
#[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
d[~a.mask] = d[~a.mask] + b[~a.mask]
print(d)
#[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
我预计 c
不会改变,但我想这里发生了一些与内存中的对象相关的事情。此外,+=
保留原始对象,而 =
和 +
创建新的 d
.
我只是不太明白添加到 c
的数据来自哪里。
我将从一个更简单的示例开始,以便更好地理解:
b = np.ma.masked_where(np.arange(20)>-1,np.arange(20))
#b: [-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --]
#b.data: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
c = np.zeros(b.shape)
#c: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
d = np.zeros(b.shape)
#d: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
c += b
#c: [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]
d = d + b
#d: [-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --]
#d.data: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
第一个操作 c += b
是一个 in-place 操作。换句话说,它等同于 c = type(c).__iadd__(c, b)
根据 c
的类型进行加法运算,而 c
不是掩码数组,因此 b
的数据用作未掩码。
另一方面,d = d + b
等同于 d = np.MaskedArray.__add__(d, b)
(更具体地说,由于掩码数组是 ndarrays 的子类,它使用 __radd__
)并且是 不是 in-place 赋值。这意味着它创建一个新对象并在添加时使用等式右侧的更宽类型,因此将 d(这是一个未屏蔽数组)转换为屏蔽数组(因为 b
是一个屏蔽数组),因此,加法仅使用有效值(在本例中有 none,因为 b
的所有元素都被屏蔽且无效)。这导致掩码数组 d
与 b
具有相同的掩码,而 d
的数据保持不变。
这种行为差异不是 Numpy 特有的,也适用于 python 本身。 OP 问题中提到的案例具有类似的行为,正如@alaniwi 在评论中提到的那样,带有掩码 a
的布尔索引并不是该行为的基础。使用 a
来屏蔽 b
、c
和 d
的元素只是通过 a
限制对屏蔽元素的赋值(而不是数组的所有元素)仅此而已。
为了让事情更有趣,实际上更清楚,让我们交换 b
和 d
在右侧的位置:
e = np.zeros(b.shape)
#e: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
e = b + e
#e: [-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --]
#e.data: [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]
请注意,与 d = d + b
类似,右侧使用掩码数组 __add__
函数,因此输出是一个掩码数组,但由于您将 e
添加到 b
(a.k.a e = np.MaskedArray.__add__(b, e)
),返回 b
的屏蔽数据,而在 d = d + b
中,您将 b
添加到 d
] 返回 d
的数据。