行をメモリに保持できる場合
十分なデータがメモリに収まる場合、スティーブによるawk
解決策は、パイプでコマンドに書き込むか、単にシェルレベルでunadorned の出力をパイプするかによって、非常にうまくいきます。sort
awk
awk
sort
おそらく 3% の重複がある 100 GiB のデータがある場合、100 GiB のデータをメモリに格納できる必要があります。それは多くのメインメモリです。64 ビット システムでは仮想メモリで処理できる場合がありますが、実行速度がかなり遅くなる可能性があります。
キーがメモリに収まる場合
十分なデータをメモリに収めることができない場合、先のタスクははるかに難しくなり、ファイルに対して少なくとも 2 回のスキャンが必要になります。私たちは、キーが出現した回数のカウントとともに、少なくとも各キーをメモリに収めることができると想定する必要があります。
- スキャン 1: ファイルを読み取ります。
- 各キーが入力に現れる回数を数えます。
- では
awk
、 を使用しますicount[$1]++
。
- スキャン 2: ファイルを再読み込みします。
- 各キーが出現した回数を数えます。
ocount[$1]++
.
- の場合
icount[$1] == ocount[$1]
、その行を印刷します。
(これは、キーとカウントを 2 回保存できることを前提としています。代わりicount
に、両方のスキャンで (のみ) 使用し、スキャン 1 でインクリメントし、スキャン 2 でデクリメントし、カウントがゼロになったときに値を出力します。)
awk
ファイルを Perl で再読み込みする方が簡単であるという理由だけで、私はおそらく ではなく Perl を使用しますawk
。
鍵すら入らない?
キーとその数をメモリに収めることさえできない場合はどうでしょうか? 次に、いくつかの深刻な問題に直面しています。特に、スクリプト言語がメモリ不足の状態を適切に報告しない可能性があるためです。必要であることが示されるまで、この橋を渡ろうとはしません。また、必要に応じて、何が可能かを知るために、ファイル セットに関する統計データが必要になります。
- レコードの平均長。
- 個別のキーの数。
- N = 1、2、... maxのそれぞれについて、N 回出現する個別のキーの数。
- キーの長さ。
- メモリに収まるキーとカウントの数。
そしておそらく他のいくつか...だから、私が言ったように、必要であることが示されるまで、その橋を渡ろうとしないでください.
Perl ソリューション
サンプルデータ
$ cat x000.csv
abc,123,def
abd,124,deg
abe,125,deh
$ cat x001.csv
abc,223,xef
bbd,224,xeg
bbe,225,xeh
$ cat x002.csv
cbc,323,zef
cbd,324,zeg
bbe,325,zeh
$ perl fixdupcsv.pl x???.csv
abd,124,deg
abe,125,deh
abc,223,xef
bbd,224,xeg
cbc,323,zef
cbd,324,zeg
bbe,325,zeh
$
ギガバイト規模のテストがないことに注意してください。
fixdupcsv.pl
これは、「カウントアップ、カウントダウン」テクニックを使用します。
#!/usr/bin/env perl
#
# Eliminate duplicate records from 100 GiB of CSV files based on key in column 1.
use strict;
use warnings;
# Scan 1 - count occurrences of each key
my %count;
my @ARGS = @ARGV; # Preserve arguments for Scan 2
while (<>)
{
$_ =~ /^([^,]+)/;
$count{$1}++;
}
# Scan 2 - reread the files; count down occurrences of each key.
# Print when it reaches 0.
@ARGV = @ARGS; # Reset arguments for Scan 2
while (<>)
{
$_ =~ /^([^,]+)/;
$count{$1}--;
print if $count{$1} == 0;
}
' while (<>)
' 表記は破棄@ARGV
されます (したがって、他の操作を行う前にコピーされます) が、これは、元の値に@ARGS
リセットすると、ファイルをもう一度実行することも意味します。@ARGV
Mac OS X 10.7.5 上の Perl 5.16.0 および 5.10.0 でテスト済み。
これは Perl です。TMTOWTDI。あなたが使用することができます:
#!/usr/bin/env perl
#
# Eliminate duplicate records from 100 GiB of CSV files based on key in column 1.
use strict;
use warnings;
my %count;
sub counter
{
my($inc) = @_;
while (<>)
{
$_ =~ /^([^,]+)/;
$count{$1} += $inc;
print if $count{$1} == 0;
}
}
my @ARGS = @ARGV; # Preserve arguments for Scan 2
counter(+1);
@ARGV = @ARGS; # Reset arguments for Scan 2
counter(-1);
おそらくループの本体を圧縮する方法もあるでしょうが、そこにあるものは合理的に明確であり、極端な簡潔さよりも明確さを好みます。
呼び出し
fixdupcsv.pl
正しい順序でファイル名を指定してスクリプトを提示する必要があります。1.csv から約 2000.csv までの番号が付けられたファイルがあるため、それらを英数字順にリストしないことが重要です。ls -v *.csv
他の回答では、GNUls
拡張オプションの使用が提案されています。利用可能な場合、それが最良の選択です。
perl fixdupcsv.pl $(ls -v *.csv)
それが利用できない場合は、名前に対して数値ソートを行う必要があります。
perl fixdupcsv.pl $(ls *.csv | sort -t. -k1.1n)
Awk ソリューション
awk -F, '
BEGIN {
for (i = 1; i < ARGC; i++)
{
while ((getline < ARGV[i]) > 0)
count[$1]++;
close(ARGV[i]);
}
for (i = 1; i < ARGC; i++)
{
while ((getline < ARGV[i]) > 0)
{
count[$1]--;
if (count[$1] == 0) print;
}
close(ARGV[i]);
}
}'
これは の本来の「読み取り」ループを無視awk
し、すべての読み取りを明示的に行います (BEGIN を END に置き換えても同じ結果が得られます)。このロジックは、多くの点で Perl ロジックに密接に基づいています。awk
BSDと GNUの両方を搭載した Mac OS X 10.7.5 でテスト済みawk
。興味深いことに、GNUは、BSDが要求しなかっawk
た呼び出しの括弧を要求しました。2 番目のループを機能させるには、最初のループで呼び出しが必要です。2 番目のループの呼び出しは、対称性を維持し、整理するためにありますが、1 回の実行で数百のファイルを処理する場合にも関連する可能性があります。close
awk
close()
close()