2015-09-25 25 views
2

我有一個包含正值和負值的numpy數組,並且我想調整負值條目以使得總和不是負數,從最負的條目開始。最大的調整是使負項爲零。我有一個使用循環的實現,有沒有辦法使用numpy數組方法做到這一點?這是我的代碼:通過調整負數條目來限制numpy數組中的條目總數

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 

起始陣列[50,-200,-180,110],在這種情況下,答案是[50,0,-160,110],所以最負項設置歸零,然後調整下一個最負的入口以使和爲零。

有沒有人有更簡單,更快的基於numpy的解決方案?

回答

2

這裏有一個量化的方法 -

# 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() 

採樣運行 -

功能定義 -

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 
    ...: 

案例#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]) 

案例#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]) 

運行測試 -

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 

這裏有一個略有改善(較少的代碼和更好的運行時)早前提出的方法的版本 -

# 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() 

運行測試 -

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