将 Pandas 数据帧转换为 float32 会更改低精度数字的值(通过添加不正确的高精度小数)

问题描述 投票:0回答:1

我有一个非常大的数据集,其中的值不需要太多的小数点精度。在一个测试场景中,我的数据帧为 102 MB,所有列都有 float64 数据类型。

我希望通过更改 pandas 数据帧以保存 float32 值来减少内存使用量,并可能减少输出文件大小。使用此数据框,我正在创建这些文件:

  • .csv(带有 df.to_csv)
  • .xlsx(使用 xlsxwriter)
  • .html(带有情节fig.to_html或write_html)

通过

df = df.astype('float32')
添加一行将我的数据类型转换为 float32 后,我惊讶地发现我的一些文件比以前大了很多。一个 .html 文件从 30 MB 增加到 44 MB。一个 xlsx 文件从 31 MB 增加到约 39 MB。当我查看存储的数据时,我看到小数点后的数字更多且不准确:

进一步深入研究,我发现 Pandas 如何向下转换为 float32 的意外行为 - 或者可能是各种方法和函数如何表示 float32 数据类型与 float64。

给定一个简单的 csv 文件:

59.11,59,59.86,59.86,59.0839945
60.28,59.7817845,59.75,59.75,59

一个简单的脚本:

import pandas as pd

df = pd.read_csv('float_test.csv', header=None)
s = df.iloc[0,:]

print(df.info())
print(df)
print(f's dtype: {s.dtypes}')  # Show Datatype
print(s) # Show the data as pandas prints it
print(f's to list: {s.to_list()}')  # Show data convertered to list
s32 = s.astype('float32')
print(s32)
print(f's32 to list: {s32.to_list()}')  # Convert to float32 and print as list

看着这个脚本的输出,我对发生的事情感到困惑。 数据帧的每一列都是一个“float64”。当我打印数据帧时,它显示小数值填充到任何列中最精确的浮点数(小数点后最多 6 位)。同样,当我只抓取第一行时,它会被视为一个系列,并用填充到小数点后 6 位来表示。

现在,一旦我将序列转换为 float32,我就会看到索引点 2 和 3 的值发生变化。它不再是 59.86 或 59.860000,而是变成 59.860001

当我使用 to_list() 时,我发现原始系列具有 CSV 文件中的正确值(添加“.0”以指示浮动)。但是 float32 系列,在某些值中添加了 .00000061035156,最后一个值添加了类似(但不相同)的值。

这里发生了一些我不明白的事情。为什么“59.11”的 float32 不只是“59.11000”? (<< pretend that's the right # of zeros)

我理解从 64 向下转换到 32 时会失去精度。但我不明白 float32 中应该精确的数字如何变得不精确。

虽然这似乎是 Pandas 的问题,但我发现与其他库(xlsxwriter 和plotly)相关的存储空间膨胀。我猜这是因为 float32 强制数据保留到某个小数点,但不知何故 float64 可以接受 59.11。然而,这也可能是因为数字本身略有变化(在最精细的级别),迫使保留许多小数点。

python pandas plotly-python xlsxwriter float32
1个回答
0
投票

我将把你的问题标记为这个问题的重复,但为了帮助理解为什么我也会提交这个答案。

第 1 部分:

为什么“59.11”的 float32 不只是“59.11000”?

答案:没有办法将 59.11 精确地表示为二进制浮点数(float)。

59.11 的浮点表示是其他某个数字非常接近 59.11,但不完全相等。确切的数字取决于机器对浮点数的实现,但无论如何,如果您查看足够的小数位以超出浮点数的精度,那么您可能会看到“垃圾”。对于float32来说,大约有7个精确数字(包括

.
左边的数字),其余的都是“垃圾”。对于 float64,在“垃圾”开始之前大约有 15 个精确数字。

为了向自己证明这一点,请尝试将 pandas 配置为打印荒谬的小数位数,然后再次查看您的数据。这样您就可以准确地查看存储在内存中的数字,而不是它的某种舍入版本。

import pandas as pd
data = [
    [59.11,59,59.86,59.86,59.0839945],
    [60.28,59.7817845,59.75,59.75,59],
]
df = pd.DataFrame(data)
s = df.iloc[0,:]
pandas.set_option("display.precision", 18)
print(s) 
s32 = s.astype('float32')
print(s32)

哪个打印:

0    59.109999999999999432
1    59.000000000000000000
2    59.859999999999999432
3    59.859999999999999432
4    59.083994500000002859
Name: 0, dtype: float64
0    59.110000610351562500
1    59.000000000000000000
2    59.860000610351562500
3    59.860000610351562500
4    59.083995819091796875
Name: 0, dtype: float32

第 2 部分:

至于为什么你的.csv和.xlsx文件更大,我猜测pandas在四舍五入到一定数量的小数点后打印数字的二进制浮点表示形式。对于 float64,浮点数非常接近,因此即使将其四舍五入到小数点后 6 或 7 位,在您想要查看的数据后面也只有尾随 0,并且那些尾随 0 将被省略。对于 float32,“垃圾”开始得足够早,不会被舍入隐藏,因此在结果的文本版本中占用更多字符。

如果您想将数据直接以数字形式存储在磁盘上,而不是存储舍入浮点数的文本版本,您可以尝试保存为 .pkl 等二进制格式(请参阅DataFrame.to_pickle)。请记住,该文件不可读。

© www.soinside.com 2019 - 2024. All rights reserved.