例えば、Testテーブルがあって、そこに英語やら数学の得点が入っているとします。
じゃあここから個人の偏差値を算出するにはどういう風にコードを書けば良いのか、、、
にぶち当たったたので簡単に整理しておきます。
まず、メソッドとしては、以下の3つが中心かなと思います。
- pluck
引数に指定したカラムの値を配列で返してくれるメソッド
//pluckメソッドの基本構文
モデル名.pluck(:カラム名)
モデル名.all.map(&:カラム名) # 上記と同じ
- map
mapメソッドは、配列の要素の数だけブロック内で処理を繰り返して、新しい配列を返す
//mapメソッドの基本構文
配列.map { |変数| 実行する処理 }
# 実行する処理が複数行に渡る場合
配列.map do |変数|
実行する処理
end
- sum
配列の要素の合計を返す
[].sum #=> 0
[].sum(0.0) #=> 0.0
[1, 2, 3].sum #=> 6
[3, 5.5].sum #=> 8.5
[2.5, 3.0].sum(0.0) {|e| e * e } #=> 15.25
[Object.new].sum #=> TypeError
[].sum #=> 0
[].sum(0.0) #=> 0.0
[1, 2, 3].sum #=> 6
[3, 5.5].sum #=> 8.5
[2.5, 3.0].sum(0.0) {|e| e * e } #=> 15.25
[Object.new].sum #=> TypeError
init引数を明示的に指名すると数値以外のオブジェクトにも使えます。
["a", "b", "c"].sum("") #=> "abc"
[[1], [[2]], [3]].sum([]) #=> [1, [2], 3]
["a", "b", "c"].sum("") #=> "abc"
[[1], [[2]], [3]].sum([]) #=> [1, [2], 3]
しかし、文字列の配列や配列の配列の場合 Array#joinやArray#flattenの方がArray#sumよりも高速です。
ということなので、sumは要素が数値の時に使用するのが良さそうです。
["a", "b", "c"].join #=> "abc"
[[1], [[2]], [3]].flatten(1) #=> [1, [2], 3]
これらを踏まえると、
1.pluckでテーブルから必要な要素を配列に入れる
2.sumで1の合計を出す
3.mapで標準偏差を出す
という流れになります。
偏差値の出し方
そもそも標準偏差ってどうやって求めるんだっけと昔の記憶を辿りましたが、完全に忘れていました。
以下のように求めるようです。
(1)平均点を求める
表1の結果で平均点は、10人全員の点数を合計し、人数で割ることで求められます。
60+90+50+40+40+100+40+60+70+50=600
600÷10(人)=60(点)
平均点は60点になります。
(2)標準偏差を求める
標準偏差値は、10人それぞれの生徒の点数から平均点を引いたものを2乗し、それを足したものを総人数で割った数の平方根です。
この式に数字をあてはめるために、まず(1)で求めた平均点を10人それぞれの点数から引きます。さらに、その計算で出た差(★)を2乗します。★が-の数値も2乗することで+になります。
生徒 あ い う え お か き く け こ
得点 60 90 50 40 40 100 40 60 70 50
平均点との差(★) 0 30 -10 -20 -20 40 -20 0 10 -10
★の2乗 0 900 100 400 400 1600 400 0 100 100
★の2乗をすべて足すと、
0+900+100+400+400+1600+400+0+100+100=4000
で「4000」となります。これを総人数の10で割ると
4000÷10=400
400の平方根を求めると、標準偏差「20」が導き出せます。
(3)偏差値を求める
最後に、偏差値を算出する式「偏差値=(得点-平均点)÷標準偏差×10+50」に(1)と(2)で求めた平均点と標準偏差をあてはめると
偏差値={得点-60(平均点)}÷20(標準偏差)×10+50
となります。
したがって、100点を取った生徒「か」さんの偏差値を求めると、
「か」さんの偏差値=(100-60)÷20×10+50=70
で偏差値70です。50点を取った「こ」さんの場合は、
「こ」さんの偏差値=(50-60)÷20×10+50=45
で偏差値45となります。
偏差値は「(個人の得点ー平均点)÷標準偏差×10+50」という式で計算できます。
この中の標準偏差とは、得点の散らばり具合を表すもので、模試や科目などにより異なります。
標準偏差は「(点数ー平均点)の二乗の総和÷受験者数の平方根」によって算出できます。
そこで、以下のようにメソッドを定義してみます。
def calculate_standard_deviation(data)
# 平均値の計算
average = data.sum / data.length.to_f
# 平均からの差を2乗して合計
squared_diff_sum = data.map { |x| (x - average) ** 2 }.sum
# 平均からの差を2乗した合計を総人数で割り、平方根を計算
standard_deviation = Math.sqrt(squared_diff_sum / data.length)
return standard_deviation
end
例えば、Testテーブルにenglishカラムがある場合、以下のように実行すれば標準偏差は出せます。
data = Test.pluck(:english)
sd = standard_deviation(data)
puts "標準偏差値: #{sd}"
length.to_fの代わりにcountを使用することもできますが、countは配列やコレクションの中で特定の条件を満たす要素の数を数えるために使われることが一般的であり、全体の要素数を数えるために使用すると誤った結果を出す可能性があります。例えば、データに欠損値(nil)が含まれている場合には、countは欠損値を含めて数えてしまうため、正確な結果を得ることができません。
一方、lengthは配列やコレクションの要素数を正確に返すため、全体の要素数を数える際には適切です。しかし、lengthは整数を返すため、小数点以下の計算を行いたい場合には、to_fを併用する必要があります。
[66] pry(main)> hoge.sum / hoge.length.to_f
=> 0.4877192982456140351e1
[67] pry(main)> hoge_sum / hoge.count
=> 4
(補足)e1は、指数表記における10の指数を示しています。eはexponent(指数)の略であり、10の指数を表します。例えば、1.23e2は123を意味する。
そして個人の偏差値を出す場合は、
偏差値=(得点-平均点)÷標準偏差×10+50
という式に当てはめれば良いかと思います。
例えば、Aさんが得点50点で、平均点がaverage(10)、標準偏差がsd(20)とした場合
Aさんの偏差値=(50-10)/20*10+50=70
という風に出せるはず、、、
まだ検証までできていないので、検証できたら更新したいと思います!