3 Programar do - files

3.1 Programar en Stata

Esta sección esta basada en: An introduction to Stata Programing (), en notas de clase de la profesora Erin Hengel, en Advanced Stata Topics del profesor Alexander C. Lembcke, en Seventy-six Stata tips , en Top 10 ``gotchas” de y en Data Management Using Stata. A Practical Handbook de .

3.1.0.1 ¿Qué es programar en Stata?

Programar en Stata es:

  • Escribir do - files: una secuencia de comandos ejecutables a través de un archivo .do.
  • Escribir los que formalmente en Stata es un programa: un conjunto de comandos que incluyen el comando program. Un programa en Stata se guarda como un ado - file.
  • Escribir lenguaje de programación matricial: denominado mata.

3.1.0.2 do - files

El uso de do-file garantiza la replicabilidad del análisis de datos utilizando Stata. Recordar que un do-file puede llamar a otros (ej. master do.file. La jerarquización de los do-file puede ser importante para proyectos grandes o complejos. Es importante evitar trabajar en Stata interactivamente. Únicamente hay que hacerlo para inspeccionar datos (recordar practicas Sección 3.1).

3.1.0.3 ado - files

Sirven para crear tus propios en Stata. Una vez que armes tu programa y lo guardes en la carpeta de ado-file puedes utilizarlo como cualquier otro comando de Stata. Por ejemplo, puedes agregar las opciones if, in range y otras. También puedes escribir un documento de ayuda (help} que explique el programa. Crear tus propios comandos es una muy buena forma de ser más eficiente al trabajar con Stata (recordar Sección 1).

Mata para ado-files: Los ado-files pueden realizar tareas más complejas que involucren ejecutar el comando múltiples veces.

  • El lenguaje de programación mata es mucho más rápido que un ado-file.
  • Útil para realizar tareas que sean intensivas computacionalmente.
  • No es solo un lenguajes de programación que utiliza matrices. También sirve para tareas que involucran texto o listas.
  • Próximante discutiremos este lenguaje.

3.1.1 Comandos y funciones claves (y algunos detalles)}

Revisaremos algunos aspectos necesarios para mejorar la eficiencia al trabajar en Stata.

  • Directorios y uso de profile.do.
  • Tipos de comandos.
  • Tipos de datos y uso de compress.
  • Uso de capture, preserve y restore.

3.1.1.1 Directorios

  • cd y pwd sirven para fijar y conocer mis directorios. Es importante utilizar dobles comillas en los casos en que la ruta del directorio tenga espacios.
  • El comando sysdir provee una lista de los directorios importantes para Stata.
  • El comando update sirve para actualizar los comandos.
  • La carpeta PLUS es el directorio donde se guardan comandos descargados. Si utilizas ssc, se guardara en esta carpeta.
  • PERSONAL para tus propios ado-files.

3.1.1.2 Profile do

¿Qué es profile do?

  • Una opción no tan conocida es la utilización de profile.do
  • Este archivo se ejecuta cada vez que Stata se inicia. Si no tienes uno guardado no pasa nada.
  • Este do-file permite, por ejemplo, fijar tu directorio de trabajo inicial, cambiar características de los gráficos, generar atajos.
  • También se puede hacer que Stata abra un log-file y lo guarde siempre en el mismo lugar.
  • Cada vez que inicies sesión Stata ira por profile.do. Si lo encuentra lo va a ejecutar.
  • Stata recomienda guardar profile.do en home directory (ver help profilew para ususarios de windows y profilem para Mac).
  • Vamos a ver como modificar profile.do. En particular:
    • Ajustes generales.
    • Establecer las características de los gráficos.
    • Establecer atajos
    • Abrir, cerrar y guardar un log-file con la fecha.

Ejercicio 3.3.1:

Instrucciones

  1. Descargar carpeta ejercicios-clase3.
  2. Abrir profile.do e inspeccionar cada una de las lineas del código.

Preguntas

  1. Agrega dos elementos a alguna sección.
  2. Modifica las carpetas según las características de tu computadora.
  3. Guarda profile.do en el home directory. Ejecutar nuevamente Stata y verificar que el código se ha ejecutado.

3.1.1.3 Tipos de comandos

Comandos r-class y e-class

  • Muchos comandos en Stata (ej. summarize, correlate, regress) hacen que sea posible utilizar sus resultados una vez que estos son ejecutados.
  • summarize y correlate son comandos r-class. Es decir, comandos que guardan sus resultados en r().
  • regress es un comando e-class. Es decir, comandos que guardan sus resultados en e().
  • return list retorna los valores guardados en r()
* r-class
sysuse auto.dta, clear 
summarize mpg 
return list 

summarize mpg, detail 
return list 
display "La asimetría de mpg es" r(skewness) 
summarize price, detail 
return list 

Su limitación es que solo están disponibles los valores del último comando r-class ejecutado.

