Yakstは、海外の役立つブログ記事などを人力で翻訳して公開するプロジェクトです。
11年弱前投稿 修正あり

コマンドラインでデータを扱う方法の色々

headやgrepなど基本的なコマンドから、CSVのためのcsvtoolやJSONを扱うjqコマンドまで、テキストデータを処理する時に使う各種ツールを例とともに解説する。

原文
Working with data on the command line (English)
翻訳依頼者
D98ee74ffe0fafbdc83b23907dda3665
翻訳者
D98ee74ffe0fafbdc83b23907dda3665 doublemarket
原著者への翻訳報告
未報告


RPythonのような成熟したコンピューティング環境は、詳しくデータを分析するのには素晴らしいものだ。しかし、素早くシンプルにデータの調査や捜査をしたいときには、UNIXのコマンドラインツールはものすごく効率的だ。この記事では、自分で見つけ日々使っているいくつかのツールに光を当ててみようと思う。新しいツールをあなたのレパートリーに加えられたらうれしい。

ファイルをのぞき見る( head tail less )

NASAのサンプルWebログデータをダウンロードするところから始めよう。

$ wget ftp://ita.ee.lbl.gov/traces/NASA_access_log_Jul95.gz

ダウンロードされたファイルはgzipで圧縮されており、 less はデフォルトではこれを展開まではしてくれない。しかしその代わり、 zless を使える。あるいは、 gunzip の結果をファイルに代わって、パイプで他のコマンドに出力を送るのに便利な stdout (標準出力)に送れば、 headtail でファイルの先頭や最後をそれぞれ調査することができる。

