Python pandas 带有 to_csv 的大浮点数
Posted
技术标签:
【中文标题】Python pandas 带有 to_csv 的大浮点数【英文标题】:Python pandas large floats with to_csv 【发布时间】:2018-02-24 11:27:14 【问题描述】:我有一个recurring problem,可以将 Python 中的大量数字保存到 csv。这些数字是毫秒纪元时间戳,我无法转换或截断,必须以这种格式保存。由于具有毫秒时间戳的列还包含一些 NaN 值,pandas 会自动将它们转换为浮点数(请参阅“支持整数 NA”下的陷阱中的 the documentation。
我似乎无法避免这种行为,所以我的问题是,如何在使用 df.to_csv 时将这些数字保存为整数值,即没有小数点或尾随零?我在同一个数据框中有不同浮动精度的列,我不想在那里丢失信息。在 to_csv 中使用 float_format
参数似乎对我的数据框中的所有浮点列应用相同的格式。
一个例子:
>>> df = pd.DataFrame('a':[1.25, 2.54], 'b':[1424380449437, 1425510731187])
>>> df['b'].dtype
Out[1]: dtype('int64')
>>> df.loc[2] = np.NaN
>>> df
Out[1]:
a b
0 1.25 1.424380e+12
1 2.54 1.425511e+12
2 NaN NaN
>>> df['b'].dtype
dtype('float64')
>>> df.to_csv('test.csv')
>>> with open ('test.csv') as f:
... for line in f:
... print(line)
,a,b
0,1.25,1.42438044944e+12
1,2.54,1.42551073119e+12
2,,
如您所见,我丢失了纪元时间戳的最后两位数字的精度。
【问题讨论】:
您可以将nan
值替换为零,然后将该列转换为整数。 df.b = df.b.fillna(0).astype(int)
或使用-1
来识别后续处理中的条目。
这是一种可能,但是一种相当笨拙的解决方法。我更愿意保留nan
的值,因为它允许轻松索引和过滤。此外,我用于 nan
的任何占位符值都可能自然地出现在 Dataframe 中。
【参考方案1】:
虽然pd.to_csv
没有更改单个列格式的参数,但pd.to_string
有。这有点麻烦,对于非常大的 DataFrame 来说可能是个问题,但是您可以使用它来生成格式正确的字符串,然后将该字符串写入文件(如 answer 中的类似问题所建议的那样)。 to_string
的formatters
参数采用例如函数字典来格式化各个列。在您的情况下,您可以为 "b"
列编写自己的自定义格式化程序,而为其他列保留默认值。这个格式化程序可能看起来像这样:
def printInt(b):
if pd.isnull(b):
return "NaN"
else:
return ":d".format(int(b))
现在你可以用它来生成你的字符串了:
df.to_string(formatters="b": printInt, na_rep="NaN")
给出:
' a b\n0 1.25 1424380449437\n1 2.54 1425510731187\n2 NaN NaN'
可以看到还是存在这个不是逗号分隔的问题,而且to_string
实际上没有设置自定义分隔符的参数,但是可以通过正则表达式轻松解决:
import re
re.sub("[ \t]+(NaN)?", ",",
df.to_string(formatters="b": printInt, na_rep="NaN"))
给予:
',a,b\n0,1.25,1424380449437\n1,2.54,1425510731187\n2,,'
现在可以将其写入文件:
with open("/tmp/test.csv", "w") as f:
print(re.sub("[ \t]+(NaN)?", ",",
df.to_string(formatters="b": printInt, na_rep="NaN")),
file=f)
这就是你想要的结果:
,a,b
0,1.25,1424380449437
1,2.54,1425510731187
2,,
如果您想将NaN
保留在 csv 文件中,您只需更改正则表达式:
with open("/tmp/test.csv", "w") as f:
print(re.sub("[ \t]+", ",",
df.to_string(formatters="b": printInt, na_rep="NaN")),
file=f)
将给予:
,a,b
0,1.25,1424380449437
1,2.54,1425510731187
2,NaN,NaN
如果您的 DataFrame 之前包含带有空格的字符串,那么可靠的解决方案就不是那么容易了。您可以在每个值前面插入另一个字符,表示下一个条目的开始。例如,如果所有字符串中只有一个空格,则可以使用另一个空格。这会将代码更改为:
import pandas as pd
import numpy as np
import re
df = pd.DataFrame('a a':[1.25, 2.54], 'b':[1424380449437, 1425510731187])
df.loc[2] = np.NaN
def printInt(b):
if pd.isnull(b):
return " NaN"
else:
return " :d".format(int(b))
def printFloat(a):
if pd.isnull(a):
return " NaN"
else:
return " ".format(a)
with open("/tmp/test.csv", "w") as f:
print(re.sub("[ \t][ \t]+", ",",
df.to_string(formatters="a": printFloat, "b": printInt,
na_rep="NaN", col_space=2)),
file=f)
这会给:
,a a,b
0,1.25,1424380449437
1,2.54,1425510731187
2,NaN,NaN
【讨论】:
很抱歉最后的例子不是我想要的,而是我想要避免的。所以NaN
s 应该这样表示。另外,如何让正则表达式不拆分包含多个单词的列名?
我更新了我的答案以允许列名中包含空格并保留 NaN(我以为你只希望那些留在 df
中)。这有帮助吗?
我接受了你的回答,因为它解决了我的问题,尽管它仍然是一个可怕的解决方法,主要是因为我必须为几十列指定格式化程序;)我选择了单间距正则表达式匹配,如当列名或单元格条目太长时(因为它只会在列中留下一个空格),双倍间距会在 pandas 的 to_string
函数中遇到问题。感谢您的帮助!【参考方案2】:
也许这可行:
pd.set_option('precision',15)
df = pd.DataFrame('a':[1.25, 2.54], 'b':[1424380449437, 1425510731187])
fg = df.applymap(lambda x: str(x))
fg.loc[2] = np.NaN
fg.to_csv('test.csv', na_rep='NaN')
你的输出应该是这样的(我在 Mac 上):
【讨论】:
但是在这种情况下,您必须在插入NaN
之前将 DataFrames 条目更改为字符串,不是吗?
@jotasi 是的,否则你会失去精度。我认为它可能是您的替代品
感谢您的输入,但这对我不起作用 - 我的原始数据框一直都有 NaN,我仅在示例中添加它们以显示列如何强制转换浮动。【参考方案3】:
我对大数字也有同样的问题,这是 excel 文件的正确方法 df = "\t" + df
【讨论】:
如何以及在哪里添加这行代码df = "\t" + df
?
您可以在 to_csv 操作之前添加例如:df = pd.read_excel(excel_file_path, skiprows=4) df = df.loc[:, ~df.columns.str.contains('^Unnamed')] df.dropna(subset = ["Column1"], inplace=True) df="\t" + df df.to_csv(r'file_path.csv'.format(file_path=os.path.join(DestinationPath, filename)), index=False, header=False)
@tlentali以上是关于Python pandas 带有 to_csv 的大浮点数的主要内容,如果未能解决你的问题,请参考以下文章
使用 Python Pandas 写入 to_csv:选择要插入新数据的列索引
在追加模式下使用 to_csv 时,python pandas 新行附加到 csv 中的最后一行
Python Pandas read_excel dtype str 在读取或通过 to_csv 写入时将 nan 替换为空白 ('')