« WSLでJupyter notebookを使う&ショートカット・アイコンから実行する | トップページ | macOS Catalinaにアップデートしてみた結果 »

2019.09.04

Shell ScriptとPOSIXコマンドで実践する簡単データ処理(その1)

以前、Shell Scriptとコマンドを使ったワンライナーで実践するデータ処理の記事を書きました。

ワンライナーで実践する簡単データ処理(その1 繰り返し項目集計編)
ワンライナーで実践する簡単データ処理(その2 疑似データベース編)

今回は、これらの記事に少し補足をしたいと思います。ここでは、繰返し項目処理と集計処理を取り扱います。
例に用いるデータは以下のとおり。タブ区切りテキストデータです。また、繰返し項目を” | ”で区切ります。
データ項目にスペースが入ることがある可能性を考慮して、ここでは項目区切り文字をタブに統一して取り扱います。 

list.txt


ID00001 f1ec 2013 IPC00 | IPC01 JP | US
ID00002 097d 2017 IPC02 US
ID00003 6a5b 2016 IPC03 | IPC04 | IPC05 JP | US | US
ID00004 6262 2015 IPC01 DE | EP
ID00005 8ab4 2014 IPC06 US
ID00006 3551 2016 IPC07 CN
ID00007 ca3e 2015 IPC03 | IPC08 | IPC09 CN | US
ID00008 af39 2014 IPC01 | IPC03 | IPC09 JP
ID00009 d168 2015 IPC10 JP | WO
ID00010 bc4f 2016 IPC11 | IPC01 CN | CN
ID00011 1efd 2016 IPC01 | IPC12 JP | WO
ID00012 2b22 2017 IPC13 | IPC14 | IPC15 | IPC16 DE | WO
ID00013 9776 2017 IPC04 | IPC15 US | JP | EP

4列目及び5列目が繰返し項目になっています。

list.txtの1, 2, 3, 5列目を抽出し、5列目は最初の項のみ使うこととして、この部分を抜き出します。


$ TAB=$(printf '\t'); cat list.txt | cut -f 1,2,3,5 | sed 's/'"$TAB"'\([A-Z]*\) |.*$/'"$TAB"'\1/' > I_I_Y_C.txt

ここで作成したI_I_Y_C.txtの内容は以下のとおり。


ID00001 f1ec 2013 JP
ID00002 097d 2017 US
ID00003 6a5b 2016 JP
ID00004 6262 2015 DE
ID00005 8ab4 2014 US
ID00006 3551 2016 CN
ID00007 ca3e 2015 CN
ID00008 af39 2014 JP
ID00009 d168 2015 JP
ID00010 bc4f 2016 CN
ID00011 1efd 2016 JP
ID00012 2b22 2017 DE
ID00013 9776 2017 US

次に、list.txtの1列目と4列目を抽出し、4列目の繰返し項目を正規化します。


$ LF=$(printf '\\\12_');LF=${LF%_}; cat list.txt | cut -f 1,4 | sed 's/ | /'"$LF"'/g' | awk -F '\t' 'BEGIN{OFS="\t"}{if(NF==2){UI=$1;print $1,$2}else{print UI,$1}}' > I_IPC.txt

I_IPC.txt


ID00001 IPC00
ID00001 IPC01
ID00002 IPC02
ID00003 IPC03
ID00003 IPC04
ID00003 IPC05
ID00004 IPC01
ID00005 IPC06
ID00006 IPC07
ID00007 IPC03
ID00007 IPC08
ID00007 IPC09
ID00008 IPC01
ID00008 IPC03
ID00008 IPC09
ID00009 IPC10
ID00010 IPC11
ID00010 IPC01
ID00011 IPC01
ID00011 IPC12
ID00012 IPC13
ID00012 IPC14
ID00012 IPC15
ID00012 IPC16
ID00013 IPC04
ID00013 IPC15

次に、I_I_Y_C.txtとI_IPC.txtをjoinコマンドで連結します。


$ join -t "$(printf '\t')" -1 1 -2 1 -o 1.1,1.2,1.3,2.2,1.4 I_I_Y_C.txt I_IPC.txt > table.txt

出力は以下のとおり。

table.txt


ID00001 f1ec 2013 IPC00 JP
ID00001 f1ec 2013 IPC01 JP
ID00002 097d 2017 IPC02 US
ID00003 6a5b 2016 IPC03 JP
ID00003 6a5b 2016 IPC04 JP
ID00003 6a5b 2016 IPC05 JP
ID00004 6262 2015 IPC01 DE
ID00005 8ab4 2014 IPC06 US
ID00006 3551 2016 IPC07 CN
ID00007 ca3e 2015 IPC03 CN
ID00007 ca3e 2015 IPC08 CN
ID00007 ca3e 2015 IPC09 CN
ID00008 af39 2014 IPC01 JP
ID00008 af39 2014 IPC03 JP
ID00008 af39 2014 IPC09 JP
ID00009 d168 2015 IPC10 JP
ID00010 bc4f 2016 IPC11 CN
ID00010 bc4f 2016 IPC01 CN
ID00011 1efd 2016 IPC01 JP
ID00011 1efd 2016 IPC12 JP
ID00012 2b22 2017 IPC13 DE
ID00012 2b22 2017 IPC14 DE
ID00012 2b22 2017 IPC15 DE
ID00012 2b22 2017 IPC16 DE
ID00013 9776 2017 IPC04 US
ID00013 9776 2017 IPC15 US

この出力にawk又はgrepでフィルタリングすることで任意行を抽出することが可能です。
例えば、2015以下のJPに対応するIPC数ランキングを取得するには以下のようにします。


$ cat I_IPC.txt | join -t "$(printf '\t')" -1 1 -2 1 -o 1.1,1.3,2.2,1.4 I_I_Y_C.txt - | awk -F '\t' 'BEGIN{OFS="\t"}$2<=2015 && $4=="JP"{print $0}' | cut -f 3 | sort | uniq -c | sort -k 1,1nr | sed 's/^ *//'

出力は以下のとおり。


2 IPC01
1 IPC00
1 IPC03
1 IPC09
1 IPC10

最後のsed 's/^ *//'は、カウント数が大きいと桁ずれを起こすことがあるため、固定長の前提が置けないため、むしろ先頭のスペースを削除してスペース区切りにします。こうするとExcelで読み込むときに誤りが起きにくくなります。

 

5列目のランキングはもっと簡単です。


$ cat I_I_Y_C.txt | cut -f 4 | sort | uniq -c | sort -k 1,1nr | sed 's/^ *//'

とすると、


5 JP
3 CN
3 US
2 DE

となります。

以上です。なお、この記事に続きがあるかどうかは分かりません(笑)

|

« WSLでJupyter notebookを使う&ショートカット・アイコンから実行する | トップページ | macOS Catalinaにアップデートしてみた結果 »

パソコン・インターネット」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




« WSLでJupyter notebookを使う&ショートカット・アイコンから実行する | トップページ | macOS Catalinaにアップデートしてみた結果 »