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

■ナビゲータ

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

  1. [Windows版Rubyの細道・けもの道]
    1. [1.準備編]
    2. [2.基本編]
    3. [3.応用編]
      1. [3-1.固定長データとCSVデータとの変換]
      2. [3-2.重複データの処理]
      3. [3-3.フォルダ内の一括処理]
      4. [3-4.1つのファイルを複数のファイルに分割する]
      5. [3-5.文字コードの変換]
      6. [3-6.半角全角変換]
      7. [3-7.多次元配列の処理]
      8. [3-9.その他]
    4. [スクリプトと入力データのサンプル]
Perlではどう処理する?
同じことをPerlではこうしています。

3.応用編

3-7.多次元配列の処理

3-7-1.多次元データの処理(最高点・最低点・平均点の計算)

Rubyの仕様では、1次元配列しか扱えません。そこで、2次元以上のデータを扱うには、1次元配列を要素とする配列を作成し、多次元配列を作成します。「sum」という配列が3次元配列の場合、Perlであれば、個々の要素は、

    sum[0][1][2]   
   

のように表記できますが、ここではメソッド定義により、Perlと同様の表記させるようにします。ポイントは、

def threeDarray(i,j,k)  
    (0...i).map {   
        (0...j).map { Array.new(k) }    
                }   
end     

sum =   threeDarray(4,5,4)  

   

の部分です。「(0...j).map」の部分を省略すれば、2次元テーブルになりますし、「(0...j).map」の部分を階層化していけば、4次元テーブルなどの多次元テーブルを作成することも可能です。

なお、「(0...i)」の部分で範囲演算子が「...」になっている部分に注意してください。「0からi - 1までの要素」の意味になります(「(0..i)」なら「0からiまでの要素」の意味になります)。以下のサンプルでは、「sum = threeDarray(4,5,4)」としていますが、これは「threeDarray」の中の引数を要素数にするためです。配列のインデックスは0から開始しているため、インデックスの最大数は、実際の要素の数より1少なくなっています。これを簡潔に表示するために、「(0...i)」という表記にしています。

下記のスクリプトには示していませんが、配列全体を初期化するには、

        sum = []   
   

のように「ブラケット」を利用します。

        sum = undef   
   

を行うと、「pushメソッド」などで配列の最初の列が利用できなくなるので注意が必要です。また、ゼロクリアするには、「for」文を利用して行います。三次元配列であれば、「for」文を3回繰り返し、その中で

    for i   in  0..3        
        for j   in  0..4        
            for k   in  0..3

                sum[i][j][k] = 0

            end     
        end     
    end     
   

のように行います。上記のスクリプトは範囲演算子「..」の代わりに「...」を使うと

    for i   in  0...4
        for j   in  0...5
            for k   in  0...4

                sum[i][j][k] = 0

            end     
        end     
    end     
   

となります。こちらの方が、「sum = threeDarray(4,5,4)」で指定した引数の値とも一致しますので、わかりやすくなるかもしれません。

最低点を求めるために、100点満点として、初期値に「100」を設定しています。満点が100点以外の場合は修正が必要ですが、満点を設定しておくことで、入力値がこれより小さい場合に値を置き換える作業を繰り返していけば、最低点が求まります。最高点についても同様に初期値に「0」を設定し入力値がこれより大きい場合は、最高点を置き換える作業を繰り返して、最高点を求めます。

最高点も最低点もそれぞれのクラスごとに異なりますし、全体で見たときにも必要になりますので、別個に集計を行います。

なお、集計結果に漢字を表示するため、マジックコメントと「内部エンコーディング」を使っています。「内部エンコーディング」については、[3-5-4.外部エンコーディングと内部エンコーディングによる変換]を参照してください。また、ruby1.9で実行したので、マジックコメントになっていますが、ruby1.8以前で実行するには、$KCODEを使用します。$KCODEやマジックコメントについては、[2-1-5.スクリプトの中で漢字を使う]を参照してください。

【スクリプト】
# coding:utf-8  
# tabulation.rb 
# 内容 : クラス別科目別成績データ集計  
# Copyright (c) 2002-2015 Mitsuo Minagawa, All rights reserved. 
# (minagawa@fb3.so-net.ne.jp)   
# 使用方法 : c:\>ruby tabulation.rb 
#   

# 入力ファイル  
in1_file    =   open("input.txt","r:shift_jis:utf-8")   
# 出力ファイル  
out1_file   =   open("total.txt","w:shift_jis:utf-8")   
#   
# 入力データ:以下の5項目    
#   
# クラス,出席番号,氏名,科目コード,点数 
#       
# 集計用データ: 
#                   $sum[0-3][0-4][0-3] 
#                       1次元目=>1組:1、2組:2、3組:3、合計:4の4項目      
#                       2次元目=>国語から社会までの5科目    
#                               国語:01、算数:02、理科:03、社会:04、英語:05 
#                       3次元目=>生徒数、最高点、最低点、平均点の4項目  

#       
# 3次元テーブルを作成する。 
#       
def threeDarray(i,j,k)  
    (0...i).map {   
        (0...j).map { Array.new(k) }    
            }   
end     

sum =   threeDarray(4,5,4)  
#       
# 出力用の1次元テーブルを作成する。 
#       
out1_array  =   Array.new() 

# 最低点の初期値として、100点を設定する。   
# それ以外は、0クリアする。 
for i   in  0..3        
    for j   in  0..4        
        for k   in  0..3        
            if  k   ==  2   
                sum[i][j][k]    =   100 #最低点の初期値 
            else        
                sum[i][j][k]    =   0       
            end     
        end     
    end     
