繁体   English   中英

在巨大的.csv文件中删除重复项

[英]Removing duplicates in a huge .csv file

我有这种格式的csv文件

testname unitname time data
test1 1 20131211220159 123123
test1 1 20131211220159 12345
test1 1 20131211230180 1234 

我正在尝试从此文件中删除所有旧数据,并仅保留具有最新时间戳的数据(应删除abov​​v的前两个,因为最后一个时间戳大于前两个时间戳)。 我想保留所有测试数据,除非稍后重复相同的测试和相同的单元。 输入文件按时间排序(因此较旧的数据将在下面显示)。

该文件约为15 Mb。(output_Temp.csv)。 我将其复制为output_temp2.​​csv

这就是我所拥有的。

file1=open("output_temp.csv","r")
file2=open("output_temp2.csv","r")
file3=open("output.csv","w")

flag=0
linecounter=0


for line in file1:
    testname=line[0]
    vid=line[1]
    tstamp=line[2]
    file2.seek(0) #reset
    for i in range(linecounter):
        file2.readline() #came down to the line #
    for line2 in file2:
        if testname==line2.split(",")[0] and vid==line2.split(",")[1] and tstamp!=line2.split(",")[2]:
            flag==1
            print line
        if flag==1:
            break

    if flag==0:
        file3.write(line)
    linecounter=linecounter+1 #going down is ok dont go up.
    flag=0

这确实需要很长时间才能处理,我认为这可能还可以,但实际上每100kb需要10分钟,而且还有很长的路要走。

速度较慢的主要原因是您正在读取文件中每一行的整个文件(或它的重复副本)。 因此,如果有10000行,则您正在读取10000行10000次,这意味着总行读取量为10000000!

如果您有足够的内存来保存到目前为止已读取的行,则有一个非常简单的解决方案:将到目前为止已看到的行存储在一组中。 (或者,对于每一行,存储三个重复的键的元组。)对于每一行,如果它已经在集合中,则跳过它; 否则,对其进行处理并将其添加到集合中。

例如:

seen = set()
for line in infile:
    testname, vid, tstamp = line.split(",", 3)[:3]
    if (testname, vid, tstamp) in seen:
        continue
    seen.add((testname, vid, tstamp))
    outfile.write(line)

文档中的itertools配方具有一个unique_everseen函数,可让您更好地进行包装:

def keyfunc(line):
    return tuple(line.split(",", 3)[:3])
for line in unique_everseen(infile, key=keyfunc):
    outfile.write(line)

如果集合占用过多内存,则始终可以在dict顶部伪造一个集合,并且可以使用dbm模块在数据库顶部伪造dict,这将在保持足够的内存量方面做得很好。加快速度,但不足以引起问题。 唯一的问题是dbm密钥必须是字符串,而不是三个字符串的元组……但是您总是可以保持它们的连接(或重新join )而不是拆分,然后就得到了一个字符串。


我假设当您说文件是“已排序的”时,您的意思是时间戳,而不是键列。 也就是说,无法保证重复的两行会彼此相邻。 如果有的话,这甚至更容易。 如果您使用itertools配方, 看起来似乎并不容易; 您只是用everseen代替了justseen

def keyfunc(line):
    return tuple(line.split(",", 3)[:3])
for line in unique_justseen(infile, key=keyfunc):
    outfile.write(line)

但是在幕后,这只是跟踪最后一行,而不是所有行的集合。 这样不仅速度更快,而且还节省了大量内存。


既然(我认为)我可以更好地理解您的需求,那么您实际上想要摆脱的不只是具有相同testnamevidtstamp的第一行,而是除具有相同testnamevid所有行之外的所有行。 tstamp最高的。 并且由于文件是按tstamp升序排序的,这意味着您可以完全忽略tstamp 您只想要每个的最后一场比赛。

这意味着人们everseen把戏是行不通的-我们不能跳过第一个,因为我们还不知道还有另一个。

如果仅向后迭代文件,则可以解决问题。 这也将使您的内存使用量增加一倍(因为,除了集合之外,您还将保留一个列表,以便可以按相反的顺序堆叠所有这些行)。 但是,如果可以接受,那就很简单:

def keyfunc(line):
    return tuple(line.split(",", 2)[:2])
for line in reversed(list(unique_everseen(reversed(list(infile)), key=keyfunc))):
    outfile.write(line)

如果将这些懒惰的迭代器转换为列表,以便我们可以对其进行反转,则将占用过多的内存,这可能是多次通过的最快方法:反转磁盘上的文件,然后过滤反转的文件,然后再次反转。 这确实意味着要多写两次文件,但是比起OS的虚拟内存在磁盘上来回交换数百次(或者您的程序仅因MemoryError失败)要MemoryError

如果你愿意做的工作,就不会那么难写一个后退文件迭代器,其内容从端的缓冲区和分裂的新行,并产生相同的方式, file / io.Whatever对象一样。 但是除非您确实需要它,否则我不会打扰。


如果您确实需要从文件中重复读取特定的行号,则行linecache模块通常会linecache加快速度。 当然,它的速度远不及完全不重新读取,但是比读取和解析数千个换行符要好得多。

您还浪费时间在内部循环中重复一些工作。 例如,您调用line2.split(",")三次,而不是将其拆分一次并将值存储在变量中,这将是其速度的三倍。 3倍恒定增益远不及二次至线性增益那么重要,但是当通过使您的代码更简单,更易读而免费提供它时,不妨采用它。

对于这么大的文件大小(〜15MB),Pandas将是绝佳的选择。 像这样:

import pandas as pd
raw_data = pd.read_csv()
clean_data = raw_data.drop_duplicates()
clean_data.to_csv('/path/to/clean_csv.csv')

使用上述代码片段,我能够在不到一秒钟的时间内处理大约151MB的CSV文件,其中包含超过590万行。 请注意,重复检查可以是条件操作,也可以是要匹配以进行重复检查的字段子集。 熊猫确实提供了许多现成的功能。 这里的文件

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM