SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

PerlによるCSVファイルの高速集計

PerlによるCSVファイルの高速集計

CSVを自在に扱って、業務を効率化しよう

  • X ポスト
  • このエントリーをはてなブックマークに追加

連想配列を利用する

グループ集計

 次に、「customer_data.csv」の各都道府県別人数を調べてみましょう。Perlでは言語の機能として「連想配列」(ハッシュと呼ばれることが多い)を持っており、簡単な記述でその機能をフルに使うことができます。このことが、Perlにおいてデータ集計処理を行う際の大きな武器となります。

 ハッシュには、文字列のキーとそれに対応するスカラ値を格納することができます。都道府県をキーとし、そのカウント数を値とすることでグループ集計を行うことができます。ハッシュは先頭に%を付けた変数名で表すことができ、値にアクセスする場合は$と中括弧{}を用います。

tdfk_count.pl
my %count = ();
open(IN, 'customer_data.csv');
while(<IN>){
    chomp;
    my @d = split(/,/, $_);

    # 都道府県ごとにカウントアップ
    $count{$d[3]}++;
}
close(IN);

#出力する
foreach(keys %count){
    print "$_,$count{$_}\n";
}

 whileループの中で、$count{都道府県名}++のように都道府県ごとにカウントアップし、累計値を求めています。例えば、ファイルからの読み込みが終わった後の%countの内容は、次のようになっています。

ハッシュの中身
$count{'北海道'}   =>  745
$count{'神奈川県'} => 2587
$count{'沖縄県'}   =>  487
 :
 :

 この中身をすべて出力するのが、「#出力する」以下に書かれているコードです。foreachですべての要素を処理するのですが、単純にハッシュでループさせると、キー、値、キー、値……とキーと値が交互に返ってきてしまいます。しかし、表示にはキーごとのループの方が使いやすいので、keys関数によってキーだけを取り出しています。今回は、引き数省略時に使われる$_にキーが含まれているので、キー「$_」とその値「$count{$_}」を表示して終了です。このkeysとforeachを組み合わせた使い方はハッシュの処理の基礎になるので、このままの形で覚えてしまいましょう。

ユニークチェック

 ところで、「customer_data.csv」にはたくさんの人のデータが含まれていますが、これらに同じ人は含まれていないのでしょうか? もし、ID:02547の佐藤大輔さんが2回含まれていたとすれば、佐藤大輔さんだけで北海道のカウントが2つもアップしてしまい、先ほどの都道府県ごとの集計は正しい物ではないということになってしまいます。

 これについてPerlスクリプトを利用して検証してみましょう。顧客ID行に対して、このカラムに重複がないかをチェックしてみます。

 ハッシュはユニークチェックにも威力を発揮します。次のようなスクリプトを書いてください。

uniq_check.pl
my %id_flag = ();
open(IN, 'customer_data.csv');
while(<IN>){
    chomp;
    my @d = split(/,/, $_);

    # フラグが既に立っていれば重複なので終了
    if($id_flag{$d[0]}){
        die "行が重複しました。";
    }
    # フラグを立てる
    $id_flag{$d[0]} = 1;
}
close(IN);

print "OK\n";

 if文で、顧客IDが既に%id_flagに登録されているかを判定しています。顧客IDが既に登録されていれば重複の発生なので、dieでエラー終了させます。新しい顧客IDの場合は、%id_flagに新たにそのIDを登録し、次の行のチェックへと進みます。このチェックが全行成功すれば、OKと表示して処理を終了します。

 ところで、この表現は実はもう少し短く書くことができます。短い方が覚えやすく、打つのも楽であるため、筆者は次のような書式で暗記しています。

一行でユニークチェック
die "NG\n" if($id_flag{$d[0]}++ > 0);

 Perlはif文を1行で書いた場合、ifを処理の後ろに書かねばならない点に注意してください。これは他の言語とは異なるため、慣れないと違和感があると思います。

 さて、この書き方のミソは、後置のインクリメント「++」を使っているところです。一度目のIDの登場ではハッシュの中身は空であるため、値は0として評価されるのでこのif文は偽になります。そしてifの判定後にこのIDに対する値がカウントアップされるので、2回目以降登場してしまうとこのif文は真となり、dieの処理が実行されるわけです。

 これ以外にも、if文の括弧を省略したり、unlessにしてexistsを使ったり等、いろいろな書き方がありますが、Perlは自分の書きやすいように書けるのが最大の利点です(欠点でもありますが……)。自分の覚えやすいような書き方で覚えるのが一番だと思います。

 さて、ユニークチェックをした結果、残念ながらこのCSVには重複行が存在することが分かりました。「tdfk_count.pl」を修正して、正しいユニーク値を計算するにはどうすればいいでしょう? これは、先ほどのユニークチェックをうまく組み込めば簡単に実現できます。

tdfk_count.pl ユニーク版に改修
my %id_flag = ();
my %count = ();

open(IN, 'customer_data.csv');
while(<IN>){
    chomp;
    my @d = split(/,/, $_);

    # 重複している行は無視する
    next if($id_flag{$d[0]}++ > 0);

    # カウントする
    $count{$d[3]}++;
}
close(IN);

#出力する
foreach(keys %count){
    print "$_,$count{$_}\n";
}

 %id_flagを追加して、先ほどのユニーク判定のifを利用し、重複行が出現したらnextにより、その行を飛ばしているだけです。このようにパターンさえ覚えてしまえば、任意の組み合わせで、さまざまな種類の集計を実現することができます。

次のページ
複雑なデータ型

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
PerlによるCSVファイルの高速集計連載記事一覧
この記事の著者

hiratara(ヒラタラ)

1977年に苫小牧市で生まれる。北海道大学理学部数学科卒。小学生の頃、両親に買い与えられたMZ-2500でプログラミングを始めた。学生時代、CGIの自作に没頭し、それ以降WEB開発の魅力に憑かれる。社会人になっても数学好きは変わらず、専門書を買い集めるのが最近の趣味。id:hirataraにてblogを執筆...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/904 2007/03/19 20:07

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング