将Pandas DataFrame保存到csv时,某些整数会在浮点数中转换。
它发生在一列浮点数缺少值(np.nan
)的地方。
有一种简单的方法可以避免它吗? (特别是以自动方式 - 我经常处理各种数据类型的许多列。)
例如
import pandas as pd
import numpy as np
df = pd.DataFrame([[1,2],[3,np.nan],[5,6]],
columns=["a","b"],
index=["i_1","i_2","i_3"])
df.to_csv("file.csv")
产量
,a,b
i_1,1,2.0
i_2,3,
i_3,5,6.0
我想得到的是
,a,b
i_1,1,2
i_2,3,
i_3,5,6
编辑:我完全了解Support for integer NA - Pandas Caveats and Gotchas。问题是什么是一个很好的解决方法(特别是如果有许多其他类型的各种类型,我事先不知道哪些"整数"列有缺失的值)。
答案 0 :(得分:6)
在to_csv函数中使用float_format = '%.12g'
为我解决了类似的问题。它保留合法浮点数的小数位数,最多12个有效数字,但是因为存在NaN的情况而强制浮动的内容会丢弃它们:
In [4]: df
Out[4]:
a b
i_1 1 2.0
i_2 3 NaN
i_3 5.9 6.0
In [5]: df.to_csv('file.csv', float_format = '%.12g')
输出是:
, a, b
i_1, 1, 2
i_2, 3,
i_3, 5.9, 6
答案 1 :(得分:5)
这段代码可以满足您的需要,而且应该相对有效。
import numpy as np
import pandas as pd
EPSILON = 1e-9
def _lost_precision(s):
"""
The total amount of precision lost over Series `s`
during conversion to int64 dtype
"""
try:
return (s - s.fillna(0).astype(np.int64)).sum()
except ValueError:
return np.nan
def _nansafe_integer_convert(s):
"""
Convert Series `s` to an object type with `np.nan`
represented as an empty string ""
"""
if _lost_precision(s) < EPSILON:
# Here's where the magic happens
as_object = s.fillna(0).astype(np.int64).astype(np.object)
as_object[s.isnull()] = ""
return as_object
else:
return s
def nansafe_to_csv(df, *args, **kwargs):
"""
Write `df` to a csv file, allowing for missing values
in integer columns
Uses `_lost_precision` to test whether a column can be
converted to an integer data type without losing precision.
Missing values in integer columns are represented as empty
fields in the resulting csv.
"""
df.apply(_nansafe_integer_convert).to_csv(*args, **kwargs)
我们可以使用一个简单的DataFrame测试它,它应涵盖所有基础:
In [75]: df = pd.DataFrame([[1,2, 3.1, "i"],[3,np.nan, 4.0, "j"],[5,6, 7.1, "k"]]
columns=["a","b", "c", "d"],
index=["i_1","i_2","i_3"])
In [76]: df
Out[76]:
a b c d
i_1 1 2 3.1 i
i_2 3 NaN 4.0 j
i_3 5 6 7.1 k
In [77]: nansafe_to_csv(df, 'deleteme.csv', index=False)
生成以下csv
文件:
a,b,c,d
1,2,3.1,i
3,,4.0,j
5,6,7.1,k
答案 2 :(得分:4)
我正在扩展此处的示例数据,希望确保这样可以处理您正在处理的情况:
df = pd.DataFrame([[1.1,2,9.9,44,1.0],
[3.3,np.nan,4.4,22,3.0],
[5.5,8,np.nan,66,4.0]],
columns=list('abcde'),
index=["i_1","i_2","i_3"])
a b c d e
i_1 1.1 2 9.9 44 1
i_2 3.3 NaN 4.4 22 3
i_3 5.5 8 NaN 66 4
df.dtypes
a float64
b float64
c float64
d int64
e float64
我认为如果你想要一个通用的解决方案,由于pandas不允许在int列中使用NaN,因此必须对其进行显式编码。我在这里做的是检查整数值(因为我们无法真正检查类型,因为如果它们包含NaN,它们将被重新设置为浮动),如果它是一个整数值,则转换为字符串格式,并将'NAN'
转换为''
(空)。当然,这不是你想要存储整数的方式,除非是在输出之前的最后一步。
for col in df.columns:
if any( df[col].isnull() ):
tmp = df[col][ df[col].notnull() ]
if all( tmp.astype(int).astype(float) == tmp.astype(float) ):
df[col] = df[col].map('{:.0F}'.format).replace('NAN','')
df.to_csv('x.csv')
这是输出文件,如果你把它读回到pandas中它看起来像什么,虽然它的目的可能是将它读入其他数字包。
%more x.csv
,a,b,c,d,e
i_1,1.1,2,9.9,44,1.0
i_2,3.3,,4.4,22,3.0
i_3,5.5,8,,66,4.0
pd.read_csv('x.csv')
Unnamed: 0 a b c d e
0 i_1 1.1 2 9.9 44 1
1 i_2 3.3 NaN 4.4 22 3
2 i_3 5.5 8 NaN 66 4
答案 3 :(得分:2)
@EdChum的建议是评论很好,你也可以使用float_format
参数(参见docs)
In [28]: a
Out[28]:
a b
0 0 1
1 1 NaN
2 2 3
In [31]: a.to_csv(r'c:\x.csv', float_format = '%.0f')
退出:
,a,b
0,0,1
1,1,
2,2,3