■Windows版Rubyの細道・けもの道

■ナビゲータ

[南北館(最初のメニュー)]

  1. [Windows版Rubyの細道・けもの道]
    1. [1.準備編]
    2. [2.基本編]
      1. [2-1.基本処理]
      2. [2-2.キーブレイク処理]
      3. [2-3.マッチング(照合)処理]
      4. [2-4.ソート(並べ替え)処理]
        1. [2-4-1.レコード全体のソート(並べ替え)]
        2. [2-4-2.CSVファイルの文字列型昇順ソート(並べ替え)]
        3. [2-4-3.CSVファイルの文字列型・数値型/昇順・降順ソート(その1)]
        4. [2-4-4.CSVファイルの文字列型・数値型/昇順・降順ソート(その2)]
        5. [2-4-5.大量のCSVファイルのソート(並べ替え)する場合の考慮事項]
        6. [2-4-6.大量のCSVファイルの文字列型/昇順ソート(並べ替え)]
        7. [2-4-7.大量のCSVファイルの文字列型・数値型/昇順・降順ソート(並べ替え)]
      5. [2-5.パターンマッチ処理]
    3. [3.応用編]
    4. [スクリプトと入力データのサンプル]
Perlではどう処理する?
同じことをPerlではこうしています。

2.基本編

2-4.ソート(並べ替え)処理

2-4-1.レコード全体のソート(並べ替え)

大量のCSVファイルのデータをソート(sort:並べ替え)するのは、本来はデータベース側で行うことであって、rubyで行うことではありません。しかし、実際にはrubyで行うことになる場合もあります。そうした場合にrubyでどのような対応を行うかについて説明していきます。ここで大量のCSVファイルとは、[2-4-2.CSVファイルの文字列型昇順ソート]で並べ替える場合は100万件以上、[2-4-3.CSVファイルの文字列型・数値型/昇順・降順ソート(その1)]で並べ替える場合は10万件以上のCSVデータが1つのファイルに入っていることを前提にしています。これ以上のデータを並べ替える(ソートする)には以下の手順で行います。

まず、並べ替えの方式のどれかを確認します。

  1. すべての並べ替え(ソート)のキーが文字列型の昇順である場合
  2. 並べ替え(ソート)のキーが文字列型の降順または数値型の昇順や降順を含む場合

1の場合は、以下の手順で行います。

    1. まず、下記に示すスクリプトでソート(並べ替え)キーごとの件数と累計件数が出ますので、データが100万件程度ずつになるような適切なソート(並べ替え)キーを探します(100万件を大幅に超える件数でソート(並べ替え)キーが同じ場合は、ソート(並べ替え)せずにそのまま出力するようにします)。
    2. 次に、[2-4-6.大量のCSVファイルの文字列型/昇順ソート]を参考にデータの並べ替えを行います。
    3. 上記の場合ソート(並べ替え)できる件数は1000万件程度までです(PCの性能によって異なりますが、Windows7 Professionalでメモリ8GBのときで約7分強かかります。この点ではPerlのほうが処理時間が早い(2分強程度)ので、rubyで処理するのは実用性は低いと思います。また、件数によってはruby1.9ではできない場合もありますので、その場合はruby1.8を使ってください。どの程度の件数が適切かは各自のPCのOSやメモリ等によって異なりますので、各自のPCで確認してから実行してください。また、他のアプリケーションの起動は極力避け、タスクマネージャーでCPUやメモリ、ディスクの状況などを確認しながら実行してください)。それ以上であれば、[2-4-6.大量のCSVファイルの文字列型/昇順ソート]のスクリプトを2つ以上作成した後、[3-3-2.複数のファイルをまとめて、1つのファイルに出力する]で1つのファイルにまとめます。

2の場合は、以下の手順で行います。

    1. まず、下記に示すスクリプトでソート(並べ替え)キーごとの件数件数と累計件数が出ますので、データが10万件程度ずつになるような適切なソート(並べ替え)キーを探します。(10万件を大幅に超える件数でソート(並べ替え)キーが同じ場合は、ソート(並べ替え)せずにそのまま出力するようにします)。
    2. 次に、[2-4-7.大量のCSVファイルの文字列型・数値型/昇順・降順ソート]を参考にデータの並べ替えを行います。
    3. 上記の場合ソート(並べ替え)できる件数は100万件程度までです(PCの性能によって異なりますが、Windows7 Professionalでメモリ8GBのときで約5分強かかります。この点ではPerlのほうが処理時間が早い(2分強程度)ので、rubyで処理するのは実用性は低いと思います。また、また、件数によってはruby1.9ではできない場合もありますので、その場合はruby1.8を使ってください。どの程度の件数が適切かは各自のPCのOSやメモリ等によって異なりますので、各自のPCで確認してから実行してください。また、他のアプリケーションの起動は極力避け、タスクマネージャーでCPUやメモリ、ディスクの状況などを確認しながら実行してください)。それ以上であれば、[2-4-7.大量のCSVファイルの文字列型・数値型/昇順・降順ソート]のスクリプトを2つ以上作成した後、[3-3-2.複数のファイルをまとめて、1つのファイルに出力する]で1つのファイルにまとめます。

