*! Date : 23 june 2021 *! Version : 4.0 *! Author : Charlie Joyez, Universite Cote d'Azur *! Email : charlie.joyez@univ-cotedazur.fr * Computes Complexity indexes. See Haussman & Hidalgo Atlas for Economic Complexity (2012) * or simply the website https://oec.world/fr/resources/methodology/ for the methodology followed * See Tachela et al (2012) for fitness method. *Major changes : v2.0 Add MR computation and fitness one. Method() option is added to chose the computation technique * v2.1 Add the sign correction for Eigenvector method by correlating with MR * v3.0 method() option added, including both MR and fitness. Bug fix for small values, and iteration choice for MR method * v3.0 Returns EV Stata matrix * v3.1 In fitness method : Returns a product complexity score, reverse to the initial fitness score (cf Tachela et al (2012) : "Finally inverting the sum makes Q coherent with its positive meaning of complexity") * v4.0 allows Stata variables as inputs. Therefore removes the .dta possibility for matsource (no longer useful) * v4.0 introduces RCA option, and makes default input non RCA matrix capture program drop complexity program complexity, rclass version 9 syntax , [ VARlist(varlist) Matrix(string) MATSource(string) Projection(string) METhod(string) ITERations(string) Xvar Transpose RCA] ***************** *Options *Source if (mi(`"`matsource'"')) & "`varlist'"=="" & "`matrix'"=="" { display as err "invalid options : varlist() or matsource() required." exit 198 } *Varlist : use varlist as specilisation inputs if "`varlist'"!="" { capture matrix drop A mkmat `varlist' ,mat(A) mata comp_M=st_matrix("A") } ***MATSource if not Varlist * mata by default if (mi(`"`matsource'"')) & "`varlist'"=="" & "`matrix'"!="" { local matsource="mata" } *If something else is type : error. if !inlist(`"`matsource'"', "", "mata", "dta", ".dta", "matrix") & "`varlist'"=="" { noi display "matsource() only accepts following arguments : {res}dta {err}(stata .dta file), {res}matrix {err}(stata matrix) or {res}mata {err}(mata matrix, default)" exit 198 } *Load Matrix into mata if "`matsource'"!=""{ if "`matsource'"!="mata"{ if "`matsource'"=="matrix"{ /*Stata matrix*/ mata comp_M=st_matrix(`"`matrix'"') } /* if "`matsource'"=="dta" | "`matsource'"==".dta" { preserve use `"`matrix'"',clear mata comp_M=st_data(.,.) restore } */ } else{ mata comp_M=`matrix' } } **** *Computation options *Projection individuals (e.g countries) / Nodes (e.g Products) if (mi(`"`projection'"')){ local projection="indiv" } if !inlist(`"`projection'"', "", "indiv", "nodes") { display as err "invalid option projection(), only {res}nodes {err}or {res}indiv {txt}(default) arguments are possible" exit 198 } *Alternative methods (MR, eingenvalue, fitness) if (mi(`"`method'"')){ local method="eigenvalue" } if !inlist(`"`method'"', "", "eigenvalue", "mr", "fitness") { display as err "invalid option {res}method() {err}, only {res}mr {err} (Method of Reflections), {res}fitness {err} or {res}eigenvalue {txt}(default) arguments are possible" exit 198 } *Iterations (for MR only (and sign correction in Eigenvalue)) if "`method'"=="fitness"{ if (mi(`"`iterations'"')){ } else{ noi di "note: iteration option not considered. Only for MR method" } } *if "`method'"!="fitness"{ if (mi(`"`iterations'"')){ local it=20 /*default nb of iteration : 20 as recommended by Hidalgo for the Economic Complexity*/ } else{ local it=`"`iterations'"' *di `it' } *noi di "iteration nb:" `it' capture set obs 1 capture drop _iterisodd_ gen _iterisodd_=mod(`it',2) if _iterisodd_[1]==1{ noi di as error "iteration should be of even order" exit } else{ drop _iterisodd_ } *} *Transpose RCA matrix if required if "`transpose'"!=""{ mata comp_M=comp_M' } ********************** ***** Core of program ********************** *Transform specialization data in RCA if not yet. if "`rca'"==""{ mata T=comp_M mata comp_M=(T:/rowsum(T)):/(colsum(T):/sum(T)) } mata comp_M=mm_cond(comp_M:<1,0,1) /*make binary matrix if not initially, requires more_mata from SSC*/ mata comp_D=rowsum(comp_M) /*Diversification*/ mata comp_U=rowsum(comp_M') /*ubiquity*/ if "`method'"!="fitness"{ *Method of Reflection mata kc0=comp_D mata kp0=comp_U forvalues j=1/`it'{ local k=`j'-1 mata kc`j'=(comp_M*kp`k'):/kc0 mata kp`j'=(comp_M'*kc`k'):/kp0 } *eci is of even order (because iteration starts from 1) and normalized as in the Atlas : *Stop iteration before it if ranking stops to vary local optiter=`it' /*optimal iteration set to max iteration initialy, changes if optimal iteration found*/ local ns=0 forvalues j=2 (2)`it'{ local jm2=`j'-2 mata st_matrix("_newiter",kc`j') mata st_matrix("_olditer",kc`jm2') capture drop _newiter _olditer capture drop _old_rank _new_rank _drank svmat _newiter svmat _olditer gen _ini_rank = _n sort _newiter gen _new_rank=_n sort _olditer gen _old_rank=_n gen _drank=_new_rank-_old_rank sort _ini_rank qui su _drank local s=r(max) /*s captures max changes in rank*/ drop _newiter _olditer drop _old_rank _new_rank _drank _ini_rank if `s'==0 { /*rank stops to change (max delta rank=0)*/ local ns=`ns'+1 if `ns'==1 { if "`method'"=="mr"{ noi di "note : MR's optimal iteration reached at the `j'th" } local optiter=`j' mata mkc`optiter'=sum(kc`optiter')/rows(kc`optiter') mata dkc`optiter'=(kc`optiter':-mkc`optiter') mata sdkc`optiter'=sqrt((1/rows(kc`optiter'))*(sum(dkc`optiter':^2))) mata comp_i_MR=(kc`optiter' :- mkc`optiter') :/sdkc`optiter' } } if `s'!=0 & `j'==`it' { if"`method'"=="mr"{ /*rank still changes but max iter reached*/ noi di "note : MR's optimal iteration is of higher order than specified" } mata mkc`it'=sum(kc`it')/rows(kc`it') mata dkc`it'=(kc`it':-mkc`it') mata sdkc`it'=sqrt((1/rows(kc`it'))*(sum(dkc`it':^2))) mata comp_i_MR=(kc`it' :- mkc`it') :/sdkc`it' } } *pci is of odd order and normalized as in the Atlas local itm1=`optiter'-1 /*takes optimal iteration level if reached, or `it' otherwise*/ mata mkp`itm1'=sum(kp`itm1')/rows(kp`itm1') mata dkp`itm1'=(kp`itm1':-mkp`itm1') mata sdkp`itm1'=sqrt((1/rows(kp`itm1'))*(sum(dkp`itm1':^2))) mata comp_n_MR=(kp`itm1' :- mkp`itm1') :/sdkp`itm1' *EigenValue Method if "`method'"!="mr"{ *Problem in mata with very small numbers, sometimes return missing eigensystem: Solved with inflate if missing values. *If eingensystem missing, then inflate square matrix by a fixed value. Doesn't change the selected eigenvector *Complexity of individuals mata comp_R=(comp_M:/comp_D)*(comp_M':/comp_U) mata eigensystemselecti(comp_R, (1,2), comp_X=., comp_L=.) mata mis=missing(eigenvalues(comp_R)) mata inflate=0 /*inflate matrix to avoid mata issue with eigenvalues of large matrices with low values*/ mata if (mis>0) comp_R=comp_R:*1e+100 ; ; mata if (mis>0) inflate=1 ; ; mata eigensystemselecti(comp_R, (1,2), comp_X=., comp_L=.) mata comp_K=comp_X[.,2] /*Eigenvector of M~cc′\tilde{M}_{c{c}'}​M​~​​​cc​′​​​​ associated with the second largest eigenvalue.*/ mata comp_k=sum(comp_K)/rows(comp_K) mata comp_d=(comp_K:-comp_k):^2 mata comp_std=sqrt((1/rows(comp_R))*sum(comp_d)) mata Comp_i=(comp_K:-comp_k):/comp_std mata comp_i=Re(Comp_i) mata st_matrix("Complexity_i", comp_i) *Complexity of nodes mata comp_V=(comp_M':/comp_U)*(comp_M:/comp_D) mata eigensystemselecti(comp_V, (1,2), comp_X=., comp_L=.) mata mis=missing(eigenvalues(comp_V)) mata inflate_V=0 mata if (mis>0) comp_V=comp_V:*1e+100 ; inflate_V=0 ; mata if (mis>0) inflate_V=1; ; mata eigensystemselecti(comp_V, (1,2), comp_X=., comp_L=.) mata comp_Q=comp_X[.,2] /*Eigenvector of M~cc′\tilde{M}_{c{c}'}​M​~​​​cc​′​​​​ associated with the second largest eigenvalue.*/ mata comp_q=sum(comp_Q)/rows(comp_Q) mata comp_d=(comp_Q:-comp_q):^2 mata comp_stdev=sqrt((1/rows(comp_Q))*sum(comp_d)) mata Comp_n=(comp_Q:-comp_q):/comp_stdev mata comp_n=Re(Comp_n) mata st_matrix("Complexity_n", comp_n) *Correct ECI/PCI sign if required : Correlate with MR quietly{ mata st_matrix("comp_i_MR", comp_i_MR) mata st_matrix("comp_n_MR", comp_n_MR) count local n=r(N) svmat Complexity_i svmat comp_i_MR corr Complexity_i1 comp_i_MR1 local r=r(rho) mata signcor=0 if `r'<0 { mata comp_i = - comp_i mata st_matrix("Complexity_i", comp_i) mata signcor=1 /*stores info whether the sign has been corrected*/ } drop Complexity_i1 drop comp_i_MR1 drop if _n>`n' count local n=r(N) quie svmat Complexity_n quie svmat comp_n_MR corr Complexity_n1 comp_n_MR1 local r=r(rho) if `r'<0 { mata comp_n = - comp_n mata st_matrix("Complexity_n", comp_n) } drop Complexity_n1 comp_n_MR1 capture rename Complexity_n1 Complexity_n drop if _n>`n' } } if "`xvar'"==""{ if "`method'"!="mr"{ if "`projection'"!="nodes"{ quie count local N=r(N) mata n=rows(comp_M) *mata n mata st_local("n", strofreal(n)) *noi di `n' if `n'>`N'{ set obs `n' } capture drop Complexity_i svmat Complexity_i capture rename Complexity_i1 Complexity_i } else{ quie count local N=r(N) mata n=rows(comp_M') *mata n mata st_local("n", strofreal(n)) *noi di `n' if `n'>`N'{ set obs `n' } capture drop Complexity_n svmat Complexity_n capture rename Complexity_n1 Complexity_n } } if "`method'"=="mr"{ mata st_matrix("comp_i_MR", comp_i_MR) mata st_matrix("comp_n_MR", comp_n_MR) if "`projection'"!="nodes"{ quie count local N=r(N) mata n=rows(comp_M) *mata n mata st_local("n", strofreal(n)) *noi di `n' if `n'>`N'{ set obs `n' } capture drop MR_Complexity_i svmat comp_i_MR capture rename comp_i_MR MR_Complexity_i } else{ quie count local N=r(N) mata n=rows(comp_M') *mata n mata st_local("n", strofreal(n)) *noi di `n' if `n'>`N'{ set obs `n' } capture drop MR_Complexity_n svmat comp_n_MR capture rename comp_n_MR MR_Complexity_n } } } mata Ubiquity=comp_U mata Diversity=comp_D mata st_matrix("Ubiquity", Ubiquity) mata st_matrix("Diversity", Diversity) return matrix Ubiquity=Ubiquity return matrix Diversity=Diversity if "`method'"!="mr"{ return matrix Complexity_indiv_EV=Complexity_i return matrix Complexity_node_EV=Complexity_n } if "`method'"=="mr"{ return matrix Complexity_individualMR=comp_i_MR return matrix Complexity_nodeMR=comp_n_MR } return scalar iterations=`it' } *Fitness if "`method'"=="fitness"{ mata fkc0=comp_D mata fkp0=comp_U forvalues j=1/`it'{ local k=`j'-1 mata fkc`j'=(comp_M*fkp`k') mata mfkc`j'=sum(fkc`j')/rows(fkc`j') mata fkc`j' = fkc`j':/mfkc`j' mata fkp`j'=(comp_M'*(1:/fkc`k')) mata mfkp`j'=sum(fkp`j')/rows(fkp`j') mata fkp`j' = fkp`j':/mfkp`j' } mata st_matrix("fitness_i", fkc`it') mata st_matrix("fitness_n", fkp`it') if "`projection'"!="nodes"{ capture drop fitness_i if "`xvar'"==""{ capture drop fitness_i svmat fitness_i capture rename fitness_i1 fitness_i } } else{ capture drop fitness_n if "`xvar'"==""{ capture drop fitness_n svmat fitness_n capture rename fitness_n1 fitness_n } } capt drop complexity_n capture replace fitness_n=1/fitness_n mata fitness_i=fkc`it' mata fitness_n=1:/fkp`it' *mata complexity_n=1:/fkp`it' mata st_matrix("fitness_n", fitness_n) mata st_matrix("Ubiquity", fkp0) mata st_matrix("Diversity", fkc0) return matrix Ubiquity=Ubiquity return matrix Diversity=Diversity return matrix Fitness_individual=fitness_i return matrix Fitness_node=fitness_n *return matrix Complexity_node=complexity_n return scalar iterations=`it' } if "`method'"=="eigenvalue"{ if "`projection'"=="indiv"{ su Complexity_i } if "`projection'"=="nodes"{ su Complexity_n } } if "`method'"=="mr"{ if "`projection'"=="indiv"{ su MR_Complexity_i } if "`projection'"=="nodes"{ su MR_Complexity_n } } if "`method'"=="fitness"{ if "`projection'"=="indiv"{ su fitness_i } if "`projection'"=="nodes"{ su fitness_n } } end