-
Notifications
You must be signed in to change notification settings - Fork 0
/
checkinvariant.ado
165 lines (144 loc) · 4.48 KB
/
checkinvariant.ado
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
*! version 1.0.1 02feb2021 Luís Fonseca, https://github.com/luispfonseca
*! -checkinvariant- Check if a variable is invariant within a group
program define checkinvariant, rclass
syntax [varlist], [by(varlist) ALLOWMISSing fill DROPINVARiant DROPVARiant KEEPINVARiant KEEPVARiant VERBose]
if "`allowmissing'" == "" & "`fill'" != "" {
di as error "The fill option can only be called with the allowmissing option."
error 198
}
if "`dropinvariant'" != "" & "`dropvariant'" != "" {
di as error "Choose only one between dropinvariant and dropvariant."
error 198
}
if "`keepinvariant'" != "" & "`keepvariant'" != "" {
di as error "Choose only one between keepinvariant and keepvariant."
error 198
}
local dropcondition "`dropvariant'`dropinvariant'"
local keepcondition "`keepvariant'`keepinvariant'"
if "`dropcondition'" != "" & "`keepcondition'" != "" {
di as error "Choose only one condition between keep and drop."
error 198
}
* if no varlist is passed, assume all variables are passed
if "`varlist'" == "" {
local varlist _all
}
* display " within by_variables" only if by is not empty
if "`by'" != "" {
local within_string " within "
}
* ensure no duplicates in the varlist to loop over
local varlist : list uniq varlist
* exclude the by variables from the list if they are passed (e.g. in _all)
local varlist: list varlist - by
tempvar originalsort
qui gen `originalsort' = _n
* compute results
foreach var in `varlist' {
*gegen doesn't take strings as input
if substr("`:type `var''" , 1, 3) == "str" {
tempvar grouped_string
qui gegen `grouped_string' = group(`var')
local finalvar `grouped_string'
}
else {
local finalvar `var'
}
tempvar first_value
gegen `first_value' = firstnm(`finalvar'), by(`by')
local missing_condition ""
if "`allowmissing'" != "" {
local missing_condition " | mi(`finalvar')"
}
cap assert `finalvar' == `first_value' `missing_condition' , fast
if c(rc) == 0 {
if "`verbose'" != "" {
di as result "Invariant`within_string'`by': `var'"
}
local invariantvarlist `invariantvarlist' `var'
if "`fill'" != "" & "`allowmissing'" != "" {
cap assert `finalvar' == `first_value' , fast
if c(rc) {
* gegen doesn't take strings as input, so I have to recover
* the nonmissing values of strings in an inefficient way
if substr("`:type `var''" , 1, 3) == "str" {
qui hashsort `by' - `var'
qui by `by': replace `var' = `var'[1]
}
else { // for numeric variables, can recover previously computed
qui replace `var' = `first_value' if mi(`var')
}
local filledvarlist `filledvarlist' `var'
local invariantvarlist: list invariantvarlist - var
}
}
}
else {
if "`verbose'" != "" {
di as result " Variant`within_string'`by': `var'"
}
local variantvarlist `variantvarlist' `var'
}
}
qui hashsort `originalsort'
if "`invariantvarlist'" != "" {
di as result "Invariant`within_string'`by':"
di as result "`invariantvarlist'"
}
if "`variantvarlist'" != "" {
di as result "Variant`within_string'`by':"
di as result "`variantvarlist'"
}
if "`filledvarlist'" != "" {
di as result "Variables whose missing values were replaced by unique non-missing value:"
di as result "`filledvarlist'"
}
return local varlist = "`varlist'"
return local by = "`by'"
return local invariantvarlist "`invariantvarlist'"
return local variantvarlist "`variantvarlist'"
return local filledvarlist "`filledvarlist'"
return scalar numinvariant = `:word count `invariantvarlist''
return scalar numvariant = `:word count `variantvarlist''
if "`fill'" != "" & "`allowmissing'" != "" {
return scalar numfilled = `:word count `filledvarlist''
}
if "`dropinvariant'" != "" {
if "`invariantvarlist'" != "" {
di as result "Dropping invariant variables"
local todrop `invariantvarlist'
}
if "`filledvarlist'" != "" {
di as result "Dropping filled variables"
local todrop `todrop' `filledvarlist'
}
cap drop `todrop'
}
if "`dropvariant'" != "" {
if "`variantvarlist'" != "" {
di as result "Dropping variant variables"
drop `variantvarlist'
}
}
if "`keepinvariant'" != "" {
if "`invariantvarlist'" != "" {
di as result "Keeping invariant variables"
local tokeep `invariantvarlist'
}
if "`filledvarlist'" != "" {
di as result "Keeping filled variables"
local tokeep `tokeep' `filledvarlist'
}
local tokeep `by' `tokeep'
keep `tokeep'
}
if "`keepvariant'" != "" {
if "`variantvarlist'" != "" {
di as result "Keeping variant variables"
local tokeep `variantvarlist'
}
local tokeep `by' `tokeep'
keep `tokeep'
}
end