program sampsi_sccs, rclass *! version 1.3.0 Philip Ryan 2009-05-02 *! sample size estimation for self controlled case series design *! adapted from: Musonda et al, Stats in Med 2006; 25:2618-2631 * binomial: based on Eqn (6) and (3) * signed root likelihood: based on Eqn (7) and (3) * SRL with age effects: based on Eqn (12) to give n, and on substituting [p_jay/1-SUM(p_jay)] * for p_jay and p0=0 in Eqn (10) to give n1. * ************* * history * ************* * * version 1.0.0 2009-04-15 first posted on SSC * version 1.0.1 2009-04-21 temporarily disable n1 and n0 from age effects method [posted] * version 1.2.0 2009-05-01 fix n1 and n0 for age effects method (pers comm Paddy Farrington) * version 1.3.0 2009-05-02 further fix for age effects method (pers comm Paddy Farrington) * *! *! syntax is: *! *! sampsi_sccs [anything] [, alpha(real 0.05) power(real 0.9) rho(real 2) *! ONEsided MEthod(string)] *! *! where option [method] is one of binomial, SRL or age, and option [anything] must be ? version 10 syntax [anything] [, Alpha(real 0.05) Power(real 0.9) RHO(real 2) ONEsided MEthod(string)] if !inrange(`alpha',0,1) { di _new as err "Alpha must lie between 0 and 1" exit 198 } if !inrange(`power',0,1) { di _new as err "Power must lie between 0 and 1" exit 198 } if `rho' < 0 { di _new as err "Rho must exceed 0" exit 198 } if "`anything'" != "" & "`anything'" != "?" { exit 198 } if "`anything'" == "?" { which sampsi_sccs exit 0 } local tail 2 if "`onesided'" != "" { local tail 1 } if "`method'" != "" & !inlist(lower(substr("`method'",1,3)), "bin", "srl", "age") { di _new as err " invalid Method specified" exit 198 } if "`method'" == "" | lower(substr("`method'",1,3)) == "bin" { * equation (6) and (3): Musonda et al Stats in Med 2006; 25:2618-2631 (page 2621) * binomial method for self-controlled case series sample size di _new di _new in ye "Binomial method" di _new(2) in ye "input " in wh "duration of post-exposure risk period (e.g. 2)" _col(75) _request(_w) if `w' < 0 { di _new as err "duration of risk must exceed 0" exit 198 } di in ye "input " in wh "duration of entire observation period (e.g. 200)" _col(75) _request(_W) if `W' < 0 { di _new as err "duration of observation period must exceed 0" exit 198 } if `w' > `W' { di _new as err "risk period must not exceed total observation period" exit 198 } di in ye "input " in wh "proportion of subjects exposed at all during obs period (e.g. 1)" _col(75) _request(_p) if `p' < 0 | `p' >1 { di _new as err "proportion exposed must lie betweeen 0 and 1" exit 198 } local r = `w'/`W' local num = ((invnormal(1-(`alpha'/`tail'))) + (abs(invnormal(1-`power'))))^2 local d1a = (`rho'*`r')/((`rho'*`r')+1 - `r') local d1 = asin(sqrt(`d1a')) local d2 = asin(sqrt(`r')) local den = 4*((`d1' - `d2')^2) local factor = (1+(`p'*`r'*(`rho'-1)))/(`p'*((`rho'*`r')+1-`r')) local n1 = ceil(`num'/`den') local n = ceil(`n1'*`factor') local n0 = `n'-`n1' di _new _dup(66) in ye "=" di in ye _dup(10) "=" " sample size for SCCS Design: binomial method " _dup(10) "=" di _dup(66) in ye "=" di _new in wh _skip(2) "`tail'-tailed alpha is:" _col(60) `alpha' di _skip(2) "power is:" _col(60) `power' di _skip(2) "relative incidence associated with exposure is:" _col(60) `rho' di _skip(2) "post-exposure risk period is:" _col(60) `w' di _new(2) in gr " total number of events required in exposed subjects is:" _col(61) in ye `n1' di _new(1)in gr " total number of events required in unexposed subjects is:" _col(61) in ye `n0' di _new(1) in gr " Total number of events required is:" _col(61) in ye `n' di _dup(66) in ye "=" return scalar n_exp = `n1' return scalar n_unexp = `n0' return scalar n_total = `n' } // end if for binomial if lower("`method'") == "srl" { * equation (7) and (3): Musonda et al Stats in Med 2006; 25:2618-2631 (page 2622) * signed root likelihood method for self-controlled case series sample size di _new di _new in ye "Signed root likelihood method" di _new(2) in ye "input " in wh "duration of post-exposure risk period (e.g. 2)" _col(75) _request(_w) if `w' < 0 { di _new as err "duration of risk must exceed 0" exit 198 } di in ye "input " in wh "duration of entire observation period (e.g. 200)" _col(75) _request(_W) if `W' < 0 { di _new as err "duration of observation period must exceed 0" exit 198 } if `w' > `W' { di _new as err "risk period must not exceed total observation period" exit 198 } di in ye "input " in wh "proportion of subjects exposed at all during obs period (e.g. 1)" _col(75) _request(_p) if `p' < 0 | `p' >1 { di _new as err "proportion exposed must lie betweeen 0 and 1" exit 198 } local r = `w'/`W' local b=ln(`rho') local A11 = ((`rho'*`r')/((`rho'*`r')+1-`r'))*`b' local A12 = ln((`rho'*`r')+1-`r') local A1 = 2*(`A11' - `A12') local B11 = ((`b')^2)*(`rho'*`r')*(1-`r') local B12 = ((`rho'*`r')+1-`r')^2 local B1 = `B11'/(`A1'*`B12') local num = ((invnormal(1-(`alpha'/`tail'))) + ((sqrt(`B1'))*(abs(invnormal(1-`power')))))^2 local den = `A1' local factor = (1+(`p'*`r'*(`rho'-1)))/(`p'*((`rho'*`r')+1-`r')) local n1 = ceil(`num'/`den') local n = ceil(`n1'*`factor') local n0 = `n'-`n1' di _new _dup(66) in ye "=" di in ye _dup(3) "=" " sample size for SCCS Design: signed root likelihood method " _dup(3) "=" di _dup(66) in ye "=" di _new in wh _skip(2) "`tail'-tailed alpha is:" _col(60) `alpha' di _skip(2) "power is:" _col(60) `power' di _skip(2) "relative incidence associated with exposure is:" _col(60) `rho' di _skip(2) "post-exposure risk period is:" _col(60) `w' di _new(2) in gr " total number of events required in exposed subjects is:" _col(61) in ye `n1' di _new(1)in gr " total number of events required in unexposed subjects is:" _col(61) in ye `n0' di _new(1) in gr " Total number of events required is:" _col(61) in ye `n' di _dup(66) in ye "=" return scalar n_exp = `n1' return scalar n_unexp = `n0' return scalar n_total = `n' } // end if method is root if lower(substr("`method'",1,3)) == "age" { * equation (12) and (3): Musonda et al Stats in Med 2006; 25:2618-2631 (page 2625-6) * signed root likelihood method for self-controlled case series sample size with age effects di _new di _new in ye "Signed root likelihood method controlling for age effects" di _new di in ye "input " in wh "number of age groups" _col(30) in ye " (e.g. 5)" _col(75) _request(_groups) if `groups' <2 { di _new as err " number of age groups must be at least 2" exit 198 } tempname K matrix `K' = J(`groups',11,0) forvalues i=1/`groups' { mat `K'[`i',1] = `i' } di _new mat `K'[1,2] = 1 forvalues i=2/`groups' { di in ye "input " in wh "age specific incidence [relative to age group 1] in age group" in ye " `i'" _col(75) _request(_expd) if `expd' <0 { di _new as err " incidence must be non-negative" exit 198 } mat `K'[`i',2] = `expd' mat `K'[`i',3] = ln(`expd') } di _new forvalues i=1/`groups' { di in ye "input " in wh "length of observation period for age group" in ye " `i'" _col(75) _request(_ejay) if `ejay' <0 { di _new as err " observation period must be non-negative" exit 198 } mat `K'[`i',4] = `ejay' } di _new di in ye "input " in wh "post-exposure risk period [assumed the same for each group]" _col(75) _request(_estar) if `estar' <0 { di _new as err " risk period must be non-negative" exit 198 } forvalues i=1/`groups' { mat `K'[`i',5] = `estar' } local rden = 0 forvalues i=1/`groups' { local rden = `rden' + ((`K'[`i',2])*(`K'[`i',4])) } forvalues i=1/`groups' { mat `K'[`i',6] = ((`K'[`i',2])*(`K'[`i',5]))/`rden' } forvalues i=1/`groups' { mat `K'[`i',7] = (`rho'*`K'[`i',6]) / ((`rho'*`K'[`i',6]) +1-`K'[`i',6]) } di _new forvalues i=1/`groups' { di in ye "input " in wh "proportion of subjects exposed during obs period in age group" in ye " `i'" _col(75) _request(_pjay) if `pjay' < 0 | `pjay' >1 { di _new as err "proportion exposed must lie betweeen 0 and 1" exit 198 } mat `K'[`i',8] = `pjay' } *** setup for n local sumpjay = 0 forvalues i=1/`groups' { local sumpjay = `sumpjay'+ `K'[`i',8] } if `sumpjay' >= 1 { di _new as error "sum of proportions must be less than 1 exit 198 } *** local p0 = 1-`sumpjay' *** set up for n1 * forvalues i=1/`groups' { mat `K'[`i', 10] = `K'[`i',8]/`p0' } *** *** set up for n local denvn = 0 forvalues i=1/`groups' { local denvn = `denvn' + ( (`K'[`i',8]) * ( (`K'[`i',6]*`rho')+1-`K'[`i',6]) ) } local denvn = `p0'+`denvn' forvalues i=1/`groups' { mat `K'[`i',9] = (`K'[`i',8]*((`K'[`i',6]*`rho')+1-`K'[`i',6]))/`denvn' } *** *** setup for n1 local denvn1 = 0 forvalues i=1/`groups' { local denvn1 = `denvn1' + ( (`K'[`i',10]) * ( (`K'[`i',6]*`rho')+1-`K'[`i',6]) ) } local denvn1 = 0 +`denvn1' forvalues i=1/`groups' { mat `K'[`i',11] = (`K'[`i',10]*((`K'[`i',6]*`rho')+1-`K'[`i',6]))/`denvn1' } *** *** setup for n local hA =0 forvalues i=1/`groups' { local hA = `hA'+ ( (`K'[`i',9]) * ( (`K'[`i',7]*ln(`rho')) - (ln((`K'[`i',6]*`rho')+1-`K'[`i',6] )) ) ) } local A = 2*`hA' local B1 = 0 forvalues i=1/`groups' { local B1 = `B1' + ( `K'[`i',9] * `K'[`i',7] * (1-`K'[`i',7]) ) } local B = ((ln(`rho'))^2) * `B1'/`A' local num = ((invnormal(1-(`alpha'/`tail'))) + ((sqrt(`B'))*(abs(invnormal(1-`power')))))^2 local den = `A' *** *** setup for n1 local hA =0 forvalues i=1/`groups' { local hA = `hA'+ ( (`K'[`i',11]) * ( (`K'[`i',7]*ln(`rho')) - (ln((`K'[`i',6]*`rho')+1-`K'[`i',6] )) ) ) } local A = 2*`hA' local B1 = 0 forvalues i=1/`groups' { local B1 = `B1' + ( `K'[`i',11] * `K'[`i',7] * (1-`K'[`i',7]) ) } local B = ((ln(`rho'))^2) * `B1'/`A' local num1 = ((invnormal(1-(`alpha'/`tail'))) + ((sqrt(`B'))*(abs(invnormal(1-`power')))))^2 local den1 = `A' *** local n = ceil(`num'/`den') local n1 = ceil(`num1'/`den1') local n0 = `n'-`n1' di _new _dup(66) in ye "=" di in ye _dup(10) "=" " sample size for SCCS Design with age effects " _dup(10) "=" di _dup(66) in ye "=" di _new in wh _skip(2) "`tail'-tailed alpha is:" _col(60) `alpha' di _skip(2) "power is:" _col(60) `power' di _skip(2) "relative incidence associated with exposure is:" _col(60) `rho' di _skip(2) "common post-exposure risk period is:" _col(60) `estar' di _new(2) in gr " total number of events required in exposed subjects is:" _col(61) in ye `n1' di _new(1)in gr " total number of events required in unexposed subjects is:" _col(61) in ye `n0' di _new(1) in gr " Total number of events required is:" _col(61) in ye `n' di _dup(66) in ye "=" return scalar n_exp = `n1' return scalar n_unexp = `n0' return scalar n_total = `n' } // end if method is age end