« Mac mini Early 2009にHigh Sierraをインストールしてみる | トップページ | ワンライナーで実践する簡単データ処理(その2 疑似データベース編) »

2018.09.19

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

テキストファイルからなるデータの集計を取るとき、shellを使ってやると手早く処理することができます。

Perlで同じことができるのは当然ですが、時にはshellとコマンドを組み合わせた方が手軽なこともありますよね。今回は、その小技を忘れないようにメモ。

例として、論文の著者の所属機関の国籍(または地域)を集計する場合を考えます。共同研究の場合、1論文の著者は複数機関に亘ることになります。元データはWeb of Scienceの検索結果をexportしたもので、以下のようなタブ区切りのテキストファイルsavedrecs_0001_2440.txtです。


... C1 ... UT ...
... [Yin, Wotao] Rice Univ, Dept Computat & Appl Math, Houston, TX 77005 USA; [Osher, Stanley; Darbon, Jerome] Univ Calif Los Angeles, Dept Math, Los Angeles, CA 90095 USA; [Goldfarb, Donald] Columbia Univ, Dept Ind Engn & Operat Res, New York, NY 10027 USA ... WOS:000207567300006 ...
... [Wang, Yilun; Yin, Wotao; Zhang, Yin] Rice Univ, Dept Computat & Appl Math, Houston, TX 77005 USA; [Yang, Junfeng] Nanjing Univ, Dept Math, Nanjing 210093, Jiangsu Prov, Peoples R China ... WOS:000207567500003 ...
...

C1は著者の所属の繰り返し項目です。UTはアクセッション番号、要するに主キーです。
まず、主キーUTと繰り返し項目C1を正規化してやります。

C1は23番目、UTは63番目のカラムの項目です。

繰り返し項目C1について、'; ['を区切り文字として改行を入れ、改行を入れた行の直前にUTを挿入するという操作をします。コマンドは以下のとおりになります。


$ LF=$(printf '\\\012_');LF=${LF%_};cat savedrecs_0001_2440.txt | cut -f23,63 | awk -F'\t' 'BEGIN{OFS="\t"}{print $2,$1}' | sed 's/; \[/'"$LF"'\[/g' | awk -F'\t' 'BEGIN{OFS="\t"}{if(NF==2) {UT=$1;print $1,$2} else {print UT,$1}}' | sed '1,1d' | sort -k1 > temp01.txt

すると、次のようなtemp01.txtが生成されます。

WOS:000207567300006 [Goldfarb, Donald] Columbia Univ, Dept Ind Engn & Operat Res, New York, NY 10027 USA
WOS:000207567300006 [Osher, Stanley; Darbon, Jerome] Univ Calif Los Angeles, Dept Math, Los Angeles, CA 90095 USA
WOS:000207567300006 [Yin, Wotao] Rice Univ, Dept Computat & Appl Math, Houston, TX 77005 USA
WOS:000207567500003 [Wang, Yilun; Yin, Wotao; Zhang, Yin] Rice Univ, Dept Computat & Appl Math, Houston, TX 77005 USA
WOS:000207567500003 [Yang, Junfeng] Nanjing Univ, Dept Math, Nanjing 210093, Jiangsu Prov, Peoples R China
...

コマンドについて逐次的に説明します。

LF=$(printf '\\\012_');LF=${LF%_};
sedコマンドで区切り文字を改行に置換する前準備です。変数LFに\012(0x0aすなわちLF(改行)コード)を入れたいのですが、そのままだと改行をコマンド行の最後と認識してしまうので、後ろに'_'を付けて後からトリミングすることで、shellを介してsedコマンドに改行コードを通してやります。
この方法は、松浦智之,「すべてのUNIXで20年動くプログラムはどう書くべきか」,C&R研究所,に記載されています。

cat savedrecs_0001_2440.txt
テキストファイルの内容を後続コマンドに流してやります。

cut -f23,63
23カラム目と63カラム目を抽出します。

awk -F'\t' 'BEGIN{OFS="\t"}{print $2,$1}'
UTが前にある方が都合が良いので、ひっくり返すためだけに入れています。-F と OFSを指定して入力区切り文字と出力区切り文字にタブを設定しています。

sed 's/; \[/'"$LF"'\[/g'
'; ['を改行+'['に変換します。

awk -F'\t' 'BEGIN{OFS="\t"}{if(NF==2) {UT=$1;print $1,$2} else {print UT,$1}}'
カラム数が2であれば1カラム目をアクセッション番号として変数UTに保存し、カラム数が1であれば保存したアクセッション番号を付加します。

