如何在Python中将浮点数列表输出到二进制文件

37

我有一个Python中的浮点数值列表:

floats = [3.14, 2.7, 0.0, -1.0, 1.1]
我想使用IEEE 32位编码将这些值写入二进制文件。在Python中最好的方法是什么?我的列表实际上包含约200 MB的数据,因此最好选择“不太慢”的方法。
由于有5个值,我只需要20字节的文件作为输出。

你的数组在数字之间缺少逗号。 - Scott Kirkwood
8个回答

49

Alex 是绝对正确的,这样做更有效率:

from array import array
output_file = open('file', 'wb')
float_array = array('d', [3.14, 2.7, 0.0, -1.0, 1.1])
float_array.tofile(output_file)
output_file.close()

然后像这样读取数组:

input_file = open('file', 'rb')
float_array = array('d')
float_array.fromstring(input_file.read())

array.array 对象也有一个 .fromfile 方法,可以用于读取文件,如果您事先知道项目的数量(例如从文件大小或其他机制)。


3
数组中的第一个参数应该是“f”,因为要求32位编码,不应该吗? - dmagree
2
顺便说一句,结果表明结构体答案实际上更快。我对几种不同的替代方案进行了一些分析,结构体是最快的,而且快得多。https://gist.github.com/deanmalmgren/fd1714799dc5b5643b87#file-write_profiler-py - dino
我有一个二维浮点数数组。我正在迭代元素(for row in myArray:),然后对row进行struct.pack处理。 - IAbstract

25

详见:Python 的 struct 模块

import struct
s = struct.pack('f'*len(floats), *floats)
f = open('file','wb')
f.write(s)
f.close()

实际上,这就是我现在正在做的。我只是认为一定有更好的方法。 - Frank Krueger
1
还有更好的方法——数组模块。请参阅http://docs.python.org/library/array.html获取所有详细信息。 - Alex Martelli
你应该使用 with 语法。 - Martin Thoma
3
数组模块并不更快,实际上。请参见此处的分析:https://gist.github.com/deanmalmgren/fd1714799dc5b5643b87#file-write_profiler-py - dino
将两个dict以JSON格式写入文件和打包一个3.2M的浮点数表格之间的差异似乎微不足道。也就是说,在这两个操作之间,控制台中的响应似乎没有任何滞后。@AlexMartelli:在我看来,“更好”完全是主观的。这种说法缺乏资格证明。 - IAbstract

14

标准库中的数组模块可能比所有人都在建议的结构体模块更适合这个任务。当处理200 MB的数据时,使用数组的性能应该明显更好。

如果你想尝试各种选项,请在你的系统上使用类似此处的分析工具。


11

我不确定在您的应用程序中使用 NumPy 的性能如何比较,但这可能值得调查。

使用 NumPy

from numpy import array
a = array(floats,'float32')
output_file = open('file', 'wb')
a.tofile(output_file)
output_file.close()

结果也会生成一个20字节的文件。


1
不如使用结构体快。请在此处查看性能分析:https://gist.github.com/deanmalmgren/fd1714799dc5b5643b87#file-write_profiler-py - dino

6

我在无意中写了一个100+ GB的csv文件时遇到了类似的问题。这里的答案非常有帮助,但为了找到问题的根源,我对所有提到的解决方案以及其他方案进行了分析。所有分析都是在使用Python 2.7的2014年Macbook Pro上进行的。从我的观察来看,从性能角度来看,struct方法绝对是最快的:

6.465 seconds print_approach    print list of floats
4.621 seconds csv_approach      write csv file
4.819 seconds csvgz_approach    compress csv output using gzip
0.374 seconds array_approach    array.array.tofile
0.238 seconds numpy_approach    numpy.array.tofile
0.178 seconds struct_approach   struct.pack method

5

我的“答案”实际上是对各种答案的评论。由于我没有50个声望,因此无法发表评论。

如果文件将由Python读取,则使用“pickle”模块。这个工具可以读写许多二进制文件。

但是,问题的提问方式是说“IEEE 32位编码”,这听起来像是文件将在其他语言中被读取。在这种情况下,应指定字节顺序。问题是大多数机器都是x86,采用小端字节顺序,而第一数据处理语言是Java / JVM,采用大端字节顺序。因此,Python“tofile()”将使用C,由于机器是小端,所以使用小端,然后Java / JVM上的数据处理代码将使用大端进行解码,从而导致错误。

要使用JVM:

# convert to bytes, BIG endian, for use by Java
import struct
f = [3.14, 2.7, 0.0, -1.0, 1.1]
b = struct.pack('>'+'f'*len(f), *f)

with open("f.bin", "wb") as file:
    file.write(b)

在Java方面:
try(var stream = new DataInputStream(new FileInputStream("f.bin")))
{
    for(int i = 0; i < 5; i++)
        System.out.println(stream.readFloat());
}
catch(Exception ex) {}

现在的问题是 Python 代码 'f'*len(f) - 希望解释器实际上不会创建一个超长的 "ffffff..." 字符串。

我会使用 Numpy 数组和字节交换。

import numpy, sys
f = numpy.array([3.14, 2.7, 0.0, -1.0, 1.1], dtype=numpy.float32)

if sys.byteorder == "little":
    f.byteswap().tofile("f.bin") # using BIG endian, for use by Java
else:
    f.tofile("f.bin")

请避免写“为什么我要写答案而不是评论”,如果您已经阅读了如何编写答案并且您的答案符合所有参数,请直接回答。 - Shanteshwar Inde
2
@ShanteshwarInde 这是一条合理的信息,也许可以稍微松懈一下。 - WestCoastProjects

3

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接