【发布时间】:2022-01-24 16:10:36
【问题描述】:
假设如下代码:
import numpy as np
import pandas as pd
df = pd.DataFrame({'A': np.random.randint(0, 10, 10)})
df['B'] = df['A'].diff()
x, x_diff = 1, df['B'].iloc[1:]
df['C'] = np.r_[x, x_diff].cumsum()
A B C
# 0 6 NaN 1.0
# 1 6 0.0 1.0
# 2 0 -6.0 -5.0
# 3 7 7.0 2.0
# 4 5 -2.0 0.0
# 5 3 -2.0 -2.0
# 6 3 0.0 -2.0
# 7 8 5.0 3.0
# 8 8 0.0 3.0
# 9 8 0.0 3.0
C 列的变化如预期的那样漂亮。这似乎没有问题。但是,当我使用十进制数字时,它们会四舍五入为 0,而我最终的起始值根本没有改变。任何想法如何防止这种情况?理论上我可以再次将数字相乘,但有没有更好的方法来解决这个问题?问题演示如下:
import pandas as pd
import numpy as np
df = pd.DataFrame({'A': np.random.randint(0, 10, 10)})/100000000000000
df['B'] = df['A'].diff()
x, x_diff = 1, df['B'].iloc[1:]
df['C'] = np.r_[x, x_diff].cumsum()
# A B C
# 0 9.000000e-14 NaN 1.0
# 1 7.000000e-14 -2.000000e-14 1.0
# 2 1.000000e-14 -6.000000e-14 1.0
# 3 9.000000e-14 8.000000e-14 1.0
# 4 9.000000e-14 0.000000e+00 1.0
# 5 4.000000e-14 -5.000000e-14 1.0
# 6 6.000000e-14 2.000000e-14 1.0
# 7 9.000000e-14 3.000000e-14 1.0
# 8 7.000000e-14 -2.000000e-14 1.0
# 9 0.000000e+00 -7.000000e-14 1.0
【问题讨论】:
-
这不是十进制数字的问题,而是数字太小的问题。计算机有效地将它们视为零,因为您可以在计算机中处理的数量级差异很小。你所能做的就是让 C 从 0 开始而不是 1,如果这适用于你的用例。
-
您可以通过查询您的machine epsilon 来检查是否是这种情况:
eps = np.finfo(float).eps。你应该得到1+eps >1,但是添加任何小于eps的东西应该是1。你需要用你实际使用的任何数据类型替换float(例如,numpy.float32的eps约为10^- 7) -
如果我在 64 位实现上使用您的代码并将精度设置为 16
pd.set_option('precision', 16)则列B可以看到cumsum到列C。或者正如@Puff 所说,从零开始列C从零开始。另一种显示方式是添加df['D'] = df['C'] - 1