Bash:uniq统计大数据集

时间:2017-04-15 11:01:10

标签: bash uniq

我有一组超过70GB的CVS文件,其中35GB属于我感兴趣的领域(每行有大约100字节的字段)

数据高度重复(抽样显示前1000名覆盖了50%以上的行数)并且我有兴趣获得总的uniq数

如果数据集不是那么大,我会这样做

cat my.csv | cut -f 5 | sort | uniq -c | sort --numeric,效果很好

然而我遇到的问题是(据我的理解)因为中间sort,这个命令需要保存在RAM中(然后在磁盘上,因为它不适合我的16Go RAM)整个数据集,然后将其流式传输到uniq -c

我想知道是否有命令/脚本awk / python一步完成sort | uniq -c,以便RAM消耗量应该低得多?

2 个答案:

答案 0 :(得分:2)

你可以试试这个:

perl -F, -MDigest::MD5=md5 -lanE 'say unless $seen{ md5($F[4]) }++' < file.csv >unique_field5.txt

对于每个唯一的field-5(例如$F[4]),它将在内存中保留16字节长的md5-digest。或者你可以使用

cut -d, -f5 csv | perl -MDigest::MD5=md5 -lnE 'say unless $seen{md5($_)}++'

得到相同的结果。

当然,md5目前还不具备加密安全性,但可能足以进行排序......当然,可以使用sha1sha256,只需使用-MDigest::SHA=sha255。当然,sha-digest更长 - 例如需要更多的记忆。

它与注释中链接的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

e.g:

表示md5

  • 内存使用量:5,435,239,618字节 - 例如appox 5.4 GB
  • 独特记录:49,983,353
  • 运行时间:10分钟

for sha256

  • 内存使用量:6,234,973,266字节 - 例如appox 6.2 GB
  • 独特记录:49,983,353
  • 运行时间:16分钟

相反,使用&#34;通常&#34;进行纯文本唯一搜索。的方法:

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内存笔记本开始大量交换(因为有SSD,所以没什么大不了的 - 但仍然......)
  • 独特记录:49,983,353
  • 运行时间:8:30分钟

结果

只需使用

即可
cut -d, -f5 csv | perl -MDigest::MD5=md5 -lnE 'say unless $seen{md5($_)}++'

你应该足够快地获得独特的线条。

答案 1 :(得分:0)

你可以试试这个:

split --filter='sort | uniq -c | sed "s/^\s*//" > $FILE' -b 15G -d "dataset" "dataset-"

此时你应该有大约5 dataset-<i>,每个15G应该比merge.bash小得多。

要合并文件,您可以将以下bash脚本保存为#! /bin/bash # read prev_line prev_count=${prev_line%% *} while read line; do count="${line%% *}" line="${line#* }" # This line does not handle blank lines correctly if [ "$line" != "$prev_line" ]; then echo "$prev_count $prev_line" prev_count=$count prev_line=$line else prev_count=$((prev_count + count)) fi done echo "$prev_count $prev_line"

sort -m -k 2 dataset-* | bash merge.sh > final_dataset.

运行命令:

merge.bash

注意:空白行未正确处理,如果符合您的需要,您可以从数据集中删除它们或更正{{1}}。