Los comandos e-class son comandos de estimación. Para verlos ereturn list y para llamarlos individualmente e(nombre). Guardan más información que los de r-class : matrices, vectores y funciones. La información de los comandos e-class sigue estando disponible una vez que utilizamos algún comando r-class`. Aquí una diferencia!

Un ejemplo clásico son las estimaciones de regresión lineal.

regress mpg weight length rep78 
display "La regresión se estimo para " e(N) " observaciones." 
ereturn list

En general cualquier comando de estimación se guarda en esta clase de formato.

3.1.1.4 Tipos de datos

numeric y string

  • La mayor distinción entre tipos de datos es entre numeric y string.
  • Al trabajar con datos muchas veces es necesario realizar conversiones entre estos dos formatos. Los comandos destring, tostring y encode son útiles para estas tareas (si alguien tiene dudas con alguno me escriben).
  • strings pueden soportar un máximo de 244 caracteres con un byte por cada carácter.
  • Por ejemplo, una variable del tipo str20 requiere 20 bytes por observación.

Clasificación de los datos

  • Para las variables en formato numeric los tipos de datos son: byte, int, long, float y double.
  • Los tres primeros solo puede almacenar valores enteros. long puede almacenar todos los números de 9 dígitos, pero es limitado para 10 dígitos.
  • float y double pueden almacenar números grandes.
  • No asumir que float será aritméticamente exacto. Por ejemplo:
    display float(16777216)
        16777216
    display float(16777217)
        16777216

¿Como utilizar esto para programar efectivamente en Stata?

  • IDs con muchos dígitos (y caracteres) guardarlos como string. No como integers, float o doubles.
  • No confiar en test exactos contra una constante con datos en formato float. Utilizar formato double para cualquier serie que necesita ser precisa (ej. suma de los residuos de una regresión).
  • Utiliza integers cuando sea apropiado (ej. variables dicotómicas). Guardar valores como int o byte ayuda a utilizar de forma más eficiente el espacio en el disco.
  • compress examina cada variable y determina si estas pueden ser guardadas de forma más eficiente. Utilizarlo.

Si se especifican valores iniciales fuera de los rangos permitidos para cada tipo de dato el resultado será un missing.

clear all 
set obs 10 
generate byte var1 = 101
summarize var1 

Notar que no se genera ninguna alerta de missing cuando este se crea. Esto es distinto para variables que ya existen.

clear all
set obs 10
generate byte var1 = 1
replace var1 = 101

Si el valor esta fuera de los rangos permitidos, la variable se guarda en un formato mayor. byte a int, int a long, a float a double. En este caso un mensaje aparece.

3.1.1.5 capture, preserve y restore

Manejando errores: capture

  • Sirve para evitar que Stata aborte cuando detecta un error.
  • Bueno para cuando quieres borrar algo que ya no se encuentra. Lo malo es que suprime todos los errores y oculta todo lo que puede ir mal.
set obs 10
generate byte var1 = 5
generate byte var2 = 10 
capture drop var1 var2 var3 
describe var1 var2

Código crea var1 y var2, luego las elimina incluyendo una variable que no existe. Al contrario de lo que se intuye, no se borran las variables, dado que hay una variable que no existe. La recomendación es siempre utilizar capture con moderación. Puede ser utilizado en bloque para así no tener que utilizarlo en cada linea.

sysuse auto.dta, clear
capture{
reg price mpg-trunk
reg price mpg-weight
reg price mpg-foreign
}
ereturn list 

preserve y restore

  • Algunos comandos en Stata remplazan la base actual por una nueva (ej. collapse o contract).
  • Utilizar preserve y restore es útil en estos casos.
  • En caso de que queramos obtener estadística descriptiva agregada y asociarla a observaciones podemos utilizar estos comandos.
* Ejemplo con collapse
sysuse auto.dta, clear
generate lprice = log(price)
preserve
collapse (max) max_lprice=lprice max_mpg=mpg ///
(iqr) iqr_lprince = lprice iqr_mpg = mpg if !missing(rep78), by(rep78) 
sort rep78 
tempfile repstats
save `repstats'
restore 
sort rep78 
merge m:1 rep78 using `repstats'
assert _merge != 2
summarize lprice max_lprice max_mpg 

* Ejemplo con contract
sysuse auto.dta, clear
preserve
contract mpg, cfreq(cumfreq) percent(percentage) cpercent(cumpercent)
sort mpg 
tempfile mpgfreq
save `mpgfreq'
restore 
sort mpg
merge m:1 mpg using `mpgfreq'
assert _merge != 2
summarize _freq cumfreq percentage cumpercent

3.1.1.6 Missing values

  • En general uno utiliza if variable !=. para evitar incluir missings.
  • Mejor practica es variable <. para excluir todos los valores en missing. Otra opción es if !missing(variable).
  • Los missing están codificados internamente como valores mayores a cualquier número. El menor valor de todos los missing es el punto. Al utilizar if variable <. es como decir, solo utiliza los números.
  • Como vimos en la Sección 3.1 hay muchos problemas al no tratar bien los missings. Este problema se incrementa al momento de trabajar con bases de datos imposibles de inspeccionar. Veremos algunas recomendaciones que pueden ser utiles para evitar estos errores.

Los comandos tratan distinto a los missings

  • Cualquier función de datos missing será missing.
  • Cuando se calcula un promedio o una desviación estándar solo valores no missing son considerados (ej. sum).
  • Algunos comandos en Stata manejan los missing de otras formas. Por ejemplo, las funciones max, min y las funciones para filas de egen: rowmax(), rowmean(), rowmin(), rowsd() y rowtotal() ignoran los missing. Por ejemplo, rowmean(x1,x2,x3) calcula el promedio de las variables y solo retornara missing si todas lo son.
  • Por ejemplo, rowmean(x1,x2,x3) calcula el promedio de las variables y solo retornara missing si todas lo son.
  • collapse (sum) trata a los missing como ceros.

Calcular un promedio con missings

clear
set obs 10 
gen var1 = rnormal()
gen var2 = 5 
gen var3 = .
gen promedio = (var1 + var2 + var3)/3
egen promedio1 = rowmean(var1 var2 var3)

PromedioSinMissing da como resultado missings. PromedioConMissing no considera missings. Es importante tratar de entender como funcionan los missings de los comandos que utilizas.

Generar variables considerando missings: Al crear una variable dicotómica, gen y gen byte tratan a los missing de formas distintas.

    local N = 500
    set obs `N'
    gen indicador = uniform() < .5
    replace indicador =. if mod(_n, 2) == 0
    
    * Si indicador es missing, variable sera missing
    gen variable_primercaso = 1 if indicador == 1 
    replace variable_primercaso = 0 if indicador == 0
    * Si indicador es missing, variable sera cero. 
    gen variable_segundocaso=(indicador==1)
    \end{minted}
    \end{itemize}

sum() considera missing como ceros.

    clear all 
    set obs 4 
    generate byte var1 = cond(mod(_n,2)==1, 1, .)
    generate byte var1sum = sum(var1)
    list, noobs

max() trata a los missing como si no estuviesen allí.

  display max(-5,.)

tabmiss, mvdecode y mvencode

  • En ocasiones los missing difieren en notación (ej. al importar datos de otro paquete). Siempre que trabajes con una base de datos nueva es importante recodificar. Notar que no todos tienen que ir a un punto. Todo depende del origen de los missing.
  • mvdecode y mvencode pueden ser útiles en este tipo de casos.
  • mvdecode permite recodificar valores numéricos como missing. Útil cuando valores son representados como -99, -999.
  • mvencode hace lo inverso. Mapea missing como numéricos.
  • El comando tabmiss inspecciona todas las variables de una base de datos y reporta los missing totales y como fracción del total de observaciones.
