Perl Hackers Hub

第69回表形式データを操るUNIXシェル型Perl製コマンド群 ―ビッグデータ時代の汎用的なデータ整備と分析のために(2)

前回の(1)こちらから。

表データの把握

(2)では、表データの把握に役立つコマンドを紹介します。紹介するcolsummaryの結果を眺めることで、入力データ各列の値の範囲や最頻値などを容易に把握できます(a)⁠b)⁠d)⁠f)は部分的に、(g)⁠h)⁠i)⁠j)は大幅に解決します⁠⁠。vennにより、複数のデータの重なり具合を把握できます(e)を検出し、(i)⁠j)を解決します⁠⁠。適当な列をcselでいくつか選んで、crosstableでクロス集計表を作成することで、2列の値の関係がわかります(c)⁠d)を簡便に検出できます⁠⁠。

クロス集計表の作成 ─⁠─crosstable

crosstableの基本機能は、クロス集計表を出力することです。Excelのピボットテーブルとほぼ同じですが、Excelだとマウス操作を伴うので再現性に難があります。シェル芸やSQLでは実現困難です。

Twitterから収集したデータに対する例

Twitterのツイートを特定のキーワード群で検索して取得したデータであるtwitter.tsvに対してcrosstableを実行し、何日の何時台に何件取得したかを調べてみましょう。

twitter.tsvは各行に日付、時刻、ツイート内容が記録されているので、時刻の時未満の部分を除去し、crosstableで集計します。

各行「年-月-日\t時:分:秒\t発言内容」の:以下をPerlで除去
$ crosstable <( perl -pe's/:.*//' twitter.tsv )

得られた結果をターミナルからExcelにコピー&ペーストし、Excelの条件付き書式の機能で値に応じて色を付けたのが図1です。

図1 crosstableでツイート収集件数を日付×時刻帯ごとに集計
図1 crosstableでツイート収集件数を日付×時刻帯ごとに集計

出力からの知見

図1から、Twitterのツイートの取得に失敗していた日時や、投稿が不自然に増減した日時を色の濃さで容易に判読できます。また、2列に現れた値の範囲や欠損を明確にする目的にも使えます。

全列の特徴量の表示 ─⁠─colsummary

全列のさまざまな特徴量を、読みやすく整理して画面に表示するのがcolsummaryです。列の特徴量として表1の各項目を色付きで出力します。

表1 colsummaryの出力表の各列の意味
出力表の列名
(列の色)
意味
列番(白)入力表の列の番号。1始まり
異なる値(緑)入力表のその列が、何通りの異なる値を持っていたかを表す。すべて同じ値なら1となる
値の範囲(白)各出現値を「0の0回以上の繰り返し」「数値」「それ以外の文字列」に分類し、それぞれを最小値~最大値の形(または辞書順の先頭~末尾)で表し、|で区切る
最頻値(白)頻度(出現回数)の多い値を、上位から順に抽出指定数で指定した個数だけ|で区切って出力する
頻度(重複)
(緑)
上位と下位の頻度を、それぞれ抽出指定数で指定した個数だけ|で区切って出力し、上位~下位の形で表す。同じ頻度が複数ある場合は、括弧内にその重複数を示す
桁数(藍)各値の文字数を0か1以上により|で区切り、1以上を最小値~最大値の形で表す

これらの出力項目を目で読み取ることで、多数の表が提供された場合でも全列の意味がわかりやすくなります。その結果、どの表のどの列が大事なのか、複数の表のどの列を組み合わせると意味のある結合になるのかなどが一目瞭然となり、分析がはかどります。

TRC書誌情報の表に対する例