sed '1,1d'
先頭行はカラム名なので削除します。

sort -k1
1カラム目のアクセッション番号でソートします。

最後に、国ごとのシェアを集計します。
所属機関はカンマで区切られていますが、最後の項目は所属機関国籍(地域)です。
ただし、USAの場合のみ、州の住所が付加されているので、USAのみを抽出します。
例:CA 94720 USA
1論文あたり、著者の所属機関が複数ある場合、もし、同じ国/地域であれば国/地域を1つとカウントします。要するに、国/地域の関連度を計ることになります。

すると、コマンドは次のとおりになります。


$ cat temp01.txt | awk -F'\t' 'BEGIN{OFS="\t"}{c=split($2,a,", ");print $1, a[c]}' | sed 's/'"$(printf '\t')"'.*USA/'"$(printf '\t')"'USA/' | sort | uniq | cut -f2 | sort | uniq -c | sort -k1nr

コマンドについて逐次的に説明します。

cat temp01.txt
テキストファイルtemp01.txtの内容を後続コマンドに流してやります。

awk -F'\t' 'BEGIN{OFS="\t"}{c=split($2,a,", ");print $1, a[c]}'
awkのsplit関数を使って', 'で区切られた項目を配列aに格納します。cに項目数を格納するので、a[c]には最後の項目が格納されています。

sed 's/'"$(printf '\t')"'.*USA/'"$(printf '\t')"'USA/'
タブの後にUSAがある場合、USAの前に付加された文字列を削除します。

sort
uniq コマンドを使うための準備としてソートします。

uniq
アクセッション番号及び国コードのまとまりで重複排除します。

cut -f2
国/地域のみ抽出します。

sort
uniq コマンドを使うための準備としてソートします。

uniq -c
国/地域ごとの出現回数をカウントします。

sort -k1nr
国/地域をその出現回数で降順に並べ替えます。

1242 Peoples R China
 649 USA
 253 England
 228 Australia
 135 Singapore
 133 Canada
 105 France
 105 Germany
 103 Spain
 101 India
...
このような形で結果が表示されました。 この程度であれば、Perlを使わなくてもshellとコマンドで集計できますね。

最後に、所属機関+国/地域の数をみてみましょう。


$ TAB=$(printf '\t');cat temp01.txt | sed 's/\[.*\] //' | awk -F'\t' 'BEGIN{OFS="\t"}{c=split($2,a,", ");print $1, a[1], a[c]}' | sed 's/\'"$TAB"'\(.*\)'"$TAB"'.*USA/'"$TAB"'\1'"$TAB"'USA/' | sort | uniq | cut -f2,3 | sort | uniq -c | sort -k1nr

TAB=$(printf '\t');
変数TABにタブコードを格納します。

cat temp01.txt
テキストファイルtemp01.txtの内容を後続のコマンドに流します。

sed 's/\[.*\] //'
[]で囲まれた文字列を削除します。

awk -F'\t' 'BEGIN{OFS="\t"}{c=split($2,a,", ");print $1, a[1], a[c]}'
', 'で区切られた項目ごとに分割し、1カラム目と最初と最後の項目を表示します。

sed 's/\'"$TAB"'\(.*\)'"$TAB"'.*USA/'"$TAB"'\1'"$TAB"'USA/'
3カラム目のUSAの前に付加された文字列を削除します。2カラム目と3カラム目をTABで区切って出力します。

sort
後続のuniqコマンドのためにソートします。

uniq
アクセッション番号、所属機関、国/地域のまとまりで重複排除します。

cut -f2,3
2カラム目と3カラム目を抽出します。

sort
後続のuniqコマンドのために予めソートします。

uniq -c
所属機関と国/地域のまとまりで出現回数をカウントします。

sort -k1nr
1カラム目の出現回数で降順にソートします。

結果は以下のようになります。


126 Chinese Acad Sci Peoples R Chin
100 Harbin Inst Technol Peoples R China
78 King Abdulaziz Univ Saudi Arabia
77 Nanyang Technol Univ Singapore
...

|

« Mac mini Early 2009にHigh Sierraをインストールしてみる | トップページ | ワンライナーで実践する簡単データ処理(その2 疑似データベース編) »

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

コメント

コメントを書く



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




トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1024423/74061573

この記事へのトラックバック一覧です: ワンライナーで実践する簡単データ処理(その1 繰り返し項目集計編):

« Mac mini Early 2009にHigh Sierraをインストールしてみる | トップページ | ワンライナーで実践する簡単データ処理(その2 疑似データベース編) »