$ gunzip -c NASA_access_log_Jul95.gz | head -n 5
199.72.81.55 - - [01/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245
unicomp6.unicomp.net - - [01/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085
burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0
199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179

列をフィルタリングする( awk cut )

最初の列には、リクエスト元の(サブ)ドメインかIPアドレスが書いてあるようだ。こういった部分を抜き出すのによく使われるツールは awkcut だ。

$ gunzip -c NASA_access_log_Jul95.gz | head -n 5 | awk '{print $1}'
199.72.81.55
unicomp6.unicomp.net
199.120.110.21
burger.letters.com
199.120.110.21

ユニークな行を数える

ユニーク(一意)な(サブ)ドメインやIPアドレスを数えるには、行を並べ替えて( sort )、重複を排除し( uniq )、その結果の行数を数えれば( wc )よい。並べ替えの際には全ての行がメモリにロードされる点に注意しよう。

$ gunzip -c NASA_access_log_Jul95.gz | awk '{print $1}' | sort | uniq | wc -l
   81983

行をフィルタリングする( grep )

テキストファイルから正規表現にマッチする行をフィルタリングするのは非常によくあるオペレーションだ。これは、 grep とその変化形で実現できる。ソースIPがあるリクエストだけを見たいとしよう。

$ gunzip -c NASA_access_log_Jul95.gz | egrep '^\d+\.\d+\.\d+\.\d+' | head -n 5
199.72.81.55 - - [01/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245
199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085
199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179
205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985
129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074

$ gunzip -c NASA_access_log_Jul95.gz | egrep '^\d+\.\d+\.\d+\.\d+' | wc -l
  419797

指定のパターンを除外したい場合は、単純に -v オプションを付けよう。

$ gunzip -c NASA_access_log_Jul95.gz | egrep -v '^\d+\.\d+\.\d+\.\d+' | head -n 5
unicomp6.unicomp.net - - [01/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0
burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0
burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0
d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985

$ gunzip -c NASA_access_log_Jul95.gz | egrep -v '^\d+\.\d+\.\d+\.\d+' | wc -l
 1471918

データを抽出する( shuf head )

もうひとつ、ファイルからランダムに行を抽出するというのもよくあるタスクだ。データがメモリに収まるなら、行をシャッフルするのに shuf を使い、その一部を head で取り出せる。OS Xでは、 shufcoreutils をインストールすれば使える。homebrewを使っているなら、 brew install coreutils でよい。デフォルトでは、前にインストールされたバージョンとの重複を避けるためにツール名の最初に「g」がついた状態でインストールされることに注意しよう。つまり、 shufgshuf として使えるようになる。

$ gunzip -c NASA_access_log_Jul95.gz | gshuf | head -n 5
rs710.gsfc.nasa.gov - - [24/Jul/1995:15:32:01 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713
annex-065.gower.net - - [21/Jul/1995:23:37:43 -0400] "GET /images/ HTTP/1.0" 200 17688
cevennes.jpl.nasa.gov - - [23/Jul/1995:16:03:57 -0400] "GET /images/vab-small.gif HTTP/1.0" 200 35709
eoi18.eda.mke.ab.com - - [05/Jul/1995:09:08:35 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985
131.156.47.24 - - [05/Jul/1995:10:13:50 -0400] "GET /shuttle/missions/sts-71/mission-sts-71.html HTTP/1.0" 200 8192

CSVファイルを操作する( csvtool )

CSV(comma-separated values)ファイルをコマンドラインから操作するのに一番簡単なのは column -s, -t file.csv だが、値が含まれなかったり、値自体にコンマが含まれているような場合にはややこしくなる。CSVファイルを扱うのにデザインされたツールはたくさんあるが、ここでは csvtool を紹介しよう。

DebianベースのLinuxディストリビューションでは、 csvtoolsudo apt-get install csvtool でインストールできる。OS Xでインストールした状態にするのは少々骨が折れるが、努力の価値はある。このツールはOCamlで書かれているので、まずはこれをインストールする必要がある。Homebrewでなら、OCamlのパッケージマネージャOPAMをインストールすれば、依存するOCamlがインストールされる。関連するパッケージをインストールするのにもOPAMを使えばよい。

$ brew install opam
...
$ opam install csv
...
$ sudo ln -s ~/.opam/system/bin/csvtool /usr/local/bin/
$ csvtool --help | head -n 6
csvtool - Copyright (C) 2005-2006 Richard W.M. Jones, Merjis Ltd.

csvtool is a tool for performing manipulations on CSV files from shell scripts.

Summary:
  csvtool [-options] command [command-args] input.csv [input2.csv [...]]

インストールはこれでやっつけたので、サンプルのCSVデータをダウンロードしよう。イギリス内閣府が配布している、高級官僚の高所得者給料一覧だ。

$ wget https://www.gov.uk/government/uploads/system/uploads/attachment_data/file/83716/high-earners-pay-2012.csv
$ wc -l high-earners-pay-2012.csv
     259 high-earners-pay-2012.csv
$ head -n1 high-earners-pay-2012.csv
Post Unique Reference,Surname,Forename(s),Grade Equivalent,Job Title,Job/Team Function,Parent Department,Organisation,Total Pay Floor (£),Total Pay Ceiling (£),Change from 2011,Notes,Contact Email

上を見ると、このファイルには258行と、カラム名が含まれたヘッダが1行あるのがわかる。タブを区切り文字として、カラムの一部(job title, organisation, total pay floor, total pay ceiling)を取り出したいとしよう。

$ csvtool -u TAB col 5,8-10 high-earners-pay-2012.csv | head -n 5
Job Title   Organisation    Total Pay Floor (£) Total Pay Ceiling (£)
Director    Serious Fraud Office    165000  169999
Non-Executive Director Defence Board and Chair Audit Committee  Ministry of Defence 30000   34999
Chairman    Olympic Delivery Authority  250000  254999
HM Inspector of Constabulary Northern Region    HM Inspectorate of Constabulary 185000  189999

賃金の下限(total pay floor)で降順に並べ替えて、その上位のレコードを確認しよう。まずはヘッダの行を削除して、ファイルの代わりに stdin (標準入力)から読み込むよう - を指定した csvtool にパイプで渡す。それから、3番目のカラムを元に降順に行を並べ替え、さらに表示を整えるために csvtool にパイプで渡せばよい。

$ csvtool drop 1 high-earners-pay-2012.csv | csvtool -u TAB col 5,8-10 - | sort -t$'\t' -k3rn | head -n 4 | csvtool -t TAB readable -
Chief Executive         Olympic Delivery Authority        310000 314999
Chief Executive         Office of Fair Trading            275000 279999
Chief Executive Officer Nuclear Decommissioning Authority 265000 269999
NHS Chief Executive     Department of Health              265000 269999

JSONフォーマットのデータを操作する( jq )

最後に一番重要なこととして、JSONフォーマットのデータを操作するのに素晴らしく便利なツールであるjqについて書きたい。バイナリはダウンロードページから入手できる。OS Xでは、Homebrewを使って brew install jq でもインストールできる。

forecast.ioから、現在のロンドンの天気予報データをダウンロードしよう。

$ curl "https://api.forecast.io/forecast/$API_KEY/51.51121389999999,0.11982439999997041" > forecast.json
$ wc forecast.json
       0     142   25804 forecast.json

なんと1行で巨大なレスポンスが返ってきた。 jq できれいにフォーマットできる。

$ cat forecast.json | jq . | head
{
  "flags": {
    "units": "us",
    "darksky-stations": [
      "uk_london"
    ],
    "datapoint-stations": [
      "uk-324164",
      "uk-350286",
      "uk-351142",

JSONオブジェクトに含まれるキーを全て出してみよう。

$ cat forecast.json | jq 'keys'
[
  "currently",
  "daily",
  "flags",
  "hourly",
  "latitude",
  "longitude",
  "minutely",
  "offset",
  "timezone"
]

現在の天気をフィルターしてみる。

$ cat forecast.json | jq '.currently'
{
  "ozone": 311.54,
  "temperature": 42.65,
  "precipProbability": 0,
  "precipIntensity": 0,
  "nearestStormBearing": 191,
  "nearestStormDistance": 176,
  "icon": "wind",
  "summary": "Breezy",
  "time": 1390689251,
  "apparentTemperature": 34.98,
  "dewPoint": 35.04,
  "humidity": 0.74,
  "windSpeed": 15.9,
  "windBearing": 286,
  "visibility": 10,
  "cloudCover": 0,
  "pressure": 1013.82
}

jq には何ができるのかをほんの少し見てみた。この他にもたくさんある素晴らしい機能については、 jq の豊富なドキュメントを参照して欲しい。

追記 : @jeroenhjanssenのブログ7 command-line tools for data scienceもチェックするのを忘れないように。ここに挙げたツールの他にも、より高度なものについても書かれている。もうすぐ発売される「Data Science at the Command Line」という彼の本にも注目だ。

次の記事
MySQLとその仲間の過去・現在・未来 - MySQLの過去を振り返る Part 1
前の記事
MySQLをインストールしたら、必ず確認すべき10の設定

Feed small 記事フィード

新着記事Twitterアカウント