次の例では、図書館流通センターのTRC新刊図書オープンデータのページの「TRC新刊書誌情報 2021年06月05日(tsv形式⁠⁠」に対して、colsummaryを実行しています。

ダウンロードしたZIPファイルの解凍。次で使うT*0710.txtを生成
$ unzip TRCOpenBibData_20210710.zip

出力表の視認性のためにexpandtabを使用
$ colsummary -g3 -v9 -j -m0 T*0710.txt | expandtab -s30

実行結果は図2です。図2の出力の18行は、T*0710.txt[1]にあった18列に対応しています。4個のオプションの指定[2]は図を見やすくするためであり、なくともかまいません。

図2 colsummaryでTSVファイルの各列の性質を把握
図2 colsummaryでTSVファイルの各列の性質を把握

複数データの重なり把握 ─⁠─venn

vennは、各ファイルで延べ何個の文字列が行として出現したかを、具体的な出現値(辞書順の先頭と末尾)と併せて出力します。コマンド名のもととなったベン図は、集合どうしの重なりを表す図です。vennでは出現延べ数と具体値も出力されるため、ベン図だけではわからない多くの情報を整理して読み取ることができます。これにより、複数のファイル上のデータの重なり具合や包含関係を把握できます。

4個のファイルに対する例

ここでは、ファイルを4個用意する代わりに、プロセス置換を4個同時に使って実演します。

自作シェル関数yを作成。引数を文字ごとにばらして改行を挿入
$ function y(){ echo -n $* | perl -pe's/./$&\n/g' }

cardi.はcardinalityの略。minstrとmaxstrは辞書順の先頭と末尾
$ venn <(y ABCCC) <(y AABDEFG) <(y ABBDEFG) <(y DEEEFG)
cardi.  file1   file2   file3   file4   minstr  maxstr
2.      2       3       3       0       'A'     'B'
1.      3       0       0       0       'C'
4.      0       4       4       6       'D'     'G'

最初に自作シェル関数yを定義します。yは、引数を1文字ずつにばらして改行を挿入します。

次のコマンドラインは、あたかも{A,B,C,C,C}{A,A,B,D,E,F,G}{A,B,B,D,E,F,G}{D,E,E,E,F,G}に対応する4個のファイル(以下、file1~4とします)があって、それぞれの各行に1文字を格納した状態で[3]⁠、その4ファイルをvennに与えるかのように振る舞います。

vennのアルゴリズム

上記の例をもとに、vennのアルゴリズムを説明します。

まず、A~Gの各文字列が、file1~4に「現れたか/否か」「1/0」で表現します。すると、A~Bは1110、Cは1000、D~Gは0111となり、出力にある3パターンに分類されます。

次に、各パターンの異なる文字列を数えると、それぞれ2個(A、B⁠⁠、1個(C⁠⁠、4個(D、E、F、G)です。それを、左端のcardi.列[4]に記載します。

file1~4のそれぞれに、A~Bは2、3、3、0回、Cは3、0、0、0回、D~Gは0、4、4、6回現れています。これらの数を出力の各ファイルの列に記載します。

最後に、各パターンに出現した文字列の辞書順の先頭と末尾を右端のminstrとmaxstrの列に記載します。

出力表から読みとれること

出力の0の出現パターンなどから、⁠file1の持つ2+3+0=5個の文字列と、file4の持つ0+0+6=6個の文字列は共通部分がない⁠⁠、文字列Cは「出現延べ回数は3+0+0+0=3である」⁠file1以外に現れない」⁠同じ出現パターンを示す文字列はほかにない」⁠DやGと同じファイルに出現することはない」ことが読み取れます。

データの値の簡便な解読

本節では、意味や役割のわかりにくい値の解読や、使われ方を知るためのコマンドを紹介します。紹介するdigitdemogは、1行ごとにデータを読み取り、文字列の出現パターンを集計することにより、(a)⁠b)も検出しつつ、符号化された文字列パターンの解読に使うことで(f)を解決に導きます。colgrepを意味の不明な値を持つ行の抽出に使えば、(b)⁠f)を解決に導きます。

各行各桁に出現した全文字の頻度集計 ─⁠─digitdemog

ある列の文字列の出現パターンを知る場合は、digitdemog[5]が有用です。digitdemogの基本機能は、全行において各文字が何桁目に何回現れたかの出力です。

以下では、プロセス置換内でcsel -p11を使うことにより、T*0710.txtの11列目を取り出しています。

各文字が各行先頭から何文字目に何度出現したか。-.で追加のヒント
$ digitdemog -. <( csel -p11 T*0710.txt ) | expandtab
    1   2   3     4     5     6   7   8   9
'.' 0   1.  0     0     0     0   0   0   0
'0' 0   132 0     0     7     0   0   0   0
'1' 504 308 0     7     4     0   0   0   0
'2' 753 70  0     22    3     1.  0   0   0
'3' 102 13  0     7     3     0   0   0   0
'4' 0   10  0     0     0     0   0   0   0
'5' 0   96  0     0     2     0   0   0   0
'6' 0   312 0     0     11    0   0   0   0
'7' 0   31  1.    0     3     0   0   0   0
'8' 0   101 0     0     1     0   0   0   0
'9' 1.  286 0     0     3     0   0   0   0
'c' 0   0   1323. 0     0     36. 1.  0   0
'm' 0   0   0     1323. 0     0   36. 1.  0
'×' 0   0   36.   1.    0     0   0   0   0
end 134 0   0     0     1323. 0   0   36. 1.
※各値の末尾に仮想的な文字endを伴うと見なしている

この結果から、⁠ピリオド.は、先頭から2文字目に1回だけ出現する」0は、先頭から2文字目に132回、5文字目に7回現れ、ほかの場所には現れない」⁠空文字列が134個ある」ことがわかります。

文字列パターンの解読

上記でdigitdemogにオプション-.を与えたので、1.(8ヵ所⁠⁠、36.(4ヵ所⁠⁠、1323.(3ヵ所)のように出力数値にピリオドを伴うものがあります。これらは、同じ入力行に由来することを意味します。たとえば1323.と示された3ヵ所は、同じ1323行の各3箇所(3~5文字目)のそれぞれで定まる文字'c''m'endを数えています。

この追加のヒントから元データをもとに謎解きをすると、T*0710.txtの11列目の出現値は、空文字列(134個)『整数cm』⁠1323個)『整数×整数cm』⁠36個)『9.7×整数cm』⁠1個)のみ」とわかります。

