将数组四舍五入为另一个数组中给定的值
Rounding an array to values given in another array
假设我有一个数组:
values = np.array([1.1,2.2,3.3,4.4,2.1,8.4])
我想将这些值四舍五入为任意数组的成员,例如:
rounds = np.array([1.,3.5,5.1,6.7,9.2])
理想情况下返回一个四舍五入的数字数组和一个残数数组:
rounded = np.array([1.,1.,3.5,5.1,1.,9.2])
residues = np.array([-0.1,-1.2,0.2,0.7,-1.1,0.6])
有没有好的 pythonic 方法来做到这一点?
找到最接近的x轮数:
def findClosest(x,rounds):
return rounds[np.argmin(np.absolute(rounds-x))]
遍历所有值:
rounded = [findClosest(x,rounds) for x in values]
residues = values - rounded
这是一种简单的方法,但使用有序的回合数组可以提高效率。
def findClosest(x,rounds):
for n in range(len(rounds)):
if x > rounds[n]:
if n == 0:
return rounds[n]
elif rounds[n]-x > x-rounds[n-1]:
return rounds[n-1]
else:
return rounds[n]
return rounds[-1]
这可能比 argmin 方法快,但不一定比 argmin 方法快,因为您在 python for 循环中浪费了时间,但您不必检查整个回合数组。
一个选项是:
>>> x = np.subtract.outer(values, rounds)
>>> y = np.argmin(abs(x), axis=1)
然后rounded
和residues
分别是:
>>> rounds[y]
array([ 1. , 1. , 3.5, 5.1, 1. , 9.2])
>>> rounds[y] - values
array([-0.1, -1.2, 0.2, 0.7, -1.1, 0.8])
本质上 x
是 values
中的每个值减去 rounds
中的每个值的二维数组。 y
是x
每一行绝对值最小的索引的一维数组。这个 y
然后用于索引 rounds
.
我应该警告这个答案,注意如果 len(values) * len(rounds)
很大(例如开始超过 10e8
),内存使用可能会开始成为问题。在这种情况下,您可以考虑迭代地构建 y
,以避免必须将大块内存分配给 x
。
由于 rounds
数组中的项目已排序(或者如果未对它们排序),我们可以使用 numpy.searchsorted
:
O(n logn)
时间
from functools import partial
def closest(rounds, x):
ind = np.searchsorted(rounds, x, side='right')
length = len(rounds)
if ind in (0, length) :
return rounds[ind]
else:
left, right = rounds[ind-1], rounds[ind]
val = min((left, right), key=lambda y:abs(x-y))
return val
f = partial(closest, rounds)
rounded = np.apply_along_axis(f, 1, values[:,None])[:,0]
residues = rounded - values
print repr(rounded)
print repr(residues)
输出:
array([ 1. , 1. , 3.5, 5.1, 1. , 9.2])
array([-0.1, -1.2, 0.2, 0.7, -1.1, 0.8])
选择的答案已经很棒了。对于那些不一定习惯于更复杂的列表理解的人来说,这可能看起来很复杂,但如果你熟悉它,它实际上很清楚 (IMO)。
(有趣的是,这恰好 运行 比选择的答案快。为什么 numPy 版本会比这个慢?嗯...)
values = np.array([1.1,2.2,3.3,4.4,2.1,8.4])
rounds = np.array([1.,3.5,5.1,6.7,9.2])
rounded, residues = zip(*[
[
(rounds[cIndex]),
(dists[cIndex])
]
for v in values
for dists in [[r-v for r in rounds]]
for absDists in [[abs(d) for d in dists]]
for cIndex in [absDists.index(min(absDists))]
])
print np.array(rounded)
print np.array(residues)
时间复杂度与 Ashwini Chaudhary 的答案相同,但完全矢量化:
def round_to(rounds, values):
# The main speed is in this line
I = np.searchsorted(rounds, values)
# Pad so that we can index easier
rounds_p = np.pad(rounds, 1, mode='edge')
# We have to decide between I and I+1
rounded = np.vstack([rounds_p[I], rounds_p[I+1]])
residues = rounded - values
J = np.argmin(np.abs(residues), axis=0)
K = np.arange(len(values))
return rounded[J,K], residues[J,K]
假设我有一个数组:
values = np.array([1.1,2.2,3.3,4.4,2.1,8.4])
我想将这些值四舍五入为任意数组的成员,例如:
rounds = np.array([1.,3.5,5.1,6.7,9.2])
理想情况下返回一个四舍五入的数字数组和一个残数数组:
rounded = np.array([1.,1.,3.5,5.1,1.,9.2])
residues = np.array([-0.1,-1.2,0.2,0.7,-1.1,0.6])
有没有好的 pythonic 方法来做到这一点?
找到最接近的x轮数:
def findClosest(x,rounds):
return rounds[np.argmin(np.absolute(rounds-x))]
遍历所有值:
rounded = [findClosest(x,rounds) for x in values]
residues = values - rounded
这是一种简单的方法,但使用有序的回合数组可以提高效率。
def findClosest(x,rounds):
for n in range(len(rounds)):
if x > rounds[n]:
if n == 0:
return rounds[n]
elif rounds[n]-x > x-rounds[n-1]:
return rounds[n-1]
else:
return rounds[n]
return rounds[-1]
这可能比 argmin 方法快,但不一定比 argmin 方法快,因为您在 python for 循环中浪费了时间,但您不必检查整个回合数组。
一个选项是:
>>> x = np.subtract.outer(values, rounds)
>>> y = np.argmin(abs(x), axis=1)
然后rounded
和residues
分别是:
>>> rounds[y]
array([ 1. , 1. , 3.5, 5.1, 1. , 9.2])
>>> rounds[y] - values
array([-0.1, -1.2, 0.2, 0.7, -1.1, 0.8])
本质上 x
是 values
中的每个值减去 rounds
中的每个值的二维数组。 y
是x
每一行绝对值最小的索引的一维数组。这个 y
然后用于索引 rounds
.
我应该警告这个答案,注意如果 len(values) * len(rounds)
很大(例如开始超过 10e8
),内存使用可能会开始成为问题。在这种情况下,您可以考虑迭代地构建 y
,以避免必须将大块内存分配给 x
。
由于 rounds
数组中的项目已排序(或者如果未对它们排序),我们可以使用 numpy.searchsorted
:
O(n logn)
时间
from functools import partial
def closest(rounds, x):
ind = np.searchsorted(rounds, x, side='right')
length = len(rounds)
if ind in (0, length) :
return rounds[ind]
else:
left, right = rounds[ind-1], rounds[ind]
val = min((left, right), key=lambda y:abs(x-y))
return val
f = partial(closest, rounds)
rounded = np.apply_along_axis(f, 1, values[:,None])[:,0]
residues = rounded - values
print repr(rounded)
print repr(residues)
输出:
array([ 1. , 1. , 3.5, 5.1, 1. , 9.2])
array([-0.1, -1.2, 0.2, 0.7, -1.1, 0.8])
选择的答案已经很棒了。对于那些不一定习惯于更复杂的列表理解的人来说,这可能看起来很复杂,但如果你熟悉它,它实际上很清楚 (IMO)。
(有趣的是,这恰好 运行 比选择的答案快。为什么 numPy 版本会比这个慢?嗯...)
values = np.array([1.1,2.2,3.3,4.4,2.1,8.4])
rounds = np.array([1.,3.5,5.1,6.7,9.2])
rounded, residues = zip(*[
[
(rounds[cIndex]),
(dists[cIndex])
]
for v in values
for dists in [[r-v for r in rounds]]
for absDists in [[abs(d) for d in dists]]
for cIndex in [absDists.index(min(absDists))]
])
print np.array(rounded)
print np.array(residues)
时间复杂度与 Ashwini Chaudhary 的答案相同,但完全矢量化:
def round_to(rounds, values):
# The main speed is in this line
I = np.searchsorted(rounds, values)
# Pad so that we can index easier
rounds_p = np.pad(rounds, 1, mode='edge')
# We have to decide between I and I+1
rounded = np.vstack([rounds_p[I], rounds_p[I+1]])
residues = rounded - values
J = np.argmin(np.abs(residues), axis=0)
K = np.arange(len(values))
return rounded[J,K], residues[J,K]