■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-3.CSVファイルの文字列型・数値型/昇順・降順ソート(その1)

大量のCSVファイルを文字列型や数値型に区別して、昇順にも降順にも並べ替えることのできるスクリプトです。

[2-4-5.大量のCSVファイルのソート(sort:並べ替え)する場合の考慮事項]でソート(並べ替え)キーごとの累計件数から割り出したソート(sort:並べ替え)キーをもとに大量のCSVファイルを分割しながら並べ替えていきます。下記の例で"01203"の箇所に分割して並べ替える際の基準になるソート(sort:並べ替え)キーを指定していきます。このとき、「<=」や「<」で比較する場合には、ソート(sort:並べ替え)キーを小さい順に指定し、「>=」や「>」で比較する場合には、ソート(sort:並べ替え)キーを大きい順に指定します。

    if      (in1[1]     <=      "01203")    
            in1_rec01[in1_ctr01]    =   in1.join("\t")  
            in1_ctr01   +=  1   
   

並べ替える項目が文字列型か数値型かによって指定する方法が変わります。

実際に応用する場合は、入出力データのファイル名を変更し、sort_byメソッド中の引数である「a_array[1].to_i,a_array[0]」の部分などを変更します。2つ以上のキーで並べ替えを行うには、第1キーから「,(カンマ)」で区切ってキーの指定を続けていきます。この例では、2番目の項目を整数として第1キーで、1番目の項目を文字列として第2キーで並べ替えるように指定しています。rubyでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。

文字列型を昇順に並べ替えるのであれば、「a_array[0]」のように、文字列型を降順に並べ替えるのであれば、マイナスをつけて「-a_array[0]」のように指定します。[]の中には何番目の項目を並べ替えるのかを指定します。

また、数値型を昇順に並べ替えるのであれば、整数として並べ替える場合、「a_array[0].to_i」のように、整数として降順に並べ替えるのであれば、マイナスをつけて「-a_array[0].to_i」のように指定します。実数として並べ替えるのであれば、それぞれ「to_i」の部分を「to_f」に変更します。

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

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

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

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

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

    in1 =   record.split("\t",-1)   
   


【スクリプト】
# csvsort2_many.rb  
# 内容 : 大量のCSVファイルをソートする 
#     文字列型の昇順・降順、数値型の昇順・降順の並べ替えを行う。    
#     (通貨記号や","つきの数字は数値型としての並べ替えはできない)   
# Copyright (c) 2011-2015 Mitsuo Minagawa, All rights reserved. 
# (minagawa@fb3.so-net.ne.jp)   
# 使用方法 : c:\>ruby csvsort2_many.rb  
#   
# 入力ファイル  
in1_file    =   IO.readlines("input.txt")   
# 出力ファイル  
out1_file   =   open("output.txt","w")  

# 分割してソートするレコードを入れる配列    
in1_rec01   =   Array.new   
in1_rec02   =   Array.new   
in1_rec03   =   Array.new   
in1_rec04   =   Array.new   
in1_rec05   =   Array.new   
in1_rec06   =   Array.new   
in1_rec07   =   Array.new   
in1_rec08   =   Array.new   
in1_rec09   =   Array.new   
in1_rec10   =   Array.new   

# 分割してソートした後のレコードを入れる配列    
sort_rec01  =   Array.new   
sort_rec02  =   Array.new   
sort_rec03  =   Array.new   
sort_rec04  =   Array.new   
sort_rec05  =   Array.new   
sort_rec06  =   Array.new   
sort_rec07  =   Array.new   
sort_rec08  =   Array.new   
sort_rec09  =   Array.new   
sort_rec10  =   Array.new   

# 分割してソートするレコードの件数  
in1_ctr01   =   0   
in1_ctr02   =   0   
in1_ctr03   =   0   
in1_ctr04   =   0   
in1_ctr05   =   0   
in1_ctr06   =   0   
in1_ctr07   =   0   
in1_ctr08   =   0   
in1_ctr09   =   0   
in1_ctr10   =   0   


