ファイルから重複レコードを削除し、一意の識別子を無視します。

ファイルから重複レコードを削除し、一意の識別子を無視します。

28フィールド/ヘッダー/プロパティ(カンマ区切り)を含むファイルがあります。フィールド#はレコードを一意にします。ただし、残りのフィールドは同じにすることができます。重複した項目を見つけて1つだけ保管する必要があります。 2回目の反復よりも1回目の反復を維持する方が簡単な場合は問題ありません。例:

入力ファイル:

1,ed23,jon,doe,director,usa
2,ed23,jon,doe,director,usa
3,er67,jake,Kogan,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

希望の出力:

2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

答え1

入力例は混乱しています。最初の行(列見出し)にはフィールド区切り記号のコンマも含まれておらず、ほとんどの行には成績と成績フィールドの間にカンマがありません。

少し健全な入力を提供するために、次のように編集しました。

$ cat input.txt 
ID, uid  ,firstname ,lastname,   grade    , country n28
1 , ed23 , jon     ,   doe   ,  director  ,  usa
2 , ed23 ,  jon     ,  doe   ,  director     , usa
3 , er67 ,  jake     , Kogan ,  director     , usa
4 , er67 ,  jake     , Kogan ,  director     , usa
5 , dc10 ,  Charls     ,Morg ,  manager      , usa
6 , kc56 ,  patel     ,Kumar ,  associate    , india

チートを削除する簡単な実装は次のとおりです。

$ awk -F' *, *' -v OFS=, \
    'NR==1 {$1=$1;$0=$0; print; next};
     {id=$1; $1=""; $0=$0; if (!seen[$0]++) {print id $0}}' input.txt 
ID,uid,firstname,lastname,grade,country n28
1,ed23,jon,doe,director,usa
3,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

これは、入力フィールド区切り文字(FS)を0個以上のスペースに設定し、その後にカンマ、0個以上のスペースを設定し、OFS出力フィールド区切り文字()をコンマのみに設定します。つまり、すべてのフィールドで前後のスペースを効果的に削除します。

最初の入力行(NR==1)の場合は、awkトリックを使用して入力行の形式を再指定します。つまり、フィールドを変更(元の値に設定)を設定します$0=$0。行は新しいOFSを使用するように再フォーマットされます。その後、印刷して次の行に移動します。

残りの入力では、$ 1という変数に$ 1をid空の文字列に設定し、$0=$0IDと行の残りの部分を印刷する前にトリックを再利用します(行から$ 1を効果的に削除します)。

出力例とは異なり、以下は印刷されます。最初最後の行ではなく重複した行 - 最初に見たときは検出しやすいが、最後に見たときは検出しにくい(入力内容をすべて読まないと不明)。また、これは反復回数をカウントしません。

これらの両方を行うには、出力を生成する前に入力ファイル全体を読み、2番目の配列(重要な行にすることもできます)ids

$ awk -F' *, *' -v OFS=, \
   'NR==1 {$1=$1;$0=$0",count";print;next};
   {id=$1; $1=""; $0=$0; seen[$0]++; ids[$0]=id};
   END { for (id in ids) {print ids[id] id, seen[id]} }' input.txt  | \
 sort -n
ID,uid,firstname,lastname,grade,country n28,count
2,ed23,jon,doe,director,usa,2
4,er67,jake,Kogan,director,usa,2
5,dc10,Charls,Morg,manager,usa,1
6,kc56,patel,Kumar,associate,india,1

sort -nこれは、awkの連想配列が順序がないため、半ランダムな順序で表示されるために使用されます。 GNU awkには、配列に使用asort()できる値で配列を並べ替える関数がありますが、idsa)移植性がなく、b)出力をsort -n

答え2

コンマで区切られたきちんとした入力には、次のスクリプトがawk適している可能性があります。

awk -F, '{X=""; for (i=2;i<29;i++) X=X " " $i;} \
     seen[X]!=1 {print;} \
     {seen[X]=1;}' < input

最初のawkルールは、入力から2〜28の「単語」を選択して「キー」を作成します(引数に応じてカンマで区切られ-F,たすべての項目は「単語」です)。次のルールは、「キー」がすでに登録されていない限り、その行を印刷し、3番目のルールはその行のキーを登録します。

答え3

ファイルが「シンプルなCSV」形式であるとします。つまり、データにカンマや改行が含まれていないことを意味します。

$ tac file | awk -F , '{ key = $0; sub("[^,]*,", "", key) } !seen[key]++' | tac
2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

awk上記のパイプラインの中央にあるコードは、最初の行を除くすべてのフィールドのハッシュのキーとして使用される文字列を生成します。それは印刷されます最初特定のキーを持つ行が表示され、すべての重複エントリは無視されます。

手に入れたいと思うから最後tac繰り返すには、(GNU coreutilsで)を使用してプログラムに入力する前に、入力行の順序を逆に置き換えますawk。その後、プログラムの出力を反転しますawk

このアプローチの欠点は、計算されたキーがすべての一意の行から最初のフィールドを引いたサイズを合計しただけのメモリを使用することです。

以下はよりメモリ効率の良いアプローチですが、重複行が常に一緒に表示されるように入力が整列しているとします。

$ tac file | awk -F , '{ key = $0; sub("[^,]*,", "", key) } key != prev; { prev = key }' | tac
2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

答え4

uniq上記の説明を詳しく説明する - 方法:

$ tr ',' '\t' < temp/testfile | uniq -f 1 | tr '\t' ','
1,ed23,jon,doe,director,usa
3,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

\tデータ内のスペースを避けるための区切り文字として使用されます。

uniq見つかった最初の一意の行が保持されます。 「最後」エントリを維持する必要がある場合は、ファイルの終わりから最初に作業する必要があります。次の方法を使用してこれを実行できますtac

$ tac temp/testfile|tr ',' '\t' | uniq -f 1 | tr '\t' ','|tac
2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

関連情報