はじめに
Perlと言えば、いまだにCGIを思い浮かべる人は多いと思います。しかし、Perlは決してそれだけの言語ではなく、その言語仕様はテキストファイルを処理し、集計結果をレポートするのに非常に向いています。特によく利用されるテキストファイルとして、CSV形式が挙げられます。CSVを集計したい場合、Excelに読み込ませて集計関数やマクロを駆使しているという人も多いかと思いますが、Perlを利用することで、高度な集計作業を簡単かつ高速にできます。
本稿では、筆者がPerlでCSV集計を行う際によく使うノウハウを紹介したいと思います。
対象読者
- まだ、Perl=CGIと思っている方。
- 65,536件を超える大量のCSVデータを前に、手も足も出なくなっている方。
必要な環境
- テキストエディタ。
- Perl 5.8.X。ただし、ほとんどのコードはそれ以下のバージョンでも動きます。
Perlのインストール
Windowsユーザーの方は、ActiveStateのサイトからActivePerlの最新版をダウンロードし、インストールしてください。ここではCドライブに「usr」というフォルダを作り、そこにインストールしてください。Mac OS X、UNIX/Linux系の方は、ほとんどの場合デフォルトでインストールされています。
サンプルデータ
実際の動作を確認できるように、サンプルデータを用意しました。Windows環境で試すときは「サンプルデータ(Windows用:Shift-JIS、CRLF)」、Unix環境で試すときは「サンプルデータ(Unix用:UTF-8、LF)」を、それぞれダウンロードしてください。
条件にマッチする行数を数える
まずは基本形として、CSVファイルの読み込み処理をPerlで記述してみます。ですが、ただの読み込みでは実用的でないので、「条件にあてはまる行が何行存在するのか」を数えるようにしてみます。
次のような「customer_data.csv」ファイルがあるものとします。
02547,佐藤大輔,25,北海道,苫小牧市 15983,田中久志,19,沖縄県,那覇市 00893,本間雅洋,29,神奈川県,横浜市 : :(以下、100,000件) :
「customer_data.csv」は、「顧客ID」「氏名」「年齢」「都道府県」「市区」の5つのカラムを含むカンマ区切りのファイルであり、100,000件のデータを含んでいます。ここで、100,000件という前提条件が重要になります。なぜなら、コンピュータでデータ処理をする場合、メモリの使用量はパフォーマンスに重大な影響を与えるからです。このフォーマットの文書が、UTF-8で100,000件書かれていた場合、想定されるファイル容量は約4MBです。最近のPCのメモリ事情を考えれば、この程度の量であればすべてをメモリにのせてもまったく問題がありません。筆者の感覚ですと、データが100万件以内であれば、すべてメモリにのせて処理できると思っています。よって、以降はメモリのことについて深く考えずにスクリプトを書くこととします。
では、これらのデータの中に、年齢が20代の人が何人含まれるているのかを、Perlを使って数えてみます。Perlは、テキストエディタでプログラムを記述し、DOSプロンプトやシェル内からコマンドで起動することができます。また、Windowsの場合は、perlスクリプトと「perl.exe」が拡張子.plで紐づけられるため、ダブルクリックで実行することもできます。ただし、ダブルクリックによる実行は、実行結果のウィンドウがすぐに閉じてしまうため、スクリプトにエラーがあった場合に追跡が困難になります。慣れるまでは、DOSプロンプトから実行する方が良いでしょう。
では、早速テキストエディタを立ち上げ、次のようにスクリプトを書きます。スクリプトは「customer_data.csv」と同じディレクトリに「count.pl」という名称で保存してください。
open(IN, 'customer_data.csv'); while(<IN>){ chomp; my @d = split(/,/, $_); # ------------------------ # ここに、集計処理を書く # ------------------------ } close(IN);
これが、PerlでCSVを扱う場合の基本形となります。最初にopen()
で、ファイルを開きます。次のwhile(<IN>) { ... }
は、開いたファイルを1行ずつ繰り返し処理することを意味します。Perlでは引き数を省略した場合は$_
が暗黙的に使われることになっているので、おのおのの行の内容は$_
に保持されています。chomp
は、末尾の改行コードを取り除く関数です。ここでも引き数を省略し、$_
に対して処理させています。split
は文字列を分解する関数です。引き数にカンマを渡し、カンマで文字列を分解して@d
という配列に代入しています。Perlの文法では、配列変数には先頭に@
、その個々の要素にアクセスする場合には先頭に$
をつけて、大括弧[]
で添字を指定します。
例えば1行目の処理であれば、@d
には次のような内容が入っています。
$d[0] => 02547 $d[1] => 佐藤大輔 $d[2] => 25 $d[3] => 北海道 $d[4] => 苫小牧市
後は、集計処理を書けば完成します。20代であればカウンタを1追加する処理を記述し、最後にカウンタの内容をプリントさせます。スクリプトは次のようになりました。ステップ数で言えば10行以下です。
my $count = 0; open(IN, 'customer_data.csv'); while(<IN>){ chomp; my @d = split(/,/, $_); # 20~29歳ならカウントアップ if(20 <= $d[2] && $d[2] <= 29){ $count++; } } close(IN); # 結果を表示 print $count, "\n";
では、これを実行します。DOSプロンプト(又はシェル)を開き、まずは「customer_data.csv」と「count.pl」の置いてあるディレクトリに移動します。GUIのある環境であれば、スクリプトのアイコンをプロンプトにドラッグすると、パスを手で打つ手間がなく楽です。ファイル名を消して、cdしてください。
> cd D:\csvdata\
後はスクリプトを実行するだけです。perlコマンドの後にスクリプト名を指定します。CSVファイルの名前はスクリプト内に書かれているので必要がありません。
> perl count.pl 36457
スクリプトは一瞬~数秒で終わるはずです。20代の人は36,457人いるようです。