问题
我有一个包含大整数值的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.NA
和np.nan
(例如这里(,似乎有很多工作正在进行中,所以这很可能是一个bug。
请注意,t = pd.array([1665045912937689151, np.nan], dtype='Int64')
也会失败,因为它以[1665045912937689088, <NA>]
结束。问题似乎是np.nan
和pd.NA
之间的差异,因为s = pd.array([1665045912937689151, pd.NA], dtype='Int64')
产生了正确的[1665045912937689151, <NA>]
。您可能要等到np.nan
在pd.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