删除非常大的数据集上的重复项
Posted
技术标签:
【中文标题】删除非常大的数据集上的重复项【英文标题】:Removing duplicates on very large datasets 【发布时间】:2019-02-23 17:22:05 【问题描述】:我正在处理一个 13.9 GB 的 csv 文件,其中包含大约 1600 万行和 85 列。我知道可能有几十万行是重复的。我运行了这段代码来删除它们
import pandas
concatDf=pandas.read_csv("C:\\OUT\\Concat EPC3.csv")
nodupl=concatDf.drop_duplicates()
nodupl.to_csv("C:\\OUT\\Concat EPC3- NoDupl.csv",index=0)
low_memory=False
但是,这让我陷入了 MemoryError。我的内存是 16GB,不能再高了。有没有一种更有效的方法来删除重复项,可能无需我将 csv 文件分解成更小的文件,就可以将其分块?
【问题讨论】:
"Large data" work flows using pandas的可能重复 dask 可能会给你带来一些运气 如果你有 Linux,这很好用:cat Concat\ EPC3.csv |排序 | uniq(如果有,请注意标题) 在这种情况下对 df 进行分块将是有效的,因为它会删除块中出现的重复项,但如果重复出现在多个块中,它们将不会被删除(只是块中的重复项将被删除) )。 我建议做的一件事是查看是否可以通过指定每列的 dtype 来减少 DF 的内存。例如,pandas 尝试预先猜测数据类型。有时您认为列可能是纯类型 Int 或 float,但 Pandas 可能会将其分配为对象,或者可能存在重复的列是可以分配类别的对象。 Obj 到 Float 或 int 的转换大大减少了内存,对象到类别也是如此。 【参考方案1】:最简单的解决方案是为文件中的每一行创建一个哈希表——在你的工作内存中存储 16M 哈希应该不是问题(取决于哈希大小)——然后你可以再次迭代你的文件并确保您只记下每个哈希的一次出现。您甚至不需要解析 CSV,也不需要 Pandas。
import hashlib
with open("input.csv", "r") as f_in, \
open("output.csv", "w") as f_out:
seen = set() # a set to hold our 'visited' lines
for line in f_in: # iterate over the input file line by line
line_hash = hashlib.md5(line.encode()).digest() # hash the value
if line_hash not in seen: # we're seeing this line for the first time
seen.add(line_hash) # add it to the hash table
f_out.write(line) # write the line to the output
这使用 MD5 作为哈希,因此每行大约需要 16B + 设置开销,但这仍然远远低于将所有内容存储在内存中 - 对于 16M 行 CSV 文件,您可以预期约 500MB 的内存使用量。
【讨论】:
只循环一次输入行不是更有效吗? I/O 操作可能是最昂贵的。 就 CSV 而言,某些行可能是等效的,但散列到不同的值。例如 CSV 处理空格和引号。 @norok2 - 没错,我已经用单通道方法对其进行了更新。 @norok2 - 集合的查找速度(它们本身就是哈希表)比列表快得多,尤其是随着seen
的大小增长。
@norok2 - 如果我们要计算操作,in set()
是 O(1)
操作(哈希表查找),list.append()
和 set.add()
也是 O(1)
,但需要注意的是集合需要更长的时间来处理(由于散列)以及底层系统可能需要更多操作作为列表和集合增长的事实(即不计算摊销复杂度)。同时,in list()
是一个O(n)
操作,当您向列表中添加更多元素时,它会线性变慢。除了非常非常小的列表之外,集合在此类任务中将总是优于它们。【参考方案2】:
UNIX 模拟器怎么样?
uniq <filename> >outputfile.txt
(类似的)
【讨论】:
uniq
仅在重复行彼此跟随时才有效(它会跳过与前一行相同的每一行)。因此,对于大多数应用程序,您需要先sort
,然后再使用uniq
。但如果您的 csv 文件中有带换行符的字符串,它通常不起作用。或者,如果您在 csv 文件中有索引(因为索引是唯一的)。而且您需要注意标题,以免对其进行排序。但如果这一切都很好,那么uniq
会非常快。并在 O(n) 中运行。【参考方案3】:
与zwer 的想法基本相同,但检查具有相同哈希的行中的相等性(而不是自动丢弃重复的哈希)。
file_in = "C:\\OUT\\Concat EPC3.csv"
file_out = "C:\\OUT\\Concat EPC3- NoDupl.csv"
with open(file_in, 'r') as f_in, open(file_out, 'w') as f_out:
# Skip header
next(f_in)
# Find duplicated hashes
hashes = set()
hashes_dup =
for row in f_in:
h = hash(row)
if h in hashes:
hashes_dup[h] = set()
else:
hashes.add(h)
del hashes
# Rewind file
f_in.seek(0)
# Copy header
f_out.write(next(f_in))
# Copy non repeated lines
for row in f_in:
h = hash(row)
if h in hashes_dup:
dups = hashes_dup[h]
if row in dups:
continue
dups.add(row)
f_out.write(next(f_in))
【讨论】:
以上是关于删除非常大的数据集上的重复项的主要内容,如果未能解决你的问题,请参考以下文章