【Awk&Sed活用講座】CSVファイルから重複データを見つける&重複をカウントする


2022/05/04

思いついたときに書き綴るシェルコマンドからのCSVデータ使いこなし講座です。

以前、AwkでCSVテキストのデータセットの重複を操作するための応用的な課題を取り上げたことがあります。

そちらの記事ではCSVデータセットをマトリックス生データを意識して重複を操作することを中心に解説していたので、普通のCSVデータシートとはまた少し違った取り扱いになっていました。

今回はCSVファイルからデータの重複を取り扱うテクニックの基礎を取り上げてみます。


Uniqコマンドで重複を操作する

まずはAwkやSedなどを使って技巧的なやり方に頼らずにシェルコマンドの基本に従うと、「Uniqコマンド」でテキストの重複を操作できます。

とりあえず例として以下の重複有りの従業員リストを対象に重複操作を考えます。

なお、ローカルにcsvテキストで保存して読み出しても良いのですが、今回は変数・
EMPLOYEEにデータを入れて使います。

            
            $ EMPLOYEE=$(cat <<EOF
山下モゲ雄,営業部,本社,3年
島田フガ子,経理部,名古屋支部,15年
上岡ムメ美,海外部,メキシコ支部,8年
岡田ピポ太,製造部,山口工場,8年
佐藤ゴバ文,製造部,山口工場,11年
田川ポゥ子,人事部,本社,9年
沢口モフ代,人事部,本社,4年
銭形ガメ吉,海外部,メキシコ支部,11年
上岡ムメ美,営業部,本社,23年
京谷マハ次,製造部,山口工場,3年
園田フマ由,人事部,本社,17年
山田ケム紀,営業部,本社,15年
田川ポゥ子,製造部,ベトナム工場,12年
満田クタ郎,営業部,本社,2年
島寺ルン大,営業部,本社,18年
香下ウル蔵,製造部,山口工場,5年
田頭モフ雄,営業部,名古屋支部,16年
岡田ピポ太,営業部,本社,4年
蒲田ウオ奈,海外部,メキシコ支部,9年
郷田ポポ生,営業部,名古屋支部,4年
梅岡ボル伍,経理部,本社,25年
香下ウル蔵,営業部,名古屋支部,2年
岡田ピポ太,製造部,山口工場,11年
亀川ヲル士,製造部,山口工場,14年
EOF
)
        
では、Uniqコマンドで重複を削除表示します。

uniqの出来ることは基本に
重複行の出現回数を調べる重複していない行だけ出力する重複した行だけ出力するの主に3つです。

単純な重複をテキストの各行に行うことに特化したコマンドになります。

uniq単体だと複雑な処理は不可能ですので、CSVの任意の列だけ取り出すため
Cutコマンド、重複が連続していなとuniqが重複を見つけられないためSortコマンドの補助をそれぞれ必要とします。

            
            $ echo "$EMPLOYEE" | cut -f1 -d, | sort | uniq
上岡ムメ美
亀川ヲル士
京谷マハ次
佐藤ゴバ文
園田フマ由
山下モゲ雄
山田ケム紀
岡田ピポ太
島寺ルン大
島田フガ子
梅岡ボル伍
沢口モフ代
満田クタ郎
田川ポゥ子
田頭モフ雄
蒲田ウオ奈
郷田ポポ生
銭形ガメ吉
香下ウル蔵
        
ということで、Uniqコマンドはデータから重複を見つけるだけになります。

さほど応用は期待できません。


Awkで重複を見つける

AwkでCSVデータの重複を取り扱うメリットには、列(カラム)番号を個別に制御するような操作に向いています。

重複を除するのに良く紹介されている使い方は、

            
            $ echo "$EMPLOYEE" | awk -F, '!name[$1]++ { print $0; }'
山下モゲ雄,営業部,本社,3年
島田フガ子,経理部,名古屋支部,15年
上岡ムメ美,海外部,メキシコ支部,8年
岡田ピポ太,製造部,山口工場,8年
佐藤ゴバ文,製造部,山口工場,11年
田川ポゥ子,人事部,本社,9年
沢口モフ代,人事部,本社,4年
銭形ガメ吉,海外部,メキシコ支部,11年
京谷マハ次,製造部,山口工場,3年
園田フマ由,人事部,本社,17年
山田ケム紀,営業部,本社,15年
満田クタ郎,営業部,本社,2年
島寺ルン大,営業部,本社,18年
香下ウル蔵,製造部,山口工場,5年
田頭モフ雄,営業部,名古屋支部,16年
蒲田ウオ奈,海外部,メキシコ支部,9年
郷田ポポ生,営業部,名古屋支部,4年
梅岡ボル伍,経理部,本社,25年
亀川ヲル士,製造部,山口工場,14年
        
とするだけで簡単に強力な重複操作がAwkだけで実現します。

なお、先程のAwkのスクリプト部分はもっと圧縮できて、

            
            $ echo "$EMPLOYEE" | awk -F, '!_[$1]++'
        
と出来るのは良く知られたところです。


Sedで重複を見つける

先程の項目で紹介したように、重複操作においてソート・対象列抽出・正規表現ともに利用できるAwkの独壇場であることが分かりますが、たまにAwk(gawk)が使えない環境もあります。

Sedでもかなり頑張れば重複操作は出来なくはありません。

ただし、
ホールドスペースとパターンスペースの理解をしていないといけませんので、万人に使いやすいテクニックとは言えないのが現状です。

uniq同様、離れた行に重複する要素が存在すると、重複する要素を探す処理に時間がかかるので、一度sortを読み込んで連続行に揃えおく処理をはさみます。

さらに入力各行ごとの比較はできるものの、柔軟に部分文字を抽出してからの比較となるとかなり厳しい処理が必要で、やはりCutコマンドでターゲットのCSV列を抽出する下処理が必要です。

Sedでややこしいのが、日本語を含むUnicode文字では正規表現のキャプチャが出来ないかもしれないので、sedコマンド前に、
LC_ALL=Cをロケールを明示して利用する必要もあるかも知れません。

            
            $ echo "$EMPLOYEE" | sort | cut -f1 -d, | LC_ALL=C sed -e '
    $!N              #最終行以外は全て先読みで次の行をパターンスペースに改行&追加
    /^\(.*\)\n\1$/!P #2行目に同じ行パターン来た場合、パターンスペースの最初の改行までを表示
    D                #パターンスペースを空にして次の入力行からプログラムを繰り返す
'
#👇出力
上岡ムメ美
亀川ヲル士
京谷マハ次
佐藤ゴバ文
園田フマ由
山下モゲ雄
山田ケム紀
岡田ピポ太
島寺ルン大
島田フガ子
梅岡ボル伍
沢口モフ代
満田クタ郎
田川ポゥ子
田頭モフ雄
蒲田ウオ奈
郷田ポポ生
銭形ガメ吉
香下ウル蔵
        
...Uniqコマンド相当のことしか出来ないのに、Sedスクリプトの実装はNやらPやらDと、利用上奥の深いコマンドを駆使する必要があります。

この点において、Sedの勉強には良いのですが、本番の重複操作はやるべきではないでしょう。

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。