pandas读取大文件筛选数据

pandas读取大文件筛选数据

在参加某比赛的时候遇到过给出的训练数据集大小达到近9个G的 csv 文件,但是这9个G的数据中的数据不一定都是我需要的,我需要对文件进行筛选。这样的情况需要将数据都读进内存中,如果内存够大,那么完全没问题(我尝试过至少要32G),这样一次性全量读取数据集并且操纵这个数据集对电脑的要求是比较高的,那么这时候我们可以使用分块读取数据。

python中分块读取文件

数据分块读取或许在python中很陌生,因为绝大多数人读取数据都是采用的 read 方法或 readline 方法

file = open(file="filename", mode="r", encoding="utf8")
text = file.read()
print(text)
file.close()
file = open(file="filename", mode="r", encoding="utf8")
while line:=file.readline():
    print(line)
file.close()

到这里我们的问题基本就解决了,可以按行读取9个G的 csv 然后对文件进行筛选,但是这样的效率及其低下,可以试想一下9个G的 csv 有多少行,python的速度是很慢的,因此我们一次读取应该读取更多行。

如果我需要读取一个1TB的文件,这个文件只有一行,但是这个文件比较特殊,每个段落都是以 <--!--> 作为的分隔符,我要将每个段落读取出来然后存储到数据库中

这时候 readline 就不能解决问题了,这时候我们需要使用 read 方法,也许是python通常都是被当作脚本语言,所以很少有人会去发掘里面的一些问题,其实在其他语言中,如Go、Java、C++在学习文件读取的时候都会讲按字节读取,但是python基本不会,在python中我如果要读取一个文件的前5个字符(如果要读取前5个字节那么读取方式就为 rb ),那么只需要给 read 方法指定 size 就行 现在假设这 1TB 的文件内容如下,文件名为 source

qwertyuiop<--!-->asdfghjkl<--!-->zxcvbnm
file = open(file="./source", mode="r", encoding="utf8")
t = file.read(5)
print(t)
file.close()

这样就读取了前5个字符那么当我们指定了 size 之后那么这个文件对象中就会存在一个文件位置的类似于指针的对象,我们可以将这个文件对象理解为一个生成器,就像 readline 一样,只不过我们每次读取这个文件时由行变为了我们指定的字符数。

file = open(file="./source", mode="r", encoding="utf8")
while t := file.read(3):
    print(t)

那么我们就可以一点一点的读取文件,将读取到的内容拼接起来,如在拼接的字符串中发现了 <--!--> 那么就做一个切片将字符串进行切割

secret = "<--!-->"
file = open(file="./source", mode="r", encoding="utf8")
buffer = ""
while chunk := file.read(5):
    buffer += chunk
    postion = buffer.find(secret)
    if postion >= 0:
        print(buffer[:postion])
        buffer = buffer[postion+len(secret)]
print(buffer)
file.close()

至于最后的输出是因为我们的文件读到最后之后如果没有分隔符的话那么最后的字符是不会在循环中被输出的。当然我们也可以将判断是否读到文件末尾的逻辑移到循环中,那么在循环中就能输出了。

那么到这里我们的问题基本就已经讲完了,我们可以将这个循环封装为一个生成器函数,那么这样我们操作的空间将变得更大。

def seek(file: IO, secret: str) -> Iterator:
    buffer = ""
    while chunk := file.read(5):
        buffer += chunk
        postion = buffer.find(secret)
        if postion >= 0:
            yield buffer[:postion]
            buffer = buffer[postion+len(secret)]
    yield buffer

pandas分块读取文件

在pandas中读取表类文件的时候有一个参数 chunksize ,只要指定了这个参数的数值,那么得到的结果就不是一个 DataFrame 对象,而是一个 TextFileReader ,这个对象是一个生成器对象

import pandas as pd
reader = pd.read_csv('训练用数据.csv', chunksize=1000)
for chunk in reader:
    print(chunk)

上面的程序中指定的 chunksize=1000 表示1次读取1000行,那么第二次读的时候就是从1001行开始读取。

那么我们就能将读取出的内容做一个筛选,并且由于每次只读取了100行,这样占用的计算机内存将会被控制得比较小。

这里我就简化我的工作过程,我需要的目标数据是存放在另外一个 csv 中的某一类列里,但是我就生成一个列表来进行筛选

import pandas as pd