clear all 
set seed 1234
local N = 50
set obs `N'
gen income = abs(int(rnormal(0,5)))
assert income >= 0
replace income =. if mod(_n, 2) == 0
* Para ver los missing en variables
tabmiss 

* Transformar un valor númerico a missing
mvdecode income, mv(2)

* Transformar varios valores númericos a missing.
mvdecode income, mv(2 5)

* Transformar varios a missing, pero identific ́andolos.
mvdecode income, mv(3 = .a \ 6 = .b)

* Missings se cambian de vuelta a su valor original.
mvencode income, mv(.a = 3 \ .b = 6)

Ejercicio 3.3.2:

Instrucciones

Abrir ejercicios-clase3.do y ejecute las líneas correspondientes al ejercicio 2.

Pregunta

Compare las opciones 1, 2 y 3 en relación a como tratan los missing. Explique las diferencias entre cada una de estas opciones.

generate, replace y missing: Sólo unas pequeños detalles.

clear 
cd "$ejercicio3"
use census2c, clear

* Opción 1
gen smallpop_o1 = 1 if pop<=5000
replace smallpop_o1 = 0 if pop>5000

* Opción 2
gen smallpop_o2 = (pop <= 5000) 

* Opcion 3
gen smallpop_o3 = (pop <= 5000) if !missing(pop)

Opción 1 es la típica. Ojo no esta considerando missing. Es necesario agregar & !missing(pop). Escrito de esta forma, si pop es missing, smallpop será cero. Opción 2 es más simple, pero si cualquier valor de pop es missing será evaluado como un cero también. La razón es que los missing en Stata son considerados como números muy grandes para el programa. La Opción 3 soluciona el problema.

3.1.1.7 String a numeric y al revés

De string a numeric

  • Si las variables han sido mal clasificadas como string puedes utilizar la función real().
  • Por ejemplo: generate idpaciente = real(pacienteid).
  • El comando anterior genera missing para todas las observaciones que no puedan ser interpretadas como numéricas.
  • Mucho mejor es utilizar destring, replace.
  • Otro caso usual es que tenemos datos en formato string y queremos que tengan un equivalente. El comando encode. No es aconsejable utilizar este comando para valores numéricos guardados como string.

De numeric a string

  • Hay veces en las que se quiere generar un equivalente string a valores numéricos.
  • Tres comandos: string() , tostring() y decode().
  • Un ejemplo es querer mantener los 0 que estan al inicio de un código ID.
  • El comando tostring zip, format(\%05.0f) generate(idstring) genera un string de cinco digitos con los ceros al inicio.
  • decode() sirve para un caso en que tengas un id en numérico, pero que no la tengas en string.

Strings entre comillas: importa poner las comillas bien.

display "Este es un string normal"

display "Este no es un string con "comillas" "

display `"Este si es un string con "comillas""'

3.1.1.8 Funciones para generar variables

generate y replace: función cond()

  • Si quiero que un resultado sea “a” si una condición es verdadera y “b” si es falsa.
  • La función cond(x,a,b) posee esta capacidad sin la necesidad de utilizar if.
* Útil para construir una tabla
generate netmarr2x = cond(marr/divr > 2, 1, 2) 
label define netmarr2xc 1 "marr > 2 divr" 2 "marr <= 2 divr" 
label values netmarr2x netmarr2xc 
tabstat pop medage, by(netmarr2x) 

Las observaciones en Stata estás numeradas desde el 1. _N es el mayor número de observaciones, mientras que el actual es _n. sort (ascendete) y gsort (ascendete o descendente) alteran el orden de las observaciones. Como recomendación eviten generar variables o condiciones que dependan de la posición especifica de una observación.

* Ejemplo 1: uso de gsort
gsort region -pop 
by region: generate totpop = sum(pop)

* Ejemplo 2: uso de _n y _N
by region: list region totpop if _n == _N

* Ejemplo 3: sort
generate largepop = 0
replace largepop = 1 if pop > 5000 & !missing(pop)
gen smallpop = (pop <= 5000) if !missing(pop)
generate popsize = smallpop + 2*largepop
label variable popsize "Population size code"
label define popsize 1  "<= 5 million" 2 "> 5 million", modify
label values popsize popsize 
bysort region popsize: egen meanpop2 = mean(pop) 

recode para variables discretas: recode crea una nueva variable basada en otra variable.

* Esta no es una buena opción 
replace newcode = 5 if oldcode == 2 
replace newcode = 8 if oldcode == 3 
replace newcode = 12 if inlist(oldcode, 5, 6, 7)
* Esta si es una buena opción 
recode oldcode (2 = 5) (3 = 8) (5/7 = 12), gen(newcode) 

El signo (\(=\)) es para indicar valor antiguo a valor nuevo. No es necesario aplicarlo linea por linea. recode produce un código más eficiente.

recode para variables continuas: recode(x,x1,x2,x3,x4,xn) para variables continuas de forma tal de generar intervalos tal que \(x \leq x_1 ; x_1 \leq x \leq x_2\) y así sucesivamente. Los resultados son iguales a los límites creados.

use census2c, clear
generate breaks = recode(medage, 29, 30, 31, 32, 33)

Otros comandos que cumplen una función parecida son floor y ceil(). Ambos sirven para generar un valor entero. El primero para redondear hacia abajo y el otro hacia arriba. floor(x) retorna el entero \(n\) tal que \(n \leq x < n + 1\) mientras que ceil(x) es tal que \(n - 1 < x \leq n\).

use census2c, clear
generate popurbfloor = floor(popurb)
generate popurbceil = ceil(popurb)

irecode para variables continuas: irecode(x,x1,x2,x3,x4,x_n) es una alternativa para categorizar grupos también. Por ejemplo:

generate size = irecode(pop, 1000, 4000, 8000, 20000)
label define popsize 0 "<1m" 1 "1-4m" 2 "4-8m" 3 ">8m" 
label values size popsize
tabstat pop, stat(mean min max) by(size)

Categorizara cada grupo según el intervalo en el que este. Parte del cero!. $x x_1 $, \(x_1 \leq x \leq x_2 \rightarrow 1\) y así sucesivamente.