この11列目は、単に文字列として扱うのではなく、各値から2個以内の数値を取り出す変換を行うのが良いでしょう。すると、大小比較や比の演算対象となる数として扱えるので、分析の観点から適切です。

指定列で正規表現検索 ─⁠─colgrep

colgrepは、指定列をgrepして該当する行全体を抽出します[6]⁠。

以下では、表中の空文字列(以下、⁠空」とします)が何を意味するかの調査例として、ISBNコードが空欄の図書について調べています。

1列目のISBNコードが空の図書をすべて検出。cselでタイトルなどのみ表示
$ colgrep -c1 -e'^$' T*0710.txt | csel -p11,17,2
match:7 unmatch:1487 total:1494 (colgrep)
30cm    ¥1800   東京2020オリンピック公式ガイドブック
30cm    ¥1800   東京2020パラリンピック公式ガイドブック
21cm    ¥545    現代俳句 令和3年7月号
26cm    ¥8100   法令全書 令和3年5月号
30cm    ¥3000   賃金事情調査 令和2年
30cm    ¥4500   水産油脂統計年鑑 2020年
26cm    ¥1000   法と民主主義 NO.559(2021-6)

-c1-e'^$'により、1列目(ISBNコード)で正規表現^$にマッチ[7]した行を出力します(1494行中7行⁠⁠。

上記の結果から、ISBNコードが何かの事情で未記載の、分析作業で注意を要する可能性のある図書7冊を把握できます。

特別値を持つ行の特徴を比較でとらえる

また別の少し実用的な例で試してみます。⁠11列目(本の大きさ)が空の場合」に何が起きたのかを調べます。

抽出した行全体についての性質をcolsummaryで調べる
$ colgrep -c11 -e'^$' T*0710.txt | colsummary
134 line(s) read;...
※11列目が空なら10、15、16、18列目もすべて空とわかる

比較のために、上記で抽出した行以外(-~指定)を調べる
$ colgrep -~ -c11 -e'^$' T*0710.txt | colsummary
1360 line(s) read;...
※比較してわかることは、すべてが空でないのは10列目のみ

ここではcolsummaryも併用しています。別の列との関係をきちんと把握するには、11列目が空でない場合の行の様子と比較する必要があります。そのため、2つ目のコマンドではcolgrepにオプション-~を与えて、1つ目のコマンドの検索条件を反転しています。

紙幅の都合で出力は省略しましたが、それぞれ図2に似た出力2個が得られます。それを見れば※コメントのようなことが確認できるので、⁠11列目が空」「10列目(ページ数など)が空」がわかります(⇔は2個の命題の真偽が一致することを表します⁠⁠。図書の「ページ数」が決まってないなどの理由で情報がない場合は、⁠大きさ」の情報もありません。逆もしかりです。

このようにcolgrepcolsummaryを使った比較で、特別値の意味を探ることができます。

まとめ

本稿は、表形式のテキストデータを目で見て理解可能にするPerl製コマンド群を紹介しました。列の特徴量を整理して表示したり、複数のデータの重なり具合を把握できたり、値の書式の解読などに使えたりするコマンド群で、データ分析上の困難がどう解決するかの一端を示しました。このようなコマンドの機能で、世の中で蓄積されて眠ったままのデータが有効活用されることを願っています。

さて、次回の執筆者は礒部浩行さんで、テーマは「Rest APIフレームワークRaisinを使ってみよう」です。お楽しみに。

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.130

2022年8月24日発売
B5判/168ページ
定価1,628円
(本体1,480円+税10%)
ISBN978-4-297-13000-8

  • 特集1
    イミュータブルデータモデルで始める
    実践データモデリング

    業務の複雑さをシンプルに表現!
  • 特集2
    いまはじめるFlutter
    iOS/Android両対応アプリを開発してみよう
  • 特集3
    作って学ぶWeb3
    ブロックチェーン、スマートコントラクト、NFT

おすすめ記事

記事・ニュース一覧