David M. Drukker, Director Ejecutivo de Econometría.
Mostraré
cómo escribir una función en Mata, el lenguaje de programación matricial que
forma parte de Stata. Esta publicación utiliza conceptos introducidos en Programación de un comando de estimación en Stata:
Mata 101.
Esta
es la décimo segunda publicación de la serie Programando un comando de estimación en Stata. Te recomiendo que empieces por el
principio.
Funciones Mata
Los
comandos funcionan en Stata. Las funciones funcionan en Mata. Los comandos
operan en objetos Stata, como variables, y los usuarios especifican opciones
para alterar el comportamiento. Las funciones de Mata aceptan argumentos,
operan en los argumentos y pueden devolver un resultado o alterar el valor de
un argumento para contener un resultado.
Considere
myadd() definido a continuación.
myadd() acepta dos argumentos, X y Y, coloca la suma de X y Y dentro de A, y devuelve A. Por ejemplo,
Ejemplo 1: Definir y usar una función
Después
de definir myadd(), creo las matrices C y D, y paso C
y D a myadd(), que devuelve su suma. Mata asigna la suma devuelta
a W, la cual muestro. Tenga en cuenta que dentro de la función myadd(),
C y D se conocen respectivamente como X e Y.
Solo
se puede acceder a la A creada en myadd() dentro de myadd(),
y no entra en conflicto con una A definida fuera de myadd(); es
decir, A es local a la función myadd(). Ahora ilustraré que A
es local para myadd().
Ejemplo 2: A es local a myadd()
Habiendo
ilustrado que la A definida dentro de myadd() es local a myadd(),
debo señalar que las matrices C y D que definí en el ejemplo 1
están en la memoria global de Mata. Al igual que en los programas ado, no
queremos utilizar nombres fijos en la memoria global de Mata en nuestros
programas para no destruir los datos de los usuarios. Afortunadamente, este problema
se resuelve fácilmente escribiendo funciones Mata que escriben sus resultados
en Stata y no devuelven resultados. Proporcionaré discusiones detalladas de
esta solución en los comandos que desarrollé en publicaciones posteriores.
Cuando
una función Mata cambia el valor de un argumento dentro de la función, eso
cambia el valor de ese argumento fuera de la función; en otras palabras, los
argumentos se pasan por dirección. Las funciones de Mata pueden calcular más de
un resultado almacenando estos resultados en argumentos. Por ejemplo, sumproduct()
devuelve tanto la suma como el producto basado en elementos de dos matrices.
sumproduct()
retorna la suma de los argumento X
y Y en el argumento S y el producto por elementos en P.
Ejemplo 3: Devolviendo resultados en
argumentos
Después
de definir sumproduct(), uso I() para crear A y uso rowshape()
para crear B. Luego creo W y Z; cada uno es un valor
escalar perdido. Debo crear W y Z antes de pasarlos como
argumentos; de lo contrario, estaría haciendo referencia a argumentos que no
existen. Después de llamar a sumproduct(), muestro W y Z
para ilustrar que ahora contienen la suma y el producto de X y Y
por elementos.
En
myadd() y sumproduct(), no especifiqué qué tipo de cosa debe ser
cada argumento, ni especifiqué qué tipo de cosa devolvería cada función. En
otras palabras, utilicé declaraciones implícitas. Las declaraciones implícitas
son más fáciles de escribir que las declaraciones explícitas, pero hacen que
los mensajes de error y el código sean menos informativos. Recomiendo declarar
explícitamente devoluciones, argumentos y variables locales para que su código
y sus mensajes de error sean más legibles.
myadd2() es una versión de myadd() que
declara explícitamente el tipo de cosa devuelta, el tipo de cosa que debe ser
cada argumento y el tipo que debe ser cada cosa local a la función.
myadd2() devuelve una matriz numérica
que se construye agregando la matriz numérica X a la matriz
numérica Y. El objeto local A de la función es también una matriz
numérica. Una matriz numérica es una matriz real o una matriz
compleja; no puede ser una matriz de cadena.
La
declaración explícita de devoluciones, argumentos y variables locales hace que
el código sea más informativo. Inmediatamente veo que myadd2() no
funciona con matrices de cadenas, pero esta propiedad está oculta en el código
de myadd().
No
puedo enfatizar lo suficiente la importancia de escribir código fácil de leer.
Leer el código de otras personas es una parte esencial de la programación. Es
instructivo y le permite adoptar las soluciones que otros han encontrado o
implementado. Si eres nuevo en la programación, es posible que aún no te des
cuenta de que después de unos meses, leer tu propio código es como leer el
código de otra persona. Incluso si nunca le da su código a nadie más, es
esencial que escriba un código fácil de leer para poder leerlo en una fecha
posterior.
Las
declaraciones explícitas también hacen que algunos mensajes de error sean más
fáciles de rastrear. En los ejemplos 4 y 5, paso una matriz de cadena a myadd()
y myadd2(), respectivamente.
Ejemplo 4: Pasando una matriz de
cadena a myadd()
Ejemplo 5: Pasando una matriz de
cadena a myadd2()
El
mensaje de error en el ejemplo 4 indica que en algún lugar de myadd(),
un operador o una función no pudieron realizar algo en dos objetos porque sus
tipos no eran compatibles. No se deje engañar por la simplicidad de myadd().
Rastrear una falta de coincidencia de tipos en el código real puede ser
difícil.
Por
el contrario, el mensaje de error en el ejemplo 5 dice que la matriz C
que pasamos a myadd2() no es una matriz real ni compleja como requiere
el argumento de myadd2(). Al mirar el código y el mensaje de error,
inmediatamente me informa que el problema es que pasé una matriz de cadena a
una función que requiere una matriz numérica.
Las
declaraciones explícitas son tan recomendables que Mata tiene una configuración
que lo requiere, como se ilustra a continuación.
Ejemplo 6: Encendiendo matastrict
Establecer
matastrict en on hace que el compilador Mata requiera que las
variables de retorno y locales se declaren explícitamente. Por defecto, matastrict
está desactivado, en cuyo caso las variables locales y de retorno pueden
declararse implícitamente.
Cuando
matastrict está activado, no es necesario que los argumentos se declaren
explícitamente porque algunos argumentos contienen resultados cuyos tipos de
entrada y salida pueden diferir. Considere makereal() definido y usado
en el ejemplo 7.
Ejemplo 7: Cambiar un tipo de argumentos
La
declaración de makereal() especifica que makereal() no devuelve
nada porque void viene antes del nombre de la función. A pesar de que matastrict
está activado, no declaré qué tipo de cosa debe ser A. Las dos líneas
ejecutables de makereal() aclaran que A debe ser una cadena como
elemento de entrada y que A será real como elemento de salida, lo que
luego ilustraré.
Utilizo la función de que los argumentos pueden declararse implícitamente para que mi código sea más fácil de leer. Muchas de las funciones de Mata que escribo reemplazan los argumentos con resultados. Declaro explícitamente argumentos que son entradas, y declaro implícitamente argumentos que contienen salidas. Considere sumproduct2().
sumproduct2() no devuelve nada porque void
viene antes del nombre de la función. El primer argumento X es una matriz
real, el segundo argumento Y es una matriz real, el tercer
argumento S se declara implícitamente y el cuarto argumento P se
declara implícitamente. Mi convención de codificación de que las entradas se
declaran explícitamente y que las salidas se declaran implícitamente de
inmediato me informa que X y Y son entradas pero que S y P
son salidas. Que X y Y son entradas y que S y P son
salidas se ilustra en el ejemplo 8.
Ejemplo 8: Declarando explícitamente
entradas, pero no salidas
Hecho y sin hacer
Mostré cómo escribir una función en Mata y discutí
las declaraciones con cierto detalle. Escriba help m2_declarations para
muchos más detalles.
En mi próxima publicación usaré las funciones de Mata
para realizar los cálculos para un comando de estimación simple.
Drukker, David. (22 de diciembre
de 2015). Programming
an estimation command in Stata: Mata functions. (Trad. Ángel Cruz). The Stata
Blog. Not Elsewhere Classified. Recuperado de https://blog.stata.com/2015/12/22/programming-an-estimation-command-in-stata-mata-functions/