简体   繁体   English

通过调整负条目来限制 numpy 数组中的条目总和

[英]Limit sum of entries in numpy array by adjusting negative entries

I have a numpy array containing positive and negative values, and I want to adjust the negative entries so that the sum is not negative, starting with the most negative entry.我有一个包含正值和负值的 numpy 数组,我想调整负条目,使总和不为负,从最负的条目开始。 The maximum adjustment is to make a negative entry zero.最大的调整是使负条目为零。 I have an implementation using a loop, is there a way to do it using numpy array methods?我有一个使用循环的实现,有没有办法使用 numpy 数组方法来实现? Here is my code:这是我的代码:

initial_values = np.asarray([50,-200,-180,110])
sorted_index = np.argsort(initial_values)

final_values = initial_values
for i, entry in enumerate(final_values[sorted_index]):
    ss = final_values.sum()
    if ss >= 0:
        break
    adjustment = max(entry, ss)
    final_values[sorted_index[i]] -= adjustment

print final_values

The starting array is [50,-200,-180,110], the answer in this case is [50, 0, -160, 110], so the most negative entry is set to zero, and then the next most negative entry is adjusted to make the sum zero.起始数组是[50,-200,-180,110],本例的答案是[50, 0, -160, 110],所以最负的条目设置为零,然后调整下一个最负的条目使总和为零。

Does anyone have a simpler, faster numpy based solution?有没有人有更简单、更快的基于 numpy 的解决方案?

Here's one vectorized approach -这是一种矢量化方法 -

# Get a copy of input as the output
out = initial_values.copy()

# Get sorted indices
sorted_index = np.argsort(out)

# Mask of elements that would be made zero for sure and zero them
mask = out.sum() < out[sorted_index].cumsum()
out[sorted_index[mask]] = 0

# There might be one element left to make the sum absolutely zero. 
# Make it less negative to make the absolute sum zero.
out[sorted_index[np.where(mask)[0][-1]+1]] -= out.sum()

Sample run -样品运行 -

Function definitions -函数定义 -

In [155]: def vectorized(initial_values):
     ...:   out = initial_values.copy()
     ...:   sorted_index = np.argsort(out)
     ...:   mask = out.sum() < out[sorted_index].cumsum()
     ...:   out[sorted_index[mask]] = 0
     ...:   out[sorted_index[np.where(mask)[0][-1]+1]] -= out.sum()
     ...:   return out
     ...: 
     ...: def org_app(initial_values):  
     ...:   final_values = initial_values.copy()
     ...:   sorted_index = np.argsort(initial_values)
     ...:   for i, entry in enumerate(final_values[sorted_index]):
     ...:       ss = final_values.sum()
     ...:       if ss >= 0:
     ...:           break
     ...:       adjustment = max(entry, ss)
     ...:       final_values[sorted_index[i]] -= adjustment
     ...:   return final_values
     ...: 

Case #1 :情况1 :

In [156]: initial_values
Out[156]: array([  50, -200, -180,  110])

In [157]: vectorized(initial_values)
Out[157]: array([  50,    0, -160,  110])

In [158]: org_app(initial_values)
Out[158]: array([  50,    0, -160,  110])

Case #2 :案例#2:

In [163]: initial_values
Out[163]: array([ 50, -20, -14, -22, -15,   6, -21, -19, -17,   4,   5, -56])

In [164]: vectorized(initial_values)
Out[164]: array([ 50,   0, -14,   0, -15,   6,   0, -19, -17,   4,   5,   0])

In [165]: org_app(initial_values)
Out[165]: array([ 50,   0, -14,   0, -15,   6,   0, -19, -17,   4,   5,   0])

Runtime tests -运行时测试 -

In [177]: initial_values = np.random.randint(-100,20,(50000))

In [178]: np.array_equal(vectorized(initial_values),org_app(initial_values))
Out[178]: True

In [179]: %timeit org_app(initial_values)
1 loops, best of 3: 2.08 s per loop

In [180]: %timeit vectorized(initial_values)
100 loops, best of 3: 5.7 ms per loop

Here's a slightly improved (lesser code and better runtime) version of the earlier proposed approach -这是较早提出的方法的略微改进(更少的代码和更好的运行时)版本 -

# Get a copy of input as the output
out = initial_values.copy()

# Get sorted indices
sorted_index = np.argsort(out)

# Last index in sorted indexed indices for setting elements in input array to 0's
idx = np.where(out.sum() < out[sorted_index].cumsum())[0][-1]

# Set until idx indexed into sorted_index in turn indexed into input array t0 0's
out[sorted_index[:idx+1]] = 0

# There might be one element left to make the sum absolutely zero. 
# Make it less negative to make the absolute sum zero.
out[sorted_index[idx+1]] -= out.sum()

Runtime tests -运行时测试 -

In [18]: initial_values = np.random.randint(-100,20,(50000))

In [19]: %timeit vectorized(initial_values)
100 loops, best of 3: 5.58 ms per loop

In [20]: %timeit vectorized_v2(initial_values) # improved version
100 loops, best of 3: 5.4 ms per loop

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM