Pandas read_csv()丢失数值精度

pftdvrlh  于 2023-03-06  发布在  其他
关注(0)|答案(4)|浏览(281)
    • 问题**

我有一个包含大整数值的csv文件,我想对它执行一些算术运算,这些字段可能包含nan值,现在当我使用pandas to_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

如您所见,第3列的值不正确,正确的值应为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?

agyaoht7

agyaoht71#

是的,不幸的是Pandas还没有原生地支持它的新扩展dtypes(比如可以为空的整数数组)。要做的工作在https://github.com/pandas-dev/pandas/issues/29752中跟踪。
pd.read_csv的相关更新刚刚登陆main,即参考https://github.com/pandas-dev/pandas/pull/48776,并计划在下一个Pandas版本1.6.0中发布。定于12月发布的新版本最近被重命名为2.0.0)。
你已经可以用夜间的scipy轮子测试它了。

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

3b6akqbq2#

很有趣,也很奇怪,我得到的是一个保留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)
krugob8w

krugob8w3#

有趣的是df = pd.read_csv('./file.csv', dtype='Int64')在这种情况下不起作用。这是一个实验特性,似乎在这里中断了。似乎有很多关于pd.NAnp.nan的工作正在进行中(例如here),所以很可能是一个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

wko9yo5t

wko9yo5t4#

默认情况下,当存在空值或NaN值时,Pandas会将整数转换为浮点型,如果您有大整数,这会导致精度损失。要克服此问题,请在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

相关问题