分散分析(一元配置法と二元配置法)

# -*- coding: UTF-8 -*-
from math import *
import numpy as np

############################################
# Γ(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 * exp(-x) * pow(x,x-0.5) * s

	else :

		err = 1.0e-20
		w   = x
		t   = 1.0

		if x < 1.5 :

			if x < err :
				k = int(x)
				y = float(k) - x
				if abs(y) < err or abs(1.0-y) < err :
					ier[0] = -1

			if ier[0] == 0 :
				while w < 1.5 :
					t /= w
					w += 1.0

		else :
			if w > 2.5 :
				while w > 2.5 :
					w -= 1.0
					t *= w

		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

	return g

################################################
# 標準正規分布N(0,1)の計算(P(X = x), P(X < x))
#      w : P(X = x)
#      return : P(X < x)
################################################

def normal(x, w) :
			# 確率密度関数(定義式)
	w[0] = exp(-0.5 * x * x) / sqrt(2.0*pi)
			# 確率分布関数(近似式を使用)
	y = 0.70710678118654 * abs(x)
	z = 1.0 + y * (0.0705230784 + y * (0.0422820123 + y * (0.0092705272 + y * (0.0001520143 + y * (0.0002765672 + y * 0.0000430638)))))
	P = 1.0 - z ** (-16.0)

	if x < 0.0 :
		P = 0.5 - 0.5 * P
	else :
		P = 0.5 + 0.5 * P

	return P

#######################################
# 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

	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 * sqrt(1.0-x)
			pp = 1.0 - sqrt(1.0-x)
			ia = 2
			ib = 1

	else :
		if df2%2 == 0 :
			u  = 0.5 * sqrt(x) * (1.0 - x)
			pp = sqrt(x)
			ia = 1
			ib = 2
		else :
			u  = sqrt(x*(1.0-x)) / pi
			pp = 1.0 - 2.0 * atan2(sqrt(1.0-x), sqrt(x)) / pi
			ia = 1
			ib = 1

	if ia != df1 :
		for i1 in range(ia, df1-1, 2) :
			pp -= 2.0 * u / i1
			u  *= x * (i1 + ib) / i1

	if ib != df2 :
		for i1 in range(ib, df2-1, 2) :
			pp += 2.0 * u / i1
			u  *= (1.0 - x) * (i1 + df1) / i1

	dd[0] = u / ff

	return pp

############################################
# 二分法による非線形方程式(f(x)=0)の解
#      fn : f(x)を計算する関数名
#      x1,x2 : 初期値
#      eps1 : 終了条件1(|x(k+1)-x(k)|<eps1)
#      eps2 : 終了条件2(|f(x(k))|<eps2)
#      max : 最大試行回数
#      ind : 実際の試行回数
#            (負の時は解を得ることができなかった)
#      return : 解
#      coded by Y.Suganuma
############################################

