大量の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" }