web-dev-qa-db-ja.com

行の順序を維持しながら重複する行を削除する

[root@server]# awk '!seen[$0]++' out.txt > cleaned
awk: (FILENAME=out.txt FNR=8547098) fatal error: internal error
Aborted
[root@server]#

「サーバー」には、8 GByte RAM + 16 GByte SWAP、x> 300 GByteの空き容量、AMD64、デスクトップCPUが搭載されています。ScientificLinux6.6。LOADを作成するために、他に何も実行されません。 awkは数秒後に中止します。out.txtは約1.6 GBですGNU Awk 3.1.7。

質問:行の順序を維持しながら重複行を削除するにはどうすればよいですか?大文字と小文字も重要です。例:「A」と「a」は2つの異なる行です。保持する必要があります。ただし、「a」と「a」は重複しています。最初の1つだけが必要です。

答えは何でもかまいません.. awkがこれに適さない場合..次にPerl/sed ..問題は何でしょうか?

[root@server]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 61945
max locked memory       (kbytes, -l) 99999999
max memory size         (kbytes, -m) unlimited
open files                      (-n) 999999
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 99999999
cpu time               (seconds, -t) unlimited
max user processes              (-u) 61945
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
[root@server]# 

更新:RHELマシンでこれを試してみましたが、中止されませんでしたが、完了するのを待つ時間がありませんでした。SLlinuxがRHELと異なるのはなぜですか?

更新:私はUbuntu 14の仮想推測を試みています。 ulimitの問題ではありません:mawk 1.3.

root@asdf-VirtualBox:~# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 51331
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 51331
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
root@asdf-VirtualBox:~# 
14
somelooser28533

それが違いを生むとは思えませんが、念のため、Perlで同じことを行う方法を次に示します。

Perl -ne 'print if ++$k{$_}==1' out.txt

問題が一意の行をメモリに保持している場合は、試したawkと同じ問題が発生します。したがって、別のアプローチは次のとおりです。

cat -n out.txt | sort -k2 -k1n  | uniq -f1 | sort -nk1,1 | cut -f2-

使い方:

  1. GNUシステム、cat -nは、いくつかのスペースの後に各行の行番号を追加し、その後に<tab>文字が続きます。 catは、この入力表現をsortにパイプします。

  2. sort-k2オプションは、ソート時に2番目のフィールドから行末までの文字のみを考慮するように指示し、sortはデフォルトで空白でフィールドを分割します(またはcatの挿入されたスペースと<tab>
    その後に-k1nsortは、2番目のフィールドを最初に考慮し、次に2番目のフィールドを考慮します(同一の場合-k2 fields —最初のフィールドを考慮しますが、数値的にソートされています。そのため、繰り返された行は一緒に並べ替えられますが、表示された順になります。

  3. 結果はuniqにパイプされます—最初のフィールド-f1-また、空白で区切られている)—これにより、元のファイルに一意の行のリストが表示され、sortにパイプで戻されます。
  4. 今回はsortが最初のフィールドcatの挿入された行番号)で数値的にソートされ、ソート順が元のファイルの内容を調べ、これらの結果をcutにパイプします。
  5. 最後に、cutは、catによって挿入された行番号を削除します。これは、2番目のフィールドから行末までのcut印刷によってのみ影響を受けます(およびcutのデフォルトの区切り文字は<tab>です。文字)

説明する:

$ cat file
bb
aa
bb
dd
cc
dd
aa
bb
cc
$ cat -n file | sort -k2 | uniq -f1 | sort -k1 | cut -f2-
bb
aa    
dd
cc
22
terdon
#!/usr/bin/Perl 
use DB_File;
tie %h, 'DB_File';

while(<>){ not $h{$_} and print and $h{$_}=1 }

編集1:本当に機能しますか? (比較)

Sol1 : Terdon et all Schwartzian-transform-like one-liner
    cat -n _1 | sort -uk2 | sort -nk1 | cut -f2-

Sol2 : Perl  + DB_File (this answer)
    Perl dbfile-uniq _1

Sol3 : PO (John W. Gill solution has a similar behavior)
    awk '!seen[$0]++' _1

Sol4: Terdon Perl
    Perl -ne 'print if ++$k{$_}==1' _1

Case1:100_000_000の乱数(各5桁)、566Mバイト、31_212の異なる値:

$ while true ; do echo $RANDOM; done | head -100000000 > _1

ケース2:50_000_000ランド番号(各10桁)、516Mバイト、48_351_464異なる値:

$ shuf _1 |  sed 'N;s/\n/ /' > _11

(次の数値はあまり正確ではありません):

