Pandas read_csv()丢失数字精度



问题

我有一个包含大整数值的csv文件,我想对其执行一些算术运算,这些字段可能包含nan值,现在,当我使用pandasto_csv方法将这些值加载到df中时,当不存在nan值时,这些值被加载为"int",精度似乎是正确的,但当存在nan时,这些被转换为"float",我看到了精度的损失。

示例csv文件->

,epoch_1,epoch_2
0,1665045912937687151,1665045912937689151
1,,

加载后->

[1] df = pd.read_csv('sample.csv', index_col=0)
[2] df
epoch_1       epoch_2
0  1.665046e+18  1.665046e+18
1           NaN           NaN
[3] df['diff'] = df['epoch_2'] - df['epoch_1']
[4] df
epoch_1       epoch_2    diff
0  1.665046e+18  1.665046e+18  2048.0
1           NaN           NaN     NaN

正如您所看到的,第三列的值不正确,正确的值应该是2000。

如果没有nan值,则计算的结果是正确的。

我尝试过的

我尝试在加载数据时将dtype指定为Int64

[1] df = pd.read_csv('sample.csv', index_col=0, dtype={'epoch_1': pd.Int64Dtype(), 'epoch_2': pd.Int64Dtype()})
[2] df
epoch_1              epoch_2
0  1665045912937687296  1665045912937689088
1                 <NA>                 <NA>
[3] df['diff'] = df['epoch_2'] - df['epoch_1']
[4] df
epoch_1              epoch_2  diff
0  1665045912937687296  1665045912937689088  1792
1                 <NA>                 <NA>  <NA>

正如你所看到的,这也会导致精度损失和输入错误的结果。

解决方法我不想使用

我能做的是将数据加载为str,删除NaN列,然后将这些字段转换为"int64"并计算结果,这会给出正确的结果:

[1] df = pd.read_csv('sample.csv', index_col=0, dtype={'epoch_1': str, 'epoch_2': str})
[2] df
epoch_1              epoch_2
0  1665045912937687151  1665045912937689151
1                  NaN                  NaN
[3] df = df[~df['epoch_1'].isna()]
[4] df['diff'] = df['epoch_2'].astype(int) - df['epoch_1'].astype(int)
[5] df
epoch_1              epoch_2  diff
0  1665045912937687151  1665045912937689151  2000

但我需要在最终的df中保留nan值的条目,所以必须将这些条目添加回来,这种方法在进行转换之间花费了大量计算,并且当df&要计算的字段数量增加了,这也不是很优雅,所以我正在寻找更好的方法来实现这一点。

更新

另一件似乎有效的事情:-

[1] df = pd.read_csv('sample.csv', index_col=0, dtype=str)
[2] df
epoch_1              epoch_2
0  1665045912937687151  1665045912937689151
1                  NaN                  NaN
[3] df['diff'] = df['epoch_2'].astype('Int64') - df['epoch_1'].astype('Int64')
[4] df
epoch_1              epoch_2  diff
0  1665045912937687151  1665045912937689151  2000
1                  NaN                  NaN  <NA>

这似乎比删除na值并再次添加要好,尽管这也需要在操作之前进行类型转换,如果可能的话,我希望避免这种转换。

这也引发了另一个疑问,即为什么在read_csv中将列的dtype指定为Int64时会失去精度,但在加载为str然后转换为Int64后却能正常工作,read_csv是否会在内部加载数据为float64,然后将其转换为指定的dtype?

是的,遗憾的是,pandas还没有原生地支持其新的扩展dtypes(如可为null的整数数组(。要完成的工作在中进行跟踪https://github.com/pandas-dev/pandas/issues/29752。

pd.read_csv的相关更新刚刚登陆main,即参考https://github.com/pandas-dev/pandas/pull/48776并被安排为下一个熊猫发布CCD_ 3。(编辑:12月到期的新版本最近已更名为2.0.0(。

你已经可以用夜间旋转的轮子来测试了。

mamba create -n test_pandas -c conda-forge python pandas pip
mamba activate test_pandas
pip install --pre --upgrade --extra-index https://pypi.anaconda.org/scipy-wheels-nightly/simple pandas
In [5]: pd.__version__
Out[5]: '1.6.0.dev0+350.g2f7dce4e6e'
In [6]: pd.read_csv("sample.csv", use_nullable_dtypes=True, index_col=0).assign(diff=lambda df: df.epoch_2 - df.epoch_1)
Out[6]:
epoch_1              epoch_2  diff
0  1665045912937687151  1665045912937689151  2000
1                 <NA>                 <NA>  <NA>

非常有趣,也非常奇怪。我想到的是一个保留NaN值的方法

def diff(x,y):
if math.isnan(float(x)) or math.isnan(float(y)):
return np.nan
else:
z = np.int64(y)- np.int64(x)
return z
df['diff'] = df.apply(lambda x: diff(x['epoch_1'],x['epoch_2']), axis=1)
有趣的是df = pd.read_csv('./file.csv', dtype='Int64')在这种情况下不起作用。这是一个实验性的特点,似乎在这里突破了。关于pd.NAnp.nan(例如这里(,似乎有很多工作正在进行中,所以这很可能是一个bug。

请注意,t = pd.array([1665045912937689151, np.nan], dtype='Int64')也会失败,因为它以[1665045912937689088, <NA>]结束。问题似乎是np.nanpd.NA之间的差异,因为s = pd.array([1665045912937689151, pd.NA], dtype='Int64')产生了正确的[1665045912937689151, <NA>]。您可能要等到np.nanpd.read_csv中切换到pd.NA

默认情况下,当有空值或NaN值时,panda将integer强制转换为float,如果有大整数,则会导致精度损失。要克服此问题,请在read_csv((中使用na_filter=False。

解决方案:

import pandas as pd
import numpy as np
df = pd.read_csv('sample.csv', index_col=0, na_filter=False)
print(df)

输出:

epoch_1              epoch_2
0  1665045912937687151  1665045912937689151
1                                          

相关内容

  • 没有找到相关文章

最新更新