end     

#       
# メイン処理    
#       
while   (line1  =   in1_file.gets)  
    line1.chomp!    
    in1 =   line1.split("\t",-1)    
#       
# クラスの振り分け  
#       
    case    in1[0]  
        when    "1"         #"1"は1組  
            i   =   0   
        when    "2"         #"2"は2組  
            i   =   1   
        when    "3"         #"3"は3組  
            i   =   2   
    end     
#       
# 科目の振り分け    
#       
    case    in1[3]      
        when    "01"    #国語   
            j   =   0   
        when    "02"    #算数   
            j   =   1   
        when    "03"    #理科   
            j   =   2   
        when    "04"    #社会   
            j   =   3   
        when    "05"    #英語   
            j   =   4   
    end     
#       
#       
# 生徒数    
#       
    sum[i][j][0]    +=  1       #クラス別の生徒数   
    sum[3][j][0]    +=  1       #全クラスの生徒数   
#       
# 最高点(初期値:0)  
#       
    if      (sum[i][j][1]       <   in1[4].to_i)        
            sum[i][j][1]        =   in1[4].to_i     #クラス別科目別の最高点 
    end     
    if      (sum[3][j][1]   <   in1[4].to_i)        
            sum[3][j][1]        =   in1[4].to_i     #全クラスの科目別の最高点   
    end     
#       
# 最低点(初期値:100)    
#       
    if      (sum[i][j][2]       >   in1[4].to_i)        
            sum[i][j][2]        =   in1[4].to_i     #クラス別科目別の最低点 
    end     
    if      (sum[3][j][2]   >   in1[4].to_i)        
            sum[3][j][2]        =   in1[4].to_i     #全クラスの科目別の最低点   
    end     
#       
# 平均点(初期値:0)(最後に受験者数で割る)    
#       
    sum[i][j][3]    +=  in1[4].to_i         #クラス別科目別の合計点 
    sum[3][j][3]    +=  in1[4].to_i         #全クラスの科目別の合計点   
end     
#       
# 平均点算出    
#       
for i   in  0..3        
    for j   in  0..4        
#       
#   クラス別科目別で生徒数がゼロなら、最高点と最低点、平均点をブランクにする。  
#   それ以外は、クラス別科目別の平均点を算出する。  
#       
        if      (sum[i][j][0]       ==  0)      
                sum[i][j][1]        =   ""          #クラス別科目別の最高点 
                sum[i][j][2]        =   ""          #クラス別科目別の最低点 
                sum[i][j][3]        =   ""          #クラス別科目別の平均点 
        else        
                sum[i][j][3]        =   sprintf("%5.2f",(sum[i][j][3].to_f  /   sum[i][j][0].to_f)) 
        end     
    end     
end     
#       
# 成績データ出力    
# 各列の項目    
out1_title  =   ["項目","国語","算数","理科","社会","英語"] 
#       
# 各行の項目    
out1_item   =   ["生徒数","最高点","最低点","平均点"]   
#       

for i   in  0..3        
    for k   in  0..3        
        if          (k      ==  0)  
            if      (i      ==  0)  
                    out1_file.print "1組","\n" 
            elsif   (i      ==  1)  
                    out1_file.print "2組","\n" 
            elsif   (i      ==  2)  
                    out1_file.print "3組","\n" 
            elsif   (i      ==  3)  
                    out1_file.print "総合計","\n"   
            end     
                    out1_file.print out1_title.join("\t"),"\n"  #クラス名等出力 
        end     
        out1_array  =   []  
        out1_array.push(out1_item[k])               #各行の項目名セット 

        for j   in  0..4        
                out1_array.push(sum[i][j][k])       #各行の値セット 
        end     
        out1    =   out1_array.join("\t")   
        out1_file.print out1,"\n"   
    end     
end     

# ファイルのクローズ    
in1_file.close  
out1_file.close 
    
【スクリプトとデータのサンプル】

スクリプトはこちらにあります(必ずutf-8で保存してください)。

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

【入力データ】
1   01  青木 昇    01  67  
1   01  青木 昇    02  40  
1   01  青木 昇    03  86  
1   01  青木 昇    04  83  
1   01  青木 昇    05  95  
   (中  略)
3   36  横川 宗治  01  31  
3   36  横川 宗治  02  75  
3   36  横川 宗治  03  81  
3   36  横川 宗治  04  30  
3   36  横川 宗治  05  88  
    
【出力データ】
1組    
項目    国語    算数    理科    社会    英語    
生徒数  35      35      35      35      35  
最高点  98      99      100     99      100 
最低点  27      26      28      31      30  
平均点  60.23   63.51   59.34   66.20   66.54   
2組    
項目    国語    算数    理科    社会    英語    
生徒数  36      36      36      36      36  
最高点  100     100     97      100     98  
最低点  25      25      25      25      29  
平均点  59.14   60.28   59.86   62.64   62.94   
3組    
項目    国語    算数    理科    社会    英語    
生徒数  36      36      36      36      36  
最高点  99      99      99      100     100 
最低点  25      31      27      27      26  
平均点  62.81   67.72   65.14   61.11   60.39   
総合計  
項目    国語    算数    理科    社会    英語    
生徒数  107     107     107     107     107 
最高点  100     100     100     100     100 
最低点  25      25      25      25      26  
平均点  60.73   63.84   61.47   63.29   63.26   
    



Copyright (c) 2004-2016 Mitsuo Minagawa, All rights reserved.