┌────────┬────────┬────────────────┬────────┬──────┐
│        │ Sol1   │ Sol2           │ Sol3   │ Sol4 │
│        │ sort...│ Perl DB        │ awk    │ Perl │
├────────┼────────┼────────────────┼────────┼──────┤
│ case 1 │ 6m15   │ 6m17           │ 0m28   │ 0m28 │
├────────┼────────┼────────────────┼────────┴──────┤
│ case 2 │ 11m15  │ 81m44          │ out of memory │
├────────┼────────┼────────────────┼────────┬──────┤
│ case 2 │        │ 5m54 /cache=2G │        │      │
└────────┴────────┴────────────────┴────────┴──────┘

キャッシュ付きのsol2は次のとおりです。

use DB_File;
use Fcntl ;

$DB_HASH->{'cachesize'} = 2000_000_000;
tie %h, 'DB_File', "_my.db", O_RDWR|O_CREAT|O_TRUNC, 0640, $DB_HASH;

while(<>){ not $h{$_} and print and $h{$_}=1 }

ソートは、cachesizeオプションを追加して最適化することもできます(行われません)。

簡単な結論:

  • sortは素晴らしいコマンドです!
7
JJoao

私は使いました

awk -v BINMODE=rw '!($0 in a){a[$0];print}' infile >> outfile

BINMODE = rw:行末のターミネーターを満足させるため。 (私は混合os環境に住んでいます)

ロジックはシンプルです。

現在の行が連想配列にない場合は、それを連想配列に追加して、出力に出力します。

このアプローチにはメモリの制限があるかもしれません。非常に大きなファイルとファイルのセットの場合、私はこれのバリエーションを使用して、ファイルストレージを使用して制限を超えました。

1
John

問題の順序を維持するセマンティクスには、すばらしい特性があります。問題を細分化できます。できるよ split -l 1000000入力ファイル;それが生成する1000000行の断片は、字句的に順序付けられた名前を持っています。次に、ピースを一意化します。次に(2番目のパスとして)それらの出力を単一化します。

これは、メモリ不足の問題を(メモリ要件に上限を設けることで)解決しますが、マルチパスソリューションに変えます。

具体的には:

入力データを生成します。

$ cat make-uniqm-input.py
#!/usr/bin/env python
import random
n = 1000000
for i in xrange(0, n):
    print random.randint(1000, 2000)

$ python make-uniqm-input.py  > uniqm-input.txt

$ wc -l uniqm-input.txt
 1000000 uniqm-input.txt

入力データを分割します。

$ split -l 10000 uniqm-input.txt

$ ls x?? | head
xaa
xab
xac
xad
xae
xaf
xag
xah
xai
xaj

$ ls x?? | wc -l
     100

$ cat x?? | wc -l
 1000000

Uniqifierを一度にすべて実行します(すべての一意の入力行をメモリに保持します)。

# 'uniqm' is any order-preserving uniq implementation, such as
# gawk '!counts[$0]++'.
$ uniqm < uniqm-input.txt > output-no-splitting.txt

$ wc -l output-no-splitting.txt
    1001 output-no-splitting.txt

分割された部分に対してuniqifierを実行し(各部分からの一意の入力行のみをメモリに保持)、2番目のパスとして削減します。

$ for x in x??; do uniqm < $x; done | uniqm > output-with-splitting.txt

$ wc -l output-with-splitting.txt
    1001 output-with-splitting.txt

比較:

$ diff output-no-splitting.txt output-with-splitting.txt

$ head uniqm-input.txt
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

$ head output-with-splitting.txt
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

入力内の一意の行と一意でない行の比率、および入力行の混合度がわからないため、必要な分割ファイルの数に関して行う調整がいくつかあります。

0
John Kerl

別のアプローチ(別の答えとして投稿する価値があります)は、一時ファイルを作成するファイル分割アプローチの代わりに、uniqifierソフトウェア自体でバッチ処理を実行します。たとえば、説明のためにRuby uniqifier実装を使用します。

require 'set'
line_batch_count = 50000 # tunable parameter
lines_seen = Set.new
line_number = 0
ARGF.each do |line|
   line_number += 1
   if (line_number % line_batch_count) == 0
     lines_seen.clear
   end
   unless lines_seen.include? line
      puts line
      lines_seen << line
   end
end

アイデアは、ハッシュセットを時々クリアすることです。次に、これは反復的になります:

$ cat uniqm-input.txt | Ruby uniqm-capped.rb | wc -l
   20021

$ cat uniqm-input.txt | Ruby uniqm-capped.rb | Ruby uniqm-capped.rb | wc -l
    1001

$ cat uniqm-input.txt | Ruby uniqm-capped.rb | Ruby uniqm-capped.rb | head
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

したがって、行数が1つの反復から次の反復に変更されなくなるまで、この上限付きバージョンを繰り返し実行できます。

このcapped-uniqm手法は言語に依存しないことに注意してください。lines_seen awk、python、Perl、C++などを使用しているかどうかに関係なく、N行ごとに配列します。これらすべての言語にはset-clearメソッドがあります。 awkdeleteは非標準ですが一般的だと思います。

0
John Kerl