/*
     kr20: reliability of a set of dichotomous items
     Version 1.2 -- Date: September 14th, 1999
     By: HervŽ CACI (hcaci@wanadoo.fr)

     v1.0: Initial release (August 1998)
     v1.2: Added item-rest point biserial correlation
           Deleted item-test point biserial correlation
           Added "sort" option
           Deleted "total" option
           Save statistics in global macros S3-S6
*/

program define kr20
version 5.0
*! Version 1.2 -- September 14th, 1999

/* Parse standard Stata commands */

local varlist "required existing min(2)"
local if "optional"
local in "optional"
local options "sort(string)"
parse "`*'"
parse "`varlist'", parse(" ")
local nitems : word count `varlist'

/* Which sort option ? */

local sortkey = -1
if "`sort'" == "" { local sortkey = 0 }
  else if substr("`sort'",1,4) == "diff" { local sortkey = 1 }
  else if substr("`sort'",1,4) == "corr" { local sortkey = 2 }
  else if substr("`sort'",1,4) == "item" { local sortkey = 3 }
  else {
    di in red _n "Bad sort option"
    exit
  }

/* Here we go... nam..  = Name of the variable
                 d..    = Item difficulty
                 tdiff  = Average value of the item difficulties
                 v..    = Item variance
                 n..    = # of observations
                 m..    = # of missing observations
                 totMean= Mean of the total score
                 totSD  = Standard deviation of the total score
                 totVar = Variance of the total score
                 r..    = Item-Rest P-B correlation coefficient
                 tcorr  = Average value of the item-test pb coefficients
                 tpbcorr= Average value of the item-rest pb coefficients
*/

tempvar totMean totSD totVar touse

local theSum 0
local tdiff 0
local tcorr 0
local tpbcorr 0

/*
   "If" "in" implementation
*/

