############################
# 分散分析
# coded by Y.Suganuma
############################
############################################
# Γ(x)の計算(ガンマ関数,近似式)
# ier : =0 : normal
# =-1 : x=-n (n=0,1,2,・・・)
# return : 結果
# coded by Y.Suganuma
############################################
def gamma(x, ier)
ier[0] = 0
if x > 5.0
v = 1.0 / x
s = ((((((-0.000592166437354 * v + 0.0000697281375837) * v + 0.00078403922172) * v - 0.000229472093621) * v - 0.00268132716049) * v + 0.00347222222222) * v + 0.0833333333333) * v + 1.0
g = 2.506628274631001 * Math.exp(-x) * (x ** (x-0.5)) * s
else
err = 1.0e-20
w = x
t = 1.0
if x < 1.5
if x < err
k = Integer(x)
y = Float(k) - x
if y.abs() < err or (1.0-y).abs() < err
ier[0] = -1
end
end
if ier[0] == 0
while w < 1.5
t /= w
w += 1.0
end
end
else
if w > 2.5
while w > 2.5
w -= 1.0
t *= w
end
end
end
w -= 2.0
g = (((((((0.0021385778 * w - 0.0034961289) * w + 0.0122995771) * w - 0.00012513767) * w + 0.0740648982) * w + 0.0815652323) * w + 0.411849671) * w + 0.422784604) * w + 0.999999926
g *= t
end
return g
end
################################################
# 標準正規分布N(0,1)の計算(P(X = x), P(X < x))
# w : P(X = x)
# return : P(X < x)
################################################
def normal(x, w)
# 確率密度関数(定義式)
w[0] = Math.exp(-0.5 * x * x) / Math.sqrt(2.0*Math::PI)
# 確率分布関数(近似式を使用)
y = 0.70710678118654 * x.abs()
z = 1.0 + y * (0.0705230784 + y * (0.0422820123 + y * (0.0092705272 + y * (0.0001520143 + y * (0.0002765672 + y * 0.0000430638)))))
pp = 1.0 - z ** (-16.0)
if x < 0.0
pp = 0.5 - 0.5 * pp
else
pp = 0.5 + 0.5 * pp
end
return pp
end
#######################################
# f分布の計算(P(X = ff), P(X < ff))
# dd : P(X = ff)
# df1,df2 : 自由度
# return : P(X < ff)
#######################################
def f(ff, dd, df1, df2)
if ff < 1.0e-10
ff = 1.0e-10
end
x = ff * df1 / (ff * df1 + df2)
if df1%2 == 0
if df2%2 == 0
u = x * (1.0 - x)
pp = x
ia = 2
ib = 2
else
u = 0.5 * x * Math.sqrt(1.0-x)
pp = 1.0 - Math.sqrt(1.0-x)
ia = 2
ib = 1
end
else
if df2%2 == 0
u = 0.5 * Math.sqrt(x) * (1.0 - x)
pp = Math.sqrt(x)
ia = 1
ib = 2
else
u = Math.sqrt(x*(1.0-x)) / Math::PI
pp = 1.0 - 2.0 * Math.atan2(Math.sqrt(1.0-x), Math.sqrt(x)) / Math::PI
ia = 1
ib = 1
end
end
if ia != df1
i1 = ia
while i1 < df1-1
pp -= 2.0 * u / i1
u *= x * (i1 + ib) / i1
i1 += 2
end
end
if ib != df2
i1 = ib
while i1 < df2-1
pp += 2.0 * u / i1
u *= (1.0 - x) * (i1 + df1) / i1
i1 += 2
end
end
dd[0] = u / ff
return pp
end
############################################
# 二分法による非線形方程式(f(x)=0)の解
# x1,x2 : 初期値
# eps1 : 終了条件1(|x(k+1)-x(k)|<eps1)
# eps2 : 終了条件2(|f(x(k))|<eps2)
# max : 最大試行回数
# ind : 実際の試行回数
# (負の時は解を得ることができなかった)
# fn : f(x)を計算する関数名
# return : 解
# coded by Y.Suganuma
############################################
def bisection(x1, x2, eps1, eps2, max, ind, &fn)
x0 = 0.0
f1 = fn.call(x1)
f2 = fn.call(x2)
if f1*f2 > 0.0
ind[0] = -1
else
ind[0] = 0
if f1*f2 == 0.0
if f1 == 0.0
x0 = x1
else
x0 = x2
end
else
sw = 0
while sw == 0 && ind[0] >= 0
sw = 1
ind[0] += 1
x0 = 0.5 * (x1 + x2)
f0 = fn.call(x0)
if f0.abs() > eps2
if ind[0] <= max
if (x1-x2).abs() > eps1 && (x1-x2).abs() > eps1*x2.abs()
sw = 0
if f0*f1 < 0.0
x2 = x0
f2 = f0
else
x1 = x0
f1 = f0
end
end
else
ind[0] = -1
end
end
end
end
end
return x0
end
############################################
# Newton法による非線形方程式(f(x)=0)の解
# x0 : 初期値
# eps1 : 終了条件1(|x(k+1)-x(k)|<eps1)
# eps2 : 終了条件2(|f(x(k))|<eps2)
# max : 最大試行回数
# ind : 実際の試行回数
# (負の時は解を得ることができなかった)
# fn : f(x)とその微分を計算する関数名
# return : 解
# coded by Y.Suganuma
############################################
def newton(x0, eps1, eps2, max, ind, &fn)
x1 = x0
x = x1
ind[0] = 0
sw = 0
while sw == 0 and ind[0] >= 0
sw = 1
ind[0] += 1
g = fn.call(0, x1)
if g.abs() > eps2
if ind[0] <= max
dg = fn.call(1, x1)
if dg.abs() > eps2
x = x1 - g / dg
if (x-x1).abs() > eps1 && (x-x1).abs() > eps1*x.abs()
x1 = x
sw = 0
end
else
ind[0] = -1
end
else
ind[0] = -1
end
end
end
return x
end
################################################################
# 標準正規分布N(0,1)のp%値(P(X > u) = 0.01p)(二分法を使用)
# ind : >= 0 : normal(収束回数)
# = -1 : 収束しなかった
################################################################
def p_normal(ind)
normal_snx = Proc.new { |x|
y = Array.new(1)
1.0 - $p - normal(x, y)
}
u = bisection(-7.0, 7.0, 1.0e-6, 1.0e-10, 100, ind, &normal_snx)
return u
end
######################################
# f分布のp%値(P(X > u) = 0.01p)
# ind : >= 0 : normal(収束回数)
# = -1 : 収束しなかった
######################################
def p_f(ind)
f_snx = Proc.new { |sw, x|
y = Array.new(1)
z = f(x, y, $dof1, $dof2)
if sw == 0
z - 1.0 + $p
else
z = y[0]
end
}
max = 340
ff = 0.0
sw = 0
# 初期値計算の準備
# while文は,大きな自由度によるガンマ関数の
# オーバーフローを避けるため
while sw >= 0
df1 = 0.5 * ($dof1 - sw)
df2 = 0.5 * $dof2
a = 2.0 / (9.0 * ($dof1 - sw))
a1 = 1.0 - a
b = 2.0 / (9.0 * $dof2)
b1 = 1.0 - b
yq = p_normal(ind)
e = b1 * b1 - b * yq * yq
if e > 0.8 or $dof1+$dof2-sw <= max
sw = -1
else
sw += 1
if ($dof1-sw) == 0
sw = -2
end
end
end
if sw == -2
ind[0] = -1
else
# f0 : 初期値
if e > 0.8
x = (a1 * b1 + yq * Math.sqrt(a1*a1*b+a*e)) / e
f0 = x ** 3.0
else
y1 = Float($dof2) ** (df2-1.0)
y2 = Float($dof1) ** df2
x = gamma(df1+df2, ind) / gamma(df1, ind) / gamma(df2, ind) * 2.0 * y1 / y2 / $p
f0 = x ** (2.0/$dof2)
end
# ニュートン法
ff = newton(f0, 1.0e-6, 1.0e-10, 100, ind, &f_snx)
end
return ff
end
#######################################
# 分散分析(一元配置法と二元配置法)
# method : 1 or 2
# np[0] : 因子1の水準数
# [1] : 因子2の水準数
# nn : データ数
# name[0] : 因子1の名前
# [1] : 因子2の名前
# a : a %値
# x : データ
# coded by Y.Suganuma
#######################################
def aov(method, np, nn, name, a, x)
# 一元配置法
if method == 1
xi = Array.new(np[0])
for i1 in 0 ... np[0]
xi[i1] = 0.0
for i2 in 0 ... nn
xi[i1] += x[i1][0][i2]
end
xi[i1] /= nn
end
xa = 0.0
for i1 in 0 ... np[0]
for i2 in 0 ... nn
xa += x[i1][0][i2]
end
end
xa /= (np[0] * nn)
sp = 0.0
for i1 in 0 ... np[0]
sp += (xi[i1] - xa) * (xi[i1] - xa)
end
sp *= nn
se = 0.0
for i1 in 0 ... np[0]
for i2 in 0 ... nn
se += (x[i1][0][i2] - xi[i1]) * (x[i1][0][i2] - xi[i1])
end
end
vp = sp / (np[0] - 1)
ve = se / (np[0] * (nn - 1))
fp = vp / ve
$p = 0.01 * a
$dof2 = np[0] * (nn - 1)
sw = Array.new(1)
print("全変動: 平方和 " + String(sp+se) + " 自由度 " + String(np[0]*nn-1) + "\n")
$dof1 = np[0] - 1
print(name[0])
print("の水準間: 平方和 " + String(sp) + " 自由度 " + String(np[0]-1) + " 不偏分散 " + String(vp) + " F " + String(fp) + " " + String(a) + "%値 " + String(p_f(sw)) + "\n")
print("水準内: 平方和 " + String(se) + " 自由度 " + String(np[0]*(nn-1)) + " 不偏分散 " + String(ve) + "\n")
# 二元配置法
else
xi = Array.new(np[0])
for i1 in 0 ... np[0]
xi[i1] = 0.0
for i2 in 0 ... np[1]
for i3 in 0 ... nn
xi[i1] += x[i1][i2][i3]
end
end
xi[i1] /= (np[1] * nn)
end
xj = Array.new(np[1])
for i1 in 0 ... np[1]
xj[i1] = 0.0
for i2 in 0 ... np[0]
for i3 in 0 ... nn
xj[i1] += x[i2][i1][i3]
end
end
xj[i1] /= (np[0] * nn)
end
xij = Array.new(np[0])
for i1 in 0 ... np[0]
xij[i1] = Array.new(np[1])
for i2 in 0 ... np[1]
xij[i1][i2] = 0.0
for i3 in 0 ... nn
xij[i1][i2] += x[i1][i2][i3]
end
xij[i1][i2] /= nn
end
end
xa = 0.0
for i1 in 0 ... np[0]
for i2 in 0 ... np[1]
for i3 in 0 ... nn
xa += x[i1][i2][i3]
end
end
end
xa /= (np[0] * np[1] * nn)
sp = 0.0
for i1 in 0 ... np[0]
sp += (xi[i1] - xa) * (xi[i1] - xa)
end
sp *= (np[1] * nn)
sq = 0.0
for i1 in 0 ... np[1]
sq += (xj[i1] - xa) * (xj[i1] - xa)
end
sq *= (np[0] * nn)
si = 0.0
for i1 in 0 ... np[0]
for i2 in 0 ... np[1]
si += (xij[i1][i2] - xi[i1] - xj[i2] + xa) * (xij[i1][i2] - xi[i1] - xj[i2] + xa)
end
end
si *= nn
se = 0.0
for i1 in 0 ... np[0]
for i2 in 0 ... np[1]
for i3 in 0 ... nn
se += (x[i1][i2][i3] - xij[i1][i2]) * (x[i1][i2][i3] - xij[i1][i2])
end
end
end
vp = sp / (np[0] - 1)
vq = sq / (np[1] - 1)
vi = si / ((np[0] - 1) * (np[1] - 1))
ve = se / (np[0] * np[1] * (nn - 1))
fp = vp / ve
fq = vq / ve
fi = vi / ve
$p = 0.01 * a
$dof2 = np[0] * np[1] * (nn - 1)
sw = Array.new(1)
print("全変動: 平方和 " + String(sp+sq+si+se) + " 自由度 " + String(np[0]*np[1]*nn-1) + "\n")
$dof1 = np[0] - 1
print(name[0])
print("の水準間: 平方和 " + String(sp) + " 自由度 " + String(np[0]-1) + " 不偏分散 " + String(vp) + " F " + String(fp) + " " + String(a) + "%値 " + String(p_f(sw)) + "\n")
$dof1 = np[1] - 1
print(name[1])
print("の水準間: 平方和 " + String(sq) + " 自由度 " + String(np[1]-1) + " 不偏分散 " + String(vq) + " F " + String(fq) + " " + String(a) + "%値 " + String(p_f(sw)) + "\n")
$dof1 = (np[0] - 1) * (np[1] - 1)
print("相互作用: 平方和 " + String(si) + " 自由度 " + String((np[0]-1)*(np[1]-1)) + " 不偏分散 " + String(vi) + " F " + String(fi) + " " + String(a) + "%値 " + String(p_f(sw)) + "\n")
print("水準内: 平方和 " + String(se) + " 自由度 " + String(np[0]*np[1]*(nn-1)) + " 不偏分散 " + String(ve) + "\n")
end
end
$p = 0.0
$dof1 = 1
$dof2 = 2
name = ["", ""]
np = [1, 1]
ss = gets().split(" ")
method = Integer(ss[0]) # 因子の数
nn = Integer(ss[1]) # 各水準におけるデータ数
a = Float(ss[2]) # 有意水準(%)
if method == 1 or method == 2
for i1 in 0 ... method
ss = gets().split(" ")
name[i1] = ss[0]
np[i1] = Integer(ss[1])
end
x = Array.new(np[0])
for i1 in 0 ... np[0]
x[i1] = Array.new(np[1])
for i2 in 0 ... np[1]
x[i1][i2] = Array.new(nn)
end
end
for i1 in 0 ... np[1]
for i2 in 0 ... nn
ss = gets().split(" ")
for i3 in 0 ... np[0]
x[i3][i1][i2] = Float(ss[i3])
end
end
end
aov(method, np, nn, name, a, x)
else
print("一元配置法,または,二元配置法だけです!\n")
end
=begin
-------- 一元配置法に対するデータ例(コメント部分を除いて下さい)--------
1 6 5 # 因子の数 各水準におけるデータ数 有意水準(%)
工場 3 # 因子の名前 水準の数
3.1 4.7 5.1 # 各水準に対する1番目のデータ
4.1 5.6 3.7 # 各水準に対する2番目のデータ
3.3 4.3 4.5 # 各水準に対する3番目のデータ
3.9 5.9 6.0 # 各水準に対する4番目のデータ
3.7 6.1 3.9 # 各水準に対する5番目のデータ
2.4 4.2 5.4 # 各水準に対する6番目のデータ
-------- 二元配置法に対するデータ例(コメント部分を除いて下さい)--------
2 3 5 # 因子の数 各水準におけるデータ数 有意水準(%)
薬剤 5 # 1番目の因子の名前 その水準の数
品種 2 # 2番目の因子の名前 その水準の数
3 4 12 -4 -4 # 1番目の因子の各水準に対して,2番目の因子の水準1に対する1番目のデータ
8 -8 31 12 19 # 1番目の因子の各水準に対して,2番目の因子の水準1に対する2番目のデータ
7 -5 8 0 23 # 1番目の因子の各水準に対して,2番目の因子の水準1に対する3番目のデータ
8 -10 9 10 15 # 1番目の因子の各水準に対して,2番目の因子の水準2に対する1番目のデータ
-5 11 26 -1 13 # 1番目の因子の各水準に対して,2番目の因子の水準2に対する2番目のデータ
10 -6 13 -7 -6 # 1番目の因子の各水準に対して,2番目の因子の水準2に対する3番目のデータ
=end