in1_file.each   {|record|   
    record.chomp!   
# CSVファイルの場合 
    in1 =   (record + ',')  
            .scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)   
            .collect{|x,y| y || x.gsub(/(.)/, '\1')}    
# タブ区切りファイルの場合  
#   in1 =   record.split("\t",-1)   

    if      (in1[1]     <=      "01203")    
            in1_rec01[in1_ctr01]    =   in1.join("\t")  
            in1_ctr01   +=  1   
    elsif       (in1[1]     <=      "01210")    
            in1_rec02[in1_ctr02]    =   in1.join("\t")  
            in1_ctr02   +=  1   
    elsif       (in1[1]     <=      "01223")    
            in1_rec03[in1_ctr03]    =   in1.join("\t")  
            in1_ctr03   +=  1   
    elsif       (in1[1]     <=      "01346")    
            in1_rec04[in1_ctr04]    =   in1.join("\t")  
            in1_ctr04   +=  1   
    elsif       (in1[1]     <=      "01453")    
            in1_rec05[in1_ctr05]    =   in1.join("\t")  
            in1_ctr05   +=  1   
    elsif       (in1[1]     <=      "01547")    
            in1_rec06[in1_ctr06]    =   in1.join("\t")  
            in1_ctr06   +=  1   
    elsif       (in1[1]     <=      "01636")    
            in1_rec07[in1_ctr07]    =   in1.join("\t")  
            in1_ctr07   +=  1   
    elsif       (in1[1]     <=      "01691")    
            in1_rec08[in1_ctr08]    =   in1.join("\t")  
            in1_ctr08   +=  1   
    elsif       (in1[1]     <=      "02205")    
            in1_rec09[in1_ctr09]    =   in1.join("\t")  
            in1_ctr09   +=  1   
    elsif       (in1[1]     <=      "02401")    
            in1_rec10[in1_ctr10]    =   in1.join("\t")  
            in1_ctr10   +=  1   
    end 
}   

# CSVファイルの並べ替え 
# 文字列型の昇順(1番目の項目):w_array[0]    
# 文字列型の降順(2番目の項目):-w_array[1]   
# 整数型の昇順(3番目の項目):w_array[2].to_i 
# 整数型の降順(4番目の項目):-w_array[3].to_i    
# 実数型の昇順(5番目の項目):w_array[4].to_f 
# 実数型の降順(6番目の項目):-w_array[5].to_f    
# 複数項目の並べ替えは並べ替えをするキーの順番にカンマで区切って並べていく。    

i   =   0   
sort_rec01  =   in1_rec01.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   

sort_rec02  =   in1_rec02.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   

sort_rec03  =   in1_rec03.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   

sort_rec04  =   in1_rec04.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   

sort_rec05  =   in1_rec05.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   

sort_rec06  =   in1_rec06.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   

sort_rec07  =   in1_rec07.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   

sort_rec08  =   in1_rec08.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   

sort_rec09  =   in1_rec09.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   

sort_rec10  =   in1_rec10.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   

sort_rec01.each     {|record|       
                out1_file.print record,"\n" 
            }   
sort_rec02.each     {|record|       
                out1_file.print record,"\n" 
            }   
sort_rec03.each     {|record|       
                out1_file.print record,"\n" 
            }   
sort_rec04.each     {|record|       
                out1_file.print record,"\n" 
            }   
sort_rec05.each     {|record|       
                out1_file.print record,"\n" 
            }   
sort_rec06.each     {|record|       
                out1_file.print record,"\n" 
            }   
sort_rec07.each     {|record|       
                out1_file.print record,"\n" 
            }   
sort_rec08.each     {|record|       
                out1_file.print record,"\n" 
            }   
sort_rec09.each     {|record|       
                out1_file.print record,"\n" 
            }   
sort_rec10.each     {|record|       
                out1_file.print record,"\n" 
            }   

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

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

【スクリプトの解説】

基本的には[2-4-3.CSVファイルの文字列型・数値型/昇順・降順ソート(その1)]を応用したスクリプトになっています。

