#=======================================================================
# Author: Donovan Parks
#
# Perform Games-Howell post-hoc test.
#
# Copyright 2011 Donovan Parks
#
# This file is part of STAMP.
#
# STAMP is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# STAMP is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with STAMP.	If not, see <http://www.gnu.org/licenses/>.
#=======================================================================

import math

from numpy import var, mean

from stamp.plugins.multiGroups.AbstractPostHocTestPlugin import AbstractPostHocTestPlugin
from stamp.metagenomics.stats.distributions.QTable import QTable

class GamesHowell(AbstractPostHocTestPlugin):
	'''
	Perform Games-Howell post-hoc test.
	'''
	
	def __init__(self, preferences):
		AbstractPostHocTestPlugin.__init__(self, preferences)
		self.name = 'Games-Howell'
		self.qtable = QTable(preferences)
	
	def run(self, data, coverage, groupNames):
		note = ''
		k = len(data)
		
		# calculate mean and variance of each group
		groupMean = []
		groupVar = []
		for i in xrange(0, len(data)):
			groupMean.append(mean(data[i]))
			groupVar.append(var(data[i], ddof=1))
		
		# calculate Fs, effect size, and CI for each pair of groups
		pValues = []
		effectSize = []
		lowerCI = []
		upperCI = []
		labels = []
		for i in xrange(0, len(data)):
			ni = len(data[i])
			
			for j in xrange(i+1, len(data)):
				nj = len(data[j])
				
				# calculate Games-Howell degree of freedom
				vn1 = groupVar[i] / ni
				vn2 = groupVar[j] / nj

				if vn1 == 0:
					note = 'degenerate case: two groups with zero variance; variance set to 1e-6.'
					vn1 = 1e-6
					
				if vn2 == 0:
					note = 'degenerate case: two groups with zero variance; variance set to 1e-6.'
					vn2 = 1e-6
				
				df = (vn1 + vn2) * (vn1 + vn2)
				df /= (vn1*vn1)/(ni-1) + (vn2*vn2)/(nj-1)
				
				q_cv = self.qtable.cvInterpolate(1.0-coverage, k, df)
				
				cv001 = self.qtable.cvInterpolate(0.001, k, df)
				cv01 = self.qtable.cvInterpolate(0.01, k, df)
				cv02 = self.qtable.cvInterpolate(0.02, k, df)
				cv05 = self.qtable.cvInterpolate(0.05, k, df)
				cv1 = self.qtable.cvInterpolate(0.1, k, df)
				
				# effect size
				es = groupMean[i] - groupMean[j]
				effectSize.append(es)
				
				# calculate Games-Howell unequal variance adjustment
				varAdj = math.sqrt( (vn1 + vn2) / 2.0)

				# p-value
				qs = abs(es) / varAdj
				
				if qs > cv001:
					pValue = '< 0.001'
				elif qs > cv01:
					pValue = '< 0.01'
				elif qs > cv02:
					pValue = '< 0.02'
				elif qs > cv05:
					pValue = '< 0.05'
				elif qs > cv1:
					pValue = '< 0.1'
				else:
					pValue = '>= 0.1'

				pValues.append(pValue)

				# confidence interval
				confInter = q_cv*varAdj
				lowerCI.append(es - confInter)
				upperCI.append(es + confInter)
				
				labels.append(groupNames[i] + ' : ' + groupNames[j])
			
		return pValues, effectSize, lowerCI, upperCI, labels, note

if __name__ == "__main__": 
	pass