Crear cuartiles con xtile: Con xtile podemos querer clasificar las variables según cuantiles (quintiles, deciles, cuartiles, etc).

* Creamos cuartiles para población 
xtile popcuart = pop , nq(4)
tabstat pop, stat(n mean min max) by(popcuart)

Ejercicio 3.3.3:

Instrucciones

  1. Cargar la base auto.dta

Pregunta

  1. Genere una variable que sirva para redondear hacia abajo la variable mpg en en múltiplos de 5, de modo que cualquier valor de 10 a 14 se redondee a 10, cualquier valor de 15 a 19 a 15 y así sucesivamente.

3.1.1.9 Funciones de egen

Todos y todas conocemos algunas de las típicas funciones de egen.

clear 
* Ejemplo del uso de egen
generate size = irecode(pop, 1000, 4000, 8000, 20000)
label define popsize 0 "<1m" 1 "1-4m" 2 "4-8m" 3 ">8m" 
label values size popsize
bysort size: egen avgpop = mean(pop)
generate popratio = 100 * pop / avgpop 
format popratio \%7.2f
list state pop avgpop popratio if size == 0 

Otras funciones son iqr(), kurt(),mad(), mdev(), median(), mode(), pc(), pctile(), rank(), sd(), skwe(), std().

egenmore

  • Menos conocida es la colección de funciones adicionales de egen hechas por Nicholas J. Cox.
  • Estas funciones están contenidas en el comando egenmore.
  • bom() y eom() crean variables de fechas que corresponde al primer y último día de un mes determinado.
  • corr() calcula correlaciones y covarianzas mientras que var() calcular la varianza.
  • semean() calcula la desviación estándar del error de una media.
  • record() permite calcular el valor más alto o más bajo de una serie.
* Ejemplo 1: Generar variable que tenga la primera palabra de una frase (wordof)
egen firstword = wordof(make),  word(1)
list firstword make in  1/15

* Ejemplo 2: Para generar automáticamente valores extremos (outside, 1,5 RIQ)
egen    extrmpg = outside(mpg)
tab extrmpg,    missing

rall() y rany() son útiles para el análisis de datos. Evaluan una condición y genera un indicador si todas o alguna observación la cumple.

set obs 12
gen a = 1 in 1 
gen b = 2 in 2/4
gen c  = -3  in 5/7
gen d = 4  in 8/10
gen e = . in 11/12
egen any = rany(a b c d e) , c(@ > 0 & !missing(@))
egen all = rall(a b c d e) , c(@ > 0 & !missing(@)) 

3.1.2 Macros locales y globales

3.1.2.1 Nombrar macros

  • Una macro es un contenedor que puede almacenar números o nombres de variables.
  • Puede ser local o global. La primera es temporal, la segunda no.
  • Un ejemplo de variable local es:
local NivelEstres Nada Medio Moderado Severo 
display "Los niveles de estrés son: `NivelEstres'"

El primer comando define la macro y sus valores. Para llamar a la macro hay que utilizar las comillas (“). local nombre texto \(\rightarrow\) local nombre = text \(\rightarrow\) local nombre = "text".

Ojo con las rutas:

  • En ocasiones voy a querer utilizar una macro dentro de la ruta de una carpeta.
  • Es importante utilizar siempre / o bien \\. De otra forma no lo reconocerá.
local filename base.dta 
use "H:\ECStata\`filename'"
r(601);
* Para corregir el error, dos caminos: 
use "H:\ECStata\\`filename'" 
use "H:/ECStata/`filename'"

Ejercicio 3.3.4: Compare los siguientes comandos y comente las diferencias:

  • display "Dos mas dos = 2 + 2"
  • display "Dos mas dos= 2 + 2’“`

Ojo con el signo igual: Algunas veces es bueno colocar un signo \(=\) al definir macros. Por ejemplo, cuando redefinimos variables.

local contador 0
local NivelEstres Nada Medio Moderado Severo 
foreach a of local NivelEstres { 
local contador = `contador' + 1 
display "Nivel de Estres `contador' : `a'"
}

La primera parte sirve para definir la macro mientras que la segunda sirve para dar cuenta de su valor actual. Al actualizar, ocupen igual.

Sin signo igual: En algunas ocasiones queremos escribir una macro dentro de un loop. En estos casos es conveniente evitar el signo igual.

local contador 0
local NivelEstres Nada Medio Moderado Severo 
foreach a of local NivelEstres {
local contador = `contador' + 1 
local nuevalista `nuevalista' `contador' 'a' 
} 
display "`nuevalista'" 

El local nuevalista define una macro como string que posee su propio contenido, el valor de contar y el valor del iterador.

3.1.2.2 Generar variables, contadores y condiciones con macros

Podemos utilizar macros para renombrar variables.

clear 
forvalues a = 10/20{ 
gen v`a' = rnormal()
}

* Renombramos variables 
forvalues i = 11/15 { 
rename v`i' x`=1960 + `i''
}

En este fragmento de código, Stata evalúa la expresión 1960 + 'i' antes de evaluar la macro externa. Por ejemplo, cuando pase por el iterador i = 11, el nuevo nombre de la variable será x1971.

Resumir condiciones: Podemos utilizar macros para resumir condiciones. Esto es útil para estimar modelos o generar estadística descriptiva.

clear 
sysuse auto 
local cond "if foreign==0"
local varlist "mpg rep78 trunk weight turn"

* Estimar la regresión considerando la condicíòn
reg price `varlist' `cond'

Agrupar en base a una condición: Imaginemos que ahora queremos estimar una regresión para todas las compañías de auto que empiezan empiezan con B.

local autoname B 
reg price mpg weight if substr(make,1,1)=="`autoname'"

La mejor manera de pensar en esto es hacer lo que hace Stata: reemplazar "ctyname" por su contenido substr(país,1,1)=="ctyname’“. Al hacerlo, se convierte ensubstr(país,1,1)==”B”. Si se omiten las comillas dobles, se obtienesubstr(country,1,1)==B`, lo que da lugar a un error.

Contadores: Las macros también pueden ser útiles para contadores.

* Para adelante
local i = 1 
local ++i 
di `i' 

* Para atrás
local i = 1
local --i 
di `i' 

Muy útil para hacer gráficos o guardar datos en matrices.

Utilizar una macro para estimar regresiones: Imaginen que desean estimar regresiones sobre un conjunto de variables donde una parte de este conjunto esta fija y la otra parte es variable.

local rhs mpg weight
reg price `rhs' if foreign == 0 
local rhs "`rhs' headroom trunk"  
reg price `rhs' if foreign == 0 

¿Qué ocurre si queremos hacerlo por separado? ¿Uno a uno?

local rhs "mpg weight \`add_var'" 
local add_var "headroom"
reg price `rhs' if foreign == 0  
local add_var "trunk" 
reg price `rhs' if foreign == 0  
local add_var "turn" 
reg price `rhs' if foreign == 0  

Lo que ocurre cuando hacemos referencia a una macro es que su valor se introduce en ese punto. El uso de una barra invertida en su lugar hace que se introduzca la referencia de la macro, es decir, no se sustituirá el valor de add_var sino el término `add_var'. Así que cada vez que llamamos a la local rhs el valor actual de el local add_var es sustituido.

Ejercicio 3.3.5:

Instrucciones

  1. Cargar la base auto.dta

Preguntas

  1. Defina una variable macro llamada control que contenga mpg, rep78 y headroom. Estime una regresión entre price y control para vehículos extranjeros y de nuevo para vehículos domésticos.

  2. Ejecute summarize mpg junto con return list. Defina dos macros display local mean1 r(mean) y local mean2 = r(mean). ¿Son iguales?

3.1.2.3 Globales

Crear macros globales

  • Se crean con el comando global.
  • Útiles para fijar directorios o programas.
  • En otros casos es mejor utilizar local.
* Para generarla 
global variable

* En caso de querer llamarla
display $variable

3.1.2.4 Funciones extendidas de macros

¿Qué son las funciones de macros extendidas?

  • Stata también define ciertas macro. Estas se denominan extended macro functions o macros extendidas.
  • En algunos casos contienen información sobre tu sistema operativo, sobre la última estimación que se realizo o sobre la base de datos.
  • El help extended_fcn y la documentación que la acompaña proporcionan una descripción completa de la sintaxis de cada función de macro extendida (hay muchas). Muchas tienen ligeras variaciones de sintaxis entre ellas (por ejemplo, algunas requieren que las macros estén entre comillas dobles; otras no lo permiten).
* La sintaxis general es: 
local nombremacro: función macro extendida

Mirar labels

* Para mirar los labels de trunk
sysuse auto, clear
local tlab : variable label trunk 
display "`tlab'"

variable label recupera el nombre asignado a una variable.

Contar dentro de un local

* Para contar dentro de un local
local NivelEstres Nada Medio Moderado Severo 
local wds: word count `NivelEstres' 
display "Hay `wds' niveles de estres:"
forvalues i = 1/`wds' {
local wd: word `i' of `NivelEstres'
display "Nivel `i' is `wd'"
}

word count y word como funciones de extensión que operan sobre strings.

Conocer tipos de datos

* Para conocer tipos de datos
sysuse auto, clear
local stortype : type make 
display "`stortype'"

Ojos con las comillas dobles (nuevamente): Algunas veces las macros contienen comillas dobles. Para poder escribirlos sin errores es necesario modificar levemente la forma en que se llama a la variable local

* Con error 
local answers yes no "do not know" 
display "`answers'"

* Sin error 
local answers yes no "do not know" 
display `"`answers'"'

Ejercicio 3.3.6:

Preguntas

  1. Use una macro extendida para mostrar el tipo de dato (ej. int, float, long,…) de mpg. Revise el help de extended_fcn.
  2. Use una macro extendida para retornar el valor del label asociado a foreign cuando es igual a 1.
  3. Use una macro extendida para mostrar solo la primera variable de `controls'.
  4. Utilice una función extendida de macros para mostrar todos los archivos de su directorio actual (sugerencia: utilice comillas compuestas cuando muestre los nombres de los archivos).

3.1.2.5 Funciones de macro extendidas para listas

Stata también define ciertas macro para operar sobre listas. Estas funciones permiten combinar listas, buscar elementos dentro de una lista o bien buscar elementos comunes entre dos listas. Conveniente revisar help macrolist para más funciones.

* Sintaxis: 
local nombre macro: list función

* Ejemplo de lista
local animales "gato perro gato loro loro" 
local uniqanimales : list uniq animales 
display "`uniqanimales'"

Función levelsof: El comando levelsof lista los valores distintos de una variable. Al agregar la opción local(nombremacro) esto se guardara como una macro.

* Sintaxis básica
sysuse auto, clear
levelsof rep78 
display "`r(levels)'"

* Sintaxis cuando hay variables categóricas.
levelsof foreign, local(levels)
foreach l of local levels {
di "-> foreign = `: label (foreign) `l''"
reg price mpg if foreign == `l'
}

3.1.2.6 Manipulación de locales vía listas de macros

Listas de macros: Las macro lists permiten obtener el número de elementos de una macro, trabajar con valores duplicados, ordenar elementos. Veremos cuatro aplicaciones:

  1. Elementos duplicados.
  2. Agregar y remover elementos.
  3. Uniones e intersecciones.
  4. Ordenar elementos.

Elementos duplicados: dups extrae todos los elementos sobrantes.

* Deja solo los unicos
local fib 0 1 1 2 3
local fib_nodups : list uniq fib
display "`fib_nodups'"

* Quita todo los duplicados
local fib 0 1 1 2 3
local fib_dups : list dups fib
display "`fib_dups'"

Agregar y remover elementos: Es básicamente pegar elementos de una macro con otra. Definamos dos variables locales: vars y coef y peguémoslos.

local vars x y z 
local coefs a b c 
local vars_coefs `vars' `coefs'
display "`vars_coefs'"

Para remover tenemos que definir un nuevo local con los elementos que queremos quitar y sustraerlo del original. Supongamos que queremos actualizar el contenido de vars eliminado “y”.

local not y
local vars : list vars - not
display "`vars'"

Unión de elementos: Podemos pegar todos los elementos diferentes entre dos listas.

local A house tree car
local B computer car bike
local all_things : list A | B
display "`all_things'"

Noten que los elementos de car no fueron pegados. Notar que esto es distinto a simplemente pegar macros entre sí.

Intersección de elementos: Podemos hacer la intersección entre dos elementos de una lista. Esto corresponde a los elementos que pertenecen a ambas macros.

local A house tree car
local B computer car bike
local common_things : list A & B
display "`common_things'"

Noten que en este caso solo car se mantiene.

Ordenar elementos: En ocasiones queremos ordenar los elementos de una lista contenida en una macro.

local names camila camilo pedro paula
local names : list sort names
display "`names'"

Para hacer que los elementos de una macro se ordenen aleatoriamente es conveniente utilizar mata

local nums 1 2 3 4 5
mata : st_local("random_nums", ///
invtokens(jumble(tokens(st_local("nums"))')'))
display "`random_nums'"

3.1.2.7 creturn

Macros con creturn

  • En algunos casos al utilizar local o global vamos a querer fijar algunos parámetros.
  • Para este propósito utilizar creturn. Algunos ejemplos son: c(current_date), c(pwd), c(current_time), c(stata_version), c(pi), c(alpha), c(Wdays).

Ejercicio 3.3.7:

Preguntas

  1. Define una macro llamada comestibles con peras, manzanas, fresas, yogur, vino y queso en ella. Ponla en orden alfabético.
  2. Define una macro llamada unión que contenga los miembros de la macro animales y comestibles y luego utiliza una función de lista extendida de la macro para mostrar el número de palabras que contiene.
  3. Ordena unión y muestra la posición de la palabra “vino” utilizando una función de lista extendida de macros.

3.1.3 Estructuras de datos

3.1.3.1 Escalares

  • Los escalares pueden contener valores numéricos o strings. Un escalar solo puede contener un valor.
  • La sintaxis para generar un escalar es scalar scalar_name = exp.
quietly: summarize mpg 
scalar mean_mpg = r(mean)
quietly: summarize rep78
scalar mean_rep78 = r(mean)
display "r(mean) guarda el promedio de rep78: " r(mean) 
display "Pero tambien podemos recuperarlo:" mean_mpg

Para utilizar un escalar en una operación solo es necesario llamarlo por su nombre. Para listar el contenido de todos los escalares scalar list. Para borrar scalar drop scalar_name o si quiero eliminar todo scalar drop _all. Los escalares permiten parametrizar un do-file.

use fem2, clear 
scalar lb1 = 80 
scalar ub1 = 88 
scalar lb2 = 89 
scalar ub2 = 97 
forvalues i = 1/2 {
display _n "IQ" "lb`i'" "-" "ub`i'" 
tabulate anxiety if inrange(iq, lb`i', ub`i')
}

3.1.3.2 Elementos y operaciones con matrices

Stata puede generar matrices.

reg weight age age2
matrix b = e(b)
matrix list b
matrix V = e(V)
matrix list V

Las matrices son muy importantes para guardar resultados y exportarlos de forma conveniente. También son útiles para cuando se quieren hacer estimaciones de muchos parámetros y para distintos conjuntos de datos.

Operaciones con matrices: Se puede operar con matrices. Por ejemplo, sabemos que \(b = (X'X)^{-1}X'y\) y que \(V = \sigma^{2}(X'X)^{-1}\). Si queremos extraer solo la matriz \(X'y\) tenemos que operar, calculando \(\frac{1}{\sigma^{2}}V^{-1}b\).

matrix define b = e(b)' 
matrix define xty = inv(V) * b /e(rmse)^2
matrix list xty 

e(b)’ indica la traspuesta, mientras que inv() es para calcular la matriz inversa.

Llamar a los elementos de una matriz: Se puede acceder a los escalares contenidos en las matrices. Por ejemplo, si queremos obtener los elementos de la matriz \(e(b)\) tenemos:

display "The coefficient on weight is: " _b[weight] 
display "Its standard error is: " _se[weight] 
* Valores predichos 
generate anxietyhat = _b[_cons] + _b[weight] * weight + /// 
_b[age] * age + _b[age2] * age2

También podemos utilizar las posiciones de los elementos de la matriz (b[i,k)]).

3.1.3.3 Funciones

Solo disponibles para comandos e-class. Por ejemplo, si estimamos una regresión veremos que la unica función disponible es e(sample).

sysuse auto 
regress mpg weight length rep78
ereturn list

e(sample) nos indica si una determinada observación se utilizó para estimar la regresión. Es decir, es igual a uno si una observación estaba en la muestra de estimación y 0 si fue excluida.

Ejercicio 3.4.1:

Preguntas:

  1. Estime una regresión de mpg contra weight length rep78 utilizando base de datos auto, pero solo para los autos extranjeros (foreign == 1).
  2. Genere una nueva variable, llamada enmuestra que tome los valores dados por la función e(sample).
  3. Utilizando br, observe los valores de enmuestra. ¿Qué observa?
  4. Calcule la estadística de mpg solo para las observaciones que fueron incluidas en la regresión.

3.1.4 Iteradores

3.1.4.1 Foreach

foreach y forvalues: forvalues itera sobre una lista de números y foreach recorre los elementos de una macro, o los nombres de las variables de una lista de variables, o los elementos de una lista de números.

* Foreach
foreach animal in cats and dogs { 
display "`animal'"
}

* Forvalues 
forvalues i = 1(1)100 { 
generate x`i' = runiform()
}

Hay algunas variaciones en foreach según el tipo de lista. La sintaxis es similar a la recien presentada, pero difiere en dos aspectos:

  • in se remplaza por of.
  • Hay que llamar al identificador.
  • Veamos como iterar sobre una lista de globales y locales. \end{itemize}
* Sobre locales y globales
local money "Franc Dollar Lira Pound" 
foreach currency of local money { 
display "`currency'"
}

Veamos como iterar sobre una lista de variables:

* Sobre lista de variables
foreach var of varlist mpg weight-turn { 
quietly summarize `var' summarize `var' if `var' > r(mean)
}

Veamos como iterar sobre una lista de de nuevas variables:

* Sobre lista de nuevas variables
foreach var of newlist z1-z20 { 
generate `var' = runiform()
}

Veamos como iterar sobre una lista de números:

foreach num of numlist 1 4/8 13(2)21 103 { 
display `num'
}

3.1.4.2 Combinar macros con iteradores

foreach y forvalues combinados con macros pueden utilizarse para ahorrarnos mucho trabajo. Podemos generar tun conjunto de locales a partir de iteraciones.

use replicate.dta, replace
levelsof cty
local ctries "`r(levels)'" 

foreach ctr in `ctries' { 
        sum hours_t if cty == "`ctr'" 
        local nombre `ctr' = `r(mean)'
} 

Como ya hemos visto, el comando levelsof devuelve una lista de todos los valores distintos de una variable categórica y los guarda en la macro r(levels). Esto lo hace en el caso de que nosotros no le asignemos un nombre. Podemos utilizar esta lista para los países y años de nuestra muestra para definir dos iteraciones que recorran todos los valores posibles. Para cada valor resumimos la población y definimos una macro local compuesta por el código de país y el año (por ejemplo, USA1990) que toma el valor de la población en ese año para ese país.

Una aplicación del forvalues:

use gdp4cty, clear 
forvalues i = 1/4 {
generate double lngdp'i' = log(gdp'i')
summarize lngdp'i'
}

Utilizando dos forvalues:

forvalues y = 1995(2)1999 {
    forvalues i = 2(2)4 {
        summarize gdp`i'_`y'
    }
}

foreach y recode:

use gdp4cty, clear 
local ctycode 111 112 136 134 
local i 0
foreach c of local ctycode{ 
        local ++i 
        local rc "`rc' (`i'=`c')" 
}
display "`rc'"
recode cc `rc', gen(newcc)

Loops anidados: foreach y forvalues

use gdp4cty, clear 
local country US UK DE FR 
local yrlist 1995 1999 
forvalues i = 1/4 {
    local cnaine: word `i' of `country'
    display "`cnaine'"
    foreach y of local yrlist {
            rename gdp`i'_`y' gdp`cnaine'_`y'
    } 
}

En estos casos es bueno utilizar espacios para hacer el código más amigable. Ojo que a Stata no le interesa esto para ejecutar, es solo una cuestión de estilo.

sysuse auto, clear
foreach y of varlist mpg rep78 headroom trunk weight length { 
    foreach x of varlist rep78 price displacement gear_ratio foreign { 
    regress `y' `x'
} 

También útil para estimar regresiones.

Tokenize: Podemos almacenar los elementos de la lista de países en macros numeradas con tokenize.

use gdp4cty, clear
local country US UK DE FR 
local yrlist 1995 1999 
local ncty: word count `country'
display "`ncty'"
tokenize `country'
forvalues i = 1/`ncty'{
    foreach y of local yrlist {
            rename gdp`i'_`y' gdp``i''_`y'
        }
}

Aquí los nombres de los países se almacenan como valores de las macros numeradas. Debemos referenciar doblemente la macro \(i\). El contenido de esa macro la primera vez que se pasa por el bucle es el número 1. Para acceder al primer código de país, debemos referenciar la macro \(`1'\).

3.1.4.3 While loop

Realiza la iteración o se repite una lista de comandos mientras la condición while sea verdadera. La sintaxis es:

while exp { 
hace algo
}

¿Cuando es útil? : Cuando no este seguro(a) cuantas veces se realizará la iteración. Notar que si no hay convergencia, va iterar infinitamente.

while reldif (nueva, antigua) > 0.001 { 
}

También se puede combinar con macros. Es importante utilizar los incrementales:

local i=1 
while `i'<=5 {
display "loop number" `i' 
local i = `i'+1
}

La primera parte define el inicio del contador, mientras que la segunda indica la condición para que sea ejecutado. El local final actualiza (incremental). Si el incremento es unitario podemos utilizar local ++i.

3.1.4.4 Branching

Hacer una cosa en caso de que alguna condición sea cierta y otra cosa en caso de que sea falsa. La sintaxis básica es:

if algo es verdadero { 
hacer esto
} else {
hacer lo contrario
}

Ejercicio 3.4.2:

Preguntas

  1. Defina una macro llamada mimacro que sea igual a un número entero aleatorio entre 1 y 99.
  2. Utilizando if y else muestre un mensaje que diga si es par o impar.

3.1.4.5 Aplicaciones

Seguir secuencias especiales: creturn posee varias constantes y valores a los que se puede acceder. Por ejemplo:

  • c(filename) nombre del último nombre del archivo guardado.
  • c(alpha/ALPHA) lista de letras minúsculas/mayusculas.
  • c(Mons) lista de nombres de los meses abreviados.
  • c(Months) lista de los nombres de los meses no abreviados.
  • c(Wdays) lista de los dias de la semana abreviados a tres caracteres.
  • c(Weekdays) lista de los días de la semana no abreviados.

Con un iterador es posible aplicar estas listas para agregar labels. Suponga que tenemos valores de 1 a 12 que representan meses.

clear all
set obs 12
gen month = _n 
tokenize `c(Months)' 
forvalues i = 1/12 {
    label define monthlab `i' "``i''" , modify 
                   }
label val month monthlab

3.1.4.6 Monitorear un loop

Ejemplo proveniente de (Stata tip 41). Cualquier loop puede ser modificado para que muestre su progreso con el comando _dots. Esto es importante para cuando se requieren hacer procesos que toman varias horas y es necesario monitorear avances.

_dots 0, title(Loop ejecutando) reps(75) 
forvalues i = 1/75 { 
_dots ‘i’ 0
}

----+--- 1 ---+--- 2 ---+--- 3 ---+--- 4 ---+--- 5
..................................................    50
.........................

El primer comando _dots establece las lineas. Titulo y número de repeticiones son opcionales. reps solo acepta enteros como argumento. _dots ‘i’ 0 tiene dos elementos:

  • El primer argumento es el número de repetición, que registra el número de intentos en curso. En el ejemplo, esta automáticamente determinado por el loop.
  • El segundo argumento es el código de retorno, el cual indica el tipo de símbolo. En el ejemplo tenemos un 0.
  • Los códigos de retorno alternativos producen una “s” (-1), “.” (), “x” roja (1), una “e” (2), una “n” (3) o un “?” (cualquier otro valor).

Ejemplo no numérico: La idea es definir una macro local para que actue como un contador.

sysuse auto
_dots 0, reps(10)
foreach var of varlist price - gear_ratio {
  sum `var', d
  local i = `i'+1
  _dots `i' 0
}

Contar repeticiones:

  • El número de repeticiones en rep no es calculado por _dots. Es necesario contar manualmente las variables e introducir el número.
  • Esto lo vamos a hacer con la ayuda de una función de macro extendida sizeof.
  • unab permite ingresar una lista de variable abreviada y expandirla, de forma tal de que la pueda contar.
sysuse auto
unab myvars : price - gear_ratio
local N : list sizeof myvars
_dots 0, reps(`N')
foreach var of varlist `myvars' {
  ...
  local i = `i'+1
  _dots `i' 0
}

Un ejemplo más complejo es:

noisily _dots 0, title(Looping until 70 successes...) 
local rep 1 
local nsuccess 0 
while ‘nsuccess’ < 70 { 
local fail = uniform() < .2 
local nsuccess = ‘nsuccess’ + (‘fail’ == 0) 
noisily _dots ‘rep++’ ‘fail’
}

Se ejecuta hasta que logre 70 aciertos. En este ejemplo artificial, cada iteración tiene un éxito aleatorio con una probabilidad del 80%. Los éxitos se indican con un punto (.) y los fracasos con una x.

3.1.5 Manejo de bases de datos

3.1.5.1 Prefijo: by

Los prefijos en Stata ejecutan tareas repetitivas sin la necesidad de especificar el rango de valores sobre la tarea que es ejecutada. Un prefijo muy conocido es by. Por ejemplo, by varlist [, sort]: command. command es repetido para cada valor de la variable. Es más, repeticiones siguen el orden de la variable sea string o numeric.

use bpress, clear
bysort sex agegrp: summarize bp 

3.1.5.2 Prefijo: xi

xi es útil para cuando queremos producir un variable indicador para las observaciones que son distintas entre si.

xi i.agegrp

El ejemplo le esta diciendo a Stata que genere variables indicadores. Esto es muy útil para reducir los códigos.

Interpretando prefijo xi: xi es comúnmente utilizado como un prefijo. La principal ventaja es cuando existen múltiples interacciones entre las variables.

* Caso 1: incluye indicadores de ambas variables.
xi: regress bp i.agegrp i.sex

* Caso 2: incluye además interacciones entre ellas.
xi: regress bp i.agegrp*i.sex

* Caso 3: Interactúa una variable continua con una discreta.
xi: regress bp i.agegrp*bp0

* Caso 4: Incluye solo interacciones con variable continua (además de principal). 
xi: regress bp i.agegrp|bp0 

3.1.5.3 Prefijo: statsby

statsby permite ampliar by. Este último tiene la limitación de permitir únicamente un comando.

statsby mean=r(mean) sd=r(sd) n=r(N), by(agegrp sex): summarize bp

Se produce una nueva base de datos con una observación por grupo con los estadísticos incorporados. Útil para calcular estadística descriptiva.

3.1.5.4 Prefijo: rolling

statsby permite obtener estadísticas para sub-muestras que no se traslapan. rolling sirve para sub-muestras traslapadas. Por ejemplo, al trabajar con series de tiempo se quiere calcular estadísticas para datos que están traslapados (el. calcular una media móvil).

Vamos a calcular medias y medianas utilizando una ventana de 90 días:

use ibm, clear 
rolling mean=r(mean) median=r(p50), window(90): summarize spx, d 
tsset end 
tsline mean

start y end indican el inicio y fin de la ventana.

3.1.5.5 Merge y Append

Recomendaciones para utilizar merge

  • Especifique siempre el tipo de fusión (1:1, m:1 o 1:m). Si no se especifica el tipo de fusión, se llama a la versión antigua y no robusta de la fusión.
  • Nunca haga fusiones de muchos a muchos (m:m), o al menos, sólo hágalo cuando tenga una muy buena razón.
  • Incluya siempre la opción assert() para indicar qué patrón de observaciones coincidentes espera.
  • Incluya siempre la opción keep() para indicar qué observaciones deben conservarse del conjunto de datos fusionados.
  • Siempre que sea posible, incluya la opción keepusing() y enumere explícitamente qué variables pretende añadir al conjunto de datos; puede incluir esta opción incluso cuando mantenga todas las variables de los datos utilizados.
  • Utilice la opción nogen, excepto cuando piense utilizar explícitamente la variable _merge más adelante. Nunca debe guardar un conjunto de datos que tenga _merge; si necesita esta variable más adelante, dele un nombre más informativo.

Con respecto a especificar _merge y assert notar que:

     merge ..., assert(match master) keep(match)

     * Es equivalente a: 
     merge ...
     assert _merge==1 | _merge==3
     keep if _merge==3

Append con ciudado:

  • El comando append es muy útil para manejos de bases de datos. Una precaución común es con respecto al nombre de las variables. Si dos variables (ej. PRECIO y precio) difieren, se generaran dos columnas nuevas al hacer el append en vez de una.
  • Una precaución un poco menos conocida guarda relación con el tipo de variables. ¿Qué ocurre si dos variables se llaman igual, pero estan guardadas en formatos distintos?
  • En este caso, el orden en el cual se combine la base de datos va a importar y puede generar diferencias al momento de pegar datos. Esto es especialmente importante cuando una variable esta guardada en numérico en una base de datos y en string en la otra.

Veamos un ejemplo con la base auto.dta. Vamos a crear dos bases de datos según la procedencia de los autos y ejecutar el comando append.

sysuse auto 
drop if foreign
save autodom 
sysuse auto 
drop if !foreign 
rename foreign nondom 
generate str foreign = "foreign" if nondom 
save autofor

use autdom 
append using autofor 
describe foreign 

Notar que append genera el siguiente mensaje: foreign is str 7 in using data byt will be byte now. Noten que el contenido de la variable string se ha perdido: 22 casos son ahora missing.

¿Qué ocurre si hacemos el proceso al revés? Vamos a cargar autos extranjeros y le vamos a pegar autos domésticos:

use autfor 
append using autodom, force
describe foreign 
codebook foreign

El formato de los datos de la primer base de datos manda. * No utilizar force sin cuidado. * Con distintos tipos de datos, append es sensible al orden en que los archivos son pegados. Tener cuidado y revisar consistencia en los datos. Hacer test aquí tambíen es importante.