*! Version 2.1 13December2017 ************************************************************************************************************ * nopalera : NOPALERA algorithm * Version 2: October 24, 2015 * * Historic: * Version 1 (2015-07-20): Jean-Benoit Hardouin /*ICC; rsbynpirt module*/ * Version 1.1 (2015-10-24): Jean-Benoit Hardouin /*NOPALERA module, ISOQOL 2015*/ * * Jean-benoit Hardouin, phD, Assistant Professor * INSERM UMR 1246-SPHERE * MethodS in Patients-centered outcomes and HEalth ResEarches * University of Nantes - Faculty of Pharmaceutical Sciences * France * jean-benoit.hardouin@anaqol.org * * News about this program: http://www.anaqol.org * * Copyright 2015,2017 Jean-Benoit Hardouin * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ************************************************************************************************************ program define nopalera, rclass version 8.0 syntax varlist(numeric min=4) [, noGraph noBootstrap corr(numlist) NBBootstrap(int 10)] set more off tempfile file1 file3 qui save `file1', replace preserve local nbitems : word count `varlist' qui count local nind=r(N) tokenize `varlist' if mod(`nbitems',2)!=0 { di in red "You must indicate an even number of items" exit } else { local nbitems=`nbitems'/2 } if "`corr'"=="" { forvalues i=1/`nbitems'{ local corr "`corr' 0" } } local listofitems1 local listofitems2 forvalues i=1/`nbitems' { local listofitems1 `listofitems1' ``i'' local listofitems2 `listofitems2' ``=`i'+`nbitems''' } di "NOPALERA: an algorithm to detect Response-shift at items level using non parametric IRT" di "Number of individuals: `nind'" di "Number of items: `nbitems'" di "Number of Boostrap replications: `nbbootstrap'" /******************************************************************************* Une boucle pour calculer le score, définir le score maximum par item et global *******************************************************************************/ tempvar varscore1 varscore2 qui gen `varscore1'=0 qui gen `varscore2'=0 label variable `varscore1' "Total score time 1" label variable `varscore2' "Total score time 2" local scoremax=0 local flag=0 local modamax=0 forvalues i=1/`nbitems' { local corr`i': word `i' of `corr' qui replace ``=`i'+`nbitems'''=``=`i'+`nbitems'''+`corr`i'' qui replace `varscore1'=`varscore1'+``i'' qui replace `varscore2'=`varscore2'+``=`i'+`nbitems''' qui su ``i'' local modamax`i'=r(max) qui su ``=`i'+`nbitems''' local modamax`i'=r(max) if r(min)!=0 { local flag=1 } local scoremax=`scoremax'+`modamax`i'' if `modamax`i''!=1 { local flagbin=0 } if `modamax`i''>`modamax' { local modamax=`modamax`i'' } } qui save `file3', replace /******************************************************************************* Quelques tests de conformité *******************************************************************************/ if `flag'==1 { *di as error "The lower answer category of each item must be 0" *exit } qui su `varscore1' local maxscore=r(max) qui su `varscore1' if `r(max)'>`maxscore' { local maxscore=r(max) } /* sauvegarde des données */ tempfile rsbynpirtfile rsbynpirtfile1 rsbynpirtfile2 tempvar score qui save `rsbynpirtfile', replace /*On récupère les données des ICC au temps 1 puis au temps 2*/ qui traces `listofitems1', nograph icc saveicc qui drop _all tempname mat1 qui matrix `mat1'=r(matscore) qui svmat `mat1', names(t1item) forvalues i=1/`nbitems' { local j: word `i' of `listofitems1' qui rename t1item`i' `j' } qui rename t1item`=`nbitems'+1' `score' qui contract `score' `listofitems1' qui sort `score' qui save `rsbynpirtfile1', replace qui use `rsbynpirtfile', clear qui traces `listofitems2', nograph icc saveicc qui drop _all tempname mat2 qui matrix `mat2'=r(matscore) qui svmat `mat2', names(t2item) /*puis on fait un seul fichier avec les deux jeux de données*/ forvalues i=1/`nbitems' { local j: word `i' of `listofitems2' qui rename t2item`i' `j' } qui rename t2item`=`nbitems'+1' `score' qui contract `score' `listofitems2' qui sort `score' qui merge 1:1 `score' using `rsbynpirtfile1' /*représentation simultanée des ICC aux deux temps pour chaque item*/ if "`graph'"=="" { forvalues i=1/`nbitems' { twoway (line ``i'' `score') (line ``=`i'+`nbitems''' `score'), name(``i'',replace) } } qui drop if `score'==0|`score'==`scoremax' /*calcul des deux AUC pour chaque item*/ local tests forvalues i=1/`nbitems' { qui su ``i'' local AUC``i''=r(mean) local AUC``i''=(`AUC``i'''*`scoremax') qui su ``=`i'+`nbitems''' local AUC`=`i'+`nbitems''=r(mean) local AUC`=`i'+`nbitems''=(`AUC`=`i'+`nbitems'''*`scoremax') tempname diff``i'' gen `diff``i'''=abs(``i''-``=`i'+`nbitems''') qui su `diff``i''' local d``i''=`r(sum)'/(`scoremax'*`modamax`i'')*100 local var``i''=`r(sd)'*100 return scalar AUC``i''=`AUC``i''' return scalar AUC``=`i'+`nbitems'''=`AUC`=`i'+`nbitems''' local tests `tests' b``i''=(r(AUC``=`i'+`nbitems''')-r(AUC``i'')) } local abc=0 /* 10 bootstrap pour estimer les intervalles de confiance des différences entre les deux AUC*/ if "`bootstrap'"=="" { tempfile file2 qui save `file2', replace qui use `file1', clear qui bootstrap `tests' , rep(`nbbootstrap') nol noh nodots : nopalera `varlist', nograph nobootstrap tempname mbootstrap matrix `mbootstrap'=r(table) *matrix list `mbootstrap' qui use `file3', clear *set trace on set tracedepth 1 /*Création de la matrice p qui va permettre de décaler les ICC*/ tempname p matrix `p'=J(`=(`scoremax'+1)*(`nbitems'+1)',`=`nbitems'*3+1',.) forvalues s=0/`scoremax' { matrix `p'[`=`s'+1',1]=`s' forvalues i=1/`nbitems' { matrix `p'[`=`s'+1',`=3*`i'-1']=`s' } } forvalues i=1/`nbitems' { forvalues s=0/`scoremax' { qui su ``i'' if `varscore1'==`s' matrix `p'[`=`s'+1',`=3*`i'']=r(mean) } forvalues s=0/`scoremax' { qui su ``=`i'+`nbitems''' if `varscore2'==`s' *di "qui su ``=`i'+`nbitems''' if `varscore2'==`s'" if `mbootstrap'[4,`i']>=0.05 { matrix `p'[`=`s'+1',`=3*`i'+1']=r(mean) local prec=1 local prec=0.01 /*pas d'arrondi*/ } else { if `nind'/`scoremax'>20 { local prec=0.5 local prec=1 local prec=0.01 /*pas d'arrondi*/ } else { local prec=1 local prec=0.01 /*pas d'arrondi*/ } matrix `p'[`=`i'*(`scoremax'+1)+`s'+1',`=3*`i'-1']=round(`s'+`mbootstrap'[1,`i']/(`modamax`i''),`prec') matrix `p'[`=`i'*(`scoremax'+1)+`s'+1',`=3*`i'+1']=r(mean) } } } *matrix list `p' *matrix list `p' di di "Research of uniform recalibration at item-level" di di "{hline 105}" di _col(45) "Bootstrap" _col(78) " Normal-based" di "Items" _col(18) "AUC t1" _col(28) "AUC t2" _col(38) "VarAUC" _col(45) "Std. Err." _col(63) "z" _col(70) "P>|z|" _col(78) "[95% Conf. Int.]" _col(96) "Correction" di "{hline 105}" forvalues i=1/`nbitems' { di "``i''/``=`i'+`nbitems'''" _col(19) %5.2f `AUC``i''' _col(29) %5.2f `AUC`=`i'+`nbitems''' _col(39) %5.2f `=`AUC``i'''-`AUC`=`i'+`nbitems'''' _col(49) %5.2f `mbootstrap'[2,`i'] _col(59) %5.2f `mbootstrap'[3,`i'] _col(69) %5.4f `mbootstrap'[4,`i'] _col(79) %5.2f `mbootstrap'[5,`i'] _col(89) %5.2f `mbootstrap'[6,`i'] _col(100) %6.2f round(`mbootstrap'[1,`i']/(`modamax`i''),`prec') } di "{hline 105}" local abc=1 } if `abc'==1 { drop _all qui svmat `p' qui rename `p'1 score local testsb forvalues i=1/`nbitems' { qui rename `p'`=`i'*3-1' score``i'' qui rename `p'`=`i'*3' t1``i'' qui rename `p'`=`i'*3+1' t2``i'' /*interpolation linéaire et nouvelles ICC corrigées*/ qui ipolate t2``i'' score``i'' , generate(t2adj``i'') forvalues j=0/`=floor(round(`mbootstrap'[1,`i']/(`modamax`i''),`prec'))' { qui replace t2adj``i''=0 if score``i''==`j'&t2adj``i''==. } forvalues j=`=`scoremax'+floor(round(`mbootstrap'[1,`i']/(`modamax`i''),`prec'))'/`scoremax' { qui replace t2adj``i''=1 if score``i''==`j'&t2adj``i''==. } qui gen diff``i''=abs(t2adj``i''-t1``i'') qui su diff``i'' local ABC``i''=`r(mean)'*`scoremax' return scalar ABC``i''=`ABC``i''' if "`graph'"=="" { qui sort score``i'' qui gen scoreunadj``i''=score``i''-round(`mbootstrap'[1,`i']/(`modamax`i''),`prec') *twoway (line t1``i'' score``i'')(line t2``i'' score``i'')(line t2adj``i'' score``i'') ,name(``i''c,replace) twoway (line t1``i'' score``i'')(line t2``i'' scoreunadj``i'')(line t2adj``i'' score``i'') ,name(``i''c,replace) *di "twoway (line t1``i'' score``i'')(line t2``i'' score``i'')(line t2adj``i'' score``i'') ,name(``i''c,replace)" *su } local testsb `testsb' c``i''=(r(ABC``i'')) } /* 10 bootstrap pour estimer les intervalles de confiance des ABC*/ if "`bootstrap'"=="" { tempfile file3 qui save `file3', replace qui use `file1', clear qui bootstrap `tests' , rep(`nbbootstrap') nol noh nodots : nopalera `varlist', nograph nobootstrap tempname mbootstrapb matrix `mbootstrapb'=r(table) *matrix list `mbootstrapb' di di "Research of non-uniform recalibration at item-level" di di "{hline 77}" di _col(29) "Bootstrap" _col(62) " Normal-based" di "Items" _col(24) "ABC" _col(29) "Std. Err." _col(47) "z" _col(54) "P>|z|" _col(62) "[95% Conf. Int.]" di "{hline 77}" forvalues i=1/`nbitems' { local z`i'=`ABC``i'''/`mbootstrapb'[2,`i'] local p`i'=2*(1-normal(abs(`z`i''))) di "``i''/``=`i'+`nbitems'''" _col(23) %5.2f `ABC``i''' _col(33) %5.2f `mbootstrapb'[2,`i'] _col(43) %5.2f `z`i'' _col(53) %5.4f `p`i'' _col(63) %5.2f `=`ABC``i'''-1.96*`mbootstrapb'[2,`i']' _col(73) %5.2f `=`ABC``i'''+1.96*`mbootstrapb'[2,`i']' } di "{hline 77}" local abc=1 } } if "`bootstrap'"=="" { return matrix p=`p' } qui restore , preserve end