此问题与this one有关。
我有一个函数要矢量化,这是原始函数:
def aspect_good(angle: float, planet1_good: bool, planet2_good: bool):
"""
Decides if the angle represents a good aspect.
NOTE: returns None if the angle doesn't represent an aspect.
"""
if 112 <= angle <= 128 or 52 <= angle <= 68:
return True
elif 174 <= angle <= 186 or 84 <= angle <= 96:
return False
elif 0 <= angle <= 8 and planet1_good and planet2_good:
return True
elif 0 <= angle <= 6:
return False
else:
return None
这是我目前所知道的:
def aspects_good(
angles: npt.ArrayLike,
planets1_good: npt.ArrayLike,
planets2_good: npt.ArrayLike,
) -> npt.NDArray:
"""
Decides if the angles represent good aspects.
Note: this function was contributed by Mad Physicist. Thank you.
https://stackoverflow.com/q/73672739/11004423
:returns: an array with values as follows:
1 – the angle is a good aspect
0 – the angle is a bad aspect
-1 – the angle doesn't represent an aspect
"""
result = np.full_like(angles, -1, dtype=np.int8)
bad_mask = np.abs(angles % 90) <= 6
result[bad_mask] = 0
good_mask = (np.abs(angles - 120) <= 8) |\
(np.abs(angles - 60) <= 8) |\
((np.abs(angles - 4) <= 4) & planets1_good & planets2_good)
result[good_mask] = 1
return result
然而,它并没有像预期的那样工作,我用pytest写了一个测试:
def test_aspects_good():
tests = np.array([
[120, True, False, True],
[60, True, False, True],
[180, True, False, False],
[90, True, False, False],
[129, True, False, -1],
[111, True, False, -1],
[69, True, False, -1],
[51, True, False, -1],
[187, True, False, -1],
[173, True, False, -1],
[97, True, False, -1],
[83, True, False, -1],
[0, True, True, True],
[0, True, False, False],
[0, False, True, False],
[0, False, False, False],
[7, False, False, -1],
[7, True, True, True],
[9, True, True, -1],
])
angles = tests[:, 0]
planets1_good = tests[:, 1]
planets2_good = tests[:, 2]
expected = tests[:, 3]
result = aspects_good(angles, planets1_good, planets2_good)
assert np.array_equal(result, expected)
并且失败,返回False,则数组不同。
这里,我将result
和expected
数组并排组合在一起:
array([[ 1, 1],
│ [ 1, 1],
│ [ 0, 0],
│ [ 0, 0],
│ [-1, -1],
│ [-1, -1],
│ [-1, -1],
│ [-1, -1],
│ [-1, -1],
│ [-1, -1],
│ [-1, -1],
│ [-1, -1],
│ [ 0, 1],
│ [ 0, 0],
│ [ 0, 0],
│ [ 0, 0],
│ [-1, -1],
│ [-1, 1],
│ [-1, -1]])
注意:第一列是result
数组,第二列是expected
。正如你所看到的,它们在两个地方不同。现在的问题是“如何调试这个?”通常我会使用调试器,并逐步通过每个if/elif/else条件。但我不知道如何调试numpy掩码。
1条答案
按热度按时间iqih9akk1#
这个问题似乎是三件事的结合:
您会发现
tests.dtype
是dtype('int64')
还是dtype('int32')
取决于您的体系结构。这意味着包含planet1_good
和planet2_good
的列也是整数,而不是布尔值。1.按位AND(
&
)* 不是 * 逻辑运算符。按位AND运算将返回一个输入类型中最大的结果。特别是
<=
的结果,它 * 是 * 一个布尔值,和一个int
数组,结果将是int
。这意味着你可以做类似np.array([1, 2]) & np.array([True, True])
的事情来得到array([1, 0])
,而不是array([True, False])
。x
,那么x[[True, True]] = 1
将1
赋给x
的两个元素。然而,x[[1, 1]] = 1
只将1
赋给x
的第二个元素。这就是这里发生的事情。
bad_mask
是一个布尔掩码,它的工作原理和你所期望的完全一样。但是,good_mask
与几个整型数组进行AND运算,所以变成了一个包含0和1的整型数组。表达式result[good_mask] = 1
实际上是将result
的第一个和第二个元素赋值为1
,这恰好对应于您的两个测试。剩余的True
结果不能也不会被分配1
。有几种方法可以解决这个问题,按偏好降序列出(我最喜欢的在上面):
1.将所有数组转换为正确类型的numpy数组。现在你的函数不符合接受任何类似数组的约定。如果你为
angles
传入一个列表,你将得到TypeError: unsupported operand type(s) for %: 'list' and 'int'
。这是一个相当惯用的方法:1.在应用
good_mask
之前,请确保它实际上是一个掩码。您仍然应该转换angles
,但其他数组将由&
运算符自动转换:您也可以执行类似于
bad_mask
的操作:1.将掩码转换为索引,它不关心原始的dtype: