*! version 1.0 P.MILLAR 18Mar2005
*! This software can be used for non-commercial purposes only.
*! The copyright is retained by the developer.
*! Copyright 2005 Paul Millar

/* this program runs the cluster command many times to look at variation in its results */
program clustsens, byable(recall)
   version 7.0
   syntax varlist(numeric) [if] [in], [reps(integer 100) k(integer 3) seed(string) type(string) distopt(string) detail dots]

if "`type'" =="" {
   local type="kmeans"
   }
 if "`distopt'" =="" {
   local distopt="L2"
   }

if length("`seed'")==0 {
  set seed 12345789
  }
else {
  set seed `seed'
  }

tempname CLUST ONECLUST

di as text "Checking variability using `distopt' "

mat CLUST=J(`reps',`k',0)
mat ONECLUST=J(`k',1,0)
quietly summ `varlist' `if' `in'
local N=r(N)

forvalues i = 1/`reps' {
  local varnam="cl"+string(`i',"%04.0f")
  capture tab `varnam'
  if _rc == 0 {
    drop `varnam'
    }
  quietly cluster `type' `varlist' `if' `in', k(`k') gen(`varnam') `distopt'
  local seedno`i'="`c(seed)'"
  if "`dots'" == "dots" {
    di _continue as input "."
    }
  forvalues j = 1/`k' {
    quietly means `varlist' if `varnam' == `j'
    local n`j'=r(N)
    local avg`j'=r(mean)
    mat ONECLUST[`j',1]=`n`j''
    }
  matsort ONECLUST
  forvalues j = 1/`k' {
    mat CLUST[`i',`j']=ONECLUST[`j',1]
    }
  }

/* dump the individual results if requested */
if "`detail'" == "detail" {
//  mat list CLUST
  di as text " "
  di _continue " Rep#"
  forvalues grpno =1/`k' {
    di _continue " N Grp" %3.0f `grpno'
    }
  di _continue " Seed"
  di _newline " ----" _continue
  forvalues grpno =1/`k' {
    di _continue " --------"
    }
  di _continue " -------------------------------------"
  forvalues i=1/`reps' {
    di _newline as text %5.0f `i' _continue
    forvalues grpno =1/`k' {
      di _continue as result %9.0f CLUST[`i',`grpno']
      }
    di _continue " `seedno`i''"
    }
  }

di _newline " "
di as text "Size of group varies as follows:"
di " "
di as text "Cluster  Avg.   SD     Min    Max "
di as text "------  ----- ------ ------ ------"

forvalues j=1/`k' {
  local sum=0
  local min=`N'+1
  local max=-1
  forvalues i = 1/`reps' {
    if CLUST[`i',`j'] < `min' {
      local min=CLUST[`i',`j']
      }
    if CLUST[`i',`j'] > `max' {
      local max=CLUST[`i',`j']
      }
    local sum=`sum'+CLUST[`i',`j']
    }
  local nbar=`sum'/`reps'
  local devs=0
  forvalues i = 1/`reps' {
    local devs=`devs'+ ((CLUST[`i',1]-`nbar')^2)
    }
  local sd=(`devs'/`reps')^(0.5)
  di as text %6.0f `j' " " as result %6.0f `nbar' " " %6.1f `sd' " " %6.0f `min' " " %6.0f `max'
  }

end