! module PermMod ! deals with permutation testing ! contains: PValue module PermMod use SortMod use ArrayMod implicit none contains !******************************************************************************* ! all subscripts are supplied in the call ! ConCall and SusCall are two (not necessarily equivalent vectors), ! containing control and suspected diff vals, and sprinkled with MissVal ! PermMin and PermIdeal must be filled ! requires arraymod.f90 to be among main program Use statements function PValue (ConCall,SusCall,PermMin,PermIdeal) real PValue real, pointer, dimension (:) :: ConCall, SusCall, ConValid, SusValid real, pointer, dimension (:) :: Sums integer, pointer, dimension (:) :: Ranks integer, pointer, dimension (:,:) :: SampleKey integer, intent (in) :: PermMin, PermIdeal real, parameter :: MissVal = -999.0 integer :: AllocStat integer :: ConN, SusN, PoolN, PossN, MissN, SampleN, HalfN integer :: XSus, XCon, XMem, XPoss, XSample integer :: QCalcAll integer :: Integ, NextInteg, FinalPlacing, CurrentRank !******************************************* ! perform initial checks and preparations PValue = MissVal call UnBlankReal (ConCall,ConValid) ! weed out missing values and reform arrays call UnBlankReal (SusCall,SusValid) ConN = size (ConValid,1) SusN = size (SusValid,1) if (ConN.LE.0) return ! check that the sample sizes are non-zero if (SusN.LE.0) return PoolN = ConN + SusN ! pooled sample PossN = 0 ! total permutations in pooled sample XMem = PoolN + 1 ! only calc fully if < PermIdeal do XMem = XMem - 1 if (PossN.EQ.0) then PossN = XMem else PossN = PossN * XMem end if if (PossN.GT.PermIdeal) exit if (XMem.EQ.(PoolN-SusN+1)) exit end do QCalcAll = 1 ! either calc all possible if (PossN.GT.PermIdeal) QCalcAll = 2 ! or calc a random sample ! or return a MissVal if (PermMin.EQ.MissVal.OR.PermMin.LE.0) QCalcAll = -1 if (PermIdeal.EQ.MissVal.OR.PermIdeal.LE.0) QCalcAll = -1 if (PossN.LT.PermMin) QCalcAll = -1 !******************************************* ! calc p-value if (QCalcAll.EQ.1) then ! using all possible perms SampleN = PossN call KeyAllPoss (SampleN,PoolN,SusN,SampleKey) ! this mod sub: get key else if (QCalcAll.EQ.2) then ! random sampling from perms SampleN = PermIdeal call KeySample (SampleN,PoolN,SusN,SampleKey) ! this mod sub: get key do XSus = 1, SusN ! replace first sample member with suspected SampleKey(1,XSus) = XSus end do do XCon = 1, ConN SampleKey(1,(SusN+XCon)) = MissVal end do end if allocate (Sums(SampleN), stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: PValue: Allocation failure #####" Sums = 0.0 if (QCalcAll.GT.0) then call CalcSampleSums ! internal sub: calc sums deallocate (SampleKey, stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: PValue: Deallocation failure #####" call RankVector (SampleN,MissN,Sums,Ranks) ! sortmod.f90: ranks sums Integ = Ranks(1) HalfN = SampleN / 2 if (Integ.LT.SampleN.AND.Integ.GT.1) then XSample = XSample + 1 if (Integ.GT.HalfN) then NextInteg = Integ + 1 else if (Integ.LT.HalfN) then NextInteg = Integ - 1 end if FinalPlacing = 0 CurrentRank = 1 XSample = 1 do if (Ranks(XSample).EQ.NextInteg) then if (Sums(XSample).EQ.Sums(CurrentRank)) then Integ = Ranks(XSample) CurrentRank = XSample XSample = 1 end if else FinalPlacing = 1 end if if (Integ.EQ.SampleN.OR.Integ.EQ.1) exit if (FinalPlacing.EQ.1) exit end do end if if (Integ.EQ.SampleN) Integ = SampleN - 1 ! prevents p=+1.0 if (Integ.EQ.1) Integ = 2 ! prevents p=-1.0 if (Integ.LT.HalfN) Integ = Integ - SampleN - 1 ! converts to negative equivalent PValue = real(Integ) / real(SampleN) end if deallocate (Sums,ConValid,SusValid, stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: PValue: Deallocation failure #####" contains !******************************************* ! calc p-value using a random sample subroutine CalcSampleSums do XSample = 1, SampleN do XSus = 1, SusN if (SampleKey(XSample,XSus).NE.MissVal) then Sums(XSample) = Sums(XSample) + SusValid(XSus) end if end do do XCon = 1, ConN if (SampleKey(XSample,(SusN+XCon)).NE.MissVal) then Sums(XSample) = Sums(XSample) + ConValid(XCon) end if end do end do end subroutine CalcSampleSums end function PValue !******************************************************************************* ! give it PossN,PoolN,SusN and it gives back a key for the sample (PossN,PoolN) ! MissVal = not part of permutation ! positive integer = position in permutation (noramlly only .NE.MissVal is required) subroutine KeyAllPoss (PossN,PoolN,SusN,SampleKey) integer, pointer, dimension (:,:) :: SampleKey real, parameter :: MissVal = -999.0 integer :: AllocStat integer :: SusN, PoolN, PossN integer :: XSus, XPoss, XPool, XMem integer :: MemMove, MemMovePos, NextBlank, FinishedPerm !******************************************* ! initialise allocate (SampleKey(PossN,PoolN), stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: KeyAllPoss: Allocation failure #####" SampleKey = MissVal do XSus = 1, SusN SampleKey (1,XSus) = XSus end do XPoss = 1 !******************************************* ! main routine do XPoss = XPoss + 1 ! loop once for each permutation MemMove = SusN ! initialise member to move to the last member FinishedPerm = 0 ! flags when perm has been made do XPool = 1, PoolN ! initialise current perm to the last perm SampleKey (XPoss,XPool) = SampleKey ((XPoss-1),XPool) end do do MemMovePos = 0 ! find position of member to move (index) do MemMovePos = MemMovePos + 1 if (SampleKey(XPoss,MemMovePos).EQ.MemMove) exit end do NextBlank = MemMovePos ! find next blank (MissVal or index) do NextBlank = NextBlank + 1 if (NextBlank.GT.PoolN) NextBlank = MissVal if (NextBlank.EQ.MissVal) exit if (SampleKey(XPoss,NextBlank).EQ.MissVal) exit end do if (NextBlank.EQ.MissVal) then ! cannot move member down SampleKey (XPoss,MemMovePos) = MissVal MemMove = MemMove - 1 else ! can move member down SampleKey (XPoss,MemMovePos) = MissVal SampleKey (XPoss,NextBlank) = MemMove ! ...move member down if (MemMove.LT.SusN) then ! ...add in rest of members to first blanks, in order NextBlank = 0 do XMem = (MemMove+1), SusN do NextBlank = NextBlank + 1 if (SampleKey(XPoss,NextBlank).EQ.MissVal) exit end do SampleKey (XPoss,NextBlank) = XMem end do end if FinishedPerm = 1 end if if (FinishedPerm.EQ.1) exit end do if (XPoss.EQ.PossN) exit ! exit when order is inverted end do end subroutine KeyAllPoss !******************************************************************************* subroutine KeySample (SampleN,PoolN,SusN,SampleKey) integer, pointer, dimension (:,:) :: SampleKey real, parameter :: MissVal = -999.0 real :: Rand integer :: AllocStat integer :: SusN, PoolN, SampleN integer :: XSample, XMem integer :: Index !******************************************* ! initialise allocate (SampleKey(SampleN,PoolN), stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: KeySample: Allocation failure #####" SampleKey = MissVal call RandomiseSeed ! arraymod.f90 !******************************************* ! main routine do XSample = 1, SampleN do XMem = 1, SusN do do call random_number (Rand) Rand = Rand * (PoolN+1) ! avoids reduction of 1,PoolN probabilities Index = nint (Rand) if (Index.GE.1.AND.Index.LE.PoolN) exit end do if (SampleKey(XSample,Index).EQ.MissVal) SampleKey(XSample,Index) = XMem if (SampleKey(XSample,Index).EQ.XMem) exit end do end do end do end subroutine KeySample !******************************************************************************* end module PermMod