我之前发布了一个关于尝试从统计文件中重现样本大小的问题here的问题。作者向我提供了本文中使用的代码,该代码是Fortran程序。我想使用我工作团队使用的R包调用此代码。为此,我需要将此Fortran程序转换为子例程。这是我对Fortran的第一次体验,我似乎以某种方式破坏了程序并得到了错误的数字,当我更改输入时这些数字没有改变。我还应该注意,我必须对作者提供的原始代码进行一些小的更改,以使程序正常工作。
"原作"编译后生成正确结果然后运行的程序:
PROGRAM Exact_Mid_P_binomial_sample_size
real(8) probA,probB,part1,part2,part3,part4
real(8) totprA,totprB,factt, resp
! character resp
1 format ('Enter proportion ',$)
2 format ('Enter error limit ',$)
3 format ('Enter confidence level ',$)
4 format ('Calculated sample size is ',i6)
5 format ('Exact mid-P with ',f7.5,' 2-tail probability')
6 format ('Sorry, unable to mathmatically solve this problem.')
7 format ('Reported sample size is not accuarate.')
8 format ('Enter q to quit ',$)
9 format ('Actual limits for distribution ',f5.3,' - ',f5.3)
print *, 'Exact sampleroportions'
print *, 'Using Mid-P methods'
print *, 'Geoff Fosgate DVM PhD'
print *, 'College of Veterinary Medicine'
print *, 'Texas A&M University'
print *
10 print *
print 1
read *, prop1
print 2
read *,range
print 3
read *,conlev
print *
! Convert proportions less than 0.5 for algorithm
if (prop1 .lt. 0.5) then
prop = 1 - prop1
nprop = 1
else
prop = prop1
nprop = 0
end if
slimit = max ((prop - range) , 0.0001)
supper = min ((prop + range) , 0.9999)
! Probabilities cannot be calculated for p=0 and p=1
alpha = (1 - conlev)
if (alpha .gt. 1.0) go to 10
if (alpha .lt. 0.0) go to 10
if (prop .gt. 1.0) go to 10
if (prop .lt. 0.0) go to 10
numbr = (1 / (1 - prop)) - 1
! Define and initialize variables
! Note names of variables based on Fortran 77 rules
! Starting sample size is based on estimated proportion
! Resulting sample size must be large enough to obtain this proportion
100 numbr = numbr + 1
numx = (numbr * prop) + 0.001
! This is the number of binomial "successes" resulting in the proportion
if (numx .eq. numbr) go to 100
if (numx .lt. 1) go to 100
totprA = slimit**numbr
totprB = supper**numbr
do 130 loop1 = numx, (numbr - 1)
! Must initialize variables within loop
factt = 1.0
probA = 0.0
probB = 0.0
part1 = 0.0
part2 = 0.0
part3 = 0.0
part4 = 0.0
! Start loop to calculate factorial component of binomial probability
! Note that complete factorial calculations not necessary due to cancellations
do 110 loop2 = (loop1 + 1) , numbr
factt = factt * (loop2) / (numbr - (loop2 - 1))
110 continue
! Calculate probability for this particular number of successes
! Total probability is a running total
! Note that real variables must have high precision and be comprised
! of multiple bytes because factorial component can be very large
! and exponentiated component can be very small
! Program will fail if any component is recognized as zero or infinity
part1 = slimit**loop1
part2 = (1.0-slimit)**(numbr-loop1)
part3 = supper**loop1
part4 = (1.0-supper)**(numbr-loop1)
if (part1 .eq. 0.0) part1 = 1.0D-307
if (part2 .eq. 0.0) part2 = 1.0D-307
if (part3 .eq. 0.0) part3 = 1.0D-307
if (part4 .eq. 0.0) part4 = 1.0D-307
if (factt .gt. 1.0D308) factt = 1.0D308
probA = part1 * part2 * factt
probB = part3 * part4 * factt
if (loop1 .eq. numx) then
totprA = totprA + (0.5 * probA)
totprB = totprB + (0.5 * probB)
else
totprA = totprA + probA
totprB = totprB + probB
end if
if (probA .eq. 0.0) then
print 6
print 7
print *
go to 150
end if
if (probB .eq. 0.0) then
print 6
print 7
print *
go to 150
end if
130 continue
140 if ((totprA + (1 - totprB)) .gt. alpha) go to 100
! go to beginning and increase sample size by 1 if have not
! reached specified level of confidence
150 if (nprop .eq. 1) then
print 4,numbr
print 9, (1-supper),(1-slimit)
else
print 4,numbr
print 9, slimit,supper
end if
if (totprA+(1-totprB) .lt. alpha) print 5,(totprA+(1-totprB))
print *
print 8
result = resp
! print *
! if (resp .ne. 'q') go to 10
print *
print *
999 end
上面的程序变成了一个子程序:
subroutine midpss(prop1, range, conlev, numbr)
integer numbr, nprop
real(8) prop1, range, conlev, prop, slimit, supper
real(8) probA,probB,part1,part2,part3,part4,factt
real(8) totprA,totprB, resp
c character resp
c Convert proportions less than 0.5 for algorithm
if (prop1 .lt. 0.5) then
prop = 1 - prop1
nprop = 1
else
prop = prop1
nprop = 0
end if
slimit = max ((prop - range) , 0.0001)
supper = min ((prop + range) , 0.9999)
numbr = (1 / (1 - prop)) - 1
c Define and initialize variables
c Note names of variables based on Fortran 77 rules
c Starting sample size is based on estimated proportion
c Resulting sample size must be large enough to obtain this proportion
100 numbr = numbr + 1
numx = (numbr * prop) + 0.001
c This is the number of binomial "successes" resulting in the proportion
if (numx .eq. numbr) go to 100
if (numx .lt. 1) go to 100
totprA = slimit**numbr
totprB = supper**numbr
do 130 loop1 = numx, (numbr - 1)
c Must initialize variables within loop
factt = 1.0
probA = 0.0
probB = 0.0
part1 = 0.0
part2 = 0.0
part3 = 0.0
part4 = 0.0
c Start loop to calculate factorial component of binomial probability
c Note that complete factorial calculations not necessary due to cancellations
do 110 loop2 = (loop1 + 1) , numbr
factt = factt * (loop2) / (numbr - (loop2 - 1))
110 continue
c Calculate probability for this particular number of successes
c Total probability is a running total
c Note that real variables must have high precision and be comprised
c of multiple bytes because factorial component can be very large
c and exponentiated component can be very small
c Program will fail if any component is recognized as zero or infinity
part1 = slimit**loop1
part2 = (1.0-slimit)**(numbr-loop1)
part3 = supper**loop1
part4 = (1.0-supper)**(numbr-loop1)
if (part1 .eq. 0.0) part1 = 1.0D-307
if (part2 .eq. 0.0) part2 = 1.0D-307
if (part3 .eq. 0.0) part3 = 1.0D-307
if (part4 .eq. 0.0) part4 = 1.0D-307
if (factt .gt. 1.0D308) factt = 1.0D308
probA = part1 * part2 * factt
probB = part3 * part4 * factt
if (loop1 .eq. numx) then
totprA = totprA + (0.5 * probA)
totprB = totprB + (0.5 * probB)
else
totprA = totprA + probA
totprB = totprB + probB
end if
130 continue
140 if ((totprA + (1 - totprB)) .gt. alpha) go to 100
return
end
要编译原始程序,我在命令行中运行它:
gfortran midpSS_original.f
要编译子例程,我在命令行中运行它:
R CMD SHLIB midpSS_subroutine.f
然后从R控制台运行:
> dyn.load("midpSS_subroutine.dll")
> is.loaded("midpss")
[1] TRUE
> .Fortran("midpss", prop1=as.numeric(0.9), range=as.numeric(0.1), conlev=as.numeric(0.90), numbr=as.integer(0)) # numbr should be 29
$prop1
[1] 0.9
$range
[1] 0.1
$conlev
[1] 0.9
$numbr
[1] 2091
> .Fortran("midpss", prop1=as.numeric(0.9), range=as.numeric(0.1), conlev=as.numeric(0.95), numbr=as.integer(0)) # numbr should be 47
$prop1
[1] 0.9
$range
[1] 0.1
$conlev
[1] 0.95
$numbr
[1] 2091
在第一次调用子程序时,numbr应该是29.在第二次调用中,numbr应该是47.在编译"原始"时,结果是正确的。 fortran程序并运行相同的参数。我无法弄清楚这里发生了什么。任何帮助将不胜感激。
答案 0 :(得分:1)
以下块检查输入数据:
if (alpha .gt. 1.0) go to 10
if (alpha .lt. 0.0) go to 10
if (prop .gt. 1.0) go to 10
if (prop .lt. 0.0) go to 10
此块使代码进入输入位置以再次读取输入数据。您可能会得到错误的结果,因为子程序中的输入数据可能超出范围。请先检查一下!
另一个缺失的块是
if (probA .eq. 0.0) then
print 6
print 7
print *
go to 150
end if
if (probB .eq. 0.0) then
print 6
print 7
print *
go to 150
end if
和标签150。你确定省略这两个块没有改变原始代码的准确性和功能吗?
将程序转换为子程序非常简单。主要步骤如下:
program
转换为subroutine
end program
转换为end subroutine
在implicit none
或program
subroutine
也是一种很好的做法
我将程序转换为我自己的子程序。请注意,子例程末尾的result = resp
可能是错误的,因为据我所知,命令'result'是针对函数而不是子例程。
在子程序中,您必须指定输出。由于我不知道哪个变量是输出,我没有在子例程名称后面的括号'()'中包含该变量。请写在那里。 我的子程序如下:
! PROGRAM Exact_Mid_P_binomial_sample_size
subroutine midpss (prop1, range, conlev, output)
implicit none
real(8) probA,probB,part1,part2,part3,part4
real(8) totprA,totprB,factt, resp
integer numbr, nprop,loop1,loop2
real(8) prop1,prop,range,conlev,slimit,supper,alpha,numx,output
! character resp
!1 format ('Enter proportion ',$)
!2 format ('Enter error limit ',$)
!3 format ('Enter confidence level ',$)
4 format ('Calculated sample size is ',i6)
5 format ('Exact mid-P with ',f7.5,' 2-tail probability')
6 format ('Sorry, unable to mathmatically solve this problem.')
7 format ('Reported sample size is not accuarate.')
8 format ('Enter q to quit ',$)
9 format ('Actual limits for distribution ',f5.3,' - ',f5.3)
print *, 'Exact sampleroportions'
print *, 'Using Mid-P methods'
print *, 'Geoff Fosgate DVM PhD'
print *, 'College of Veterinary Medicine'
print *, 'Texas A&M University'
print *
!10 print *
! print 1
! read *, prop1
! print 2
! read *, range
! print 3
! read *, conlev
! print *
! Convert proportions less than 0.5 for algorithm
if (prop1 .lt. 0.5) then
prop = 1 - prop1
nprop = 1
else
prop = prop1
nprop = 0
end if
slimit = max ((prop - range) , 0.0001)
supper = min ((prop + range) , 0.9999)
! Probabilities cannot be calculated for p=0 and p=1
alpha = (1 - conlev)
! if (alpha .gt. 1.0) go to 10
! if (alpha .lt. 0.0) go to 10
! if (prop .gt. 1.0) go to 10
! if (prop .lt. 0.0) go to 10
numbr = (1 / (1 - prop)) - 1
! Define and initialize variables
! Note names of variables based on Fortran 77 rules
! Starting sample size is based on estimated proportion
! Resulting sample size must be large enough to obtain this proportion
100 numbr = numbr + 1
numx = (numbr * prop) + 0.001
! This is the number of binomial "successes" resulting in the proportion
if (numx .eq. numbr) go to 100
if (numx .lt. 1) go to 100
totprA = slimit**numbr
totprB = supper**numbr
do 130 loop1 = numx, (numbr - 1)
! Must initialize variables within loop
factt = 1.0
probA = 0.0
probB = 0.0
part1 = 0.0
part2 = 0.0
part3 = 0.0
part4 = 0.0
! Start loop to calculate factorial component of binomial probability
! Note that complete factorial calculations not necessary due to cancellations
do 110 loop2 = (loop1 + 1) , numbr
factt = factt * (loop2) / (numbr - (loop2 - 1))
110 continue
! Calculate probability for this particular number of successes
! Total probability is a running total
! Note that real variables must have high precision and be comprised
! of multiple bytes because factorial component can be very large
! and exponentiated component can be very small
! Program will fail if any component is recognized as zero or infinity
part1 = slimit**loop1
part2 = (1.0-slimit)**(numbr-loop1)
part3 = supper**loop1
part4 = (1.0-supper)**(numbr-loop1)
if (part1 .eq. 0.0) part1 = 1.0D-307
if (part2 .eq. 0.0) part2 = 1.0D-307
if (part3 .eq. 0.0) part3 = 1.0D-307
if (part4 .eq. 0.0) part4 = 1.0D-307
if (factt .gt. 1.0D308) factt = 1.0D308
probA = part1 * part2 * factt
probB = part3 * part4 * factt
if (loop1 .eq. numx) then
totprA = totprA + (0.5 * probA)
totprB = totprB + (0.5 * probB)
else
totprA = totprA + probA
totprB = totprB + probB
end if
if (probA .eq. 0.0) then
print 6
print 7
print *
go to 150
end if
if (probB .eq. 0.0) then
print 6
print 7
print *
go to 150
end if
130 continue
140 if ((totprA + (1 - totprB)) .gt. alpha) go to 100
! go to beginning and increase sample size by 1 if have not
! reached specified level of confidence
150 if (nprop .eq. 1) then
print 4,numbr
print 9, (1-supper),(1-slimit)
else
print 4,numbr
print 9, slimit,supper
end if
if (totprA+(1-totprB) .lt. alpha) print 5,(totprA+(1-totprB))
print *
print 8
!result = resp
! print *
! if (resp .ne. 'q') go to 10
! output=*** !write the output variable instead of ***
print *
print *
999 end subroutine midpss