def bisection(fn, x1, x2, eps1, eps2, max, ind) :

	x0 = 0.0
	f1 = fn(x1)
	f2 = fn(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
		else :
			sw = 0
			while sw == 0 and ind[0] >= 0 :
				sw      = 1
				ind[0] += 1
				x0     = 0.5 * (x1 + x2)
				f0     = fn(x0)

				if abs(f0) > eps2 :
					if ind[0] <= max :
						if abs(x1-x2) > eps1 and abs(x1-x2) > eps1*abs(x2) :
							sw = 0
							if f0*f1 < 0.0 :
								x2 = x0
								f2 = f0
							else :
								x1 = x0
								f1 = f0
					else :
						ind[0] = -1

	return x0

############################################
# Newton法による非線形方程式(f(x)=0)の解
#      fn : f(x)を計算する関数名
#      dfn : f(x)の微分を計算する関数名
#      x0 : 初期値
#      eps1 : 終了条件1(|x(k+1)-x(k)|<eps1)
#      eps2 : 終了条件2(|f(x(k))|<eps2)
#      max : 最大試行回数
#      ind : 実際の試行回数
#            (負の時は解を得ることができなかった)
#      return : 解
#      coded by Y.Suganuma
############################################

def newton(fn, dfn, x0, eps1, eps2, max, ind) :

	x1     = x0
	x      = x1
	ind[0] = 0
	sw     = 0

	while sw == 0 and ind[0] >= 0 :

		sw      = 1
		ind[0] += 1
		g       = fn(x1)

		if abs(g) > eps2 :
			if ind[0] <= max :
				dg = dfn(x1)
				if abs(dg) > eps2 :
					x = x1 - g / dg
					if abs(x-x1) > eps1 and abs(x-x1) > eps1*abs(x) :
						x1 = x
						sw = 0
				else :
					ind[0] = -1
			else :
				ind[0] = -1

	return x

############################
# f分布の計算
#      coded by Y.Suganuma
############################

##########################################
# 1.0 - p - P(X>x)(関数値, 標準正規分布)
##########################################
def normal_f(x) :
	y = np.empty(1, np.float)
	return 1.0 - p - normal(x, y)

################################################################
# 標準正規分布N(0,1)のp%値(P(X > u) = 0.01p)(二分法を使用)
#      ind : >= 0 : normal(収束回数)
#            = -1 : 収束しなかった
################################################################
def p_normal(ind) :
	u = bisection(normal_f, -7.0, 7.0, 1.0e-6, 1.0e-10, 100, ind)
	return u

#######################################
# 1.0 - p - P(X > x)(関数値, f 分布)
#######################################
def f_f(x) :
	y = np.empty(1, np.float)
	return f(x, y, dof1, dof2) - 1.0 + p

#################################
# P(X = x)(関数の微分, f 分布)
#################################
def f_df(x) :
	y = np.empty(1, np.float)
	z = f(x, y, dof1, dof2)
	return y[0]

######################################
# f分布のp%値(P(X > u) = 0.01p)
#      ind : >= 0 : normal(収束回数)
#            = -1 : 収束しなかった
######################################

def p_f(ind) :

	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

	if sw == -2 :
		ind[0] = -1

	else :
			# f0 : 初期値
		if e > 0.8 :
			x  = (a1 * b1 + yq * 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)
			# ニュートン法
		ff = newton(f_f, f_df, f0, 1.0e-6, 1.0e-10, 100, ind)

	return ff

#######################################
# 分散分析(一元配置法と二元配置法)
#      method : 1 or 2
#      Np[0] : 因子1の水準数
#        [1] : 因子2の水準数
#      N : データ数
#      name[0] : 因子1の名前
#          [1] : 因子2の名前
#      a : a %値
#      x : データ
#           coded by Y.Suganuma
#######################################
def aov(method, Np, N, name, a, x) :

	global p, dof1, dof2

			# 一元配置法

	if method == 1 :

		xi = np.empty(Np[0], np.float)
		for i1 in range(0, Np[0]) :
			xi[i1] = 0.0
			for i2 in range(0, N) :
				xi[i1] += x[i1][0][i2]
			xi[i1] /= N

		xa = 0.0
		for i1 in range(0, Np[0]) :
			for i2 in range(0, N) :
				xa += x[i1][0][i2]
		xa /= (Np[0] * N)

		SP = 0.0
		for i1 in range(0, Np[0]) :
			SP += (xi[i1] - xa) * (xi[i1] - xa)
		SP *= N

		SE = 0.0
		for i1 in range(0, Np[0]) :
			for i2 in range(0, N) :
				SE += (x[i1][0][i2] - xi[i1]) * (x[i1][0][i2] - xi[i1])

		VP = SP / (Np[0] - 1)
		VE = SE / (Np[0] * (N - 1))

		FP = VP / VE

		p    = 0.01 * a
		dof2 = Np[0] * (N - 1)

		sw = np.empty(1, np.int)
		print("全変動: 平方和 " + str(SP+SE) + " 自由度 " + str(Np[0]*N-1))
		dof1 = Np[0] - 1
		print(name[0] + "の水準間: 平方和 " + str(SP) + " 自由度 " + str(Np[0]-1) + " 不偏分散 " + str(VP) + " F " + str(FP) + " " + str(a) + "%値 " + str(p_f(sw)))
		print("水準内: 平方和 " + str(SE) + " 自由度 " + str(Np[0]*(N-1)) + " 不偏分散 " + str(VE))

			# 二元配置法

	else :

		xi = np.empty(Np[0], np.float)
		for i1 in range(0, Np[0]) :
			xi[i1] = 0.0
			for i2 in range(0, Np[1]) :
				for i3 in range(0, N) :
					xi[i1] += x[i1][i2][i3]
			xi[i1] /= (Np[1] * N)

		xj = np.empty(Np[1], np.float)
		for i1 in range(0, Np[1]) :
			xj[i1] = 0.0
			for i2 in range(0, Np[0]) :
				for i3 in range(0, N) :
					xj[i1] += x[i2][i1][i3]
			xj[i1] /= (Np[0] * N)

		xij = np.empty((Np[0], Np[1]), np.float)
		for i1 in range(0, Np[0]) :
			for i2 in range(0, Np[1]) :
				xij[i1][i2] = 0.0
				for i3 in range(0, N) :
					xij[i1][i2] += x[i1][i2][i3]
				xij[i1][i2] /= N

		xa = 0.0
		for i1 in range(0, Np[0]) :
			for i2 in range(0, Np[1]) :
				for i3 in range(0, N) :
					xa += x[i1][i2][i3]
		xa /= (Np[0] * Np[1] * N)

		SP = 0.0
		for i1 in range(0, Np[0]) :
			SP += (xi[i1] - xa) * (xi[i1] - xa)
		SP *= (Np[1] * N)

		SQ = 0.0
		for i1 in range(0, Np[1]) :
			SQ += (xj[i1] - xa) * (xj[i1] - xa)
		SQ *= (Np[0] * N)

		SI = 0.0
		for i1 in range(0, Np[0]) :
			for i2 in range(0, Np[1]) :
				SI += (xij[i1][i2] - xi[i1] - xj[i2] + xa) * (xij[i1][i2] - xi[i1] - xj[i2] + xa)
		SI *= N

		SE = 0.0
		for i1 in range(0, Np[0]) :
			for i2 in range(0, Np[1]) :
				for i3 in range(0, N) :
					SE += (x[i1][i2][i3] - xij[i1][i2]) * (x[i1][i2][i3] - xij[i1][i2])

		VP = SP / (Np[0] - 1)
		VQ = SQ / (Np[1] - 1)
		VI = SI / ((Np[0] - 1) * (Np[1] - 1))
		VE = SE / (Np[0] * Np[1] * (N - 1))

		FP = VP / VE
		FQ = VQ / VE
		FI = VI / VE

		p    = 0.01 * a
		dof2 = Np[0] * Np[1] * (N - 1)

		sw = np.empty(1, np.int)
		print("全変動: 平方和 " + str(SP+SQ+SI+SE) + " 自由度 " + str(Np[0]*Np[1]*N-1))
		dof1 = Np[0] - 1
		print(name[0] + "の水準間: 平方和 " + str(SP) + " 自由度 " + str(Np[0]-1) + " 不偏分散 " + str(VP) + " F " + str(FP) + " " + str(a) + "%値 " + str(p_f(sw)))
		dof1 = Np[1] - 1
		print(name[1] + "の水準間: 平方和 " + str(SQ) + " 自由度 " + str(Np[1]-1) + " 不偏分散 " + str(VQ) + " F " + str(FQ) + " " + str(a) + "%値 " + str(p_f(sw)))
		dof1 = (Np[0] - 1) * (Np[1] - 1)
		print("相互作用: 平方和 " + str(SI) + " 自由度 " + str((Np[0]-1)*(Np[1]-1)) + " 不偏分散 " + str(VI) + " F " + str(FI) + " " + str(a) + "%値 " + str(p_f(sw)))
		print("水準内: 平方和 " + str(SE) + " 自由度 " + str(Np[0]*Np[1]*(N-1)) + " 不偏分散 " + str(VE))

p    = 0.0
dof1 = 1
dof2 = 2

----------------------------------

# -*- coding: UTF-8 -*-
import numpy as np
import sys
from math import *
from function import aov, f, gamma, normal, bisection, newton

############################
# 分散分析
#      coded by Y.Suganuma
############################

name   = ["", ""]
Np     = np.array([1, 1], np.int)
line   = sys.stdin.readline()
ss     = line.split()
method = int(ss[0])   # 因子の数
N      = int(ss[1])   # 各水準におけるデータ数
a      = float(ss[2])   # 有意水準(%)

if method == 1 or method == 2 :
	for i1 in range(0, method) :
		line   = sys.stdin.readline()
		ss     = line.split()
		name[i1] = ss[0]
		Np[i1]   = int(ss[1])
	x = np.empty((Np[0], Np[1], N), np.float)
	for i1 in range(0, Np[1]) :
		for i2 in range(0, N) :
			line = sys.stdin.readline()
			ss   = line.split()
			for i3 in range(0, Np[0]) :
				x[i3][i1][i2] = float(ss[i3])
	aov(method, Np, N, name, a, x)
else :
	print("一元配置法,または,二元配置法だけです!")

-------- 一元配置法に対するデータ例(コメント部分を除いて下さい)--------
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番目のデータ