numpy 舍入Python数字列表并保持其总和

mzillmmw  于 2022-11-24  发布在  Python
关注(0)|答案(5)|浏览(140)

我在Python中有一个十进制数的列表或数组。我需要将它们四舍五入到最接近的两位小数,因为这些是货币数量。但是,我需要保持总的和,即原始数组四舍五入到两位小数的和必须等于数组中四舍五入元素的和。
下面是我目前的代码:

myOriginalList = [27226.94982, 193.0595233, 1764.3094, 12625.8607, 26714.67907, 18970.35388, 12725.41407, 23589.93271, 27948.40386, 23767.83261, 12449.81318]
originalTotal = round(sum(myOriginalList), 2)
# Answer = 187976.61

# Using numpy
myRoundedList = numpy.array(myOriginalList).round(2)
# New Array = [ 27226.95    193.06   1764.31  12625.86  26714.68  18970.35  12725.41 23589.93 27948.4   23767.83  12449.81]

newTotal = myRoundedList.sum()
# Answer = 187976.59

我需要一种有效的方法来修改我的新舍入数组,以使总和也是187976.61。2便士的差异需要应用于第7项和第6项,因为这两项在舍入后的条目和原始条目之间存在最大差异。

j2qf4p5b

j2qf4p5b1#

正如answer by kettlehell中所提到的,考虑一下PyPI包iteround。但是,它并没有在内部使用NumPy。

>>> from iteround import saferound
>>> saferound([1.0, 2.1, 3.6], places=0)
[1.0, 2.0, 4.0]
qacovj5a

qacovj5a2#

第一步是计算期望结果与实际总和之间的误差:

>>> error = originalTotal - sum(myRoundedList)
>>> error
0.01999999996041879

这个值可以是正的,也可以是负的。由于myRoundedList中的每一项都在实际值的0.005范围内,所以这个误差将小于原始数组中每一项的0.01。您可以简单地除以0.01,然后四舍五入,得到必须调整的项的数量:

>>> n = int(round(error / 0.01))
>>> n
2

现在剩下的就是选择要调整的项。最佳结果来自于调整那些最接近边界的值。您可以通过按原始值和舍入值之间的差进行排序来找到这些值。

>>> myNewList = myRoundedList[:]
>>> for _,i in sorted(((myOriginalList[i] - myRoundedList[i], i) for i in range(len(myOriginalList))), reverse=n>0)[:abs(n)]:
    myNewList[i] += math.copysign(0.01, n)

>>> myRoundedList
[27226.95, 193.06, 1764.31, 12625.86, 26714.68, 18970.35, 12725.41, 23589.93, 27948.4, 23767.83, 12449.81]
>>> myNewList
[27226.95, 193.06, 1764.31, 12625.86, 26714.68, 18970.359999999997, 12725.42, 23589.93, 27948.4, 23767.83, 12449.81]
>>> sum(myNewList)
187976.61
uyto3xhc

uyto3xhc3#

首先,你不应该用浮点数来存储货币(用小数来代替)。但是下面我提供了一些非常通用的解决方案--你需要存储、累加和使用舍入中的差值之和。- )数字示例:

# define your accuracy
decimal_positions = 2

numbers = [27226.94982, 193.0595233, 1764.3094, 12625.8607, 26714.67907, 18970.35388, 12725.41407, 23589.93271, 27948.40386, 23767.83261, 12449.81318]
print round(sum(numbers),decimal_positions)
>>> 187976.61

new_numbers = list()
rest = 0.0
for n in numbers:
    new_n = round(n + rest,decimal_positions)
    rest += n - new_n
    new_numbers.append( new_n )

print sum(new_numbers)
>>> 187976.61
hc2pp10m

hc2pp10m4#

关于使用浮点数的所有注意事项:

delta_pence = int(np.rint((originalTotal - np.sum(myRoundedList))*100))
if delta_pence > 0:
    idx = np.argsort(myOriginalList - myRoundedList)[-delta_pence:]
    myRoundedList[idx] += 0.01
elif delta_pence < 0:
    idx = np.argsort(myOriginalList - myRoundedList)[:delta_pence]
    myRoundedList[idx] -= 0.01

>>> myRoundedList.sum()
187976.60999999999
fykwrbwg

fykwrbwg5#

我迟到了,但这里有一个通用的解决方案,与numpy一起工作,我发现它比本页提出的其他实现快100倍左右:

def round_retain_sum(x):
    x = x*100 # We want 2 decimal precision
    N = np.round(np.sum(x)).astype(int)
    y = x.astype(int)
    M = np.sum(y)
    K = N - M 
    z = y-x 
    if K!=0:
        idx = np.argpartition(z,K)[:K]
        y[idx] += 1     
    return y/100.

相关问题