Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/357.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
对Python数字列表进行四舍五入并保持其总和_Python_Numpy_Sum_Rounding - Fatal编程技术网

对Python数字列表进行四舍五入并保持其总和

对Python数字列表进行四舍五入并保持其总和,python,numpy,sum,rounding,Python,Numpy,Sum,Rounding,我在Python中有一个十进制数字列表或数组。我需要将它们四舍五入到小数点后两位,因为这些是货币金额。但是,我需要保持总的和,即原始数组四舍五入到小数点后2位的和必须等于数组中四舍五入元素的和 以下是我目前的代码: myOriginalList = [27226.94982, 193.0595233, 1764.3094, 12625.8607, 26714.67907, 18970.35388, 12725.41407, 23589.93271, 27948.40386, 23767.8326

我在Python中有一个十进制数字列表或数组。我需要将它们四舍五入到小数点后两位,因为这些是货币金额。但是,我需要保持总的和,即原始数组四舍五入到小数点后2位的和必须等于数组中四舍五入元素的和

以下是我目前的代码:

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项,因为它们在四舍五入条目和原始条目之间的差异最大。

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

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
delta_pence=int(np.rint((原始总数-np.sum(myRoundedList))*100))
如果增量便士>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

首先,你不应该使用浮点数来存储钱(而应该使用小数)。但下面我提供了一些非常通用的解决方案-您需要存储、累积和使用舍入中的差异总和。一些详细的(但不是很像Python;-)示例和您的数字:

# 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

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

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

如果您有一个很长的列表,那么上面的方法效率很低,因为它们是O(n*log(n))(对
n
元素进行排序)。如果您很有可能只更改其中的几个(或一个)索引,那么您可以使用堆(如果只有一个地方需要更改,则使用最小/最大值)

我不是一个python程序员,但这里有一个解决方案,考虑到了上述情况(但没有考虑浮点表示的不精确性(其他人已经提到过))

d
是四舍五入之和与四舍五入之和之间的数值差,它告诉我们应该更改四舍五入的位数。如果
d
为零,我们显然无事可做。如果
d
1
-1
则可以使用
min
max
轻松找到最佳位置。对于任意数字,我们可以使用
heapq.nlargest
找到最佳
D=abs(D)
位置

那么,如果
nlargest
可以,为什么会有一个
max
?!因为
min
max
的实现效率要高得多

这样,算法是O(n+D*log(n))

注意:对于堆,您也可以创建一个O(n+D^2*log(D))算法,因为顶级
D
元素应该位于堆的顶级D级,您可以按O(D^2*log(D))步骤对该列表进行排序。如果
n
很大而
D
很小,这可能意味着很多

P>(保留的复议权(因为它是午夜后))

< P>如A所述,考虑PYPI包。但是,它在内部不使用NumPy

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

您可能不应该使用浮点数来表示货币金额。有很多数字,
0.10
float
无法准确表示。您的numpy解决方案有什么问题?如果我理解正确的话,这就是你要寻找的答案…@NPE:这不就是回合的目的吗?我的意思是,数字0.1中的任何不确定性都应该与机器精度相当,对吗?@BenDundee:如果你只使用
float
来存储货币金额,并且意识到这些问题,那么你可能没问题。然而,你开始计算的那一刻就是你打开一大罐蠕虫的那一刻。如果你的前两个数字是
x.0049
y.0001
,你的取整方法会将它们转换为
x.00
y.01
,这似乎不是最好的选择。好吧,你总是可以按照
abs对列表进行排序(n%0.01)
递减,但考虑到舍入可能并不那么重要……在您的示例中,当您有
8*[0.001]+[0.009]
时,您会得到
8*[0.0]+[0.2]
这与您在我的解决方案中指定的错误类型相同。取整就是取整。@palooh这是一个错误,感谢您发现它!如果您现在运行它,结果是
7*[0.00]+2*[0.01]
,这样就可以添加额外的便士,从而最大限度地减少单个取整错误。
>>> from iteround import saferound
>>> saferound([1.0, 2.1, 3.6], places=0)
[1.0, 2.0, 4.0]