Bash: uniq count 大数据集
Bash: uniq count large dataset
我有一组超过 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 消耗应该低得多?
你可以试试这个:
perl -F, -MDigest::MD5=md5 -lanE 'say unless $seen{ md5($F[4]) }++' < file.csv >unique_field5.txt
它将在内存中为每个唯一的 field-5
(例如 $F[4]
)保留 16 字节长的 md5 摘要。或者你可以使用
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($_)}++'
而且你应该足够快地获得独特的线条。
你可以试试这个:
split --filter='sort | uniq -c | sed "s/^\s*//" > $FILE' -b 15G -d "dataset" "dataset-"
此时你应该有大约 5 dataset-<i>
个,每个都应该比 15G
少得多。
要合并文件,您可以将以下 bash 脚本另存为 merge.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
.
我有一组超过 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 消耗应该低得多?
你可以试试这个:
perl -F, -MDigest::MD5=md5 -lanE 'say unless $seen{ md5($F[4]) }++' < file.csv >unique_field5.txt
它将在内存中为每个唯一的 field-5
(例如 $F[4]
)保留 16 字节长的 md5 摘要。或者你可以使用
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($_)}++'
而且你应该足够快地获得独特的线条。
你可以试试这个:
split --filter='sort | uniq -c | sed "s/^\s*//" > $FILE' -b 15G -d "dataset" "dataset-"
此时你应该有大约 5 dataset-<i>
个,每个都应该比 15G
少得多。
要合并文件,您可以将以下 bash 脚本另存为 merge.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
.