入力データの振り分け

    if      (in1[1]     <=      "01203")    
            in1_rec01[in1_ctr01]    =   in1.join("\t")  
            in1_ctr01   +=  1   
    elsif   (in1[1]     <=      "01210")    
            in1_rec02[in1_ctr02]    =   in1.join("\t")  
            in1_ctr02   +=  1   
    elsif   (in1[1]     <=      "01223")    
            in1_rec03[in1_ctr03]    =   in1.join("\t")  
            in1_ctr03   +=  1   
    elsif   (in1[1]     <=      "01346")    
            in1_rec04[in1_ctr04]    =   in1.join("\t")  
            in1_ctr04   +=  1   
    elsif   (in1[1]     <=      "01453")    
            in1_rec05[in1_ctr05]    =   in1.join("\t")  
            in1_ctr05   +=  1   
    elsif   (in1[1]     <=      "01547")    
            in1_rec06[in1_ctr06]    =   in1.join("\t")  
            in1_ctr06   +=  1   
    elsif   (in1[1]     <=      "01636")    
            in1_rec07[in1_ctr07]    =   in1.join("\t")  
            in1_ctr07   +=  1   
    elsif   (in1[1]     <=      "01691")    
            in1_rec08[in1_ctr08]    =   in1.join("\t")  
            in1_ctr08   +=  1   
    elsif   (in1[1]     <=      "02205")    
            in1_rec09[in1_ctr09]    =   in1.join("\t")  
            in1_ctr09   +=  1   
    elsif   (in1[1]     <=      "02401")    
            in1_rec10[in1_ctr10]    =   in1.join("\t")  
            in1_ctr10   +=  1   
    end 
   

[2-4-5.大量のCSVファイルのソート(並べ替え)する場合の考慮事項]でソート(並べ替え)キーごとの累計件数から各々10万件程度になるように割り出したソート(並べ替え)キー(原則として第1キー)を指定していきます。入力ファイルの件数によって、elsifの数を増減していきます。また、必要に応じてif文やelsif文には、第2キー以降をand条件で追加していきます。ここでセットした「in1_recxx(xxには01から始まる数字が入る)」の順に並べ替えをして出力していきます。

ソート(並べ替え)処理

sort_rec01  =   in1_rec01.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   
   

上記の「in1_recxx(xxには01から始まる数字が入る)」には、並べ替えをする前のレコードが入っています。各項目をタブ区切りしたレコードを要素とする配列となっています。このレコードを「w_array = data.split("\t",-1)」でタブ区切りして、必要な項目で並べ替えられるようにします。この「data」と「w_array」の名前は、この項目名である必要はありません。

並べ替える件数にあわせて、上記の部分および「sort_recxx.each・・・」で実際に出力している箇所を増減していきます。

文字列型を昇順に並べ替えるのであれば、「w_array[0]」のように、文字列型を降順に並べ替えるのであれば、マイナスをつけて「-w_array[0]」のように指定します。[]の中には何番目の項目を並べ替えるのかを指定します。

また、数値型を昇順に並べ替えるのであれば、整数として並べ替える場合、「w_array[0].to_i」のように、整数として降順に並べ替えるのであれば、マイナスをつけて「-w_array[0].to_i」のように指定します。実数として並べ替えるのであれば、それぞれ「to_i」の部分を「to_f」に変更します。

同一のソート(並べ替え)キーで10万件を大幅に超える件数がある場合

また、同一のソート(並べ替え)キーで10万件を大幅に超える件数がある場合は、以下のように変更します。

まず、ソート(並べ替え)している以下の箇所をコメントにします。

sort_rec01  =   in1_rec01.sort_by{|a|   
                i   +=  1   
                w_array     =   data.split("\t",-1) 
                [w_array[1].to_i,w_array[0],i]  
            }   
   

また、ソート(並べ替え)結果を出力している以下の箇所ですが、

    sort_rec01.each     {|record|       
                        out1_file.print record,"\n" 
                        }   
   

上記の箇所については、以下のようにします。

    in1_rec01.each      {|record|       
                        out1_file.print record,"\n" 
                        }   
   


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