在Python中转换文本CSV二进制文件并从中获取随机行,而无需将其读入内存

在Python中转换文本CSV二进制文件并从中获取随机行,而无需将其读入内存,python,csv,numpy,bigdata,random-access,Python,Csv,Numpy,Bigdata,Random Access,我有一些CSV文本文件,格式如下: 1.3, 0, 1.0 20.0, 3.2, 0 30.5, 5.0, 5.2 这些文件的大小约为3.5Gb,我无法在一段有用的时间内将任何文件读入Pandas的内存 但我不需要读取all文件,因为我想做的是从文件中选择一些随机行并读取其中的值,我知道如果文件的格式是所有字段大小相同的,理论上是可以做到的——例如,二进制文件中的float16 现在,我想我可以使用问题答案中指定的NumPy方法来转换它: 但是,转换完成后,如何从中随机选取一行? 在普通文本

我有一些CSV文本文件,格式如下:

1.3, 0, 1.0
20.0, 3.2, 0
30.5, 5.0, 5.2
这些文件的大小约为3.5Gb,我无法在一段有用的时间内将任何文件读入Pandas的内存

但我不需要读取all文件,因为我想做的是从文件中选择一些随机行并读取其中的值,我知道如果文件的格式是所有字段大小相同的,理论上是可以做到的——例如,二进制文件中的float16

现在,我想我可以使用问题答案中指定的NumPy方法来转换它:

但是,转换完成后,如何从中随机选取一行?

在普通文本文件中,我只需执行以下操作:

import random
offset = random.randrange(filesize)
f = open('really_big_file')
f.seek(offset)                  #go to random position
f.readline()                    # discard - bound to be partial line
random_line = f.readline()      # bingo!

但是我找不到一种方法可以在由NumPy制作的二进制文件中工作。

我会使用
struct
转换为二进制:

import struct
with open('input.txt') as fin, open('output.txt','wb') as fout:
     for line in fin:
         #You could also use `csv` if you're not lazy like me ...
         out_line = struct.pack('3f',*(float(x) for x in line.split(',')))
         fout.write(out_line)
在大多数系统上,它以标准的4字节浮点形式写入所有内容

现在,再次读取数据:

with open('output.txt','rb') as fin:
    line_size = 12 #each line is 12 bytes long (3 floats, 4 bytes each)
    offset = random.randrange(filesize//line_size)  #pick n'th line randomly
    f.seek(offset*line_size) #seek to position of n'th line
    three_floats_bytes = f.read(line_size)
    three_floats = struct.unpack('3f',three_floats_bytes)

如果您关心磁盘空间,并且希望使用
np.float16
(2字节浮点)压缩数据,您也可以使用上面的基本框架,只需将
np.fromstring
替换为
struct.unpack
,将
ndarray.tostring
替换为
struct.pack
(当然,使用适当的数据类型ndarray,
行大小将降至6…。

您必须根据存储大小调整偏移量,但是:

import csv
import struct
import random

count = 0
with open('input.csv') as fin, open('input.dat', 'wb') as fout:
    csvin = csv.reader(fin)
    for row in csvin:
        for col in map(float, row):
            fout.write(struct.pack('f', col))
            count += 1


with open('input.dat', 'rb') as fin:
    i = random.randrange(count)
    fin.seek(i * 4)
    print struct.unpack('f', fin.read(4))

因此,使用helpfull答案提供的示例,如果有人感兴趣,我找到了一种使用NumPy的方法:

# this converts the file from text CSV to bin
with zipfile.ZipFile("input.zip", 'r') as inputZipFile:
    inputCSVFile = inputZipFile.open(inputZipFile.namelist()[0], 'r') # it's 1 file only zip

    with open("output.bin", 'wb') as outFile:
        outCSVFile = csv.writer(outFile, dialect='excel')
        for line in inputCSVFile:
            lineParsed = ast.literal_eval(line)
            lineOut = numpy.array(lineParsed,'float16')
            lineOut.tofile(outFile)
        outFile.close()

    inputCSVFile.close()
    inputZipFile.close()

# this reads random lines from the binary file
with open("output.bin", 'wb') as file:
    file.seek(0)

    lineSize = 20 # float16 has 2 bytes and there are 10 values:
    fileSize = os.path.getsize("output.bin")

    offset = random.randrange(fileSize//lineSize)
    file.seek(offset * lineSize)
    random_line = file.read(lineSize)
    randomArr = numpy.fromstring(random_line, dtype='float16')

@TimPietzcker——这难道不是代码片段的基本功能吗?当然,通过这种方法可以消除拾取第一行的可能性……不,因为原始文本CSV中的行具有不同的长度,因此我会得到一种偏向,倾向于选择较大的行,而不是较小的行。(即,在示例数据中,第3行被选中的概率比第1行高出近30%)@jbssm--Hmm…有趣的一点。这是为了寻找一个随机浮点,而不是一个随机浮点行。换句话说,你正在丢失你的“记录”这里的信息。@mgilson确实如此-我刚刚看到了你的答案-但基本上是一样的,只需调整你所称的
行大小
。谢谢你,你帮了我很多。通过将你的示例与我在关于NumPy的问题中提到的示例相结合,我就能够在NumPy中实现我想要的。我想我将它添加到下面。就这么说吧s代码不起作用。我得到了错误:lineOut=struct.pack('3f',*(float(x)代表x-in-line.split(','))struct.error:pack正好需要3arguments@jbssm--然后看起来您有一个包含多于(或少于)3个元素的记录。我检查了错误。我有10个元素,所以我使用了类似out\u line=struct.pack('10f')的东西*(浮动(x)表示行中的x。拆分(','))代替。谢谢。