我有一组超过 70GB 的 CVS 文件,其中 35GB 是我感兴趣的字段(每行大约有 100 个字节)
数据高度重复(采样显示前 1000 条覆盖了 50% 以上的行),我有兴趣获取总 uniq 计数
对于不太大的数据集,我会这样做
cat my.csv | cut -f 5 | sort | uniq -c | sort --numeric
而且效果很好
但是我遇到的问题是(据我了解)由于中间 sort
,此命令需要将整个数据集保存在 RAM 中(然后保存在磁盘上,因为它不适合我的 16Go RAM),然后将其流式传输到 uniq -c
我想知道是否有命令/script awk/python 来执行 sort | uniq -c
一步到位,RAM 消耗就会低得多?
最佳答案
你可以试试这个:
perl -F, -MDigest::MD5=md5 -lanE 'say unless $seen{ md5($F[4]) }++' < file.csv >unique_field5.txt
它将在内存中保存每个唯一 field-5
的 16 字节长的 md5-digest(例如 $F[4]
)。或者你可以使用
cut -d, -f5 csv | perl -MDigest::MD5=md5 -lnE 'say unless $seen{md5($_)}++'
相同的结果。
当然,现在 md5 在加密上并不安全,但可能足以进行排序...当然,可以使用 sha1
或 sha256
,只需使用-MDigest::SHA=sha255
。当然,sha-digests 更长 - 例如需要更多内存。
与注释中链接的awk
类似,不同之处在于,这里用作散列键的不是整个输入行,而是仅16byte
长MD5摘要。
编辑
因为我想知道性能,所以创建了这个测试用例:
# this perl create 400,000,000 records
# each 100 bytes + attached random number,
# total size of data 40GB.
# each invocation generates same data (srand(1))
# because the random number is between 0 - 50_000_000
# here is approx. 25% unique records.
gendata() {
perl -E '
BEGIN{ srand(1) }
say "x"x100, int(rand()*50_000_000) for 1..400_000_000
'
}
# the unique sorting - by digest
# also using Devel::Size perl module to get the final size of the data hold in the memory
# using md5
domd5() {
perl -MDigest::MD5=md5 -MDevel::Size=total_size -lnE '
say unless $seen{md5($_)}++;
END {
warn"total: " . total_size(\%seen);
}'
}
#using sha256
dosha256() {
perl -MDigest::SHA=sha256 -MDevel::Size=total_size -lnE '
say unless $seen{sha256($_)}++;
END {
warn"total: " . total_size(\%seen);
}'
}
#MAIN
time gendata | domd5 | wc -l
time gendata | dosha256 | wc -l
结果:
total: 5435239618 at -e line 4, <> line 400000000.
49983353
real 10m12,689s
user 12m43,714s
sys 0m29,069s
total: 6234973266 at -e line 4, <> line 400000000.
49983353
real 15m51,884s
user 18m23,900s
sys 0m29,485s
例如:
md5
- 内存使用量:5,435,239,618 字节 - 例如约 5.4 GB
- 唯一记录:49,983,353
- 运行时间:10 分钟
对于 sha256
- 内存使用量:6,234,973,266 字节 - 例如约 6.2 GB
- 唯一记录:49,983,353
- 运行时间:16 分钟
相比之下,使用“通常”方法进行纯文本唯一搜索:
doplain() {
perl -MDevel::Size=total_size -lnE '
say unless $seen{$_}++;
END {
warn"total: " . total_size(\%seen);
}'
}
例如运行:
time gendata | doplain | wc -l
结果:
- 内存使用量要大得多:10,022,600,682 - 我的 16GB RAM 笔记本开始大量交换(因为有 SSD,所以没什么大不了的 - 但仍然..)
- 唯一记录:49,983,353
- 运行时间:8:30 分钟
结果?
只需使用
cut -d, -f5 csv | perl -MDigest::MD5=md5 -lnE 'say unless $seen{md5($_)}++'
你应该足够快地获得独特的线条。
关于Bash:uniq计数大数据集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43425227/