実際に応用する場合は、入出力データのファイル名を変更し、「sortitem = [1,0]」の部分を変更します。この例では、2番目の項目を第1キーとし、1番目の項目を第2キーとして並べ替えるように指定しています。rubyでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。

下記の例では、フィールドを「"」(ダブルクォーテーションつき)で囲み、それぞれのデータを「,」(カンマ)で区切っている形式を前提としているため、

    in1 =   (line1 + ',')   
            .scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)   
            .collect{|x,y| y || x.gsub(/(.)/, '\1')}    
   

としていますが、フィールドを「"」(ダブルクォーテーションつき)で囲まずに、それぞれのデータを「,」(カンマ)で区切っている、いわゆる「CSV2形式」であれば、以下のようにすることができます。

    in1 =   line1.split(",",-1)    
   

また、タブ区切りであれば、以下のようにすることができます。

    in1 =   line1.split("\t",-1)    
   
【スクリプト】
# keycount.rb   
# 内容 :CSV形式のファイルのソートキーごとの件数と累計件数をカウントする。   
# Copyright (c) 2011-2015 Mitsuo Minagawa, All rights reserved. 
# (minagawa@fb3.so-net.ne.jp)   
# 使用方法 : c:\>ruby keycount.rb   
#   
# 入力ファイル  
in1_file    =   open("input.txt","r")   
# 出力ファイル      
out1_file   =   open("output.txt","w")  

# 整列の基準となる項目番号      
# ソートキー    
sortitem    =   [1,0]   
# ソートキーをセットする配列    
hash_key    =   nil 
# ソートするレコードをセットするハッシュ    
sort_rec    =   Hash.new    
# 累計件数  
total_ctr       =   0   
# ソートキーとなる項目番号からソートキーとなる文字列を作っておく    
while   (line1  =   in1_file.gets)  
    line1.chomp!    
# CSVファイルを各項目に分解する 
    in1 =   (line1 + ',')   
            .scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)   
            .collect{|x,y| y || x.gsub(/(.)/, '\1')}    
# タブ区切りファイルを各項目に分解する  
#   in1 =   line1.split("\t",-1)    
    #   整列の基準となる項目番号の項目内容をタブ値ではさんで連結してキーとする      
    hash_key    =   ""  
    sortitem.each   {|i|    
        hash_key    +=  in1[i] + "\t"   
    }   
    hash_key.chop!  
    # ソートキーに該当するキーがあれば、件数を加算する。    
    if      (sort_rec.include?(hash_key))   
            sort_rec[hash_key]  +=  1   
    # 新たなソートキーに該当する場合は、初期値1をセットする。   
    else    
            sort_rec[hash_key]  =   1   
    end 
    hash_key    =   nil 
end     

# ハッシュのキーをソートし、キー順にレコードを出力する  
#sort_rec.sort{|a,b|    a[0] <=> b[0]}.each{|key,value| 
sort_rec.sort_by{|key| key }.each{|key,value|   
    total_ctr   +=  value   
    out1    =   [key,value,total_ctr].join("\t")    
    out1_file.print out1,"\n"   
}   

# ファイルのクローズ    
in1_file.close  
out1_file.close 
    


【スクリプトとデータのサンプル】

スクリプトはこちらにあります。

入力データのサンプルはこちらにあります。

【入力データ:ソート(並べ替え)前のデータ(input.txt)】
ccc,5555   
aaa,7777
bbb,9999
aaa,1111
bbb,3333
bbb,4444
aaa,5555
ccc,1111
    
【出力データ:ソート(並べ替え)キーごとの件数と累計件数】
aaa 3   3
bbb 3   6
ccc 2   8
   



Copyright (c) 2011-2015 Mitsuo Minagawa, All rights reserved.