preserve
mark `touse' `if' `in'
markout `touse' `varlist'
quietly drop if `touse'== 0

quietly egen tempTot= rsum(`varlist')
quietly summarize `tempTot', detail
local totMean= _result(3)
local totVar = _result(4)

/* First of all, must check whether all variables are dichotomous (i.e. 0/1) */

local i=1
while "`1'" != "" {

  /* Exit with error if the variable is not an integer variable*/
  capture confirm string variable `1'
  if _rc == 0 {
       display "Sorry `1' is not a numeric variable..."
       exit
  }

  /* Look for unique values, taking missing values in consideration */
  sort `1'
  quietly by `1': gen long cnt=1 if _n==1   /* Mark first element of each category */
  quietly replace cnt=sum(cnt)
  local uniq=cnt[_N]
  capture drop cnt
  capture drop ismiss
  quietly gen byte ismiss= (`1'==.)
  quietly count if (`1'==.)
  local uniq=`uniq'-(_result(1)!=0)
  local m`i'= _result(1)          /* Remember the # of missing obs */

  /* Test the dichotomy of the variable */

  if `uniq' > 2 {
       display "`1' has more than two unique values..."
       exit
  }

  local nam`i'="`1'"
  if `uniq' < 2 {
       local v`i'=0          /* Item variance is always zero in this case */
       quietly count if (`1'!=.)
       local n`i'=_result(1) /* # of non-missing values */
       if `1'[1] ==0 {
             local d`i'=0   /* When all answers are "wrong", item difficulty is zero */
       }
       else if `1'[1]==1 {
                 local d`i'=1  /* When all answers are "right", item difficulty is one */
                 local tdiff = `tdiff'+ 1
            }
            else {
                 display "Error: `1' must be 0 or 1"
                 exit
            }
  }
  else { /* There are only 2 categories. Data are still sorted by `1' */

       quietly by `1': gen long cnt=_N	          /* cnt is number of items of each category */
       quietly by `1': gen byte first=1 if _n==1 /* mark the first item of each category */
       sort first `1'                            /* Sort: one item of each category */

       /*
          At this point, cnt[1] is number of items "0"
                         cnt[2] is number of items "1"

          So, check whether items are correctly coded as 0 or 1
       */

       if `1'[1] != 0 | `1'[2] != 1 {
            display "`1' must be 0 or 1"
            exit
       }

       local cntitem=cnt[1]+cnt[2]               /* # of non-missing 0 or 1 values */
       local n`i' = `cntitem'
       local d`i'= cnt[2]/`cntitem'              /* Item difficulty */
       local v`i'= `d`i'' * (cnt[1]/`cntitem')   /* Item variance */
       local tdiff = `tdiff'+ `d`i''
  }

/*
   Point biserial correlation coefficient item-rest

   sMean = Mean score on continuous variable of "successful" group on dichotomous variable
   uMean = Mean score on continuous variable of "unsuccessful" group on dichotomous variable
   totSD = Standard deviation of total score
*/

  quietly gen tempTot2=tempTot-`1'
  quietly summarize tempTot2 if `1'==1
  local sMean2 = _result(3)
  quietly summarize tempTot2 if `1'==0
  local uMean2 = _result(3)
  quietly summarize tempTot2 if `1'!=.
  local totSD2 = sqrt(_result(4))
  local r`i'= ((`sMean2'-`uMean2')/`totSD2')*sqrt(`v`i'')
  local tpbcorr = `tpbcorr'+`r`i''

  local theSum = `theSum' + `v`i''        /* Sum of item variances */

  capture drop cnt first tempTot2
  local i=`i'+1 /* Increment matrix index */
  macro shift
  }

  drop ismiss

/*
     Sort output
*/

  local perm 1
  while `perm'!=0 & `sortkey'!=0 {
    local i 1
    local perm 0
    while `i'<`nitems' {
      local j= `i'+1
      if (`sortkey'==1 & `d`i'' < `d`j'') | /*
*/       (`sortkey'==2 & `r`i'' < `r`j'') | /*
*/       (`sortkey'==3 & "`nam`i''" > "`nam`j''") {

di "`nam`i'' -- `nam`j''"

        local dummy "`nam`i''"
        local nam`i' "`nam`j''"
        local nam`j' "`dummy'"
        local dummy `n`i''
        local n`i' `n`j''
        local n`j' `dummy'
        local dummy `d`i''
        local d`i' `d`j''
        local d`j' `dummy'
        local dummy `v`i''
        local v`i' `v`j''
        local v`j' `dummy'
        local dummy `r`i''
        local r`i' `r`j''
        local r`j' `dummy'

        local perm 1
      }
      local i=`i'+1
    } /* While i */
  } /* While perm */

/* Final output */

  parse "`varlist'", parse(" ")
  di _n in gr "Kuder-Richarson coefficient of reliability (KR-20)"
  di in gr _n "Number of items in the scale = "`nitems'
  di in gr "Number of complete observations = " %4.0f `n1' _n
  di in gr "                     Item       Item     Item-rest"
  di in gr "  Item   |  Obs   difficulty  variance  correlation"
  di in gr "---------+" _dup(42) "-"
  local i 1
  while `i' <= `nitems' {
    di in gr _col(2) "`nam`i''" _col(10) "|" in ye /*
          */ _col(12) %4.0f `n`i'' /*
          */ _col(21) %5.4f `d`i'' /*
          */ _col(32) %5.4f `v`i'' /*
          */ _col(43) %5.4f `r`i''
    local i=`i'+1
    macro shift
    }

  di in gr "---------+" _dup(42) "-"
  di in gr "   Test  |" _col(21) %5.4f `tdiff'/`nitems' /*
                    */  _col(43) %5.4f `tpbcorr'/`nitems' _n
  local KR20=(`nitems'/(`nitems'-1))*(1-(`theSum'/`totVar'))
  di "KR20 coefficient is " %5.4f `KR20'

  global S_3=`tdiff'/`nitems'
  global S_4=`tpbcorr'/`nitems'
  global S_5=`nitems'
  global S_6=`KR20'
end