如何有效地将 X 模 Y 添加到 numpy 数组中的每个元素?
How can I efficiently add X modulo Y to every element in a numpy array?
假设我有一个非常大的 1 到 180 之间的值数组,这些值在 uint8
的数组中(可以高达 255)。我想为每个值添加 90(模 180):
original = np.array([1, 2, 3, 170, 171, 172], dtype=np.uint8)
modified = (original + 90) % 180
不幸的是,这会产生不正确的结果,因为较大的数字在添加 90 时会在第一步中溢出其 uint8 整数:170 + 90 = 260 大于 255。
# (170 + 90) % 180 is 80, not 4 :(
array([91, 92, 93, 4, 5, 6], dtype=uint8)
我在一个对性能非常敏感的环境中操作,我的输入列表非常大。因此,我想避免将此数组转换为更大的数据类型的代价,并且我想使用高效的操作(例如,避免遍历数组并单独处理每个值)。
我怎样才能做到这一点?
您可以简单地将数组转换为 np.unit16
:
>>> ((original.astype(np.uint16) + 90) % 180).astype(np.uint8)
array([91, 92, 93, 80, 81, 82], dtype=uint8)
如果你只想使用 uint8
来实现这个,你可以只增加那些大于 255-90
的元素:
>>> modified = (original + 90) % 180
>>> modified[original >= 255-90] += 256-180
>>> modified
array([91, 92, 93, 80, 81, 82], dtype=uint8)
import numpy as np
分配一个大小为 (10, 10) 的随机 numpy 整数数组,其中元素的最大值可以为 255。
np_array = np.random.randint(255, size=(10, 10))
对每个元素加 90 并对其取模。
np_array = (np_array + 90) % 180
这是一个有一些数学知识但没有向上转换的 -
def add_with_modulus(original, addval, modulusval=180):
v = original + addval
v[modulusval-original<=addval] += 256-modulusval
return v
用法:add_with_modulus(original, addval=90, modulusval=180)
。
一个非常简单的选择,因为你正在处理 uint8
,就是简单地预先计算数组中每个可能值的结果并使用它:
import numpy as np
original = np.array([1, 2, 3, 170, 171, 172], dtype=np.uint8)
value_map = ((np.arange(256) + 90) % 180).astype(np.uint8)
modified = value_map[original]
print(modified)
# [91 92 93 80 81 82]
这样做的好处是它不会占用超过 256 个元素的任何额外内存 value_map
,并且对于任何更大的数组,您也将节省大部分计算。
运行 对选角的时间基准:
import numpy as np
def add_val_mod_cast(a, val, mod):
return ((a.astype(np.uint16) + val) % mod).astype(np.uint8)
def add_val_mod_map(a, val, mod):
value_map = ((np.arange(256) + val) % mod).astype(np.uint8)
return value_map[a]
np.random.seed(0)
a = np.random.randint(256, size=10_000_000).astype(np.uint8)
val = 90
mod = 180
print((add_val_mod_cast(a, val, mod) == add_val_mod_map(a, val, mod)).all())
# True
%timeit add_val_mod_cast(a, val, mod)
# 72.6 ms ± 2.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit add_val_mod_map(a, val, mod)
# 40.8 ms ± 606 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
假设我有一个非常大的 1 到 180 之间的值数组,这些值在 uint8
的数组中(可以高达 255)。我想为每个值添加 90(模 180):
original = np.array([1, 2, 3, 170, 171, 172], dtype=np.uint8)
modified = (original + 90) % 180
不幸的是,这会产生不正确的结果,因为较大的数字在添加 90 时会在第一步中溢出其 uint8 整数:170 + 90 = 260 大于 255。
# (170 + 90) % 180 is 80, not 4 :(
array([91, 92, 93, 4, 5, 6], dtype=uint8)
我在一个对性能非常敏感的环境中操作,我的输入列表非常大。因此,我想避免将此数组转换为更大的数据类型的代价,并且我想使用高效的操作(例如,避免遍历数组并单独处理每个值)。
我怎样才能做到这一点?
您可以简单地将数组转换为 np.unit16
:
>>> ((original.astype(np.uint16) + 90) % 180).astype(np.uint8)
array([91, 92, 93, 80, 81, 82], dtype=uint8)
如果你只想使用 uint8
来实现这个,你可以只增加那些大于 255-90
的元素:
>>> modified = (original + 90) % 180
>>> modified[original >= 255-90] += 256-180
>>> modified
array([91, 92, 93, 80, 81, 82], dtype=uint8)
import numpy as np
分配一个大小为 (10, 10) 的随机 numpy 整数数组,其中元素的最大值可以为 255。
np_array = np.random.randint(255, size=(10, 10))
对每个元素加 90 并对其取模。
np_array = (np_array + 90) % 180
这是一个有一些数学知识但没有向上转换的 -
def add_with_modulus(original, addval, modulusval=180):
v = original + addval
v[modulusval-original<=addval] += 256-modulusval
return v
用法:add_with_modulus(original, addval=90, modulusval=180)
。
一个非常简单的选择,因为你正在处理 uint8
,就是简单地预先计算数组中每个可能值的结果并使用它:
import numpy as np
original = np.array([1, 2, 3, 170, 171, 172], dtype=np.uint8)
value_map = ((np.arange(256) + 90) % 180).astype(np.uint8)
modified = value_map[original]
print(modified)
# [91 92 93 80 81 82]
这样做的好处是它不会占用超过 256 个元素的任何额外内存 value_map
,并且对于任何更大的数组,您也将节省大部分计算。
运行 对选角的时间基准:
import numpy as np
def add_val_mod_cast(a, val, mod):
return ((a.astype(np.uint16) + val) % mod).astype(np.uint8)
def add_val_mod_map(a, val, mod):
value_map = ((np.arange(256) + val) % mod).astype(np.uint8)
return value_map[a]
np.random.seed(0)
a = np.random.randint(256, size=10_000_000).astype(np.uint8)
val = 90
mod = 180
print((add_val_mod_cast(a, val, mod) == add_val_mod_map(a, val, mod)).all())
# True
%timeit add_val_mod_cast(a, val, mod)
# 72.6 ms ± 2.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit add_val_mod_map(a, val, mod)
# 40.8 ms ± 606 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)