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_stringformatters 参数采用例如函数字典来格式化各个列。在您的情况下,您可以为 "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

【讨论】:

很抱歉最后的例子不是我想要的,而是我想要避免的。所以NaNs 应该这样表示。另外,如何让正则表达式不拆分包含多个单词的列名? 我更新了我的答案以允许列名中包含空格并保留 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 替换为空白 ('')

pandas to_csv read_csv编码错误

使用 pandas 的 df.to_csv 方法不适用于空格作为分隔符

使用 pandas to_csv 仅引用所需的列