0% encontró este documento útil (0 votos)
2 vistas223 páginas

Introduccion A R y Python

El documento es una introducción al entorno de programación R, diseñado para análisis de datos y gráficos. Incluye secciones sobre el uso básico de R, cálculos, objetos, y funciones, así como la lectura de datos y la creación de gráficos. También se abordan temas como la estadística y la programación condicional en R.

Cargado por

ayelen.szeliga
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
2 vistas223 páginas

Introduccion A R y Python

El documento es una introducción al entorno de programación R, diseñado para análisis de datos y gráficos. Incluye secciones sobre el uso básico de R, cálculos, objetos, y funciones, así como la lectura de datos y la creación de gráficos. También se abordan temas como la estadística y la programación condicional en R.

Cargado por

ayelen.szeliga
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Introducción a R

Notas sobre R: Un entorno de programación para Análisis de Datos y Gráficos


Versión 1.0.1 (2000-05-16)

R Development Core Team


Copyright c 1990, 1992 W. Venables
Copyright c 1997, R. Gentleman & R. Ihaka
Copyright c 1997, 1998 M. Mächler
Copyright c 2000, Andrés González y Silvia González
Copyright c 1999, 2000 R Development Core Team
Se autoriza la realización y distribución de copias literales de este manual, siempre y cuando
las advertencias del copyright y de este permiso se conserven en todas las copias.
Se autoriza la realizaci^^f3n y distribución de copias modificadas de este manual, en las mis-
mas condiciones de las copias literales, siempre y cuando la totalidad del trabajo resultante
se distribuya bajo los términos de una advertencia de permiso id^^e9ntica a esta.
Se autoriza la realizaci^^f3n y distribución de traducciones de este manual a otros idiomas,
en las mismas condiciones de las copias modificadas, siempre y cuando la traducción de la
advertencia de este permiso sea aprobada por el Equipo Central de Desarrollo de R.
i

Índice General

Prólogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1 Introducción y Preliminares . . . . . . . . . . . . . . . . 2
1.1 El entorno R . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Programas relacionados. Documentación . . . . . . . . . . . . . . . . . . 2
1.3 Estadı́stica con R . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4 R en un sistema de ventanas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.5 Utilización interactiva de R . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.6 Una sesión inicial. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.7 Ayuda sobre funciones y capacidades . . . . . . . . . . . . . . . . . . . . . 4
1.8 Órdenes de R. Mayúsculas y minúsculas. . . . . . . . . . . . . . . . . . . 5
1.9 Recuperación y corrección de órdenes previas . . . . . . . . . . . . . . 5
1.10 Ejecución de órdenes desde un archivo y redirección de la
salida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.11 Almacenamiento y eliminación de objetos . . . . . . . . . . . . . . . . 6

2 Cálculos sencillos. Números y vectores . . . . . . 7


2.1 Vectores (numéricos). Asignación . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Aritmética vectorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3 Generación de sucesiones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.4 Vectores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5 Valores faltantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.6 Vectores de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.7 Vectores de ı́ndices. Selección y modificación de subvectores
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.8 Clases de objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3 Objetos: Modos y atributos . . . . . . . . . . . . . . . 14


3.1 Atributos intrı́nsecos: modo y longitud . . . . . . . . . . . . . . . . . . . 14
3.2 Modificación de la longitud de un objeto . . . . . . . . . . . . . . . . . 15
3.3 Obtención y modificación de atributos . . . . . . . . . . . . . . . . . . . 15
3.4 Clases de objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

4 Factores Nominales y Ordinales. . . . . . . . . . . . 17


4.1 Un ejemplo especı́fico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.2 La función tapply(). Variables desastradas (ragged arrays)
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.3 Factores ordinales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
ii

5 Variables indexadas. Matrices . . . . . . . . . . . . . 20


5.1 Variables indexadas (Arrays) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.2 Elementos de una variable indexada . . . . . . . . . . . . . . . . . . . . . 20
5.3 Uso de variables indexadas como ı́ndices . . . . . . . . . . . . . . . . . 21
5.4 La función array() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.4.1 Operaciones con variables indexadas y vectores.
Reciclado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.5 Producto exterior de dos variables indexadas . . . . . . . . . . . . . 23
Ejemplo: Distribución del determinante de una matriz de
dı́gitos de 2 × 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.6 Traspuesta generalizada de una variable indexada . . . . . . . . . 24
5.7 Operaciones con matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5.7.1 Producto matricial. Inversa de una matriz.
Resolución de sistemas lineales . . . . . . . . . . . . . . . . . . . . 24
5.7.2 Autovalores y autovectores . . . . . . . . . . . . . . . . . . . . . 25
5.7.3 Descomposición en valores singulares. Determinantes
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.7.4 Ajuste por mı́nimos cuadrados. Descomposición QR
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.8 Submatrices. Funciones cbind() y rbind(). . . . . . . . . . . . . . 26
5.9 La función de concatenación, c(), con variables indexadas
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.10 Tablas de frecuencias a partir de factores. . . . . . . . . . . . . . . . 27

6 Listas y hojas de datos . . . . . . . . . . . . . . . . . . . . 28


6.1 Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
6.2 Construcción y modificación de listas . . . . . . . . . . . . . . . . . . . . 29
6.2.1 Concatenación de listas . . . . . . . . . . . . . . . . . . . . . . . . 29
6.3 Hojas de datos (Data frames) . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
6.3.1 Construcción de hojas de datos . . . . . . . . . . . . . . . . . 29
6.3.2 Funciones attach() y detach() . . . . . . . . . . . . . . . . 30
6.3.3 Trabajo con hojas de datos . . . . . . . . . . . . . . . . . . . . . 31
6.3.4 Conexión de listas arbitrarias . . . . . . . . . . . . . . . . . . . 31
6.3.5 Gestión de la trayectoria de búsqueda . . . . . . . . . . . 31

7 Lectura de datos de un archivo . . . . . . . . . . . . 33


7.1 La función [Link]() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
7.2 La función scan() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
7.3 Acceso a datos internos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
7.3.1 Acceso a datos de una biblioteca . . . . . . . . . . . . . . . . 35
7.4 Edición de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
7.5 Cómo importar datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

8 Distribuciones probabilı́sticas . . . . . . . . . . . . . . 37
8.1 Tablas estadı́sticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
8.2 Estudio de la distribución de unos datos . . . . . . . . . . . . . . . . . 38
8.3 Contrastes de una y de dos muestras . . . . . . . . . . . . . . . . . . . . . 41
iii

9 Ciclos. Ejecución condicional . . . . . . . . . . . . . . 44


9.1 Expresiones agrupadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
9.2 Órdenes de control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
9.2.1 Ejecución condicional: la orden if . . . . . . . . . . . . . . 44
9.2.2 Ciclos: Órdenes for, repeat y while . . . . . . . . . . . 44

10 Escritura de nuevas funciones . . . . . . . . . . . . 46


10.1 Ejemplos elementales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
10.2 Cómo definir un operador binario. . . . . . . . . . . . . . . . . . . . . . . 47
10.3 Argumentos con nombre. Valores predeterminados . . . . . . . 47
10.4 El argumento ‘...’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
10.5 Asignaciones dentro de una función . . . . . . . . . . . . . . . . . . . . . 48
10.6 Ejemplos más complejos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
10.6.1 Factores de eficiencia en diseño en bloques . . . . . . 49
10.6.2 Cómo eliminar los nombres al imprimir una variable
indexada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
10.6.3 Integración numérica recursiva . . . . . . . . . . . . . . . . . 50
10.7 Ámbito . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
10.8 Personalización del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
10.9 Clases. Funciones genéricas. Orientación a objetos . . . . . . . 54

11 Modelos estadı́sticos en R . . . . . . . . . . . . . . . . 55
11.1 Definición de modelos estadı́sticos. Fórmulas . . . . . . . . . . . . 55
11.1.1 Contrastes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
11.2 Modelos lineales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
11.3 Funciones genéricas de extracción de información del modelo
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
11.4 Análisis de varianza. Comparación de modelos . . . . . . . . . . 60
11.4.1 Tablas ANOVA. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
11.5 Actualización de modelos ajustados . . . . . . . . . . . . . . . . . . . . . 61
11.6 Modelos lineales generalizados . . . . . . . . . . . . . . . . . . . . . . . . . . 61
11.6.1 Familias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
11.6.2 La función glm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
11.7 Modelos de Mı́nimos cuadrados no lineales y de Máxima
verosimilitud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
11.7.1 Mı́nimos cuadrados . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
11.7.2 Máxima verosimilitud . . . . . . . . . . . . . . . . . . . . . . . . . 67
11.8 Algunos modelos no-estándar . . . . . . . . . . . . . . . . . . . . . . . . . . 67
iv

12 Procedimientos gráficos . . . . . . . . . . . . . . . . . . 69
12.1 Funciones gráficas de nivel alto . . . . . . . . . . . . . . . . . . . . . . . . . 69
12.1.1 La función plot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
12.1.2 Representación de datos multivariantes . . . . . . . . . 70
12.1.3 Otras representaciones gráficas . . . . . . . . . . . . . . . . 70
12.1.4 Argumentos de las funciones gráficas de nivel alto
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
12.2 Funciones gráficas de nivel bajo . . . . . . . . . . . . . . . . . . . . . . . . 72
12.2.1 Anotaciones matemáticas . . . . . . . . . . . . . . . . . . . . . 74
12.2.2 Fuentes vectoriales Hershey. . . . . . . . . . . . . . . . . . . . 74
12.3 Funciones gráficas interactivas . . . . . . . . . . . . . . . . . . . . . . . . . . 74
12.4 Uso de parámetros gráficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
12.4.1 Cambios permanentes. La función par() . . . . . . . 76
12.4.2 Cambios temporales. Argumentos de las funciones
gráficas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
12.5 Parámetros gráficos habituales . . . . . . . . . . . . . . . . . . . . . . . . . 76
12.5.1 Elementos gráficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
12.5.2 Ejes y marcas de división. . . . . . . . . . . . . . . . . . . . . . 78
12.5.3 Márgenes de las figuras . . . . . . . . . . . . . . . . . . . . . . . 78
12.5.4 Figuras múltiples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
12.6 Dispositivos gráficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
12.6.1 Inclusión de gráficos PostScript en documentos . . 81
12.6.2 Dispositivos gráficos múltiples . . . . . . . . . . . . . . . . . 82
12.7 Gráficos dinámicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

Apendice A Primera sesión con R . . . . . . . . . . . 84

Apendice B Ejecución de R . . . . . . . . . . . . . . . . . 88
B.1 Ejecución de R en UNIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
B.2 Ejecución de R en Microsoft Windows . . . . . . . . . . . . . . . . . . . 91

Apendice C El editor de órdenes . . . . . . . . . . . . 93


C.1 Preliminares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
C.2 Edición de acciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
C.3 Resumen del editor de lı́neas de órdenes . . . . . . . . . . . . . . . . . 93

Apendice D Índice de funciones y variables. . . 95

Apendice E Índice de conceptos . . . . . . . . . . . . . 98

Apendice F Referencias . . . . . . . . . . . . . . . . . . . . 100


Prólogo 1

Prólogo
Estas notas sobre R están escritas a partir de un conjunto de notas que describı́an los
entornos S y S-Plus escritas por Bill Venables y Dave Smith. Hemos realizado un pequeño
número de cambios para reflejar las diferencias entre R y S.
R es un proyecto vivo y sus capacidades no coinciden totalmente con las de S. En
estas notas hemos adoptado la convención de que cualquier caracterı́stica que se vaya a
implementar se especifica como tal en el comienzo de la sección en que la caracterı́stica es
descrita. Los usuarios pueden contribuir al proyecto implementando cualquiera de ellas.
Deseamos dar las gracias más efusivas a Bill Venables por permitir la distribución de
esta versión modificada de las notas y por ser un defensor de R desde su inicio.
Cualquier comentario o corrección serán siempre bienvenidos. Dirija cualquier correspon-
dencia a R-core@[Link].

Sugerencias al lector
La primera relación con R deberı́a ser la sesión inicial del Apendice A [Ejemplo de sesion],
página 84. Está escrita para que se pueda conseguir cierta familiaridad con el estilo de las
sesiones de R y para comprobar que coincide con la versión actual.
Muchos usuarios eligen R fundamentalmente por sus capacidades gráficas. Si ese es su
caso, deberı́a leer antes o después el Capı́tulo 12 [Graficos], página 69, sobre capacidades
gráficas y para ello no es necesario esperar a haber asimilado totalmente las secciones
precedentes.
Capı́tulo 1: Introducción y Preliminares 2

1 Introducción y Preliminares

1.1 El entorno R
R es un conjunto integrado de programas para manipulación de datos, cálculo y gráficos.
Entre otras caracterı́sticas dispone de:
• almacenamiento y manipulación efectiva de datos,
• operadores para cálculo sobre variables indexadas (Arrays), en particular matrices,
• una amplia, coherente e integrada colección de herramientas para análisis de datos,
• posibilidades gráficas para análisis de datos, que funcionan directamente sobre pantalla
o impresora, y
• un lenguaje de programación bien desarrollado, simple y efectivo, que incluye
condicionales, ciclos, funciones recursivas y posibilidad de entradas y salidas. (Debe
destacarse que muchas de las funciones suministradas con el sistema están escritas en
el lenguaje R)
El término “entorno” lo caracteriza como un sistema completamente diseñado y co-
herente, antes que como una agregación incremental de herramientas muy especı́ficas e
inflexibles, como ocurre frecuentemente con otros programas de análisis de datos.
R es en gran parte un vehı́culo para el desarrollo de nuevos métodos de análisis interactivo
de datos. Como tal es muy dinámico y las diferentes versiones no siempre son totalmente
compatibles con las anteriores. Algunos usuarios prefieren los cambios debido a los nuevos
métodos y tecnologı́a que los acompañan, a otros sin embargo les molesta ya que algún código
anterior deja de funcionar. Aunque R puede entenderse como un lenguaje de programación,
los programas escritos en R deben considerarse esencialmente efı́meros.

1.2 Programas relacionados. Documentación


R puede definirse como una nueva implementación del lenguaje S desarrollado en AT&T
por Rick Becker, John Chambers y Allan Wilks. Muchos de los libros y manuales sobre S
son útiles para R.
La referencia básica es The New S Language: A Programming Environment for Data
Analysis and Graphics de Richard A. Becker, John M. Chambers and Allan R. Wilks. Las
caracterı́sticas de la versión de agosto de 1991 de S están recogidas en Statistical Models
in S editado por John M. Chambers y Trevor J. Hastie. Véase Apendice F [Referencias],
página 100, para referencias concretas.

1.3 Estadı́stica con R


En la introducción a R no se ha mencionado la palabra estadı́stica, sin embargo muchas
personas utilizan R como un sistema estadı́stico. Nosotros preferimos describirlo como un
entorno en el que se han implementado muchas técnicas estadı́sticas, tanto clásicas como
modernas. Algunas están incluidas en el entorno base de R y otras se acompañan en
forma de bibliotecas (packages). El hecho de distinguir entre ambos conceptos es funda-
mentalmente una cuestión histórica. Junto con R se incluyen ocho bibliotecas (llamadas
Capı́tulo 1: Introducción y Preliminares 3

bibliotecas estándar) pero otras muchas están disponibles a través de Internet en CRAN
([Link]
Como hemos indicado, muchas técnicas estadı́sticas, desde las clásicas hasta la última
metodologı́a, están disponibles en R, pero los usuarios necesitarán estar dispuestos a traba-
jar un poco para poder encontrarlas.
Existe una diferencia fundamental en la filosofı́a que subyace en R (o S) y la de otros
sistemas estadı́sticos. En R, un análisis estadı́stico se realiza en una serie de pasos, con
unos resultados intermedios que se van almacenando en objetos, para ser observados o
analizados posteriormente, produciendo unas salidas mı́nimas. Sin embargo en SAS o SPSS
se obtendrı́a de modo inmediato una salida copiosa para cualquier análisis, por ejemplo,
una regresión o un análisis discriminante.

1.4 R en un sistema de ventanas


La forma más conveniente de usar R es en una estación de trabajo con un sistema de
ventanas. Estas notas están escritas pensando en usuarios de estas caracterı́sticas. En
particular nos referiremos ocasionalmente a la utilización de R en un sistema X-window,
aunque normalmente se pueden aplicar a cualquier implementación del entorno R.
Muchos usuarios encontrarán necesario interactuar directamente con el sistema opera-
tivo de su ordenador de vez en cuando. En estas notas se trata fundamentalmente de la
interacción con el sistema operativo UNIX. Si utiliza R bajo Microsoft Windows necesitará
realizar algunos pequeños cambios.
El ajuste del programa para obtener el máximo rendimiento de las cualidades parame-
trizables de R es una tarea interesante aunque tediosa y no se considerará en estas notas.
Si tiene dificultades busque a un experto cercano a usted.

1.5 Utilización interactiva de R


Cuando R espera la entrada de órdenes, presenta un sı́mbolo para indicarlo. El sı́mbolo
predeterminado es ‘>’, que en UNIX puede coincidir con el sı́mbolo del sistema, por lo que
puede parecer que no sucede nada. Si ese es su caso, sepa que es posible modificar este
sı́mbolo en R. En estas notas supondremos que el sı́mbolo de UNIX es ‘$’.
Para utilizar R bajo UNIX por primera vez, el procedimiento recomendado es el siguiente:
1. Cree un subdirectorio, por ejemplo ‘trabajo’, en el que almacenar los archivos de datos
que desee analizar mediante R. Éste será el directorio de trabajo cada vez que utilice
R para este problema concreto.
$ mkdir trabajo
$ cd trabajo
2. Inicie R con la orden
$ R
3. Ahora puede escribir órdenes para R (como se hace más adelante).
4. Para salir de R la orden es
> q()
Capı́tulo 1: Introducción y Preliminares 4

R preguntará si desea salvar los datos de esta sesión de trabajo. Puede responder yes
(Si), no (No) o cancel (cancelar) pulsando respectivamente las letras y, n o c, en cada
uno de cuyos casos, respectivamente, salvará los datos antes de terminar, terminará
sin salvar, o volverá a la sesión de R. Los datos que se salvan estarán disponibles en la
siguiente sesión de R.
Volver a trabajar con R es sencillo:
1. Haga que ‘trabajo’ sea su directorio de trabajo e inicie el programa como antes:
$ cd trabajo
$ R
2. Dé las órdenes que estime convenientes a R y termine la sesión con la orden q().
Bajo Microsoft Windows el procedimiento a seguir es básicamente el mismo: Cree una
carpeta o directorio. Ejecute R haciendo doble click en el icono correspondiente. Seleccione
New dentro del menú File para indicar que desea iniciar un nuevo problema (lo que eliminará
todos los objetos definidos dentro del espacio de trabajo) y a continuación seleccione Save
dentro del menú File para salvar esta imagen en el directorio que acaba de crear. Puede
comenzar ahora los análisis y cuando salga de R, éste le preguntará si desea salvar la imagen
en el directorio de trabajo.
Para continuar con este análisis posteriormente basta con pulsar en el icono de la imagen
salvada, o bien puede ejecutar R y utilizar la opción Open dentro del menú File para
seleccionar y abrir la imagen salvada.

1.6 Una sesión inicial


Se recomienda a los lectores que deseen ver cómo funciona R que realicen la sesión inicial
dada en el Apendice A [Ejemplo de sesion], página 84.

1.7 Ayuda sobre funciones y capacidades


R contiene una ayuda similar a la orden man de UNIX. Para obtener información sobre
una función concreta, por ejemplo solve, la orden es
> help(solve)
Una forma alternativa es
> ?solve
Con las funciones especificadas por caracteres especiales, el argumento deberá ir entre
comillas, para transformarlo en una ”cadena de caracteres”:
> help("[[")
Podrá utilizar tanto comillas simples como dobles, y cada una de ellas puede utilizarse
dentro de la otra, como en la frase "Dijo ’Hola y adiós’ y se marchó". En estas notas
utilizaremos dobles comillas.
En muchas versiones de R puede acceder a la ayuda escrita en formato html, escribiendo
> [Link]()
que ejecuta un lector Web (Netscape en UNIX) para leer estas páginas como hipertextos.
En UNIX, las peticiones de ayuda posteriores se envirán al sistema de ayuda basado en
html.
Capı́tulo 1: Introducción y Preliminares 5

Las versiones de Microsoft Windows de R poseen otros sistemas de ayuda opcionales.


Utilice
> ?help
para obtener detalles adicionales.

1.8 Órdenes de R. Mayúsculas y minúsculas


Técnicamente hablando, R es un lenguaje de expresiones con una sintaxis muy simple.
Consecuente con sus orı́genes en UNIX, distingue entre mayúsculas y minúsculas, de tal
modo que A y a son sı́mbolos distintos y se referirán, por tanto, a objetos distintos.
Las órdenes elementales consisten en expresiones o en asignaciones. Si una orden consiste
en una expresión, se evalúa, se imprime y su valor se pierde. Una asignación, por el contrario,
evalúa una expresión, no la imprime y guarda su valor en una variable.
Las órdenes se separan mediante punto y coma, (‘;’), o mediante un cambio de lı́nea.
Si al terminar la lı́nea, la orden no está sintácticamente completa, R mostrará un signo de
continuación, por ejemplo
+
en la lı́nea siguiente y las sucesivas y continuará leyendo hasta que la orden esté
sintácticamente completa. El signo de continuación puede ser modificado fácilmente. En
estas notas omitiremos generalmente el sı́mbolo de continuación y lo indicaremos mediante
un sangrado.

1.9 Recuperación y corrección de órdenes previas


Bajo muchas versiones de UNIX, R permite recuperar y ejecutar órdenes previas. Las
flechas verticales del teclado puede utilizarse para recorrer el historial de órdenes. Cuando
haya recuperado una orden con este procedimiento, puede utilizar las flechas horizontales
para desplazarse por ella, puede eliminar caracteres con la tecla hDELi, o añadir nuevos ca-
racteres. Más adelante se darán mayores detalles, véase Apendice C [El editor de ordenes],
página 93.
La recuperación y edición en UNIX pueden ser fácilmente adaptadas. Para ello debe
buscar en el manual de UNIX la información sobre readline.
También puede utilizar el editor de textos emacs para trabajar más cómodamente de
modo interactivo con R, mediante ESS.1

1.10 Ejecución de órdenes desde un archivo y redirección de


la salida
Si tiene órdenes almacenadas en un archivo del sistema operativo, por ejemplo
órdenes.R, en el directorio de trabajo, trabajo, puede ejecutarlas dentro de una sesión
de R con la orden
> source("órdenes.R")
En la versión de Microsoft Windows, Source también está disponible dentro del menú
File. Por otra parte, la orden sink, como por ejemplo en
1
Acrónimo en inglés de Emacs Speaks Statistics.
Capı́tulo 1: Introducción y Preliminares 6

> sink("[Link]")
enviará el resto de la salida, en vez de a la pantalla, al archivo del sistema operativo,
[Link], dentro del directorio de trabajo. La orden
> sink()
devuelve la salida de nuevo a la pantalla.
Si utiliza nombres absolutos de archivo en vez de nombres relativos, los resultados se
almacenaán en ellos, independientemente del directorio de trabajo.

1.11 Almacenamiento y eliminación de objetos


Las entidades que R crea y manipula se denominan objetos. Estos pueden ser de mu-
chos tipos: Variables, Variables indexadas, Cadenas de caracteres, Funciones, etc. Incluso
estructuras más complejas construidas a partir de otras más sencillas.
Durante una sesión de trabajo con R los objetos que se crean se almacenan por nombre
(Discutiremos este proceso en la siguiente sección). La orden
> objects()
se puede utilizar para obtener los nombres de los objetos almacenados en R. Esta función
es equivalente a la función ls(). La colección de objetos almacenados en cada momento se
denomina espacio de trabajo (workspace).
Para eliminar objetos puede utilizar la orden rm, por ejemplo:
> rm(x, y, z, tinta, chatarra, temporal, barra)
Los objetos creados durante una sesión de R pueden almacenarse en un archivo para su
uso posterior. Al finalizar la sesión, R pregunta si desea hacerlo. En caso afirmativo todos
los objetos se almacenan en el archivo ‘.RData’2 en el directorio de trabajo.
En la siguiente ocasión que ejecute R, se recuperarán los objetos de este archivo ası́ como
el historial de órdenes.
Es recomendable que utilice un directorio de trabajo diferente para cada problema que
analice con R. Es muy común crear objetos con los nombres x e y, por ejemplo. Estos
nombres tienen sentido dentro de un análisis concreto, pero es muy difı́cil dilucidar su
significado cuando se han realizado varios análisis en el mismo directorio.

2
El punto inicial del nombre de este archivo indica que es invisible en UNIX.
Capı́tulo 2: Cálculos sencillos. Números y vectores 7

2 Cálculos sencillos. Números y vectores

2.1 Vectores (numéricos). Asignación


R utiliza diferentes estructuras de datos. La estructura más simple es el vector, que es
una colección ordenada de números. Para crear un vector, por ejemplo x, consistente en
cinco números, por ejemplo 10.4, 5.6, 3.1, 6.4 y 21.7, use la orden
> x <- c(10.4, 5.6, 3.1, 6.4, 21.7)
Esta es una asignación en la que se utiliza la función c() que, en este contexto, puede
tener un número arbitrario de vectores como argumento y cuyo valor es el vector obtenido
mediante la concatenación de todos ellos.1
Un número, por sı́ mismo, se considera un vector de longitud uno.
Advierta que el operador de asignación, (‘<-’), no es el operador habitual, ‘=’, que se
reserva para otro propósito, sino que consiste en dos caracteres, ‘<’ (‘menor que’) y ‘-’
(‘guión’), que obligatoriamente deben ir unidos y ’apuntan’ hacia el objeto que recibe el
valor de la expresión.2
La asignación puede realizarse también mediante la función assign(). Una forma equi-
valente de realizar la asignación anterior es
> assign("x", c(10.4, 5.6, 3.1, 6.4, 21.7))
El operador usual, <-, puede interpretarse como una abreviatura de la función assign().
Las asignaciones pueden realizarse también con una flecha apuntando a la derecha, rea-
lizando el cambio obvio en la asignación. Por tanto, también podrı́a escribirse
> c(10.4, 5.6, 3.1, 6.4, 21.7) -> x
Si una expresión se utiliza como una orden por sı́ misma, su valor se imprime y se pierde 3 .
Ası́ pues, la orden
> 1/x
simplemente imprime los inversos de los cinco valores anteriores en la pantalla (por supuesto,
el valor de x no se modifica).
Si a continuación hace la asignación
> y <- c(x, 0, x)
creará un vector, y, con 11 elementos, consistentes en dos copias de x con un cero entre
ambas.

1
Con argumentos diferentes, por ejemplo listas, la acción de c() puede ser diferente. Vea Sección 6.2.1
[Concatenacion de listas], página 29.
2
El sı́mbolo de subrayado, ‘_’, es un sinónimo del operador de asignación, pero no aconsejamos su uti-
lización ya que produce un código menos legible.
3
Aunque, de hecho, se almacena en .[Link] hasta que se ejecute otra orden.
Capı́tulo 2: Cálculos sencillos. Números y vectores 8

2.2 Aritmética vectorial


Los vectores pueden usarse en expresiones aritméticas, en cuyo caso las operaciones
se realizan elemento a elemento. Dos vectores que se utilizan en la misma expresión no
tienen por qué ser de la misma longitud. Si no lo son, el resultado será un vector de la
longitud del más largo, y el más corto será reciclado, repitiéndolo tantas veces como sea
necesario (puede que no un número exacto de veces) hasta que coincida con el más largo.
En particular, cualquier constante será simplemente repetida. De este modo, y siendo x e
y los vectores antes definidos, la orden
> v <- 2*x + y + 1
genera un nuevo vector, v, de longitud 11, construido sumando, elemento a elemento, el
vector 2*x repetido 2.2 veces, el vector y, y el número 1 repetido 11 veces.
Los operadores aritméticos elementales son los habituales +, -, *, / y ^ para elevar
a una potencia. Además están disponibles las funciones log, exp, sin, cos, tan, sqrt,
bien conocidas. Existen muchas más funciones, entre otras, las siguientes: max y min que
seleccionan respectivamente el mayor y el menor elemento de un vector; range cuyo valor
es el vector de longitud dos, c(min(x), max(x)); length(x) que es el número de elementos
o longitud de x; sum(x) que es la suma de todos los elementos de x; y prod(x) que es el
producto de todos ellos.
Dos funciones estadı́sticas son mean(x), que calcula la media, esto es,
sum(x)/length(x)
y var(x) que calcula la cuasi-varianza, esto es,
sum((x-mean(x))^2)/(length(x)-1)
Si el argumento de var() es una matriz n × p, el resultado es la matriz de cuasi-
covarianzas p×p correspondiente a interpretar las filas como vectores muestrales p-variantes.
Para ordenar un vector dispone de la función sort(x) que devuelve un vector del mismo
tamaño que x con los elementos ordenados en orden creciente. También dispone de order()
y de [Link](), que produce la permutación del vector que corresponde a la ordenación.

Advierta que max y min seleccionan el mayor y el menor valor de sus argumentos, incluso
aunque estos sean varios vectores. Las funciones paralelas pmax y pmin devuelven un vector
(de la misma longitud del argumento más largo) que contiene en cada elemento el mayor y
menor elemento de dicha posición de entre todos los vectores de entrada.
En la mayorı́a de los casos, el usuario no debe preocuparse de si los “números” de un vec-
tor numérico son enteros, reales o incluso complejos. Los cálculos se realizan internamente
como números de doble precisión, reales o complejos según el caso.
Para trabajar con números complejos, debe indicar explı́citamente la parte compleja.
Ası́
sqrt(-17)
devuelve el resultado NaN y un mensaje de advertencia, pero
sqrt(-17+0i)
realiza correctamente el cálculo de la raı́z cuadrada de este número complejo.
Capı́tulo 2: Cálculos sencillos. Números y vectores 9

2.3 Generación de sucesiones


En R existen varias funciones para generar sucesiones numéricas. Por ejemplo, 1:30
es el vector c(1,2, ...,29,30). El operador ’dos puntos’ tiene máxima prioridad en una
expresión, ası́, por ejemplo, 2*1:15 es el vector c(2,4,6, ...,28,30). Escriba n <- 10 y
compare las sucesiones 1:n-1 y 1:(n-1).
La forma 30:1 permite construir una sucesión descendente.
La función seq() permite generar sucesiones más complejas. Dispone de cinco argu-
mentos, aunque no se utilizan todos simultáneamente. Si se dan los dos primeros indican el
comienzo y el final de la sucesión, y si son los únicos argumentos, el resultado coincide con
el operador ’dos puntos’, esto es, seq(2,10) coincide con 2:10.
Los argumentos de seq(), y de muchas funciones de R, pueden darse además de por
posición, por nombre, en cuyo caso, el orden en que aparecen es irrelevante. En esta
función los dos primeros argumentos se pueden dar por nombre mediante from=valor-inicial
y to=valor-final; por tanto seq(1,30), seq(from=1, to=30) y seq(to=30, from=1) son
formas equivalentes a 1:30.
Los dos siguientes argumentos de seq() son by=valor y length=valor, y especifican el
’paso’ y ’longitud’ de la sucesión respectivamente. Si no se suministra ninguno, el valor
predeterminado es by=1 y length se calcula.
Por ejemplo
> seq(-5, 5, by=.2) -> s3
genera el vector c(-5.0, -4.8, -4.6, ..., 4.6, 4.8, 5.0) y lo almacena en s3 . Simi-
larmente
> s4 <- seq(length=51, from=-5, by=.2)
genera los mismos valores y los almacena en s4.
El quinto argumento de esta función es along=vector, y si se usa debe ser el único
argumento, ya que crea una sucesión 1, 2, ..., length(vector), o la sucesión vacı́a si el
vector es vacı́o (lo que puede ocurrir).
Una función relacionada con seq es rep(), que permite duplicar un objeto de formas
diversas. Su forma más sencilla es
> s5 <- rep(x, times=5)
que coloca cinco copias de x, una tras otra, y las almacena en s5.

2.4 Vectores lógicos


R no solo maneja vectores numéricos, sino también lógicos. Los elementos de un vector
lógico sólo pueden tomar dos valores: FALSE (falso) y TRUE (verdadero). Estos valores se
representan también por F y T.
Los vectores lógicos aparecen al utilizar condiciones. Por ejemplo,
> temp <- x > 13
almacena en temp un vector de la misma longitud de x y cuyos valores serán, respectiva-
mente, T o F de acuerdo a que los elementos de x cumplan o no la condición indicada: ser
mayores que 13.
Capı́tulo 2: Cálculos sencillos. Números y vectores 10

Los operadores lógicos son < (menor), <= (menor o igual), > (mayor), >= (mayor o igual),
== (igual), y != (distinto). Además, si c1 y c2 son expresiones lógicas, entonces c1&c2 es
su intersección (“conjunción”), c1|c2 es su unión (“disyunción”) y !c1 es la negación de
c1.
Los vectores lógicos pueden utilizarse en expresiones aritméticas, en cuyo caso se trans-
forman primero en vectores numéricos, de tal modo que F se transforma en 0 y T en 1. Sin
embargo hay casos en que un vector lógico y su correspondiente numérico no son equiva-
lentes, como puede ver a continuación.

2.5 Valores faltantes


En ocasiones puede que no todas las componentes de un vector sean conocidas. Cuando
falta un elemento, lo que se denomina ’valor faltante’4 , se le asigna un valor especial, NA5 .
En general, casi cualquier operación donde intervenga un valor NA da por resultado NA. La
justificación es sencilla: Si no se puede especificar completamente la operación, el resultado
no podrá ser conocido, y por tanto no estará disponible.
La función [Link](x) crea un vector lógico del tamaño de x cuyos elementos sólo valdrán
T si el elemento correspondiente de x es NA, y F en caso contrario.
> z <- c(1:3,NA); ind <- [Link](z)
Nótese que la expresión lógica x == NA es distinta de [Link](x) puesto que NA no es
realmente un valor, sino un indicador de una cantidad que no está disponible. Por tanto
x == NA es un vector de la misma longitud de x con todos sus elementos NA puesto que la
expresión lógica es incompleta.
Además hay una segunda clase de valores “faltantes”, producidos por el cálculo. Son los
llamados valores NaN 6 . Este tipo de dato no existe en S, y por tanto se confunden con NA
en S-Plus. Ejemplos de NaN son
> 0/0
o
> Inf - Inf
En resumen, [Link](xx) es TRUE tanto para los valores NA como para los NaN. Para
diferenciar estos últimos existe la función [Link](xx) que sólo toma el valor TRUE para
valores NaN.

2.6 Vectores de caracteres


Las cadenas de caracteres, o frases, también son utilizadas en R, por ejemplo, para
etiquetar gráficos. Una cadena de caracteres se construye escribiendo entre comillas la
sucesión de caracteres que la define, por ejemplo, "Altura" o "Resultados de la tercera
iteración".
Los vectores de caracteres pueden concatenarse en un vector mediante la función c().
4
En la literatura estadı́stica inglesa, “missing value”
5
Acrónimo en inglés de “Not Available”, no disponible.
6
Acrónimo en inglés de “Not a Number”, esto es, “No es un número”.
Capı́tulo 2: Cálculos sencillos. Números y vectores 11

Por otra parte, la función paste() une todos los vectores de caracteres que se le sumi-
nistran y construye una sola cadena de caracteres. También admite argumentos numéricos,
que convierte inmediatamente en cadenas de caracteres. En su forma predeterminada, en
la cadena final, cada argumento original se separa del siguiente por un espacio en blanco,
aunque ello puede cambiarse utilizando el argumento sep="cadena", que sustituye el espacio
en blanco por cadena, la cual podrı́a ser incluso vacı́a.
Por ejemplo,
> labs <- paste(c("X","Y"), 1:10, sep="")
almacena, en labs, el vector de caracteres
c("X1", "Y2", "X3", "Y4", "X5", "Y6", "X7", "Y8", "X9", "Y10")
Recuerde que al tener c("X", "Y") solo dos elementos, deberá repetirse 5 veces para
obtener la longitud del vector 1:10.7

2.7 Vectores de ı́ndices. Selección y modificación de


subvectores
Puede seleccionar un subvector de un vector añadiendo al nombre del mismo un vector
de ı́ndices entre corchetes, [ y ]. En general podrá obtener un subvector de cualquier
expresión cuyo resultado sea un vector, sin más que añadirle un vector de ı́ndices entre
corchetes.
Los vectores de ı́ndices pueden ser de cuatro tipos distintos:
1. Un vector lógico. En este caso el vector de ı́ndices debe tener la misma longitud que
el vector al que refiere. Sólo se seleccionarán los elementos correspondientes a valores
T del vector de ı́ndices y se omitirá el resto. Por ejemplo,
> y <- x[![Link](x)]
almacena en y los valores no-faltantes de x, en el mismo orden. Si x tiene valores
faltantes, el vector y será más corto que x. Análogamente,
> (x+1)[(![Link](x)) & x>0] -> z
almacena en z los elementos del vector x+1 para los que el correspondiente elemento
de x es no-faltante y positivo.
2. Un vector de números naturales positivos. En este caso los elementos del vector de
ı́ndices deben pertenecer al conjunto {1, 2, . . . , length(x)}. El resultado es un vector
formado por los elementos del vector referido que corresponden a estos ı́ndices y en
el orden en que aparecen en el vector de ı́ndices. El vector de ı́ndices puede tener
cualquier longitud y el resultado será de esa misma longitud. Por ejemplo, x[6] es el
sexto elemento de x, y
> x[1:10]
es el vector formado por los diez primeros elementos de x, (supuesto que length(x) no
es menor que 10). Por otra parte,

7
paste(..., collapse=ss) permite colapsar los argumentos en una sola cadena de caracteres separándolos
mediante ss. Además existen otras órdenes de manipulación de caracteres. como sub y substring. Puede
encontrar su descripción en la ayuda del programa.
Capı́tulo 2: Cálculos sencillos. Números y vectores 12

> c("x","y")[rep(c(1,2,2,1), times=4)]


crea un vector de caracteres de longitud 16 formado por "x", "y", "y", "x" repetido
cuatro veces.
3. Un vector de números naturales negativos. En este caso, los ı́ndices indican los ele-
mentos del vector referido que deben excluirse. Ası́ pues,
> y <- x[-(1:5)]
almacena en el vector y todos los elementos de x excepto los cinco primeros (suponiendo
que x tiene al menos cinco elementos).
4. Un vector de caracteres. Esta opción solo puede realizarse si el vector posee el atributo
names (nombres) para identificar sus componentes, en cuyo caso se comportará de modo
similar al punto 2.
> fruta <- c(5, 10, 1, 20)
> names(fruta) <- c("naranja", "plátano", "manzana", "pera")
> postre <- fruta[c("manzana","naranja")]
La ventaja en este caso es que los nombres son a menudo más fáciles de recordar que
los ı́ndices numé[Link] opción es especialmente útil al tratar de la estructura de
“hoja de datos” (data frame) que veremos posteriormente.
La variable de almacenamiento también puede ser indexada, en cuyo caso la asignación
se realiza solamente sobre los elementos referidos. La expresión debe ser de la forma
vector[vector de ı́ndices] ya que la utilización de una expresión arbitraria en vez del nom-
bre de un vector no tiene mucho sentido.
El vector asignado debe ser de la misma longitud que el vector de ı́ndices y, en el caso
de un vector de ı́ndices lógico, debe ser de la misma longitud del vector que indexa. Por
ejemplo,
> x[[Link](x)] <- 0
sustituye cada valor faltante de x por un cero. Por otra parte,
> y[y < 0] <- -y[y < 0]
equivale8 a
> y <- abs(y)

2.8 Clases de objetos


Los vectores son el tipo básico de objeto en R, pero existen más tipos que veremos de
modo formal posteriormente.
• Las matrices o, más generalmente, variables indexadas (Arrays) son generalizaciones
multidimensionales de los vectores. De hecho, son vectores indexados por dos o más
ı́ndices y que se imprimen de modo especial. Véase Capı́tulo 5 [Matrices y variables
indexadas], página 20.
• Los factores sirven para representar datos categóricos. Véase Capı́tulo 4 [Factores],
página 17.
8
Tenga en cuenta que abs() no se comporta correctamente con números complejos, en ellos deberı́a usar
Mod().
Capı́tulo 2: Cálculos sencillos. Números y vectores 13

• Las listas son una forma generalizada de vector en las cuales los elementos no tienen
por qué ser del mismo tipo y a menudo son a su vez vectores o listas. Las listas
permiten devolver los resultados de los cálculos estadı́sticos de un modo conveniente.
Véase Sección 6.1 [Listas], página 28.
• Las hojas de datos (data frames) son estructuras similares a una matriz, en que cada
columna puede ser de un tipo distinto a las otras. Las hojas de datos son apropiadas
para describir ‘matrices de datos’ donde cada fila representa a un individuo y cada
columna una variable, cuyas variables pueden ser numéricas o categóricas. Muchos
experimentos se describen muy apropiadamente con hojas de datos: los tratamientos
son categóricos pero la respuesta es numérica. Véase Sección 6.3 [Hojas de datos],
página 29.
• Las funciones son también objetos de R que pueden almacenarse en el espacio de
trabajo, lo que permite extender las capacidades de R fácilmente. Véase Capı́tulo 10
[Escritura de funciones], página 46.
Capı́tulo 3: Objetos: Modos y atributos 14

3 Objetos: Modos y atributos

3.1 Atributos intrı́nsecos: modo y longitud


Las entidades que manipula R se conocen con el nombre de objetos. Por ejemplo, los
vectores de números, reales o complejos, los vectores lógicos o los vectores de caracteres.
Este tipo de objetos se denominan estructuras ‘atómicas’ puesto que todos sus elementos son
del mismo tipo o modo, bien sea numeric 1 (numérico), complex (complejo), logical (lógico)
o character (carácter).
Los elementos de un vector deben ser todos del mismo modo y éste será el modo del
vector. Esto es, un vector será, en su totalidad, de modo logical, numeric, complex o
character. La única excepción a esta regla es que cualquiera de ellos puede contener el valor
NA. Debe tener en cuenta que un vector puede ser vacı́o, pero pese a ello tendrá un modo.
Ası́, el vector de caracteres vacı́o aparece como character(0) y el vector numérico vacı́o
aparece como numeric(0).
R también maneja objetos denominados listas que son del modo list (lista) y que con-
sisten en sucesiones de objetos, cada uno de los cuales puede ser de un modo distinto.
Las listas se denominan estructuras ‘recursivas’ puesto que sus componentes pueden ser
asimismo listas.
Existen otras estructuras recursivas que corresponden a los modos function (función) y
expression (expresión). El modo function está formado por las funciones que constituyen R,
unidas a las funciones escritas por cada usuario, y que discutiremos más adelante. El modo
expression corresponde a una parte avanzada de R que no trataremos aquı́, excepto en lo
mı́nimo necesario para el tratamiento de fórmulas en la descripción de modelos estadı́sticos.
Con el modo de un objeto designamos el tipo básico de sus constituyentes fundamentales.
Es un caso especial de un atributo de un objeto. Los atributos de un objeto suministran
información especı́fica sobre el propio objeto. Otro atributo de un objeto es su longitud.
Las funciones mode(objeto) y length(objeto) se pueden utilizar para obtener el modo y
longitud de cualquier estructura.
Por ejemplo, si z es un vector complejo de longitud 100, entonces mode(z) es la cadena
"complex", y length(z) es 100.
R realiza cambios de modo cada vez que se le indica o es necesario (y también en algunas
ocasiones en que no parece que no lo es). Por ejemplo, si escribe
> z <- 0:9
y a continuación escribe
> digitos <- [Link](z)
el vector digitos será el vector de caracteres ("0", "1", "2", ..., "9"). Si a continua-
ción aplica un nuevo cambio de modo
> d <- [Link](digitos)
R reconstruirá el vector numérico de nuevo y, en este caso, d y z coinciden.2 Existe una
colección completa de funciones de la forma [Link]-que-sea(), tanto para forzar el cambio de
1
El modo numérico consiste realmente en dos modos distintos, integer (entero) y double (doble precisión).
2
En general, al forzar el cambio de numérico a carácter y de nuevo a numérico, no se obtienen los mismos
resultados, debido, entre otros, a los errores de redondeo.
Capı́tulo 3: Objetos: Modos y atributos 15

modo, como para asignar un atributo a un objeto que carece de él. Es aconsejable consultar
la ayuda para familiarizarse con estas funciones.

3.2 Modificación de la longitud de un objeto


Un objeto, aunque esté “vacı́o”, tiene modo. Por ejemplo,
> v <- numeric()
almacena en v una estructura vacı́a de vector numérico. Del mismo modo, character() es
un vector de caracteres vacı́o, y lo mismo ocurre con otros tipos. Una vez creado un objeto
con un tamaño cualquiera, pueden añadirse nuevos elementos sin más que asignarlos a un
ı́ndice que esté fuera del rango [Link] ejemplo,
> v[3] <- 17
transforma v en un vector de longitud 3, (cuyas dos primeras componentes serán NA). Esta
regla se aplica a cualquier estructura, siempre que los nuevos elementos sean compatibles
con el modo inicial de la estructura.
Este ajuste automático de la longitud de un objeto se utiliza a menudo, por ejemplo en la
función scan() para entrada de datos. (Véase Sección 7.2 [La funcion scan()], página 34.)
Análogamente, puede reducirse la longitud de un objeto sin más que realizar una nueva
asignación. Si, por ejemplo, alfa es un objeto de longitud 10, entonces
> alfa <- alfa[2 * 1:5]
lo transforma en un objeto de longitud 5 formado por los elementos de posición par del
objeto inicial.

3.3 Obtención y modificación de atributos


La función attributes(objeto) proporciona una lista de todos los atributos no
intrı́nsecos definidos para el objeto en ese momento. La función attr(objeto, nombre)
puede usarse para seleccionar un atributo especı́fico. Estas funciones no se utilizan
habitualmente, sino que se reservan para la creación de un nuevo atributo con fines
especı́ficos, por ejemplo, para asociar una fecha de creación o un operador con un objeto
de R. Sin embargo, es un concepto muy importante que no debe olvidar.
La asignación o eliminación de atributos de un objeto debe realizarse con precaución, ya
que los atributos forman parte del sistema de objetos utilizados en R.
Cuando se utiliza en la parte que recibe la asignación, puede usarse para asociar un
nuevo atributo al objeto o para cambiar uno existente. Por ejemplo,
> attr(z,"dim") <- c(10,10)
permite tratar z como si fuese una matriz de 10 × 10.

3.4 Clases de objetos


Cada objeto pertenece a una clase, y ello permite utilizar en R programación dirigida a
objetos.
Por ejemplo, si un objeto pertenece a la clase "[Link]", se imprimirá de un modo
especial; cuando le aplique la función plot() ésta mostrará un gráfico de un tipo especial;
Capı́tulo 3: Objetos: Modos y atributos 16

y otras funciones genéricas, como summary(), producirán un resultado especial; todo ello
en función de la pertenencia a dicha clase.
Para eliminar temporalmente los efectos de la clase puede utilizar la función unclass().
Por ejemplo, si invierno pertenece a la clase "[Link]", entonces
> invierno
escribe el objeto en la forma de la clase, parecida a una matriz, en tanto que
> unclass(invierno)
lo imprime como una lista ordinaria. Sólo debe utilizar esta función en situaciones muy
concretas, como, por ejemplo, si hace pruebas para comprender el concepto de clase y de
función genérica.
Las clases y las funciones genéricas serán tratadas muy brevemente en la Sección 10.9
[Orientacion a objetos], página 54.
Capı́tulo 4: Factores Nominales y Ordinales 17

4 Factores Nominales y Ordinales


Un factor es un vector utilizado para especificar una clasificación discreta de los elemen-
tos de otro vector de igual longitud. En R existen factores nominales y factores ordinales.

4.1 Un ejemplo especı́fico


Suponga que dispone de una muestra de 30 personas de Australia1 de tal modo que su
estado o territorio se especifica mediante un vector de caracteres con las abreviaturas de los
mismos:
> estado <- c("tas", "sa", "qld", "nsw", "nsw", "nt", "wa", "wa",
"qld", "vic", "nsw", "vic", "qld", "qld", "sa", "tas",
"sa", "nt", "wa", "vic", "qld", "nsw", "nsw", "wa",
"sa", "act", "nsw", "vic", "vic", "act")
Recuerde que, para un vector de caracteres, la palabra “ordenado” indica que está en
orden alfabético.
Un factor se crea utilizando la función factor():
> FactorEstado <- factor(estado)
La función print() trata a los factores de un modo distinto al de los vectores ordinarios:
> FactorEstado
[1] tas sa qld nsw nsw nt wa wa qld vic nsw vic qld qld sa
[16] tas sa nt wa vic qld nsw nsw wa sa act nsw vic vic act
Levels: act nsw nt qld sa tas vic wa
Puede utilizar la función levels() para ver los niveles de un factor:
> levels(FactorEstado)
[1] "act" "nsw" "nt" "qld" "sa" "tas" "vic" "wa"

4.2 La función tapply(). Variables desastradas (ragged


arrays)
Como continuación del ejemplo anterior, suponga que disponemos en otro vector de los
ingresos de las mismas personas (medidos con unas unidades apropiadas)
> ingresos <- c(60, 49, 40, 61, 64, 60, 59, 54, 62, 69, 70, 42, 56,
61, 61, 61, 58, 51, 48, 65, 49, 49, 41, 48, 52, 46,
59, 46, 58, 43)
Para calcular la media muestral para cada estado podemos usar la función tapply():
> MediaIngresos <- tapply(ingresos, FactorEstado, mean)
que devuelve el vector de medias con las componentes etiquetadas con los niveles:

1
Para quienes no conocen la estructura administrativa de Australia, existen ocho estados y territorios
en la misma: Australian Capital Territory, New South Wales, Northern Territory, Queensland, South
Australia, Tasmania, Victoria, y Western Australia; y sus correspondientes abreviaturas son: act, nsw,
nt, qld, sa, tas, vic, y wa.
Capı́tulo 4: Factores Nominales y Ordinales 18

> MediaIngresos
act nsw nt qld sa tas vic wa
44.500 57.333 55.500 53.600 55.000 60.500 56.000 52.250
La función tapply() aplica una función, en este ejemplo la función mean(), a cada grupo
de componentes del primer argumento, en este ejemplo ingresos, definidos por los niveles
del segundo argumento, en este ejemplo FactorEstado, como si cada grupo fuese un vector
por sı́ solo. El resultado es una estructura cuya longitud es el número de niveles del factor.
Puede consultar la ayuda para obtener más detalles.
Suponga que ahora desea calcular las desviaciones tı́picas de las medias de ingresos por
estados. Para ello es necesario escribir una función en R que calcule la desviación tı́pica de
un vector. Aunque aún no se ha explicado en este texto cómo escribir funciones2 , puede
admitir que existe la función var() que calcula la varianza muestral o cuasi-varianza, y que
la función buscada puede construirse con la asignación:
> StdErr <- function(x) sqrt(var(x)/length(x))
Ahora puede calcular los valores buscados mediante
> ErrorTipicoIngresos <- tapply(ingresos, FactorEstado, StdErr)
con el siguiente resultado:
> ErrorTipicoIngresos
act nsw nt qld sa tas vic wa
1.500000 4.310195 4.500000 4.106093 2.738613 0.500000 5.244044 2.657536
Como ejercicio puede calcular el intervalo de confianza al 95% de la media de ingresos por
estados. Para ello puede utilizar la función tapply(), la función length() para calcular los
tamaños muestrales, y la función qt() para encontrar los percentiles de las distribuciones t
de Student correspondientes.
La función tapply() puede utilizarse para aplicar una función a un vector indexado por
diferentes categorı́as simultáneamente. Por ejemplo, para dividir la muestra tanto por el
estado como por el sexo. Los elementos del vector se dividirán en grupos correspondientes
a las distintas categorı́as y se aplicará la función a cada uno de dichos grupos. El resultado
es una variable indexada etiquetada con los niveles de cada categorı́a.
La combinación de un vector3 con un factor para etiquetarlo, es un ejemplo de lo que se
llama variable indexada desastrada (ragged array) puesto que los tamaños de las subclases
son posiblemente irregulares. Cuando estos tamaños son iguales la indexación puede hacerse
implı́citamente y además más eficientemente, como veremos a continuación.

4.3 Factores ordinales


Los niveles de los factores se almacenan en orden alfabético, o en el orden en que se
especificaron en la función factor si ello se hizo explı́citamente.
A veces existe una ordenación natural en los niveles de un factor, orden que deseamos
tener en cuenta en los análisis estadı́sticos. La función ordered() crea este tipo de factores
y su uso es idéntico al de la función factor. Los factores creados por la función factor los
denominaremos nominales o simplemente factores cuando no haya lugar a confusión, y los
2
La escritura de funciones será tratada en Capı́tulo 10 [Escritura de funciones], página 46.
3
En general de una variable indexada
Capı́tulo 4: Factores Nominales y Ordinales 19

creados por la función ordered() los denominaremos ordinales. En la mayorı́a de los casos
la única diferencia entre ambos tipos de factores consiste en que los ordinales se imprimen
indicando el orden de los niveles. Sin embargo los contrastes generados por los dos tipos de
factores al ajustar Modelos lineales, son diferentes.
Capı́tulo 5: Variables indexadas. Matrices 20

5 Variables indexadas. Matrices

5.1 Variables indexadas (Arrays)


Una variable indexada (array) es una colección de datos, por ejemplo numéricos, inde-
xada por varios ı́ndices. R permite crear y manipular variables indexadas en general y en
particular, matrices.
Un vector de dimensiones es un vector de números enteros positivos. Si su longitud es k
entonces la variable indexada correspondiente es k–dimensional. Los elementos del vector
de dimensiones indican los lı́mites superiores de los k ı́ndices. Los lı́mites inferiores siempre
valen 1.
Un vector puede transformarse en una variable indexada cuando se asigna un vector
de dimensiones al atributo dim. Supongamos, por ejemplo, que z es un vector de 1500
elementos. La asignación
> dim(z) <- c(3,5,100)
hace que R considere a z como una variable indexada de dimensión 3 × 5 × 100.
Existen otras funciones, como matrix() y array(), que permiten asignaciones más
sencillas y naturales, como se verá en la Sección 5.4 [La funcion array()], página 22.
Los elementos del vector pasan a formar parte de la variable indexada siguiendo la regla1
de que el primer ı́ndice es el que se mueve más rápido y el último es el más lento.
Por ejemplo, si se define una variable indexada, a, con vector de dimensiones c(3,4,2),
la variable indexada tendrá 3×4×2 = 24 elementos que se formarán a partir de los elementos
originales en el orden a[1,1,1], a[2,1,1], ..., a[2,4,2], a[3,4,2].

5.2 Elementos de una variable indexada


Un elemento de una variable indexada puede referirse dando el nombre de la variable y,
entre corchetes, los ı́ndices que lo refieren, separados por comas.
En general, puede referir una parte de una variable indexada mediante una sucesión de
vectores ı́ndices, teniendo en cuenta que si un vector ı́ndice es vacı́o, equivale a utilizar todo
el rango de valores para dicho ı́ndice.
Ası́, en el ejemplo anterior, a[2,,] es una variable indexada 4×2, con vector de dimensión
c(4,2) y sus elementos son
c(a[2,1,1], a[2,2,1], a[2,3,1], a[2,4,1],
a[2,1,2], a[2,2,2], a[2,3,2], a[2,4,2])
en ese orden. A su vez, a[,,] equivale a la variable completa, que coincide con omitir
completamente los ı́ndices y utilizar simplemente a.
Para cualquier variable indexada, por ejemplo Z, el vector de dimensión puede referirse
explı́citamente mediante dim(Z) (en cualquiera de las dos partes de una asignación).
Asimismo, si especifica una variable indexada con un solo ı́ndice o vector ı́ndice, sólo
se utilizan los elementos correspondientes del vector de datos, y el vector de dimensión se
ignora. En caso de que el ı́ndice no sea un vector, sino a su vez una variable indexada, el
tratamiento es distinto, como ahora veremos.
1
Esta regla es la que se utiliza en el lenguaje Fortran
Capı́tulo 5: Variables indexadas. Matrices 21

5.3 Uso de variables indexadas como ı́ndices


Una variable indexada puede utilizar no sólo un vector de ı́ndices, sino incluso una
variable indexada de ı́ndices, tanto para asignar un vector a una colección irregular de
elementos de una variable indexada como para extraer una colección irregular de elementos.
Veamos un ejemplo sobre una matriz, que es una variable indexada con dos ı́ndices.
Puede construirse un ı́ndice matricial consistente en dos columnas y varias filas. Los ele-
mentos del ı́ndice matricial son los ı́ndices fila y columna para construir la matriz de ı́ndices.
Supongamos que X es una variable indexada 4 × 5 y que desea hacer lo siguiente:
• Extraer los elementos X[1,3], X[2,2] y X[3,1] con una estructura de vector, y
• Reemplazar dichos elementos de X con ceros.
Para ello puede utilizar una matriz de ı́ndices de 3 × 2 como en el siguiente ejemplo.
> x <- array(1:20,dim=c(4,5)) # Genera una variable indexada (4 × 5).
> x
[,1] [,2] [,3] [,4] [,5]
[1,] 1 5 9 13 17
[2,] 2 6 10 14 18
[3,] 3 7 11 15 19
[4,] 4 8 12 16 20
> i <- array(c(1:3,3:1),dim=c(3,2))
> i # i es una matriz de ı́ndices (3 × 2).
[,1] [,2]
[1,] 1 3
[2,] 2 2
[3,] 3 1
> x[i] # Extrae los elementos.
[1] 9 6 3
> x[i] <- 0 # Sustituye los elementos por ceros.
> x
[,1] [,2] [,3] [,4] [,5]
[1,] 1 5 0 13 17
[2,] 2 0 10 14 18
[3,] 0 7 11 15 19
[4,] 4 8 12 16 20
>
Un ejemplo algo más complejo consiste en generar la matriz de diseño de un diseño en
bloques definido por dos factores, bloques (niveles b) y variedades (niveles v), siendo el
número de parcelas n. Puede hacerlo del siguiente modo:
> Xb <- matrix(0, n, b)
> Xv <- matrix(0, n, v)
> ib <- cbind(1:n, bloques)
> iv <- cbind(1:n, variedades)
> Xb[ib] <- 1
> Xv[iv] <- 1
> X <- cbind(Xb, Xv)
Además, puede construir la matriz de incidencia, N, mediante
Capı́tulo 5: Variables indexadas. Matrices 22

> N <- crossprod(Xb, Xv)


También puede construirla directamente mediante la función table():
> N <- table(bloques, variedades)

5.4 La función array()


Una variable indexada no sólo puede construirse modificando el atributo dim de un
vector, sino también directamente mediante la función array, que tiene la forma
> Z <- array(vector de datos,vector de dimensiones)
Por ejemplo, si el vector h contiene 24 números (o incluso menos), la orden
> Z <- array(h, dim=c(3,4,2))
usa h para almacenar en Z una variable indexada de dimensión 3 × 4 × 2. Si el tamaño de
h es exactamente 24, el resultado coincide con el de
> dim(Z) <- c(3,4,2)
Sin embargo, si h es más corto de 24, sus valores se repiten desde el principio tantas veces
como sea necesario para obtener 24 elementos. (véase Sección 5.4.1 [Reciclado], página 22).
El caso extremo, muy común, corresponde a un vector de longitud 1, como en este ejemplo
> Z <- array(0, c(3,4,2))
en que Z es una variable indexada compuesta enteramente de ceros.
Además, dim(Z), el vector de dimensiones, es el vector c(3,4,2), Z[1:24], es un vector
de datos que coincide con h, y tanto Z[], con ı́ndice vacı́o, como Z, sin ı́ndices, son la
variable indexada con estructura de variable indexada.
Las variables indexadas pueden utilizarse en expresiones aritméticas y el resultado es una
variable indexada formada a partir de las operaciones elemento a elemento de los vectores
subyacentes. Los atributos dim de los operandos deben ser iguales en general y coincidirán
con el vector de dimensiones del resultado. Ası́ pues, si A, B y C son variables indexadas
similares, entonces
> D <- 2*A*B + C + 1
almacena en D una variable indexada similar, cuyo vector de datos es el resultado de las
operaciones indicadas sobre los vectores de datos subyacentes a A, B y C. Las reglas exactas
correspondientes a los cálculos en que se mezclan variables indexadas y vectores deben ser
estudiadas con detenimiento.

5.4.1 Operaciones con variables indexadas y vectores. Reciclado.


Cuando se realizan operaciones que mezclan variables indexadas y vectores, se siguen
los siguientes criterios:
• La expresión se analiza de izquierda a derecha.
• Si un vector es más corto que otro, se extiende repitiendo sus elementos (lo que se
denomina reciclado) hasta alcanzar el tamaño del vector más largo.
• Si sólo hay variables indexadas y vectores más cortos, las variables indexadas deben
tener el mismo atributo dim, o se producirá un error.
• Si hay un vector más largo que una variable indexada anterior, se produce un mensaje
de error.
Capı́tulo 5: Variables indexadas. Matrices 23

• Si hay variables indexadas y no se produce error, el resultado es una variable indexada


del mismo atributo dim que las variables indexadas que intervienen en la operación.

5.5 Producto exterior de dos variables indexadas


Una operación de importancia fundamental entre variables indexadas es el producto
exterior. Si a y b son dos variables indexadas numéricas, su producto exterior es una
variable indexada cuyo vector de dimensión es la concatenación de los correspondientes de
los operandos, en el orden de la operación, y cuyo vector de datos subyacente se obtiene
mediante todos los posibles productos de los elementos de los vectores subyacentes de a y
b. El producto exterior se obtiene mediante el operador %o%:
> ab <- a %o% b
o bien, en forma funcional, mediante outer:
> ab <- outer(a, b, "*")
La función “multiplicación” en esta última forma, puede reemplazarse por cualquier
función de dos variables. Por ejemplo, para calcular la función f (x, y) = cos(y)/(1 + x2 )
sobre la retı́cula formada por todos los puntos obtenidos combinando las ordenadas y abs-
cisas definidas por los elementos de los vectores x e y respectivamente, puede utilizar2 las
órdenes:
> f <- function(x, y) cos(y)/(1 + x^2)
> z <- outer(x, y, f)
En particular, el producto exterior de dos vectores, es una variable indexada con dos
ı́ndices (esto es, una matriz, de rango 1 a lo sumo). Debe tener en cuenta que el producto
exterior no es conmutativo.

Ejemplo: Distribución del determinante de una matriz de dı́gitos


de 2 × 2
Un ejemplo apropiado se presenta en el cálculo del determinante de una matriz 2 × 2,
[a, b; c, d], en que cada elemento de la misma es un número natural entre 0 y 9. El problema
planteado consiste en encontrar la distribución de los determinantes, ad − bc, de todas las
matrices posibles de esta forma, y representarla gráficamente, supuesto que cada dı́gito se
elige al azar de una distribución uniforme.
Para ello puede utilizar la función outer() dos veces:
> d <- outer(0:9, 0:9)
> fr <- table(outer(d, d, "-"))
> plot([Link](names(fr)), fr, type="h",
xlab="Determinante", ylab="Frecuencia")
Advierta cómo se ha forzado el atributo names de la tabla de frecuencias a numérico, para
recuperar el rango de los valores de los determinantes. La forma aparentemente “obvia” de
resolver este problema mediante iteraciones de tipo for, que se discutirán en el Capı́tulo 9
[Ciclos y ejecucion condicional], página 44, es tan ineficiente que es impracticable. Al
observar el resultado, tal vez le sorprenda que aproximadamente una de cada veinte matrices
sea singular.
2
La definición de una función en R se estudia en el capı́tulo Capı́tulo 10 [Escritura de funciones], página 46.
Capı́tulo 5: Variables indexadas. Matrices 24

5.6 Traspuesta generalizada de una variable indexada


La función aperm(a, perm) puede usarse para permutar la variable indexada a. El
argumento perm debe ser una permutación de los enteros {1, . . . , k} siendo k el número de
ı́ndices de a. El resultado es una variable indexada del mismo tamaño que a en la que la
dimensión que en la original era perm[j] será ahora la dimensión j. Si A es una matriz,
entonces
> B <- aperm(A, c(2,1))
almacena en B la matriz traspuesta de A. En el caso de matrices es más sencillo utilizar la
función t(), y bastarı́a escribir B <- t(A).

5.7 Operaciones con matrices


Como ya se ha indicado varias veces, una matriz es simplemente una variable indexada
con dos ı́ndices. Ahora bien, su importancia es tal que necesita un apartado especial. R
dispone de muchos operadores y funciones diseñados especı́ficamente para matrices. Por
ejemplo, acabamos de ver que t(X) es la matriz traspuesta de X. Las funciones nrow y ncol
devuelven el número de filas y de columnas de una matriz.

5.7.1 Producto matricial. Inversa de una matriz. Resolución de


sistemas lineales
El operador %*% realiza el producto matricial. Una matriz de n × 1 o de 1 × n puede
ser utilizada como un vector n-dimensional en caso necesario. Análogamente R puede usar
automáticamente un vector en una operación matricial convirtiéndolo en una matriz fila o
una matriz columna cuando ello es posible (A veces la conversión no está definida de modo
único, como veremos después).
Si, por ejemplo, A y B, son matrices cuadradas del mismo tamaño, entonces
> A * B
es la matriz de productos elemento a elemento, en tanto que
> A %*% B
es el producto matricial. Si x es un vector (de la dimensión apropiada) entonces
> x %*% A %*% x
es una forma cuadrática.3
La función crossprod() realiza el producto cruzado de matrices, esto es
crossprod(X,y) suministra el mismo resultado que t(X)%*%y, pero la operación es más
eficiente. Si omite el segundo argumento de la función crossprod(), ésta lo toma igual al
primero.
También existe la función diag(). Si su argumento es una matriz, diag(matriz), de-
vuelve un vector formado por los elementos de la diagonal de la misma. Si, por el contrario,
3
Si hubiese escrito x %*% x el resultado es ambiguo, pues tanto podrı́a significar x0 x como xx0 , donde x es
la forma columna. En este tipo de casos, la interpretación corresponde a la matriz de menor tamaño, por
lo que en este ejemplo el resultado es el escalar x0 x. La matriz xx0 puede calcularse mediante cbind(x)
%*% x, mediante x %*% rbind(x) o mediante x %*% rbind(x), puesto que tanto el resultado de rbind()
como el de cbind() son matrices.
Capı́tulo 5: Variables indexadas. Matrices 25

su argumento es un vector (de longitud mayor que uno), diag(vector), lo transforma en


una matriz diagonal cuyos elementos diagonales son los del vector. Y, por último, si su
argumento es un número natural, n, lo transforma en una matriz identidad de tamaño
n × n.

5.7.2 Autovalores y autovectores


Como ya hemos indicado, la función eigen() calcula los autovalores y autovectores de
una matriz simétrica. El resultado es una lista de dos componentes llamados values y
vectors. La asignación
> ev <- eigen(Sm)
almacenará esta lista en ev. Por tanto ev$val es el vector de autovalores de Sm y ev$vec es
la matriz de los correspondientes autovectores. Si sólo quisiéramos almacenar los autovalores
podrı́amos haber hecho la asignación:
> evals <- eigen(Sm)$values
y en este caso evals sólo contendrı́a los autovalores, habiéndose descartado la segunda
componente de la lista. Si se utiliza la directamente la expresión
> eigen(Sm)
se imprimen las dos componentes, con sus nombres, en la pantalla.

5.7.3 Descomposición en valores singulares. Determinantes


La función svd admite como argumento una matriz cualquiera, M, y calcula su descom-
posición en valores singulares, que consiste en obtener tres matrices, U, D y V, tales que la
primera es una matriz de columnas ortonormales con el mismo espacio de columnas que M,
la segunda es una matriz diagonal de números no negativos, y la tercera es una matriz de
columnas ortonormales con el mismo espacio de filas que M, tales que M=U%*%D%*%t(V). D
se devuelve en forma de vector formado por los elementos diagonales. El resultado de la
función es una lista de tres componentes cuyos nombres son d, u y v, y que corresponden a
las matrices descritas.
Si M es una matriz cuadrada, es fácil ver que
> AbsDetM <- prod(svd(M)$d)
calcula el valor absoluto del determinante de M. Si necesita este cálculo a menudo, puede
definirlo como una nueva función en R:
> AbsDet <- function(M) prod(svd(M)$d)
tras lo cual puede usar AbsDet() como cualquier otra función de R. Se deja como ejercicio,
trivial pero útil, el cálculo de una función, tr(), que calcule la traza de una matriz cuadrada.
Tenga en cuenta que no necesita realizar ninguna iteración; estudie atentamente el código
de la función anterior.

5.7.4 Ajuste por mı́nimos cuadrados. Descomposición QR


La función lsfit() devuelve una lista que contiene los resultados de un ajuste por
mı́nimos cuadrados. Una asignación de la forma
Capı́tulo 5: Variables indexadas. Matrices 26

> MinCua <- lsfit(X, y)


almacena los resultados del ajuste por mı́nimos cuadrados de un vector de observaciones, y,
y una matriz de diseño, X. Para ver más detalles puede consultar la ayuda, y también la de
la función [Link]() para los diagnósticos de regresión. Tenga en cuenta que no necesita
incluir un término independiente en X, ya que se incluye automáticamente.
Otras funciones estrechamente relacionadas son qr() y similares. Considere las siguientes
asignaciones:
> Xplus <- qr(X)
> b <- [Link](Xplus, y)
> fit <- [Link](Xplus, y)
> res <- [Link](Xplus, y)
que calculan la proyección ortogonal de y sobre X y la almacenan en fit, la proyección sobre
el complemento ortogonal en res y el vector de coeficientes para la proyección en b4 .
No se presupone que X sea de rango completo. Se buscan las redundancias y, si existen,
se eliminan.
Esta forma es la forma antigua, a bajo nivel, de realizar ajustes de mı́nimos cuadra-
dos. Aunque sigue siendo útil en algún contexto, deberı́a ser reemplazada por los modelos
estadı́sticos, como se verá en el Capı́tulo 11 [Modelos estadisticos en R], página 55.

5.8 Submatrices. Funciones cbind() y rbind().


Las funciones cbind() y rbind() construyen matrices uniendo otras matrices (o vec-
tores), horizontalmente (modo columna) o verticalmente (modo fila), respectivamente.
En la asignación
> X <- cbind(arg 1, arg 2, arg 3, ...)
los argumentos pueden ser vectores de cualquier longitud o matrices con el mismo número
de filas. El resultado es una matriz cuyas columnas son los argumentos concatenados, arg 1,
arg 2, . . .
Si alguno de los argumentos de cbind() es un vector, y hay alguna matriz, el vector no
puede ser más largo que el número de filas de las matrices presentes, y si es más corto, se
recicla hasta alcanzar el número indicado. Si sólo hay vectores, los más cortos se reciclan
hasta alcanzar el tamaño del mayor.
La función rbind() realiza el mismo papel, sustituyendo filas por columnas.
Supongamos que X1 y X2 tienen el mismo número de filas. Para combinar las columnas
de ambas en una matriz, X, que tendrá el mismo número de filas, y añadirle una columna
inicial de unos, puede escribir
> X <- cbind(1, X1, X2)
El resultado de cbind() o de rbind() siempre es una matriz y estas funciones consti-
tuyen, por tanto, la forma más sencilla para tratar un vector como una matriz columna o
una matriz fila, respectivamente.

4
b es esencialmente el resultado del operador “barra hacia atrás” de Matlab.
Capı́tulo 5: Variables indexadas. Matrices 27

5.9 La función de concatenación, c(), con variables


indexadas
En tanto que cbind() y rbind() son funciones de concatenación que respetan el atributo
dim, la función c() no lo hace, sino que despoja a los objetos numéricos de los atributos
dim y dimnames, lo que, por cierto, puede ser útil en determinadas situaciones.
La forma oficial de transformar una variable indexada en el vector subyacente es utilizar
la función [Link](),
> vec <- [Link](X)
Sin embargo, se obtiene un resultado análogo utilizando la función c() debido al efecto
colateral citado:
> vec <- c(X)
Existen sutiles diferencias entre ambos resultados, pero la elección entre ambas es fun-
damentalmente una cuestión de estilo (personalmente preferimos la primera forma).

5.10 Tablas de frecuencias a partir de factores


Hemos visto que un factor define una tabla de entrada simple. Del mismo modo, dos
factores definen una tabla de doble entrada, y ası́ sucesivamente. La función table()
calcula tablas de frecuencias a partir de factores de igual longitud. Si existen k argumentos
categóricos, el resultado será una variable k-indexada, que contiene la tabla de frecuencias.
Vimos en un ejemplo anterior, que FactorEstado era un factor que indicaba el estado
de procedencia. La asignación
> FrecEstado <- table(FactorEstado)
almacena en FrecEstado una tabla de las frecuencias de cada estado en la muestra. Las
frecuencias se ordenan y etiquetan con los niveles del factor. Esta orden es equivalente, y
más sencilla, que
> FrecEstado <- tapply(FactorEstado, FactorEstado, length)
Suponga ahora que FactorIngresos es un factor que define “tipos de ingresos”, por
ejemplo, mediante la función cut():
> factor(cut(ingresos,breaks=35+10*(0:7))) -> FactorIngresos
Entonces, puede calcular una tabla de frecuencias de doble entrada del siguiente modo:
> table(FactorIngresos,FactorEstado)
FactorEstado
FactorIngresos act nsw nt qld sa tas vic wa
(35,45] 1 1 0 1 0 0 1 0
(45,55] 1 1 1 1 2 0 1 3
(55,65] 0 3 1 3 2 2 2 1
(65,75] 0 1 0 0 0 0 1 0
La extensión a tablas de frecuencias de varias entradas es inmediata.
Capı́tulo 6: Listas y hojas de datos 28

6 Listas y hojas de datos

6.1 Listas
En R, una lista es un objeto consistente en una colección ordenada de objetos, conocidos
como componentes.
No es necesario que los componentes sean del mismo modo, ası́ una lista puede estar
compuesta de, por ejemplo, un vector numérico, un valor lógico, una matriz y una función.
El siguiente es un ejemplo de una lista:
> Lst <- list(nombre="Pedro", esposa="Marı́a", [Link]=3,
[Link]=c(4,7,9))
Los componentes siempre están numerados y pueden ser referidos por dicho número.
En este ejemplo, Lst es el nombre de una lista con cuatro componentes, cada uno de los
cuales puede ser referido, respectivamente, por Lst[[1]], Lst[[2]], Lst[[3]] y Lst[[4]].
Como, además, Lst[[4]] es un vector, Lst[[4]][1] refiere su primer elemento.
La función length() aplicada a una lista devuelve el número de componentes (del primer
nivel) de la lista.
Los componentes de una lista pueden tener nombre, en cuyo caso pueden ser referidos
también por dicho nombre, mediante una expresión de la forma
nombre de lista$nombre de componente
Esta convención permite la obtención de una componente sin tener que recurrir a su
número.
En el ejemplo anterior,
Lst$nombre coincide con Lst[[1]] y vale "Pedro",
Lst$esposa coincide con Lst[[2]] y vale "Marı́a",
Lst$[Link][1] coincide con Lst[[4]][1] y vale 4.
También es posible utilizar los nombres de los componentes entre dobles corchetes, por
ejemplo, Lst[["nombre"]] coincide con Lst$nombre. Esta opción es muy útil en el caso
en que el nombre de los componentes se almacena en otra variable, como en
> x <- "nombre"; Lst[[x]]
Es muy importante distinguir claramente entre Lst[[1]] y Lst[1]. ‘[[. . . ]]’ es el
operador utilizado para seleccionar un sólo elemento, mientras que ‘[. . . ]’ es un operador
general de indexado. Esto es, Lst[[1]] es el primer objeto de la lista Lst, y si es una lista
con nombres, el nombre no está incluido. Por su parte, Lst[1], es una sublista de la lista
Lst consistente en la primera componente. Si la lista tiene nombre, éste se transfiere a la
sublista.
Los nombres de los componentes pueden abreviarse hasta el mı́nimo de letras necesarios
para identificarlos de modo exacto. Ası́, en
> Lista <- list(coeficientes=c(1.3,4), covarianza=.87)
Lst$coeficientes puede especificarse mediante Lista$coe, y Lista$covarianza como
Lista$cov.
El vector de nombres es un atributo de la lista, y como el resto de atributos puede ser
manipulado. Además de las listas, también otras estructuras pueden poseer el atributo
names.
Capı́tulo 6: Listas y hojas de datos 29

6.2 Construcción y modificación de listas


La función list() permite crear listas a partir de objetos ya existentes. Una asignación
de la forma
> Lista <- list(nombre 1=objeto 1,. . . ,nombre m=objeto m)
almacena en Lista una lista de m componentes que son objeto 1, . . . , objeto m; a los
cuales asigna los nombres nombre 1, . . . ,nombre m; que pueden ser libremente elegidos1 .
Si omite los nombres, las componentes sólo estarán numeradas. Las componentes se copian
para construir la lista y los originales no se modifican.
Las listas, como todos los objetos indexados, pueden ampliarse especificando compo-
nentes adicionales. Por ejemplo
> Lst[5] <- list(matriz=Mat)

6.2.1 Concatenación de listas


Al suministrar listas como argumentos a la función c() el resultado es una lista, cuyos
componentes son todos los de los argumentos unidos sucesivamente.
> [Link] <- c(lista.A, lista.B, lista.C)
Recuerde que cuando los argumentos eran vectores, esta función los unı́a todos en un
único vector. En este caso, el resto de atributos, como dim, se pierden.

6.3 Hojas de datos (Data frames)


Una hoja de datos 2 (Data frame) es una lista que pertenece a la clase "[Link]".
Hay restricciones en las listas que pueden pertenecer a esta clase, en particular:
• Los componentes deben ser vectores (numéricos, cadenas de caracteres, o lógicos),
factores, matrices numéricas, listas u otras hojas de datos.
• Las matrices, listas, y hojas de datos contribuyen a la nueva hoja de datos con tantas
variables como columnas, elementos o variables posean, respectivamente.
• Los vectores numéricos y los factores se incluyen sin modificar, los vectores no numéricos
se fuerzan a factores cuyos niveles son los únicos valores que aparecen en el vector.
• Los vectores que constituyen la hoja de datos deben tener todos la misma longitud, y
las matrices deben tener el mismo tamaño de filas
Las hojas de datos pueden interpretarse, en muchos sentidos, como matrices cuyas colum-
nas pueden tener diferentes modos y atributos. Pueden imprimirse en forma matricial y se
pueden extraer sus filas o columnas mediante la indexación de matrices.

6.3.1 Construcción de hojas de datos


Puede construir una hoja de datos utilizando la función [Link]:

1
Aunque R permite lo contrario, deberı́an ser distintos entre sı́
2
Hemos utilizado esta traducción por analogı́a con la “hoja de cálculo”
Capı́tulo 6: Listas y hojas de datos 30

> cont <- [Link](dom=FactorEstado, bot=ingresos, dis=FactorIngresos)


Puede forzar que una lista, cuyos componentes cumplan las restricciones para ser una
hoja de datos, realmente lo sea, mediante la función [Link]()
La manera más sencilla de construir una hoja de datos es utilizar la función
[Link]() para leerla desde un archivo del sistema operativo. Esta forma se tratará
en el Capı́tulo 7 [Lectura de datos desde un archivo], página 33.

6.3.2 Funciones attach() y detach()


La notación $ para componentes de listas, como por ejemplo cont$dom, no siempre es
la más apropiada. En ocasiones, serı́a cómodo que los componentes de una lista o de una
hoja de datos pudiesen ser tratados temporalmente como variables cuyo nombre fuese el del
componente, sin tener que especificar explı́citamente el nombre de la lista.
La función attach() puede tener como argumento el nombre de una lista o de una hoja
de datos y permite conectar la lista o la hoja de datos directamente. Supongamos que
lentejas es una hoja de datos con tres variables, lentejas$u, lentejas$v y lentejas$w.
La orden
> attach(lentejas)
conecta la hoja de datos colocándola en la segunda posición de la trayectoria de búsqueda
y, supuesto que no existen variables denominadas u, v o w en la primera posición; u, v y w
aparecerán como variables por sı́ mismas. Sin embargo, si realiza una asignación a una de
estas variables, como por ejemplo
> u <- v+w
no se sustituye la componente u de la hoja de datos, sino que se crea una nueva variable,
u, en el directorio de trabajo, en la primera posición de la trayectoria de búsqueda, que
enmascarará a la variable u de la hoja de datos. Para realizar un cambio en la propia hoja
de datos, basta con utilizar la notación $:
> lentejas$u <- v+w
Este nuevo valor de la componente u no será visible de modo directo hasta que desconecte
y vuelva a conectar la hoja de datos.
Para desconectar una hoja de datos, utilice la función
> detach()
Esta función desconecta la entidad que se encuentre en la segunda posición de la trayec-
toria de búsqueda. Una vez realizada esta operación dejarán de existir las variables u,
v y w como tales, aunque seguirán existiendo como componentes de la hoja de datos.
Las entidades que ocupan en la trayectoria de búsqueda posiciones superiores a la se-
gunda, pueden desconectarse dando su posición o su nombre como argumento a la función
detach. Personalmente preferimos la segunda opción, como por ejemplo detach(lentejas)
o detach("lentejas")
Nota: La trayectoria de búsqueda puede almacenar un número finito y pequeño
de elementos, por tanto (puesto que además no es necesario) no debe conectar
una misma hoja de datos más de una vez. Del mismo modo, es conveniente
desconectar una hoja de datos cuando termine de utilizar sus variables.
Capı́tulo 6: Listas y hojas de datos 31

Nota: En la versión actual de R sólo se pueden conectar listas y hojas de datos


en la posición 2 o superior. No es posible asignar directamente en una lista u
hoja de datos conectada (por tanto, en cierto sentido, son estáticas).

6.3.3 Trabajo con hojas de datos


Una metodologı́a de trabajo para tratar diferentes problemas utilizando el mismo direc-
torio de trabajo es la siguiente:
• Reúna todas las variables de un mismo problema en una hoja de datos y déle un nombre
apropiado e informativo;
• Para analizar un problema, conecte, mediante attach(), la hoja de datos correspon-
diente (en la posición 2) y utilice el directorio de trabajo (en la posición 1) para los
cálculos y variables temporales;
• Antes de terminar un análisis, añada las variables que deba conservar a la hoja de
datos utilizando la forma $ para la asignación y desconecte la hoja de datos mediante
detach();
• Para finalizar, elimine del directorio de trabajo las variables que no desee conservar,
para mantenerlo lo más limpio posible.
De este modo podrá analizar diferentes problemas utilizando el mismo directorio, aunque
todos ellos compartan variables denominadas x, y o z, por ejemplo.

6.3.4 Conexión de listas arbitrarias


La función attach() es una función genérica, que permite conectar en la trayectoria
de búsqueda no sólo directorios y hojas de datos, sino también otros tipos de objetos, en
particular cualquier lista, como en
> attach([Link])
Posteriormente podrá desconectar el objeto utilizando la función detach, utilizando
como argumento el número de posición o, preferiblemente, su nombre.

6.3.5 Gestión de la trayectoria de búsqueda


La función search devuelve la trayectoria de búsqueda actual y por tanto es la mejor
manera de conocer qué hojas de datos, listas o bibliotecas han sido conectadas o desconec-
tadas. Si no ha realiado ninguna conexión o desconexión su valor es
> search()
[1] ".GlobalEnv" "Autoloads" "package:base"
donde .GlobalEnv corresponde al espacio de trabajo.3
Una vez conectada la hoja de datos, lentejas, tendrı́amos
> search()
[1] ".GlobalEnv" "lentejas" "Autoloads" "package:base"
> ls(2)
[1] "u" "v" "w"
3
Consulte la ayuda sobre autoload para la descripción del significado del segundo término.
Capı́tulo 6: Listas y hojas de datos 32

y, como vimos, ls (o objects) puede usarse para examinar los contenidos de cualquier
posición en la trayectoria de búsqueda.
Por último, desconectamos la hoja de datos y comprobamos que ha sido eliminada de la
trayectoria de búsqueda.
> detach("lentejas")
> search()
[1] ".GlobalEnv" "Autoloads" "package:base"
Capı́tulo 7: Lectura de datos de un archivo 33

7 Lectura de datos de un archivo


Los datos suelen leerse desde archivos externos y no teclearse de modo interactivo. Las
capacidades de lectura de archivos de R son sencillas y sus requisitos son bastante estrictos
cuando no inflexibles. Se presupone que el usuario es capaz de modificar los archivos de
datos con otras herramientas, por ejemplo con editores de texto1 , para ajustarlos a las
necesidades de R. Generalmente esta tarea es muy sencilla.
La función [Link]() puede utilizarse para leer un archivo con campos de anchura fija
no delimitados.(Esta función utiliza un programa perl para transformar el archivo en otro
adaptado para su lectura con [Link].) La función [Link]() cuenta el número
de campos por lı́nea de un archivo de campos delimitados. Estas dos funciones pueden
resolver algunos problemas elementales, pero en la mayorı́a de los casos es mejor preparar
el archivo a las necesidades de R antes de comenzar el análisis.
Si los datos se van a almacena en hojas de datos, método que recomendamos, puede leer
los datos correspondientes a las mismas con la función [Link](). Existe también una
función más genérica, scan(), que puede utilizar directamente.

7.1 La función [Link]()


Para poder leer una hoja de datos directamente, el archivo externo debe reunir las
condiciones adecuadas. La forma más sencilla es:
• La primera lı́nea del archivo debe contener el nombre de cada variable de la hoja de
datos.
• En cada una de las siguientes lı́neas, el primer elemento es la etiqueta de la fila, y a
continuación deben aparecer los valores de cada variable.
Si el archivo tiene un elemento menos en la primera lı́nea que en las restantes, obliga-
toriamente será el diseño anterior el que se utilice. A continuación aparece un ejemplo de
las primeras lı́neas de un archivo, [Link], con datos de viviendas, preparado para su
lectura con esta función.

Archivo de entrada con nombres de variables y etiquetas de filas:

Precio Superficie Área Habitaciones A~


nos Calef
01 52.00 111.0 830 5 6.2 no
02 54.75 128.0 710 5 7.5 no
03 57.50 101.0 1000 5 4.2 no
04 57.50 131.0 690 6 8.8 no
05 59.75 93.0 900 5 1.9 si
...

Predeterminadamente, los elementos numéricos (excepto las etiquetas de las filas) se


almacenan como variables numéricas; y los no numéricos, como Calef, se fuerzan como
factores. Es posible modificar esta acción.
1
En UNIX puede utilizar el programa perl o los editores sed y awk. Existen versiones para Microsoft
Windows.
Capı́tulo 7: Lectura de datos de un archivo 34

> PreciosCasas <- [Link]("[Link]")


A menudo no se dispone de etiquetas de filas. En ese caso, también es posible la lectura
y el programa añadirá unas etiquetas predeterminadas. Ası́, si el archivo tiene la forma
siguiente,

Archivo sin etiquetas de filas:

Precio Superficie Área Habitaciones A~


nos Calef
52.00 111.0 830 5 6.2 no
54.75 128.0 710 5 7.5 no
57.50 101.0 1000 5 4.2 no
57.50 131.0 690 6 8.8 no
59.75 93.0 900 5 1.9 si
...

podrá leerse utilizando un parámetro adicional


> PreciosCasas <- [Link]("[Link]", header=T)
donde el parámetro adicional, header=T, indica que la primera lı́nea es una lı́nea de
cabeceras y que no existen etiquetas de filas explı́citas.

7.2 La función scan()


Supongamos que el archivo [Link] contiene los datos correspondientes a tres vec-
tores, de la misma longitud, el primero de tipo carácter y los otros dos de tipo numérico,
escritos de tal modo que en cada lı́nea aparecen los valores correspondientes de cada uno
de ellos.
En primer lugar, utilizamos la función scan() para leer los tres vectores, del siguiente
modo
> entrada <- scan("[Link]", list("",0,0))
El segundo argumento es una estructura de lista que establece el modo de los tres vec-
tores que se van a leer. El resultado se almacena en entrada, que será una lista con tres
componentes correspondientes a los vectores leı́dos. Puede referir cada uno de los vectores
mediante la indexación:
> etiqueta <- entrada[[1]]; x <- entrada[[2]]; y <- entrada[[3]]
También podrı́a haber utilizado nombres en la lista que define el modo de lectura, por
ejemplo
> entrada <- scan("[Link]", list(etiqueta="",x=0,y=0))
En este caso, puede referir cada uno de los vectores con la notación $. Si desea acceder
a las variables separadamente deberá, o bien asignarlas a variables del espacio de trabajo,
> etiqueta <- entrada$etiqueta; x <- entrada$x; y <- entrada$y
o bien conectar la lista completa en la posición 2 de la trayectoria de búsqueda (véase
Sección 6.3.4 [Conexion de listas arbitrarias], página 31).
Si el segundo argumento hubiese sido un sólo elemento y no una lista, todos los elementos
del archivo deberı́an ser del tipo indicado y se hubiesen leı́do en un sólo vector.
Capı́tulo 7: Lectura de datos de un archivo 35

> X <- matrix(scan("[Link]", 0), ncol=5, byrow=TRUE)


La función scan permite realizar lecturas más complejas, como puede consultar en la
ayuda.

7.3 Acceso a datos internos


En la distribución de R se incluyen unos cincuenta objetos con datos, y otros más
se incluyen en las bibliotecas (incluyendo las estándar). Para utilizar estos datos, deben
cargarse explı́citamente2 utilizando la función data. Para obtener una lista de los datos
existentes en el sistema base utilice
data()
y para cargar uno, por ejemplo infert, debe suministrar dicho nombre como argumento de
la función data.
data(infert)
Normalmente una orden de este tipo carga un objeto del mismo nombre que suele ser una
hoja de datos. Sin embargo, también es posible que se carguen varios objetos, por lo que
en cada caso debe consultar la ayuda interactiva sobre el objeto concreto para conocer
exactamente la acción que realizará.

7.3.1 Acceso a datos de una biblioteca

Para acceder a los datos incluidos en una biblioteca, basta utilizar el argumento package
en la función data. Por ejemplo,
data(package="nls")
data(Puromycin, package="nls")
Si la biblioteca ya ha sido conectada mediante la función library, sus datos habrán
sido incluidos automáticamente en la trayectoria de búsqueda y no será necesario incluir el
argumento package. Ası́,
library(nls)
data()
data(Puromycin)
presentará una lista de todos los datos de todas las bibliotecas conectadas en ese momento
(que serán al menos la biblioteca base y la biblioteca nls) y posteriormente cargará los datos
Puromycin de la primera librerı́a en la trayectoria de búsqueda en que encuentre unos datos
con dicho nombre.
Las librerı́as creadas por los usuarios son una fuente valiosa de datos. Por supuesto, las
notas del Dr. Venables, fuente original de esta introducción, contienen un conjunto de datos
que se encuentra disponible en CRAN en la biblioteca Rnotes.

2
En S-Plus la carga explı́cita no es necesaria.
Capı́tulo 7: Lectura de datos de un archivo 36

7.4 Edición de datos


Una vez creada una estructura de datos, la función [Link], disponible en algunas
versiones de R, permite modificarla. La orden
%> [Link] <- [Link]([Link])
edita [Link] utilizando un entorno similar a una hoja de cálculo. Al finalizar, el objeto
se almacena en [Link]. [Link], y por tanto [Link], puede ser una matriz, una hoja
de datos, un vector o cualquier objeto atómico.
Si utiliza la función sin argumentos
> [Link] <- [Link]()
permite introducir datos desde una hoja vacı́a.

7.5 Cómo importar datos


En muchos casos es necesario importar datos desde bases de datos o, en general, desde
archivos preparados para otros programas. Se están desarrollando varias bibliotecas para
realizar estas tareas. En este momento existe la biblioteca stataread que lee y escribe
archivos de Stata, y la biblioteca foreign, en fase experimental, que lee archivos de SAS,
Minitab y SPSS. Otras bibliotecas permiten el acceso a bases de datos que soportan SQL3 ,
y se está terminando la biblioteca RODBC para acceder a bases de datos ODBC (tales
como Access en Microsoft Windows).

3
Acrónimo en inglés de Standard Query Language.
Capı́tulo 8: Distribuciones probabilı́sticas 37

8 Distribuciones probabilı́sticas

8.1 Tablas estadı́sticas


R contiene un amplio conjunto de tablas estadı́sticas. Para cada distribución soportada,
hay funciones que permiten calcular la función de distribución, F (x) = P (X ≤ x), la función
de distribución inversa1 , la función de densidad y generar números pseudoaleatorios de la
distribución. Las distribuciones son las siguientes:

Distribución nombre en R argumentos


adicionales

beta beta shape1, shape2, ncp


binomial binom size, prob
Cauchy cauchy location, scale
ji cuadrado chisq df, ncp
exponencial exp rate
F de Snedecor f df1, df1, ncp
gamma gamma shape, scale
geométrica geom prob
hipergeométrica hyper m, n, k
log-normal lnorm meanlog, sdlog
logı́stica logis location, scale
binomial negativa nbinom size, prob
normal norm mean, sd
Poisson pois lambda
t de Student t df, ncp
uniforme unif min, max
Weibull weibull shape, scale
Wilcoxon wilcox m, n

Para construir el nombre de cada función, utilice el nombre de la distribución precedido


de ‘d’ para la función de densidad, ‘p’ para la función de distribución, ‘q’ para la función
de distribución inversa, y ‘r’ para la generación de números pseudoaleatorios. El primer
argumento es x para la función de densidad, q para la función de distribución, p para la
función de distribución inversa, y n para la función de generación de números pseudoaleato-
rios (excepto en el caso de rhyper y rwilcox, en los cuales es nn). En el momento de
escribir este manual, el parámetro ncp sólo está disponible prácticamente en las funciones
de distribución. Para conocer dónde puede usarlo utilice la ayuda interactiva.
Además de las anteriores, existen las funciones ptukey y qtukey para la distribución del
rango estudentizado de muestras de una distribución normal.
Los siguientes ejemplos clarificarán estos conceptos:
> ## P valor a dos colas de la distribución t_13
> 2*pt(-2.43, df = 13)
> ## Percentil 1 superior de una distribución F(2, 7)
1
Dado q, el menor x tal que P (X ≤ x) > q
Capı́tulo 8: Distribuciones probabilı́sticas 38

> qf(0.99, 2, 7)

8.2 Estudio de la distribución de unos datos


Dados unos datos (unidimensionales), su distribución puede estudiarse de muchas formas.
La más sencilla es realizar un resumen estadı́stico, y ello puede obtenerse fácilmente con
cualquiera de las funciones summary o fivenum; y también puede realizar un diagrama de
tallo y hojas con la función stem.
> data(faithful)
> attach(faithful)
> summary(eruptions)
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.600 2.163 4.000 3.488 4.454 5.100
> fivenum(eruptions)
[1] 1.6000 2.1585 4.0000 4.4585 5.1000
> stem(eruptions)

The decimal point is 1 digit(s) to the left of the |

16 | 070355555588
18 | 000022233333335577777777888822335777888
20 | 00002223378800035778
22 | 0002335578023578
24 | 00228
26 | 23
28 | 080
30 | 7
32 | 2337
34 | 250077
36 | 0000823577
38 | 2333335582225577
40 | 0000003357788888002233555577778
42 | 03335555778800233333555577778
44 | 02222335557780000000023333357778888
46 | 0000233357700000023578
48 | 00000022335800333
50 | 0370
En vez del diagrama de tallo y hojas, puede representar el histograma utilizando la
función hist.
> hist(eruptions)
# define intervalos menores y a~
nade un gráfico de densidad
> hist(eruptions, seq(1.6, 5.2, 0.2), prob=TRUE)
> lines(density(eruptions, bw=0.1))
> rug(eruptions) # muestra los puntos
La función density permite realizar gráficos de densidad y la hemos utilizado para
superponer este gráfico en el ejemplo. La anchura de banda, bw, ha sido elegida probando
varias, ya que el valor predeterminado produce un gráfico mucho más suavizado. Si necesita
Capı́tulo 8: Distribuciones probabilı́sticas 39

utilizar métodos automáticos de elección de ancho de banda, utilice las bibliotecas MASS
y KernSmooth.
Histogram of eruptions

0.7
0.6
0.5
Relative Frequency

0.4
0.3
0.2
0.1
0.0

1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0

eruptions

Podemos representar la función de distribución empı́rica mediante la función ecdf de la


biblioteca estándar stepfun.
> library(stepfun)
> plot(ecdf(eruptions), [Link]=FALSE, verticals=TRUE)
Esta distribución, obviamente, no corresponde a ninguna de las distribuciones estándar.
Pero podemos estudiar qué ocurre con las erupciones de más de tres minutos. Vamos a
seleccionarlas, ajustarles una distribución normal y superponer la distribución ajustada.
> long <- eruptions[eruptions > 3]
> plot(ecdf(long), [Link]=FALSE, verticals=TRUE)
> x <- seq(3, 5.4, 0.01)
> lines(x, pnorm(x, mean=mean(long), sd=sqrt(var(long))), lty=3)
ecdf(long)
1.0
0.8
0.6
Fn(x)

0.4
0.2
0.0

3.0 3.5 4.0 4.5 5.0

Los gráficos cuantil-cuantil ("Q-Q plots") pueden ayudarnos a examinar los datos más
cuidadosamente.
Capı́tulo 8: Distribuciones probabilı́sticas 40

> par(pty="s")
> qqnorm(long); qqline(long)
que muestran un ajuste razonable, aunque la cola de la derecha es más corta de lo que serı́a
esperable en una distribución normal. Vamos a compararla con unos datos pseudoaleatorios
tomados de una distribución t5 .
Normal Q−Q Plot

5.0
4.5
Sample Quantiles

4.0
3.5
3.0

−2 −1 0 1 2

Theoretical Quantiles

> x <- rt(250, df = 5)


> qqnorm(x); qqline(x)
que la mayorı́a de las veces (recuerde que es una muestra pseudo aleatoria) tendrá colas más
largas de lo que serı́a esperable en una distribución normal. Podemos realizar un gráfico
cuantil-cuantil de estos datos, pero frente a la distribución t5 , mediante
> qqplot(qt(ppoints(250), df=5), x, xlab="gráfico Q-Q de t_5")
> qqline(x)
Por último, realicemos un contraste de hipótesis para comprobar la normalidad. La
biblioteca ctest permite realizar el contraste de Shapiro-Wilk
> library(ctest)
> [Link](long)

Shapiro-Wilk normality test

data: long
W = 0.9793, p-value = 0.01052
y el contraste de Kolmogorov-Smirnov
> [Link](long, "pnorm", mean=mean(long), sd=sqrt(var(long)))

One-sample Kolmogorov-Smirnov test

data: long
D = 0.0661, p-value = 0.4284
alternative hypothesis: [Link]
Capı́tulo 8: Distribuciones probabilı́sticas 41

Hemos utilizado los datos como ejemplo de uso de las funciones, sin estudiar si el mismo es
válido. En este caso no lo serı́a ya que se han estimado los parámetros de la distribución
normal a partir de la misma muestra.

8.3 Contrastes de una y de dos muestras


Acabamos de comparar una muestra con una distribución normal, pero es mucho más
habitual comparar aspectos de dos muestras. Consideremos los siguientes datos, tomados
de Rice (1995, p.490), del calor latente en la fusión del hielo expresados en cal/gm.
Método A: 79.98 80.04 80.02 80.04 80.03 80.03 80.04 79.97
80.05 80.03 80.02 80.00 80.02
Método B: 80.02 79.94 79.98 79.97 79.97 80.03 79.95 79.97
Podemos comparar gráficamente las dos muestras mediante un diagrama de cajas.
> A <- scan()
79.98 80.04 80.02 80.04 80.03 80.03 80.04 79.97
80.05 80.03 80.02 80.00 80.02

> B <- scan()


80.02 79.94 79.98 79.97 79.97 80.03 79.95 79.97

> boxplot(A, B)
que muestra claramente que el primer grupo tiende a tener mayores resultados que el se-
gundo.
80.04
80.02
80.00
79.98
79.96
79.94

1 2

Para contrastar la igualdad de medias de las dos poblaciones, se puede utilizar el con-
traste t de Student para dos muestras independientes, del siguiente modo:
> [Link](A, B)

Welch Two Sample t-test

data: A and B
t = 3.2499, df = 12.027, p-value = 0.00694
Capı́tulo 8: Distribuciones probabilı́sticas 42

alternative hypothesis: true difference in means is not equal to 0


95 percent confidence interval:
0.01385526 0.07018320
sample estimates:
mean of x mean of y
80.02077 79.97875
que indica una diferencia significativa (Bajo las condiciones del modelo, incluida la norma-
lidad). Esta función, de modo predeterminado, no presupone que las varianzas son iguales
(al contrario que la función análoga de S-Plus, [Link]). Si desea contrastar la igualdad
de varianzas, puede utilizar la función [Link] de la biblioteca ctest.
> library(ctest)
> [Link](A, B)

F test to compare two variances

data: A and B
F = 0.5837, num df = 12, denom df = 7, p-value = 0.3938
alternative hypothesis: true ratio of variances is not equal to 1
95 percent confidence interval:
0.1251097 2.1052687
sample estimates:
ratio of variances
0.5837405
que no muestra evidencia de diferencias significativas (Bajo las condiciones del modelo, que
incluyen normalidad). Si hubiésemos admitido esta hipótesis previamente, podrı́amos haber
realizado un contraste más potente, como el siguiente:
> [Link](A, B, [Link]=TRUE)

Two Sample t-test

data: A and B
t = 3.4722, df = 19, p-value = 0.002551
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
0.01669058 0.06734788
sample estimates:
mean of x mean of y
80.02077 79.97875
Como hemos indicado, una de las condiciones de aplicación de los contrastes anteriores
es la normalidad. Si ésta falla, puede utilizar el contraste de dos muestras de Wilcoxon (o
de Mann-Whitney) que solo presupone en la hipótesis nula que la distribución común es
continua.
> library(ctest)
> [Link](A, B)

Wilcoxon rank sum test with continuity correction


Capı́tulo 8: Distribuciones probabilı́sticas 43

data: A and B
W = 89, p-value = 0.007497
alternative hypothesis: true mu is not equal to 0

Warning message:
Cannot compute exact p-value with ties in: [Link](A, B)
Advierta el mensaje de advertencia (Warning . . . ): Existen valores repetidos en cada mues-
tra, lo que sugiere que los datos no proceden de una distribución continua (puede que ello
ocurra debido al redondeo).
Además del diagrama de cajas, existen más métodos para comparar gráficamente dos
muestras. Ası́, las órdenes siguientes:
> library(stepfun)
> plot(ecdf(A), [Link]=FALSE, verticals=TRUE, xlim=range(A, B))
> plot(ecdf(B), [Link]=FALSE, verticals=TRUE, add=TRUE)
representan las dos funciones de distribución empı́ricas. Por otra parte la función qqplot
realizarı́a un gráfico cuantil-cuantil de las dos muestras.
El contraste de Kolmogorov-Smirnov, que sólo presupone que la distribución común es
continua, también puede aplicarse:
> [Link](A, B)

Two-sample Kolmogorov-Smirnov test

data: A and B
D = 0.5962, p-value = 0.05919
alternative hypothesis: [Link]

Warning message:
cannot compute correct p-values with ties in: [Link](A, B)
siéndole de aplicación la misma precaución del contraste de Wilcoxon.
Capı́tulo 9: Ciclos. Ejecución condicional 44

9 Ciclos. Ejecución condicional

9.1 Expresiones agrupadas


R es un lenguaje de expresiones, en el sentido de que el único tipo de orden que posee es
una función o expresión que devuelve un resultado. Incluso una asignación es una expresión,
cuyo resultado es el valor asignado1 y que puede utilizarse en cualquier sitio en que pueda
utilizarse una expresión. En particular es posible realizar asignaciones múltiples.
Las órdenes pueden agruparse entre llaves, {expr 1;. . . ; expr m}, en cuyo caso el
valor del grupo es el resultado de la última expresión del grupo que se haya evaluado.
Puesto que un grupo es por sı́ mismo una expresión, puede incluirse entre paréntesis y ser
utilizado como parte de una expresión mayor. Este proceso puede repetirse si se considera
necesario.

9.2 Órdenes de control

9.2.1 Ejecución condicional: la orden if


Existe una construcción condicional de la forma
> if (expr 1) expr 2 else expr 3
donde expr 1 debe producir un valor lógico, y si éste es verdadero, (T), se ejecutará expr 2.
Si es falso, (F), y se ha escrito la opción else, que es opcional, se ejecutará expr 3.
A menudo suelen utilizarse los operadores && y || como condiciones de una orden if.
En tanto que & y | se aplican a todos los elementos de un vector, && y || se aplican a
vectores de longitud uno y sólo evalúan el segundo argumento si es necesario, esto es, si el
valor de la expresión completa no se deduce del primer argumento.
Existe una versión vectorizada de la construcción if/else, que es la función ifelse,
que tiene la forma ifelse(condición, a, b) y devuelve un vector cuya longitud es la del
más largo de sus argumentos y cuyo elemento i es a[i] si condición[i] es cierta, y b[i]
en caso contrario.

9.2.2 Ciclos: Órdenes for, repeat y while


Existe una construcción repetitiva de la forma
> for (nombre in expr 1) expr 2
donde nombre es la variable de control de iteración, expr 1 es un vector (a menudo de la
forma m:n), y expr 2 es una expresión, a menudo agrupada, en cuyas sub-expresiones puede
aparecer la variable de control, nombre. expr 2 se evalúa repetidamente conforme nombre
recorre los valores del vector expr 1.
Por ejemplo, suponga que ind es un vector de indicadores de clase y se quieren hacer
gráficos de y sobre x, separados para cada clase. Una posibilidad es usar la función
coplot(), que veremos más adelante, que produce una matriz de gráficos correspondientes
a cada nivel del factor. Otra forma de hacerlo es usar la función for:
1
La asignación devuelve el resultado de modo invisible. Basta escribirla entre paréntesis para comprobarlo.
Capı́tulo 9: Ciclos. Ejecución condicional 45

> xc <- split(x, ind)


> yc <- split(y, ind)
> for (i in 1:length(yc)) {
plot(xc[[i]], yc[[i]]);
abline(lsfit(xc[[i]], yc[[i]]))
}
La función split() produce una lista de vectores dividiendo un vector de acuerdo a las
clases especificadas por un factor. Consulte la ayuda para obtener más detalles.
Nota: En R, la función for() se utiliza mucho menos que en lenguajes tradi-
cionales, ya que no aprovecha las estructuras de los objetos. El código que
trabaja directamente con las estructuras completas suele ser más claro y más
rápido.
Otras estructuras de repetición son
> repeat expr
y
> while (condición) expr
La función break se utiliza para terminar cualquier ciclo. Esta es la única forma (salvo
que se produzca un error) de finalizar un ciclo repeat.
La función next deja de ejecutar el resto de un ciclo y pasa a ejecutar el siguiente2 .
Las órdenes de control se utilizan habitualmente en la escritura de funciones, que se
tratarán en el Capı́tulo 10 [Escritura de funciones], página 46, donde se verán varios ejem-
plos.

2
No existe equivalente para esta orden en Fortran o Basic
Capı́tulo 10: Escritura de nuevas funciones 46

10 Escritura de nuevas funciones


Como hemos visto informalmente hasta ahora, R permite crear objetos del modo func-
tion, que constituyen nuevas funciones de R, que se pueden utilizar a su vez en expresiones
posteriores. En este proceso, el lenguaje gana considerablemente en potencia, comodidad y
elegancia, y aprender a escribir funciones útiles es una de las mejores formas de conseguir
que el uso de R sea cómodo y productivo.
Debemos recalcar que muchas de las funciones que se suministran con R, como mean,
var o postscript, están de hecho escritas en R y, por tanto, no difieren materialmente de
las funciones que pueda escribir el usuario.
Para definir una función debe realizar una asignación de la forma
> NombreDeFuncion <-function(arg 1, arg 2, ...) expresión
donde expresión es una expresión de R (normalmente una expresión agrupada) que utiliza
los argumentos arg i para calcular un valor que es devuelto por la función.
El uso de la función es normalmente de la forma NombreDeFuncion(expr 1,expr 2,...)
y puede realizarse en cualquier lugar en que el uso de una función sea correcto.

10.1 Ejemplos elementales


En primer lugar, consideremos una función que calcule el estadı́stico t de Student para
dos muestras realizando “todos los pasos”. Este es un ejemplo muy artificial, naturalmente,
ya que hay otros modos, mucho más sencillos, de obtener el mismo resultado.
La función se define del siguiente modo:
> DosMuestras <- function(y1, y2) {
n1 <- length(y1); n2 <- length(y2)
yb1 <- mean(y1); yb2 <- mean(y2)
s1 <- var(y1); s2 <- var(y2)
s <- ((n1-1)*s1 + (n2-1)*s2)/(n1+n2-2)
tst <- (yb1 - yb2)/sqrt(s2*(1/n1 + 1/n2))
tst
}
Una vez definida esta función, puede utilizarse para realizar un contraste de t de Student
para dos muestras, del siguiente modo:
> tStudent <- DosMuestras(datos$hombre, datos$mujer); tStudent
En segundo lugar, considere por ejemplo la creación de una función1 que calcule los
coeficientes de la proyección ortogonal del vector y sobre el espacio de las columnas de la
matriz X, esto es, la estimación de los coeficientes de regresión por mı́nimos cuadrados.
Esta tarea se realizarı́a normalmente con la función qr(); sin embargo es algo compleja y
compensa tener una función como la siguiente para usarla directamente.
Dado un vector, yn×1 , y una matriz, Xn×p , entonces definimos X\y del siguiente modo:
(X 0 X)− X 0 y, donde (X 0 X)− es la inversa generalizada de X 0 X.
Podemos definir la función Proyeccion del siguiente modo
1
En Matlab, serı́a la orden \
Capı́tulo 10: Escritura de nuevas funciones 47

> Proyeccion <- function(X, y) {


X <- qr(X)
[Link](X, y)
}
Una vez creada, puede utilizarla en cualquier expresión, como en la siguiente:
> CoefReg <- Proyeccion(matrizX, variabley)
La función lsfit() realiza la misma acción2 . También utiliza las funciones qr() y
[Link]() en la forma anterior para realizar esta parte del cálculo. Por lo tanto puede ser
interesante haber aislado esta parte en una función si se va a utilizar frecuentemente. Si
ello es ası́, probablemente serı́a conveniente construir un operador binario matricial para
que el uso sea más cómodo.

10.2 Cómo definir un operador binario


Si hubiésemos dado a la función Proyeccion un nombre delimitado por sı́mbolos de
porcentaje, %, por ejemplo de la forma
%barra%
podrı́a utilizarse como un operador binario en vez de con la forma funcional. Suponga, por
ejemplo, que elige3 como nombre, entre los sı́mbolos de porcentaje, el de !. La definición
de la función deberı́a comenzar ası́:
> "%!%" <- function(X, y) { ... }
donde hay que destacar la utilización de comillas. Una vez definida la función se utilizarı́a
de la forma X %!% y.
Los operadores producto matricial, %*%, y producto exterior, %o%, son ejemplos de ope-
radores binarios definidos de esta forma.

10.3 Argumentos con nombre. Valores predeterminados


Ya vimos en la Sección 2.3 [Generacion de sucesiones], página 9 que cuando los argumen-
tos se dan por nombre, “nombre=objeto”, el orden de los mismos es irrelevante. Además
pueden utilizarse ambas formas simultáneamente: se puede comenzar dando argumentos
por posición y después añadir argumentos por nombre.
Esto es, si la función fun1 está definida como
> fun1 <- function(datos, [Link], grafico, limite) {
[aquı́ irı́a la definición]
}
las siguientes llamadas a la función son equivalentes:
> resultado <- fun1(d, hd, T, 20)
> resultado <- fun1(d, hd, grafico=T, limite=20)
> resultado <- fun1(datos=d, limite=20, grafico=T, [Link]=hd)
2
Vea también los métodos descritos en Capı́tulo 11 [Modelos estadisticos en R], página 55
3
El uso del sı́mbolo \ para el nombre, como en Matlab, no es una elección conveniente, ya que presenta
ciertos problemas en este contexto.
Capı́tulo 10: Escritura de nuevas funciones 48

En muchos casos, puede suministrarse un valor predeterminado para algunos argumentos,


en cuyo caso al ejecutar la función el argumento puede omitirse si el valor predeterminado
es apropiado. Por ejemplo, si fun1 estuviese definida como
> fun1 <- function(datos, [Link], grafico=TRUE, limite=20) { ... }
la llamada a la función
> resultado <- fun1(d, hd)
serı́a equivalente a cualquiera de las tres llamadas anteriores. Tenga en cuenta que puede
modificar los valores predeterminados, como en el caso siguiente:
> resultado <- fun1(d, hd, limite=10)
Es importante destacar que los valores predeterminados pueden ser expresiones arbi-
trarias, que incluso involucren otros argumentos de la misma función, y no están restringidos
a ser constantes como en el ejemplo anterior.

10.4 El argumento ‘...’


Otra necesidad frecuente es la de que una función pueda pasar los valores de sus argu-
mentos a otra función. Por ejemplo, muchas funciones gráficas, como plot(), utilizan la
función par(), y permiten al usuario pasar los parámetros gráficos a par() para contro-
lar el resultado gráfico. (Véase Sección 12.4.1 [La funcion par()], página 76 para detalles
adicionales sobre la función par().) Esta acción puede realizarse incluyendo un argumento
adicional, “...”, en la función, que puede ser traspasado. A continuación se incluye un
bosquejo de ejemplo.
fun1 <- function(datos, [Link], grafico=TRUE, limite=20, ...) {
[Algunas órdenes]
if (grafico)
par(pch="*", ...)
[Más órdenes]
}

10.5 Asignaciones dentro de una función


Es fundamental tener en cuenta que cualquier asignación ordinaria realizada dentro de
una función es local y temporal y se pierde tras salir de la función. Por tanto, la asignación
X <- qr(X) no afecta al valor del argumento de la función en que se utiliza.
Para comprender completamente las reglas que gobiernan el ámbito de las asignaciones
en R es necesario familiarizarse con la noción de marco (frame) de evaluación. Este es un
tema complejo que no será tratado en este manual.
Si desea realizar asignaciones globales y permanentes dentro de una función, deberá
utilizar el operador de ‘superasignación’, <<-, o la función assign. Puede encontrar una
explicación más detallada consultando la ayuda.
El operador <<- es diferente en R y en S-Plus. Las diferencias serán tratadas en la
Sección 10.7 [Ambito], página 51.
Capı́tulo 10: Escritura de nuevas funciones 49

10.6 Ejemplos más complejos

10.6.1 Factores de eficiencia en diseño en bloques


Estudiaremos ahora un ejemplo de función más complejo: el cálculo de factores de
eficiencia en un diseño en bloques. (Algunos aspectos de este problema ya han sido tratados
en la Sección 5.3 [Variables indexadas utilizadas como indices], página 21.)
Un diseño en bloques está definido por dos factores, por ejemplo bloques (b niveles) y
variedades, (v niveles). Si Rv×v y Kb×b son las matrices de réplicas y tamaño de bloque,
y Nb×v , es la matriz de incidencia, entonces los factores de eficiencia se definen como los
autovalores de la matriz

E = Iv − R−1/2 N 0 K −1 N R−1/2 = Iv − A0 A,
donde A = K −1/2 N R−1/2 .
Por ejemplo, la función podrı́a escribirse ası́:
> EfiDisBlo <- function(bloques, variedades) {
bloques <- [Link](bloques) # pequeña precaución
b <- length(levels(bloques))
variedades <- [Link](variedades) # pequeña precaución
v <- length(levels(variedades))
K <- [Link](table(bloques)) # elimina el atributo dim
R <- [Link](table(variedades)) # elimina el atributo dim
N <- table(bloques, variedades)
A <- 1/sqrt(K) * N * rep(1/sqrt(R), rep(b, v))
sv <- svd(A)
list(eficiencia=1 - sv$d^2, cvbloques=sv$u, cvvariedad=sv$v)
}
Desde el punto de vista numérico, es levemente mejor trabajar con la función descom-
posición SVD en vez de con la función de los autovalores.
El resultado de esta función es una lista que contiene los factores de eficiencia como
primera componente, y que además incluye dos contrastes, puesto que, a veces, suministran
información adicional útil.

10.6.2 Cómo eliminar los nombres al imprimir una variable


indexada
Para imprimir grandes matrices o variables indexadas en general, a menudo es interesante
hacerlo en forma compacta sin los nombres de variables. La simple eliminación del atributo
dimnames no es suficiente, sino que la solución consiste en asignar a dicho atributo cadenas
de caracteres vacı́as. Por ejemplo, para imprimir la matriz X puede escribir
> temp <- X
> dimnames(temp) <- list(rep("", nrow(X)), rep("", ncol(X))
> temp; rm(temp)
Este resultado puede conseguirse fácilmente definiendo la función SinNombres, que
aparece a continuación, que da un pequeño rodeo para conseguir el mismo resultado al
Capı́tulo 10: Escritura de nuevas funciones 50

tiempo que ilustra el hecho de que las funciones pueden ser cortas y al mismo tiempo muy
efectivas y útiles.
SinNombres <- function(a) {
## Elimina los nombres de dimensiones para impresión compacta.
d <- list()
l <- 0
for(i in dim(a)) {
d[[l <- l + 1]] <- rep("", i)
}
dimnames(a) <- d
a
}
Una vez definida la función, para imprimir la matriz X en forma compacta basta con
escribir
> SinNombres(X)
Esta función es de especial utilidad al imprimir variables indexadas de tipo entero y de
gran tamaño, en que el interés real se centra más en los posibles patrones que en los valores
en sı́ mismos.

10.6.3 Integración numérica recursiva


Las funciones pueden ser recursivas e, incluso, pueden definir funciones en su interior.
Advierta, sin embargo, que dichas funciones, y por supuesto las variables, no son heredadas
por funciones llamadas en marcos de evaluación superior, como lo serı́an si estuviesen en la
trayectoria de búsqueda.
El ejemplo siguiente muestra una forma, un tanto ingenua, de realizar integración
numérica unidimensional recursivamente. El integrando se evalúa en los extremos del
intervalo y en el centro. Si el resultado de aplicar la regla del trapecio a un solo intervalo
es bastante próxima al resultado de aplicarlo a los dos, entonces este último valor se
considera el resultado. En caso contrario se aplica el procedimiento a cada uno de los
dos intervalos. El resultado es un proceso de integración adaptativo que concentra las
evaluaciones de la función en las regiones en que es menos lineal. Conlleva, sin embargo,
un gran consumo de recursos, y la función solo es competitiva con otros algoritmos cuando
el integrando es al tiempo suave y difı́cil de evaluar. El ejemplo es también un pequeño
rompecabezas de programación en R.
area <- function(f, a, b, eps = 1.0e-06, lim = 10) {
fun1 <- function(f, a, b, fa, fb, a0, eps, lim, fun) {
## La función ‘fun1’ sólo es visible dentro de ‘area’
d <- (a + b)/2
h <- (b - a)/4
fd <- f(d)
a1 <- h * (fa + fd)
a2 <- h * (fd + fb)
if(abs(a0 - a1 - a2) < eps || lim == 0)
return(a1 + a2)
else {
return(fun(f, a, d, fa, fd, a1, eps, lim - 1, fun) +
Capı́tulo 10: Escritura de nuevas funciones 51

fun(f, d, b, fd, fb, a2, eps, lim - 1, fun))


}
}
fa <- f(a)
fb <- f(b)
a0 <- ((fa + fb) * (b - a))/2
fun1(f, a, b, fa, fb, a0, eps, lim, fun1)
}

10.7 Ámbito
Este apartado es algo más técnico que otras partes de este documento. Sin embargo,
pormenoriza una de las mayores diferencias entre S-Plus y R.
Los sı́mbolos que tienen lugar en el cuerpo de una función se dividen en tres clases:
parámetros formales, variables locales y variables libres. Los parámetros formales son los
que aparecen en la lista de argumentos de la función y sus valores quedan determinados
por el proceso de asignación de los argumentos de la función a los parámetros formales.
Las variables locales son aquellas cuyos valores están determinados por la evaluación de
expresiones en el cuerpo de las funciones. Las variables que no son parámetros formales
ni variables locales se denominan variables libres. Las variables libres se transforman en
variables locales si se les asigna valor. Para aclarar los conceptos, consideremos la siguiente
función:
f <- function(x) {
y <- 2*x
print(x)
print(y)
print(z)
}
En esta función, x es un parámetro formal, y es una variable local y z es una variable
libre.
En R la asignación de valor a una variable libre se realiza consultando el entorno en el
que la función se ha creado, lo que se denomina ámbito léxico. En primer lugar definamos
la función cubo:
cubo <- function(n) {
sq <- function() n*n
n*sq()
}
La variable n de la función sq no es un argumento para esta función. Por tanto es una
variable libre y las reglas de ámbito deben utilizarse para determinar el valor asociado con
ella. En un ámbito estático (como en S-Plus) el valor es el asociado con una variable global
llamada n. En un ámbito léxico (como en R) es un parámetro para la función cubo puesto
que hay una asignación activa para la variable n en el momento en que se define la función
sq. La diferencia de evaluación entre R y S-Plus es que S-Plus intenta encontrar una
variable global llamada n en tanto que R primero intenta encontrar una variable llamada n
en el entorno creado cuando se activó cubo.
## primera evaluación en S
Capı́tulo 10: Escritura de nuevas funciones 52

S> cubo(2)
Error in sq(): Object "n" not found
Dumped
S> n <- 3
S> cubo(2)
[1] 18
## la misma función evaluada en R
R> cubo(2)
[1] 8
El ámbito lexicográfico puede utilizarse para conceder a las funciones un estado cam-
biante. En el siguiente ejemplo mostramos cómo puede utilizarse R para simular una cuenta
bancaria. Una cuenta bancaria necesita tener un balance o total, una función para realizar
depósitos, otra para retirar fondos, y una última para conocer el balance.
Conseguiremos esta capacidad creando tres funciones dentro de [Link] y de-
volviendo una lista que los contiene. Cuando se ejecuta [Link] toma un argumento
numérico, total, y devuelve una lista que contiene las tres funciones. Puesto que estas fun-
ciones están definidas dentro de un entorno que contiene a total, éstas tendrán acceso a
su valor.
El operador de asignación especial, <<-, se utiliza para cambiar el valor asociado con
total. Este operador comprueba los entornos creados desde el actual hasta el primero
hasta encontrar uno que contenga el sı́mbolo total y cuando lo encuentra, sustituye su
valor en dicho entorno por el valor de la derecha de la expresión. Si se alcanza el nivel
superior, correspondiente al entorno global, sin encontrar dicho sı́mbolo, entonces lo crea
en él y realiza la asignación. Para muchos usos, <<- crea una variable global y le asigna
el valor de la derecha de la expresión4 . Solo cuando <<- ha sido utilizado en una función
que ha sido devuelta como el valor de otra función ocurrirá la conducta especial que hemos
descrito.
[Link] <- function(total) {
list(
deposito = function(importe) {
if(importe <= 0)
stop("Los depósitos deben ser positivos!\n")
total <<- total + importe
cat("Depositado",importe,". El total es", total, "\n\n")
},
retirada = function(importe) {
if(importe > total)
stop("No tiene tanto dinero!\n")
total <<- total - importe
cat("Descontado", importe,". El total es", total,"\n\n")
},
balance = function() {
cat("El total es", total,"\n\n")
}
)
4
En cierto sentido esto emula la conducta en S-Plus puesto que en S-Plus este operador siempre crea o
asigna a una variable global.
Capı́tulo 10: Escritura de nuevas funciones 53

Antonio <- [Link](100)


Roberto <- [Link](200)

Antonio$retirada(30)
Antonio$balance()
Roberto$balance()

Antonio$deposit(50)
Antonio$balance()
Antonio$retirada(500)

10.8 Personalización del entorno


El usuario de R puede adaptar el entorno de trabajo a sus necesidades de varias formas.
Existe un archivo de inicialización del sistema y cada directorio puede tener su propio
archivo de inicialización especial. Por último, puede usar las funciones especiales .First y
.Last.
El archivo de inicialización del sistema se denomina Rprofile y se encuentra en el
subdirectorio library del directorio inicial de R. Las órdenes contenidas en este archivo
se ejecutan cada vez que se comienza una sesión de R, sea cual sea el usuario. Existe un
segundo archivo, personal, denominado .Rprofile5 que puede estar en cualquier directorio.
Si ejecuta R desde un directorio que contenga este archivo, se ejecutarán las órdenes que
incluya. Este archivo permite a cada usuario tener control sobre su espacio de trabajo y
permite disponer de diferentes métodos de inicio para diferentes directorios de trabajo.
Si no existe el archivo .Rprofile en el directorio inicial, entonces R buscará si existe el
archivo .Rprofile en el directorio inicial del usuario y, si existe, lo utilizará.
Si existe la función .First() (en cualquiera de los dos archivos de perfil o en el archivo
de imagen ‘.RData’) recibirá un tratamiento especial, ya que se ejecutará al comienzo de
la sesión de R, y por tanto puede utilizarse para inicializar el entorno. Por ejemplo, la
definición del siguiente ejemplo sustituye el sı́mbolo de R para que sea $ y establece otras
caracterı́sticas que quedan establecidas en el resto de la sesión.
En resumen, la secuencia en que se ejecutan los archivos es, ‘Rprofile’, ‘.Rprofile’,
‘.RData’ y por último la función .First(). Recuerde que cualquier definición en un archivo
posterior enmascarará las de un archivo precedente.
> .First <- function() {
options(prompt="$ ", continue="+\t")
# $ será el sı́mbolo de sistema
options(digits=5, length=999)
# personaliza números y resultados
x11()
# abre una ventana para gráficos
par(pch = "+")
# carácter para realización de gráficos
5
Por tanto, al comenzar su nombre con un punto, en UNIX, será un archivo oculto.
Capı́tulo 10: Escritura de nuevas funciones 54

source(paste(getwd(), "/R/MisOrdenes.R", sep = ""))


# ejecuta las órdenes contenidas en MisOrdenes.R
library(stepfun)
# conecta la biblioteca stepfun
}
De modo análogo, si existe la función .Last(), se ejecutará al término de la sesión. A
continuación se muestra un ejemplo de esta función.
> .Last <- function() {
[Link]()
# Una pequeña medida de seguridad.
cat(paste([Link](),"\nAdiós\n"))
# Ya es la hora de irse.
}

10.9 Clases. Funciones genéricas. Orientación a objetos


La clase de un objeto determina de qué modo será tratado por lo que se conoce como
funciones genéricas. Volviendo la oración por pasiva, una función será genérica si realiza
una tarea o acción sobre sus argumentos especı́fica de la clase de cada argumento. Si el
argumento carece del atributo clase, o lo posee de uno no contemplado especı́ficamente por
la función genérica en cuestión, se suministra una acción predeterminada.
El mecanismo de clase ofrece al usuario la posibilidad de diseñar y escribir funciones
genéricas para propósitos especiales. Entre otras funciones genéricas se encuentran plot(),
para representar objetos gráficamente, summary(), para realizar análisis descriptivos de
varios tipos, y anova(), para comparar modelos estadı́sticos.
El número de funciones genéricas que pueden tratar una clase de modo especı́fico puede
ser muy grande. Por ejemplo, entre las funciones que pueden tratar de modo especı́fico
objetos de la clase "[Link]" se encuentran
[ [[<- any [Link]
[<- model plot summary
Puede obtener la lista completa utilizando la función methods:
> methods(class="[Link]")
Como es esperable, el número de clases que una función genérica puede tratar también
puede ser grande. Por ejemplo, la función plot() tiene variantes, entre otras, para las
siguientes clases de objetos:
[Link] default density factor
También en este caso puede obtener la lista completa actual utilizando la función methods:
> methods(plot)
Capı́tulo 11: Modelos estadı́sticos en R 55

11 Modelos estadı́sticos en R
En este apartado, suponemos al lector familiarizado con la terminologı́a estadı́stica, en
particular con el análisis de regresión y el análisis de varianza. Posteriormente haremos
algunas suposiciones más ambiciosas, particularmente el conocimiento de modelos lineales
generalizados y regresión no lineal.
Los requisitos para el ajuste de modelos estadı́sticos están suficientemente bien definidos
para hacer posible la construcción de herramientas generales de aplicación a un amplio
espectro de problemas.
R contiene un conjunto de posibilidades que hace que el ajuste de modelos estadı́sticos
sea muy simple. Como hemos mencionado en la introducción, la salida básica es mı́nima, y
es necesario utilizar las funciones extractoras para obtener todos los detalles.

11.1 Definición de modelos estadı́sticos. Fórmulas


El ejemplo básico de un modelo estadı́stico es un modelo de regresión lineal con errores
independientes y homoscedásticos
p
X
yi = βj xij + ei , ei ∼ NID(0, σ 2 ), i = 1, . . . , n
j=0

En notación matricial puede escribirse

y = Xβ + e

donde y es el vector de respuesta, y X es la matriz del modelo o matriz de diseño, formada


por las columnas x0 , x1 , . . . , xp , que son las variables predictoras. Muy a menudo x0 será
una columna de unos y definirá el punto de corte o término independiente.

Ejemplos
Antes de dar una definición formal, algunos ejemplos ayudarán a centrar las ideas.
Supongamos que y, x, x0, x1, x2, . . . son variables numéricas, que X es una matriz y que
A, B, C, . . . son factores. Las fórmulas que aparecen en la parte izquierda de la siguiente
tabla, especifican los modelos estadı́sticos descritos en la parte de la derecha.
y∼x
y∼1+x Ambos definen el mismo modelo de regresión lineal de y sobre x. El primero
contiene el término independiente implı́cito y el segundo, explı́cito.
y∼0+x
y ∼ -1 + x
y ∼ x - 1 Regresión lineal de y sobre x sin término independiente, esto es, que pasa por
el origen de coordenadas.
log(y) ∼ x1 + x2
Regresión múltiple de la variable transformada, log(y), sobre x 1 y x 2 (con un
término independiente implı́cito).
Capı́tulo 11: Modelos estadı́sticos en R 56

y ∼ poly(x,2)
y ∼ 1 + x + I(x^2)
Regresión polinomial de y sobre x de segundo grado. La primera forma utiliza
polinomios ortogonales y la segunda utiliza potencias de modo explı́cito.
y ∼ X + poly(x,2)
Regresión múltiple de y con un modelo matricial consistente en la matriz X y
términos polinomiales en x de segundo grado.
y∼A Análisis de varianza de entrada simple de y, con clases determinadas por A.
y∼A+x Análisis de covarianza de entrada simple de y, con clases determinadas por A,
y con covariante x.
y ∼ A*B
y ∼ A + B + A:B
y ∼ B %in% A
y ∼ A/B Modelo no aditivo de dos factores de y sobre A y B. Los dos primeros es-
pecifican la misma clasificación cruzada y los dos últimos especifican la misma
clasificación anidada. En términos abstractos, los cuatro especifican el mismo
subespacio de modelos.
y ∼ (A + B + C)^2
y ∼ A*B*C - A:B:C
Experimento con tres factores con un modelo que contiene efectos principales e
interacciones de dos factores solamente. Ambas fórmulas especifican el mismo
modelo.
y∼A*x
y ∼ A/x
y ∼ A/(1 + x) - 1
Modelos de regresión lineal simple separados de y sobre x para cada nivel de
A. La última forma produce estimaciones explı́citas de tantos términos inde-
pendientes y pendientes como niveles tiene A.
y ∼ A*B + Error(C)
Un experimento con dos factores de tratamiento, A y B, y estratos de error
determinados por el factor C. Por ejemplo, un experimento split plot, con
gráficos completos (y por tanto también subgráficos) determinados por el factor
C.
El operador ∼ se utiliza para definir una fórmula de modelo en R. La forma, para un
modelo lineal ordinario es
respuesta ∼ op 1 term 1 op 2 term 2 op 3 term 3 . . .
donde
respuesta es un vector o una matriz (o una expresión que evalúe a un vector o matriz)
que definen, respectivamente, la o las variables respuesta
op i es un operador, bien +, bien -, que implica la inclusión o exclusión, respectiva-
mente, de un término en el modelo. El primero, +, es opcional.
term i es un término de uno de los siguientes tipos
Capı́tulo 11: Modelos estadı́sticos en R 57

• una expresión vectorial, una expresión matricial, o el número 1; o


• un factor; o
• una expresión de fórmula consistente en factores, vectores o matrices conec-
tados mediante operadores de fórmula.
En todos los casos, cada término define una colección de columnas que deben
ser añadidas o eliminadas de la matriz del modelo. Un 1 significa un término
independiente y está incluido siempre, salvo que se elimine explı́citamente.
Los operadores de fórmula son similares a la notación de Wilkinson y Rogers utilizada en
los programas Glim y Genstat. Un cambio inevitable es que el operador ‘.’ se ha sustituido
por ‘:’ puesto que el punto es un carácter válido para nombres de objetos en R. Un resumen
de la notación se encuentra en la siguiente tabla (basada en Chambers & Hastie, 1992, p.29).
Y ∼M Y se modeliza como M.
M1 +M2
Incluye M 1 y M 2.
M1 -M2
Incluye M 1 exceptuando los términos de M 2.
M1 :M2
El producto tensorial de M 1 y M 2. Si ambos son factores, corresponde al
factor “subclases”.
M 1 %in% M 2
Similar a M 1:M 2, pero con diferente codificación.
M1 *M2
M 1 + M 2 + M 1:M 2.
M1 /M2
M 1 + M 2 %in% M 1.
M ^n Todos los términos de M junto a las “interacciones” hasta el orden n
I(M ) Aı́sla M. Dentro de M todos los operadores tienen su sentido aritmético habitual
y este término aparece en la matriz del modelo.
Advierta que, dentro de los paréntesis que habitualmente rodean los argumentos de una
función, todos los operadores tienen su sentido aritmético habitual. La función I() es
la función identidad, utilizada solamente para poder introducir términos en las fórmulas,
definiéndolos mediante operadores aritméticos.
En particular, cuando las fórmulas especifican columnas de la matriz del modelo, la
especificación de los parámetros es implı́cita. Este no es el caso en otros contextos, por
ejemplo en la especificación de modelos no lineales.

11.1.1 Contrastes
Es necesario conocer, aunque sea someramente, el modo en que las fórmulas del modelo
determinan las columnas de la matriz del modelo. Esto es sencillo si las variables son
Capı́tulo 11: Modelos estadı́sticos en R 58

continuas, ya que cada una constituirá una columna de dicha matriz. Del mismo modo, si
el modelo incluye un término independiente, contribuirá con una columna de unos.
En el caso de un factor, A, con k niveles, la respuesta depende de si el factor es nominal
u ordinal. En el caso de un factor nominal, se generan k − 1 columnas correspondien-
tes a los indicadores desde el segundo hasta el k-ésimo nivel del factor. (Por tanto, la
parametrización implı́cita consiste en contrastar la respuesta del primer nivel frente a cada
uno de los restantes niveles.) En el caso de un factor ordinal, las k − 1 columnas son los
polinomios ortogonales sobre 1, ..., k, omitiendo el término constante.
Esta situación puede parecerle complicada, pero aún hay más. En primer lugar, si el
término independiente se omite en un modelo que contiene algún término de tipo factor, el
primero de dichos términos se codifica en k columnas correspondientes a los indicadores de
todos los niveles del factor. En segundo lugar, todo este comportamiento puede cambiarse
mediante el argumento contrasts de options. Los valores predeterminados son:
options(contrasts = c("[Link]", "[Link]"))
La razón por la que se indican estos valores es que los valores predeterminados en R son
distintos de los de S en el caso de factores nominales, ya que S utiliza los contrastes de
Helmert. Por tanto, para obtener los mismos resultados que en S-Plus, deberá escribir:
options(contrasts = c("[Link]", "[Link]"))
Esta diferencia es deliberada, ya que entendemos que los contrastes predeterminados de R
son más sencillos de interpretar para los principiantes.
Caben aún más posibilidades, ya que el esquema de contraste a utilizar puede fijarse
para cada término del modelo utilizando las funciones contrasts y C.
Tampoco hemos considerado los términos de interacción, que generan los productos de
las columnas introducidas por los términos de sus componentes.
Pese a que los detalles son complicados, las fórmulas de modelos en R generan habi-
tualmente los modelos que un estadı́stico experto podrı́a esperar, supuesto que se preserve
la marginalidad. Por ejemplo, el ajuste de un modelo con interacción y, sin embargo, sin
los correspondientes efectos principales conducirá en general a resultados sorprendentes, y
debe reservarse sólo a los especialistas.

11.2 Modelos lineales


La función primaria para el ajuste de modelos múltiples ordinarios es lm() y una versión
resumida de su uso es la siguiente:
> [Link] <- lm([Link], data=[Link])
Por ejemplo
> fm2 <- lm(y ∼ x1 + x2, data=produccion)
ajustará un modelo de regresión múltiple de y sobre x 1 y x 2 (con término independiente
implı́cito).
El término data=produccion, pese a ser opcional, es importante y especifica que
cualquier variable necesaria para la construcción del modelo debe provenir en primer
lugar de la hoja de datos produccion, y ello independientemente de que la hoja de datos
produccion haya sido conectada a la trayectoria de búsqueda o no.
Capı́tulo 11: Modelos estadı́sticos en R 59

11.3 Funciones genéricas de extracción de información del


modelo
El valor de lm() es el objeto modelo ajustado; que consiste en una lista de resultados de
clase lm. La información acerca del modelo ajustado puede imprimirse, extraerse, dibujarse,
etc. utilizando funciones genéricas orientadas a objetos de clase lm. Algunas de ellas son:
add1 coef effects kappa predict residuals
alias deviance family labels print step
anova drop1 formula plot proj summary
A continuación se incluye una breve descripción de las más utilizadas.

anova(objeto 1, objeto 2)
Compara un submodelo con un modelo externo y produce una tabla de análisis
de la varianza.
coefficients(objeto)
Extrae la matriz de coeficientes de regresión.
Forma reducida: coef(objeto).
deviance(objeto)
Suma de cuadrados residual, ponderada si es lo apropiado.
formula(objeto)
Extrae la fórmula del modelo.
plot(objeto)
Crea cuatro gráficos que muestran los residuos, los valores ajustados y algunos
diagnósticos
predict(objeto, newdata=[Link])
La nueva hoja de datos que se indica debe tener variables cuyas etiquetas coin-
cidan con las de la original. El resultado es un vector o matriz de valores
predichos correspondiente a los valores de las variables de [Link].
print(objeto)
Imprime una versión concisa del objeto. A menudo se utiliza implı́citamente.
residuals(objeto)
Extrae la matriz de residuos, ponderada si es necesario.
La forma reducida es resid(objeto).
step(objeto)
Selecciona un modelo apropiado añadiendo o eliminando términos y preservando
las jerarquı́as. Se devuelve el modelo que en este proceso tiene el máximo valor
de AIC1 .
summary(objeto)
Imprime un resumen estadı́stico completo de los resultados del análisis de re-
gresión.

1
Acrónimo de Akaike’s an Information Criterion
Capı́tulo 11: Modelos estadı́sticos en R 60

11.4 Análisis de varianza. Comparación de modelos


El análisis de varianza2 es otra de las técnicas aquı́ recogidas.
La función de ajuste de modelo aov([Link], data=[Link]) opera
en el nivel más simple de modo muy similar a la función lm(), y muchas de las funciones
genéricas contenidas en la Sección 11.3 [Funciones genericas de extraccion de informacion
del modelo], página 59, le son de aplicación.
Debemos destacar que, además, aov() realiza un análisis de modelos estratificados de
error múltiple, tales como experimentos split plot, o diseños en bloques incompletos bal-
anceados con recuperación de información inter-bloques. La fórmula
respuesta ∼ [Link] + Error([Link])
especifica un experimento multiestrato con estratos de error definidos por [Link].
En el caso más sencillo, [Link] es un factor, cuando define un experimento con
dos estratos, esto es, dentro de y entre los niveles de un factor.
Por ejemplo, si todas las variables predictoras son factores, un modelo como el siguiente:
> mf <- aov(cosecha ∼ v + n*p*k +
Error(granjas/bloques), data=[Link])
serı́a utilizable para describir un experimento con media del modelo v + n*p*k y tres estratos
de error: “entre granjas”, “dentro de granjas, entre bloques” y “dentro de bloques”.

11.4.1 Tablas ANOVA

Debe tenerse en cuenta que la tabla ANOVA corresponde a una sucesión de modelos
ajustados. Las sumas de cuadrados que en ella aparecen corresponden a la disminución en
las sumas de cuadrados residuales como resultado de la inclusión de un término concreto
en un lugar concreto de la sucesión. Por tanto el orden de inclusión sólo será irrelevante en
experimentos ortogonales.
Para experimentos multiestrato el procedimiento consiste, en primer lugar, en proyectar
la respuesta sobre los estratos de error, una vez más en secuencia, y, después, en ajustar
la media del modelo a cada proyección. Para más detalles, consulte Chambers & Hastie
(1992).
Una alternativa más flexible a la tabla ANOVA completa es comparar dos o más modelos
directamente utilizando la función anova().
> anova([Link].1, [Link].2, ...)
El resultado es una tabla ANOVA que muestra las diferencias entre los modelos ajustados
cuando se ajustan en ese orden preciso. Los modelos ajustados objeto de comparación
constituyen por tanto una sucesión jerárquica. Este resultado no suministra información
distinta a la del caso predeterminado, pero facilita su comprensión y control.

2
Su acrónimo en inglés es ANOVA, de ANalysis Of Variance. En alguna literatura en español, el acrónimo
se sustituye por ANDEVA, de ANálisis DE VArianza.
Capı́tulo 11: Modelos estadı́sticos en R 61

11.5 Actualización de modelos ajustados


La función update() se utiliza muy a menudo para ajustar un modelo que difiere de uno
ajustado previamente en unos pocos términos que se añaden o se eliminan. Su forma es:
> [Link] <- update([Link], fó[Link])
En fó[Link] puede utilizarse un punto, ‘.’, para hacer referencia a “la parte co-
rrespondiente de la fórmula del modelo anterior”. Por ejemplo,
> fm05 <- lm(y ∼ x1 + x2 + x3 + x4 + x5, data = produccion)
> fm6 <- update(fm05, . ∼ . + x6)
> smf6 <- update(fm6, sqrt(.) ∼ .)
ajusta una regresión múltiple con cinco variables, (procedentes si es posible) de la hoja de
datos produccion; después ajusta un modelo adicional incluyendo un sexto regresor; y, por
último, ajusta una variante del modelo donde se aplica una transformación raı́z cuadrada a
la variable predicha.
Advierta especialmente que si especifica el argumento data en la llamada original a la
función de ajuste del modelo, esta información se pasa a su vez a través del objeto modelo
ajustado a la función update() y sus asociadas.
El nombre “.” puede utilizarse también en otros contextos, pero con un significado
levemente distinto. Por ejemplo,
> [Link] <- lm(y ∼ . , data=produccion)
ajustará un modelo con respuesta y, y como variables predictoras, todas las de la hoja de
datos produccion.
Otras funciones que permiten explorar sucesiones crecientes de modelos son add1(),
drop1() y step(). Consulte la ayuda para obtener información de las mismas.

11.6 Modelos lineales generalizados


Los modelos lineales generalizados constituyen una extensión de los modelos lineales,
para tomar en consideración tanto distribuciones de respuestas no normales como transfor-
maciones para conseguir linealidad, de una forma directa. Un modelo lineal generalizado
puede describirse según las siguientes suposiciones:
• Existe una variable respuesta, y, y unas variables estı́mulo, x1 , x2 , . . . , cuyos valores
influyen en la distribución de la respuesta.
• Las variables estı́mulo influyen en la distribución de y mediante una función lineal
solamente. Esta función lineal recibe el nombre de predictor lineal, y se escribe habi-
tualmente
η = β1 x1 + β2 x2 + · · · + βp xp ,
por tanto xi no influye en la distribución de y si y sólo si βi = 0.
• La distribución de y es de la forma
A
 
fY (y; µ, ϕ) = exp {yλ(µ) − γ (λ(µ))} + τ (y, ϕ)
ϕ

donde ϕ es un parámetro de escala (posiblemente conocido) que permanece constante


para todas las observaciones, A representa una ponderación a priori que se supone
Capı́tulo 11: Modelos estadı́sticos en R 62

conocida pero que puede variar con las observaciones, y µ es la media de y. Por
tanto, se supone que la distribución de y queda determinada por su media y tal vez un
parámetro de escala.
• La media, µ, es una función inversible del predictor lineal:

µ = m(η), η = m−1 (µ) = `(µ)

y esta función inversa, `(), se denomina función de enlace.


Estas suposiciones son suficientemente amplias para acomodar una amplia clase de mode-
los útiles en la práctica estadı́stica y, al tiempo, suficientemente estrictas como para permitir
el desarrollo de una metodologı́a unificada de estimación e inferencia, al menos aproxi-
madamente. Los interesados en este tema pueden consultar cualquiera de los trabajos de
referencia sobre este tema, tales como McCullagh & Nelder (1989) o Dobson (1990).

11.6.1 Familias
La clase de modelos lineales generalizados que pueden ser tratados en R incluye las
distribuciones de respuesta gaussian (normal), binomial, poisson, inverse gaussian (nor-
mal inversa) y gamma ası́ como los modelos de quasi-likelihood (cuasi-verosimilitud) cuya
distribución de respuesta no está explı́citamente definida. En este último caso debe especi-
ficarse la función de varianza como una función de la media, pero en el resto de casos esta
función está implı́cita en la distribución de respuesta.
Cada distribución de respuesta admite una variedad de funciones de enlace para conec-
tar la media con el predictor lineal. La tabla siguiente recoge las que están disponibles
automáticamente.

Nombre de la familia Función de enlace


binomial logit, probit, cloglog
gaussian identity
Gamma identity, inverse, log
[Link] 1/mu^2
poisson identity, log, sqrt
quasi logit, probit, cloglog, identity, inverse,
log, 1/mu^2, sqrt

La combinación de una distribución de respuesta, una función de enlace y otras informa-


ciones que son necesarias para llevar a cabo la modelización se denomina familia del modelo
lineal generalizado.

11.6.2 La función glm


Puesto que la distribución de la respuesta depende de las variables de estı́mulo solamente
a través de una función lineal, se puede utilizar el mismo mecanismo de los modelos lineales
para especificar la parte lineal de un modelo generalizado. Sin embargo la familia debe
especificarse de modo distinto.
La función glm() permite ajustar un modelo lineal generalizado y tiene la forma siguiente
Capı́tulo 11: Modelos estadı́sticos en R 63

> [Link] <- glm([Link], family =


[Link], data=[Link])
La única caracterı́stica nueva es [Link] que es el instrumento mediante el
que se describe la familia. Es el nombre de una función que genera una lista de funciones y
expresiones que, juntas, definen y controlan el modelo y el proceso de estimación. Aunque
puede parecer complicado a primera vista, su uso es bastante sencillo.
Los nombres de los generadores de familias estándar suministrados con R aparecen en
la tabla de la Sección 11.6.1 [Familias], página 62 con la denominación de “Nombre de
la familia”. Si además debe seleccionar una función de enlace, debe indicarla como un
parámetro, entre paréntesis, del nombre de la familia. En el caso de la familia quasi, la
función de varianza puede especificarse del mismo modo.
Veamos algunos ejemplos.

La familia gaussiana (gaussian)


Una expresión de la forma
> mf <- glm(y ∼ x1 + x2, family = gaussian, data = ventas)
obtiene el mismo resultado que
> mf <- lm(y ∼ x1+x2, data=ventas)
pero con menor eficiencia. Tenga en cuenta que la familia gaussiana no dispone automáti-
camente de una serie de funciones de enlace, por lo que no admite parámetros. Si en un
problema necesita utilizar la familia gaussiana con un enlace no estándar, la solución pasa
por el uso de la familia quasi, como veremos posteriormente.

La familia binomial (binomial)


Consideremos el siguiente ejemplo artificial, tomado de Silvey (1970).
Los hombres de la isla de Kalythos, en el mar Egeo, sufren una enfermedad ocular
congénita cuyos efectos se acrecientan con la edad. Se tomó una muestra de varios isleños
de diferentes edades cuyos resultados se muestran a continuación:
Edad: 20 35 45 55 70
No. sujetos: 50 50 50 50 50
No. 6 17 26 37 44
invidentes:
Consideramos el problema de ajustar un modelo logı́stico y otro probit a estos datos, y
estimar en cada modelo el parámetro LD50, correspondiente a la edad en que la probabilidad
de ceguera es del 50%.
Si y es el número de invidentes a la edad x, y n es el número de sujetos estudiados,
ambos modelos tienen la forma

y ∼ B(n, F (β0 + β1 x))


donde, para el caso probit, F (z) = Φ(z) es la función de distribución normal (0,1), y en el
caso logit (que es el predeterminado), F (z) = ez /(1 + ez ). En ambos casos, LD50 se define
como
LD50 = −β0 /β1
Capı́tulo 11: Modelos estadı́sticos en R 64

Esto es, el punto en que el argumento de la función de distribución es cero.


El primer paso es preparar los datos en una hoja de datos
> kalythos <- [Link](x = c(20,35,45,55,70), n = rep(50,5),
y = c(6,17,26,37,44))
Para ajustar un modelo binomial utilizando glm() existen dos posibilidades para la
respuesta:
• Si la respuesta es un vector, entonces debe corresponder a datos binarios y por tanto
sólo debe contener ceros y unos.
• Si la respuesta es una matriz de dos columnas, la primera columna debe contener el
número de éxitos y la segunda el de fracasos.

Aquı́ vamos a utilizar la segunda de estas convenciones, por lo que debemos añadir una
matriz a la hoja de datos:
> kalythos$Ymat <- cbind(kalythos$y, kalythos$n - kalythos$y)
Para ajustar los modelos utilizamos
> fmp <- glm(Ymat ∼ x, family = binomial(link=probit), data = kalythos)
> fml <- glm(Ymat ∼ x, family = binomial, data = kalythos)
Puesto que la función de enlace logit es la predeterminada, este parámetro puede omitirse
en la segunda expresión. Para ver los resultados de cada ajuste usaremos
> summary(fmp)
> summary(fml)
Ambos modelos se ajustan bien (demasiado bien). Para estimar LD50 podemos usar la
siguiente función:
> ld50 <- function(b) -b[1]/b[2]
> ldp <- ld50(coef(fmp)); ldl <- ld50(coef(fmp)); c(ldp, ldl)
y obtendremos los valores 43.663 y 43.601 respectivamente.

Modelos de Poisson (poisson)

Para la familia poisson el enlace predeterminado es log, y en la práctica el uso fun-


damental de esta familia es ajustar modelos loglineales de Poisson a datos de frecuencias,
cuya distribución en sı́ es a menudo multinomial. Este es un tema amplio e importante que
no discutiremos aquı́ y que incluso constituye una parte fundamental de la utilización de
modelos generalizados no gaussianos.
A veces surgen datos auténticamente poissonianos que, en el pasado se analizaban a
menudo como datos gaussianos tras aplicarles una transformación logarı́tmica o de raı́z
cuadrada. Como alternativa al último, puede ajustarse un modelo lineal generalizado de
Poisson, como en el siguiente ejemplo:
> fmod <- glm(y ∼ A + B + x, family = poisson(link=sqrt),
data = [Link])
Capı́tulo 11: Modelos estadı́sticos en R 65

Modelos de cuasi-verosimilitud (quasi)


En todas las familias, la varianza de la respuesta dependerá de la media, y el parámetro de
escala actuará como un multiplicador. La forma de dependencia de la varianza respecto de la
media es una caracterı́stica de la distribución de respuesta, por ejemplo para la distribución
de Poisson será Var[y] = µ.
Para estimación e inferencia de cuasi-verosimilitud, la distribución de respuesta precisa
no está especificada, sino más bien sólo la función de enlace y la forma en que la función
de varianza depende de la media. Puesto que la estimación de cuasi-verosimilitud utiliza
formalmente las mismas técnicas de la distribución gaussiana, esta familia permite ajustar
modelos gaussianos con funciones de enlace no estándar o incluso con funciones de varianza.
Por ejemplo, consideremos la regresión no lineal siguiente

θ 1 z1
y= +e
z2 − θ 2

que puede escribirse también de la forma

1
y= +e
β1 x1 + β2 x2

donde x1 = z2 /z1 , x2 = −1/x1 , β1 = 1/θ1 y β2 = θ2 /θ1 . Suponiendo que existe una hoja de
datos apropiada, bioquimica, podemos ajustar este modelo mediante
> nlfit <- glm(y ∼ x1 + x2 - 1,
family = quasi(link=inverse, variance=constant),
data = bioquimica)
Si desea mayor información, lea las ayudas correspondientes.

11.7 Modelos de Mı́nimos cuadrados no lineales y de


Máxima verosimilitud
Ciertas formas de modelos no lineales pueden ajustarse mediante Modelos Lineales Ge-
neralizados, con la función glm(), pero en la mayorı́a de los casos será necesario utilizar
optimización no lineal. La función que lo realiza en R es nlm(), que reemplaza aquı́ a
las funciones ms() y nlmin() de S-Plus. Buscamos los valores de los parámetros que
minimizan algún ı́ndice de falta de ajuste y nlm() lo resuelve probando varios parámetros
iterativamente. Al contrario que en la regresión lineal, por ejemplo, no existe garantı́a de
que el procedimiento converja a unos estimadores satisfactorios. La función nlm() necesita
unos valores iniciales de los parámetros a estimar y la convergencia depende crı́ticamente
de la calidad de dichos valores iniciales.

11.7.1 Mı́nimos cuadrados


Una forma de ajustar un modelo no lineal es minimizar la suma de los cuadrados de
los errores o residuos (SSE). Este método tiene sentido si los errores observados pueden
proceder de una distribución normal.
Presentamos un ejemplo debido a Bates & Watts (1988), página 51. Los datos son:
Capı́tulo 11: Modelos estadı́sticos en R 66

> x <- c(0.02, 0.02, 0.06, 0.06, 0.11, 0.11, 0.22, 0.22, 0.56, 0.56,
1.10, 1.10)
> y <- c(76, 47, 97, 107, 123, 139, 159, 152, 191, 201, 207, 200)
El modelo a ajustar es:
> fn <- function(p) sum((y - (p[1] * x)/(p[2] + x))^2)
Para realizar el ajuste necesitamos unos valores iniciales de los parámetros. Una forma de
encontrar unos valores iniciales apropiados es representar gráficamente los datos, conjeturar
unos valores de los parámetros, y dibujar sobre los datos la curva correspondiente a estos
valores.
> plot(x, y)
> xajustado <- seq(.02, 1.1, .05)
> yajustado <- 200*xajustado/(.1+xajustado)
> lines(spline(xajustado,yajustado))
Aunque se podrı́a tratar de encontrar unos valores mejores, los valores obtenidos, 200 y
0.1, parecen adecuados. Ya podemos realizar el ajuste:
> resultado <- nlm(fn,p=c(200,.1),hessian=TRUE)
Tras el ajuste, resultado$minimum contiene SSE, y resultado$estimates contiene los
estimadores por mı́nimos cuadrados de los parámetros. Para obtener los errores tı́picos
aproximados de los estimadores (SE) escribimos lo siguiente:
> sqrt(diag(2*resultado$minimum/(length(y) - 2) *
solve(resultado$hessian)))
El número 2 en dicha expresión representa el número de parámetros. Un intervalo de
confianza al 95% será: El estimador del parámetro ± 1.96 SE. Podemos representar el ajuste
en un nuevo gráfico:
> plot(x,y)
> xajustado <- seq(.02,1.1,.05)
> yajustado <- 212.68384222*xajustado/(0.06412146+xajustado)
> lines(spline(xajustado,yajustado))
La biblioteca nls contiene muchas más posibilidades para ajustar modelos no lineales por
mı́nimos cuadrados. El modelo que acabamos de ajustar es el modelo de Michaelis-Menten,
por tanto podemos usar
> df <- [Link](x=x, y=y)
> fit <- nls(y ∼ SSmicmen(x, Vm, K), df)
> fit
Nonlinear regression model
model: y ∼ SSmicmen(x, Vm, K)
data: df
Vm K
212.68370711 0.06412123
residual sum-of-squares: 1195.449
> summary(fit)

Formula: y ∼ SSmicmen(x, Vm, K)

Parameters:
Estimate Std. Error t value Pr(>|t|)
Capı́tulo 11: Modelos estadı́sticos en R 67

Vm 2.127e+02 6.947e+00 30.615 3.24e-11


K 6.412e-02 8.281e-03 7.743 1.57e-05

Residual standard error: 10.93 on 10 degrees of freedom

Correlation of Parameter Estimates:


Vm
K 0.7651

11.7.2 Máxima verosimilitud


El método de máxima verosimilitud es un método de ajuste de un modelo no lineal que
puede aplicarse incluso cuando los errores no son normales. El método busca los valores de
los parámetros que maximizan la log-verosimilitud o, lo que es igual, minimizan el valor de
la -log-verosimilitud. El siguiente ejemplo, tomado de Dobson (1990), pp. 108-11, ajusta
un modelo logı́stico a los datos de dosis-respuesta, que claramente también podrı́a ajustarse
utilizando la función glm(). Los datos son:
> x <- c(1.6907, 1.7242, 1.7552, 1.7842, 1.8113,
1.8369, 1.8610, 1.8839)
> y <- c( 6, 13, 18, 28, 52, 53, 61, 60)
> n <- c(59, 60, 62, 56, 63, 59, 62, 60)
La -log-verosimilitud a minimizar es:
> fn <- function(p)
sum( - (y*(p[1]+p[2]*x) - n*log(1+exp(p[1]+p[2]*x))
+ log(choose(n, y)) ))
Elegimos unos valores iniciales y realizamos el ajuste:
> out <- nlm(fn, p = c(-50,20), hessian = TRUE)
Tras lo cual, resultado$minimum contiene la -log-verosimilitud, y resultado$estimates
contiene los estimadores de máxima verosimilitud. Para obtener los SE aproximados de los
parámetros, escribimos:
> sqrt(diag(solve(out$hessian)))
El intervalo de confianza al 95% es: El estimador del parámetro ± 1.96 SE.

11.8 Algunos modelos no-estándar


Concluimos esta parte con una breve mención a otras posibilidades de R para regresión
especial y análisis de datos.
• Modelos mezclados. La biblioteca creada por usuarios, nlme, contiene las funciones lme
y nlme para modelos de efectos mezclados lineales y no lineales, esto es, regresiones
lineales y no lineales en las cuales algunos coeficientes corresponden a efectos aleatorios.
Estas funciones hacen un uso intensivo de las fórmulas para especificar los modelos.
• Regresión con aproximación local. La función loess() ajusta una regresión no
paramétrica utilizando regresión polinomial localmente ponderada. Este tipo de
regresión es útil para poner de relieve una tendencia en datos confusos o para reducir
datos y obtener alguna luz sobre la estructura de grandes conjuntos de datos.
Capı́tulo 11: Modelos estadı́sticos en R 68

La biblioteca modreg contiene la función loess ası́ como posibilidades de regresión


“projection pursuit”.
• Regresión robusta. Existen varias funciones para ajustar modelos de regresión
resistentes a la influencia de valores anómalos (outliers) en los datos. La función
lqs en la biblioteca del mismo nombre contiene los algoritmos más recientes para
ajustes altamente resistentes. Otras funciones menos resistentes pero más eficientes
estadı́sticamente se encuentran en otras bibliotecas creadas por usuarios, por ejemplo
la función rlm en la biblioteca MASS.
• Modelos aditivos. Esta técnica intenta construir una función de regresión a partir de
funciones aditivas suaves de las variables predictoras, habitualmente una por cada va-
riable predicha. Las funciones avas y ace en la biblioteca acepack y las funciones bruto
y mars de la biblioteca mda son ejemplos de estas técnicas contenidas en bibliotecas
creadas por usuarios para R.
• Modelos basados en árboles. En vez de buscar un modelo lineal global explı́cito para
predicción o interpretación, los modelos basados en árboles intentan bifurcar los datos,
recursivamente, en puntos crı́ticos de las variables predictoras con la finalidad de con-
seguir una partición de los datos en grupos tan homogéneos dentro del grupo y tan
heterogéneos de un grupo a otro, como sea posible. Los resultados a menudo con-
ducen a una comprensión de los datos que otros métodos de análisis de datos no suelen
suministrar.
Los modelos se especifican en la forma de un modelo lineal ordinario. La función de
ajuste es tree(), y muchas funciones genéricas, como plot() y text() pueden mostrar
los resultados de este ajuste de modo gráfico.
Puede encontrar estos modelos en las bibliotecas creadas por usuarios rpart y tree.
Capı́tulo 12: Procedimientos gráficos 69

12 Procedimientos gráficos
Las posibilidades gráficas son un componente de R muy importante y versátil. Es posi-
ble utilizarlas para mostrar una amplia variedad de gráficos estadı́sticos y también para
construir nuevos tipos de gráficos.
Los gráficos pueden usarse tanto en modo interactivo como no interactivo, pero en la
mayorı́a de los casos el modo interactivo es más productivo. Además al iniciar R en este
modo, se activa un dispositivo para mostrar gráficos. Aunque este paso es automático, es
útil conocer que la orden es X11(), aunque también puede usar windows() en Microsoft
Windows.
Las órdenes gráficas se dividen en tres grupos básicos:
• Alto nivel Son funciones que crean un nuevo gráfico, posiblemente con ejes, etiquetas,
tı́tulos, etc..
• Bajo nivel Son funciones que añaden información a un gráfico existente, tales como
puntos adicionales, lı́neas y etiquetas.
• Interactivas Son funciones que permiten interactuar con un gráfico, añadiendo o elimi-
nando información, utilizando un dispositivo apuntador, como un ratón.
Además, en R existe una lista de parámetros gráficos que pueden utilizarse para adaptar
los gráficos.

12.1 Funciones gráficas de nivel alto


Las órdenes gráficas de nivel alto están diseñadas para generar un gráfico completo a
partir de unos datos pasados a la función como argumento. Cuando es necesario se generan
automáticamente ejes, etiquetas o tı́tulos (salvo que se especifique lo contrario). Estas
órdenes comienzan siempre un nuevo gráfico, borrando el actual si ello es necesario.

12.1.1 La función plot


Una de las funciones gráficas más utilizadas en R es plot, que es una función genérica,
esto es, el tipo de gráfico producido es dependiente de la clase del primer argumento.
plot(x, y)
plot(xy) Si x e y son vectores, plot(x, y) produce un diagrama de dispersión de y
sobre x. El mismo efecto se consigue suministrando un único argumento (como
se ha hecho en la segunda forma) que sea bien una lista con dos elementos, x e
y, bien una matriz con dos columnas.
plot(x) Si x es una serie temporal, produce un gráfico temporal, si x es un vector
numérico, produce un gráfico de sus elementos sobre el ı́ndice de los mismos, y
si x es un vector complejo, produce un gráfico de la parte imaginaria sobre la
real de los elementos del vector.
plot(f )
plot(f, y)
Sean f un factor, e y un vector numérico. La primera forma genera un diagrama
de barras de f ; la segunda genera diagramas de cajas de y para cada nivel de f.
Capı́tulo 12: Procedimientos gráficos 70

plot(hd)
plot(∼ expr)
plot(y ∼ expr)
Sean hd, una hoja de datos; y, un objeto cualquiera; y expr, una lista de
nombres de objetos separados por sı́mbolos ‘+’ (por ejemplo, a + b + c). Las
dos primeras formas producen diagramas de todas las parejas de variables de
la hoja de datos hd (en el primer caso) y de los objetos de la expresión expr
(en el segundo caso). La tercera forma realiza sendos gráficos de y sobre cada
objeto nombrado en la expresión expr (uno para cada objeto).

12.1.2 Representación de datos multivariantes

R posee dos funciones muy útiles para representar datos multivariantes. Si X es una
matriz numérica o una hoja de datos, la orden
> pairs(X)
produce una matriz de gráficos de dispersión para cada pareja de variables definidas por
las columnas de X, esto es, cada columna de X se representa frente a cada una de las demás
columnas, y los n(n−1) gráficos se presentan en una matriz de gráficos con escalas constantes
sobre las filas y columnas de la matriz.
Cuando se trabaja con tres o cuatro variables, la función coplot puede ser más apro-
piada. Si a y b son vectores numéricos y c es un vector numérico o un factor (todos de la
misma longitud) entonces la orden
> coplot(a ∼ b | c)
produce diagramas de dispersión de a sobre b para cada valor de c. Si c es un factor,
esto significa que a se representa sobre b para cada nivel de c. Si c es un vector numérico,
entonces se agrupa en intervalos y para cada intervalo se representa a sobre b para los valores
de c dentro del intervalo. El número y tamaño de los intervalos puede controlarse con el
argumento [Link] de la función coplot(). La función [Link]() también es
útil para seleccionar intervalos. Asimismo, es posible utilizar dos variables condicionantes
con una orden como
> coplot(a ∼ b | c + d)
que produce diagramas de a sobre b para cada intervalo de condicionamiento de c y d.
Las funciones coplot() y pairs() utilizan el argumento panel para personalizar el tipo
de gráfico que aparece en cada panel o recuadro. El valor predeterminado es points() para
producir un diagrama de dispersión, pero si se introducen otras funciones gráficas de nivel
bajo de los dos vectores x e y como valor de panel, se produce cualquier tipo de gráfico que
se desee. Una función útil en este contexto es [Link]().

12.1.3 Otras representaciones gráficas

Existen otras funciones gráficas de nivel alto que producen otros tipos de gráficos. Al-
gunas de ellas son las siguientes:
Capı́tulo 12: Procedimientos gráficos 71

qqnorm(x)
qqline(x)
qqplot(x, y)
Gráficos de comparación de distribuciones. El primero representa el vector x
sobre los valores esperados normales. El segundo le añade una recta que pasa
por los cuartiles de la distribución y de los datos. El tercero representa los
cuantiles de x sobre los de y para comparar sus distribuciones respectivas.
hist(x)
hist(x, nclass=n)
hist(x, breaks=b, ...)
Produce un histograma del vector numérico x. El número de clases se cal-
cula habitualmente de modo correcto, pero puede elegir uno con el argumento
nclass, o bien especificar los puntos de corte con el argumento breaks. Si está
presente el argumento probability=TRUE, se representan frecuencias relativas
en vez de absolutas.
dotplot(x, ...)
Construye un gráfico de puntos de x. En este tipo de gráficos, el eje y etiqueta
los datos de x y el eje x da su valor. Por ejemplo, permite una selección visual
sencilla de todos los elementos con valores dentro de un rango determinado.
image(x, y, z, ...)
contour(x, y, z, ...)
persp(x, y, z, ...)
Gráficos tridimensionales. image representa una retı́cula de rectángulos con
colores diferentes según el valor de z, contour representa curvas de nivel de z,
y persp representa una superficie tridimensional de z.

12.1.4 Argumentos de las funciones gráficas de nivel alto


Existe una serie de argumentos que pueden pasarse a las funciones gráficas de nivel alto,
entre otros los siguientes:
add=TRUE Obliga a la función a comportarse como una función a nivel bajo, de modo que
el gráfico que genere se superpondrá al gráfico actual, en vez de borrarlo (sólo
en algunas funciones)
axes=FALSE
Suprime la generación de ejes. Útil para crear ejes personalizados con la función
axis. El valor predeterminado es axes=TRUE, que incluye los ejes.
log="x"
log="y"
log="xy" Hace que el eje x, el eje y, o ambos ejes, sean logarı́tmicos. En algunos gráficos
no tiene efecto.
type= Este argumento controla el tipo de gráfico producido, de acuerdo a las siguientes
posibilidades:
type="p" Dibuja puntos individuales. Este es el valor predeterminado
Capı́tulo 12: Procedimientos gráficos 72

type="l" Dibuja lı́neas


type="b" Dibuja puntos y lı́neas que los unen
type="o" Dibuja puntos y lı́neas que los unen, cubriéndolos
type="h" Dibuja lı́neas verticales desde cada punto al eje X
type="s"
type="S" Dibuja un gráfico de escalera. En la primera forma, la escalera
comienza hacia la derecha, en la segunda, hacia arriba.
type="n" No se realiza ningún gráfico, aunque se dibujan los ejes (salvo indi-
cación en contra) y se prepara el sistema de coordenadas de acuerdo
a los datos. Suele utilizarse para crear gráficos en los que a conti-
nuación se utilizarán órdenes de nivel bajo.
xlab=cadena
ylab=cadena
Definen las etiquetas de los ejes x e y, en vez de utilizar las etiquetas prede-
terminadas, que normalmente son los nombres de los objetos utilizados en la
llamada a la función gráfica de nivel alto.
main=cadena
Tı́tulo del gráfico, aparece en la parte superior con tamaño de letra grande.
sub=cadena
Subtı́tulo del gráfico, aparece debajo del eje x con tamaño de letra pequeño.

12.2 Funciones gráficas de nivel bajo


A veces las funciones gráficas de nivel alto no producen exactamente el tipo de gráfico
deseado. En este caso pueden añadirse funciones gráficas de nivel bajo para añadir infor-
mación adicional (tal como puntos, lı́neas o texto) al gráfico actual.
Algunas de las funciones gráficas de nivel bajo más usuales son:
points(x, y)
lines(x, y)
Añaden puntos o lı́neas conectadas al gráfico actual. El argumento type de la
función plot() puede pasarse a esta funciones (y su valor predeterminado es
"p" para points y "l" para lines.)
text(x, y, etiquetas, ...)
Añade texto al gráfico en las coordenadas x, y. Normalmente, etiquetas, es
un vector de enteros o de caracteres, en cuyo caso, etiquetas[i] se dibuja en
el punto (x[i], y[i]). El valor predeterminado es 1:length(x).
Nota: Esta función se utiliza a menudo en la secuencia
> plot(x, y, type="n"); text(x, y, nombres)
El parámetro gráfico type="n" suprime los puntos pero construye los ejes, y la
función text permite incluir caracteres especiales para representar los puntos,
como se especifica en el vector de caracteres nombres.
Capı́tulo 12: Procedimientos gráficos 73

abline(a, b)
abline(h=y)
abline(v=x)
abline([Link])
Añade al gráfico actual, una recta de pendiente b y ordenada en el origen a.
La forma h=y representa una recta horizontal de altura y, y la forma v=x,
una similar, vertical. En el cuarto caso, [Link] puede ser una lista con un
componente coefficients de longitud 2 (como el resultado de una función de
ajuste de un modelo) que se interpretan como ordenada y pendiente, en ese
orden.
polygon(x, y, ...)
Añade al gráfico actual un polı́gono cuyos vértices son los elementos de (x,y);
(opcionalmente) sombreado con lı́neas, o relleno de color si el periférico lo ad-
mite.
legend(x, y, letrero, ...)
Añade al gráfico actual un letrero o leyenda, en la posición especificada. Los
caracteres para dibujar, los estilos de lı́neas, los colores, etc. están identificados
con los elementos del vector letrero. Debe darse al menos otro argumento
más, v, un vector de la misma longitud que letrero, con los correspondientes
valores de dibujo, como sigue:
legend( , fill=v)
Colores para rellenar
legend( , col=v)
Colores de puntos y lı́neas
legend( , lty=v)
Tipos de lı́nea
legend( , lwd=v)
Anchura de lı́nea
legend( , pch=v)
Caracteres para dibujar (vector de caracteres)
title(main, sub)
Añade un tı́tulo, main, en la parte superior del gráfico actual, de tamaño grande,
y un subtı́tulo, sub, en la parte inferior, de tamaño menor.
axis(side, ...)
Añade al gráfico actual un eje en el lado indicado por el primer argumento (de
1 a 4, en el sentido de las agujas del reloj, siendo el 1 la parte inferior). Otros
argumentos controlan la posición de los ejes, dentro o fuera del gráfico, las
marcas y las etiquetas. Es útil para añadir ejes tras utilizar la función plot()
con el argumento axes=FALSE.
Las funciones gráficas de nivel bajo necesitan normalmente alguna información de
posición, como las coordenadas x e y, para determinar dónde colocar los nuevos elementos.
Las coordenadas se dan en términos de coordenadas de usuario, las cuales están definidas
Capı́tulo 12: Procedimientos gráficos 74

por las funciones gráficas de alto nivel previas y se toman en función de los datos
suministrados a estas funciones.
Los dos argumentos, x e y, pueden sustituirse por un solo argumento de clase lista con dos
componentes llamados x e y, o por una matriz con dos columnas. De este modo, funciones
como locator(), que vemos a continuación, pueden usarse para especificar interactivamente
posiciones en un gráfico.

12.2.1 Anotaciones matemáticas


En muchas ocasiones es conveniente añadir sı́mbolos matemáticos y fórmulas a un gráfico.
En R es posible hacerlo especificando una expresión, en vez de una cadena de caracteres,
en cualquiera de las funciones text, mtext, axis o title. Por ejemplo, la orden siguiente
dibuja la fórmula de la distribución binomial en la posición x, y:
> text(x, y, expression(paste(bgroup("(", atop(n, x), ")"),
p^x, q^{n-x})))
Puede obtener información detallada con las órdenes:
> help(plotmath)
> example(plotmath)

12.2.2 Fuentes vectoriales Hershey


Es posible escribir texto utilizando las fuentes vectoriales Hershey en las funciones text
y contour. Existen tres razones para utilizar estas fuentes:
• Producen mejores resultados, especialmente en pantalla, con textos rotados o de
pequeño tamaño.
• Contienen sı́mbolos que pueden no estar disponibles en las fuentes ordinarias, como
signos del zodiaco, cartográficos o astronómicos.
• Contienen caracteres cirı́licos y japoneses (Kana y Kanji).
La información detallada, incluyendo las tablas de caracteres, puede obtenerla con las
órdenes:
> help(Hershey)
> example(Hershey)
> help(Japanese)
> example(Japanese)

12.3 Funciones gráficas interactivas


R dispone de funciones que permiten al usuario extraer o añadir información a un gráfico
utilizando el ratón. La más sencilla es la función locator():
locator(n, type)
Permite que el usuario seleccione posiciones del gráfico actual, fundamental-
mente, utilizando el botón primario del ratón, hasta que haya seleccionado n
puntos (el valor predeterminado son 512) o pulse el botón secundario. El argu-
mento type permite dibujar utilizando los puntos seleccionados con el mismo
significado que en las funciones de nivel alto. Su valor predeterminado es no
Capı́tulo 12: Procedimientos gráficos 75

dibujar. La función locator() devuelve las coordenadas de los puntos selec-


cionados en una lista con dos componentes, x e y.
La función locator() suele utilizarse sin argumentos. Es particularmente útil para
seleccionar interactivamente posiciones para elementos gráficos tales etiquetas cuando es
difı́cil calcular previamente dónde deben colocarse. Por ejemplo, para colocar un texto
informativo junto a un punto anómalo, la orden
> text(locator(1), "Anómalo", adj=0)
puede ser útil. En el caso de que no se disponga de ratón, la función locator() aún puede
ser utilizada; en este caso se preguntarán al usuario las coordenadas de x e y.
identify(x, y, etiquetas)
Permite identificar cualquiera de los puntos definidos por x e y utilizando
el botón primario del ratón, dibujando la correspondiente componente de
etiquetas al lado (o el ı́ndice del punto si no existe etiquetas). Al pulsar el
botón secundario del ratón, devuelve los ı́ndices de los puntos seleccionados.
A veces interesa identificar puntos particulares en un gráfico y no sus posiciones. Por
ejemplo, si queremos que el usuario seleccione una observación de interés y, posteriormente,
manipularla en algún modo. Dado un número de coordenadas, (x, y), en dos vectores
numéricos, x e y, podrı́amos usar la función identify() del siguiente modo:
> plot(x, y)
> identify(x, y)
La función identify() no realiza ningún gráfico por sı́ misma sino que permite al usuario
mover el puntero del ratón y pulsar junto a un punto. El punto más próximo al puntero
(si está suficientemente próximo) se identificará dibujando junto a él su número ı́ndice, esto
es, la posición que ocupa en los vectores x e y. Alternativamente podrı́a haber utilizado
una cadena de caracteres (como por ejemplo el nombre del caso) para la identificación,
utilizando el argumento etiquetas, o inhabilitar la identificación utilizando el argumento
plot=FALSE. Cuando se pulsa el botón secundario, la función devuelve los ı́ndices de los
puntos seleccionados, que podrán ser utilizados para obtener los puntos correspondientes
de los vectores x e y.

12.4 Uso de parámetros gráficos


Al crear gráficos, especialmente con fines de presentación o publicación, es posible que R
no produzca de modo automático la apariencia exacta que se desea. Ahora bien, es posible
personalizar cada aspecto del gráfico utilizando parámetros gráficos. R dispone de muchos
parámetros gráficos que controlan aspectos tales como estilo de lı́nea, colores, disposición
de las figuras y justificación del texto entre otros muchos. Cada parámetro gráfico tiene un
nombre (por ejemplo, ‘col’, que controla los colores) y toma un valor (por ejemplo, "blue",
para indicar el color azul).
Por cada dispositivo gráfico activo, se mantiene una lista de parámetros gráficos, y cada
dispositivo gráfico dispone de un conjunto predeterminado de parámetros cuando se inicia-
liza. Los parámetros gráficos pueden indicarse de dos modos; bien de modo permanente, lo
que afectará a todas las funciones gráficas que accedan al dispositivo gráfico, bien tempo-
ralmente, lo que sólo afecta a la función gráfica que lo utiliza en ese momento.
Capı́tulo 12: Procedimientos gráficos 76

12.4.1 Cambios permanentes. La función par()


La función par() se utiliza para acceder a la lista de parámetros gráficos del dispositivo
gráfico actual y para modificarla.
par() Sin argumentos, devuelve una lista de todos los parámetros gráficos y sus valo-
res, para el dispositivo gráfico actual.
par(c("col", "lty"))
Con un vector de caracteres como argumento, devuelve una lista que contiene
sólo los valores de los parámetros citados.
par(col=4, lty=2)
Con argumentos con nombre (o una lista como argumento) establece los valores
de estos parámetros y devuelve (de modo invisible) una lista con los valores
originales de los parámetros.
Al modificar cualquier parámetro con la función par(), la modificación es permanente, en
el sentido de que cualquier llamada a una función gráfica (en el mismo dispositivo gráfico)
vendrá afectada por dicho valor. Puede pensarse que este tipo de asignación equivale a
modificar los valores predeterminados de los parámetros que utilizarán las funciones gráficas
salvo que se les indique un valor alternativo.
Las llamadas a la función par() siempre afectan a los valores globales de los parámetros
gráficos, incluso aunque la llamada se realice desde una función. Esta conducta a menudo no
es satisfactoria; habitualmente desearı́amos modificar algunos parámetros, realizar algunos
gráficos y volver a los valores originales para no afectar a la sesión completa de R. Este
trabajo recae en el usuario, que debe almacenar el resultado de par() cuando realice algún
cambio y restaurarlo cuando el gráfico esté completo.
> [Link] <- par(col=4,lty=2)
. . . varias órdenes gráficas . . .
> par([Link])

12.4.2 Cambios temporales. Argumentos de las funciones gráficas


Los parámetros gráficos también pueden pasarse a prácticamente todas las funciones
gráficas como argumentos con nombre, lo que tiene el mismo efecto que utilizarlos en la
función par(), excepto que los cambios sólo existen durante la llamada a la función. Por
ejemplo:
> plot(x, y, pch="+")
realiza un diagrama de dispersión utilizando el signo de sumar, +, para representar cada
punto, sin cambiar el carácter predeterminado para gráficos posteriores.

12.5 Parámetros gráficos habituales


A continuación se detallan muchos de los parámetros gráficos habituales. La ayuda
de la función par() contiene un resumen más conciso; por lo que puede considerar esta
descripción como una alternativa más detallada.
Los parámetros gráficos se presentarán en la siguiente forma:
Capı́tulo 12: Procedimientos gráficos 77

nombre=valor
Descripción del efecto del parámetro. nombre es el nombre del parámetro, esto
es, el nombre del argumento que debe usar en la función par() o cualquier
función gráfica. valor es un valor tı́pico del parámetro.

12.5.1 Elementos gráficos


Los gráficos de R están formados por puntos, lı́neas, texto y polı́gonos (regiones rellenas).
Existen parámetros gráficos que controlan cómo se dibujan los elementos gráficos citados,
como los siguientes:
pch="+" Carácter que se utiliza para dibujar un punto. El valor predeterminado varı́a
entre dispositivos gráficos, pero normalmente es ‘◦’. Los puntos tienden a apare-
cer en posición levemente distinta de la exacta, salvo que utilice el carácter "."
que produce puntos centrados.
pch=4 Si pch toma un valor entre 0 y 18, ambos inclusive, se utiliza un sı́mbolo especial
para realizar los gráficos. Para saber cuales son los sı́mbolos, utilice las órdenes
> plot(1,t="n")
> legend(locator(1), [Link](0:18), marks=0:18)
y pulse en la parte superior del gráfico.
lty=2 Es el tipo de lı́nea. Aunque algunos tipos no pueden dibujarse en determinados
dispositivos gráficos, siempre, el tipo 1 corresponde a una lı́nea continua, y los
tipos 2 o superior corresponden a lı́neas con puntos, rayas o combinaciones de
ambos.
lwd=2 Es la anchura de lı́nea, medida en múltiplos de la anchura “base”. Afecta tanto
a los ejes como a las lı́neas dibujadas con la función lines(), etc.
col=2 Es el color que se utiliza para los puntos, lı́neas, texto, imágenes y relleno
de zonas. Cada uno de estos elementos gráficos admite una lista de colores
posibles y el valor de este parámetro es un ı́ndice para esta lista. Obviamente
este parámetro solo tiene aplicación en algunos dispositivos.
font=2 Es un entero que indica qué fuente se utilizará para el texto. Si es posible, 1
corresponde a texto normal, 2 a negrilla, 3 a itálica y 4 a itálica negrilla.
[Link]
[Link]
[Link]
[Link] Es la fuente que se utilizará, respectivamente, para escribir en los ejes, para el
etiquetado de x e y, y para el tı́tulo y el subtı́tulo.
adj=-0.1 Indica cómo debe justificarse el texto respecto de la posición de dibujo. Un 0
indica justificación a la izquierda, un 1 indica justificación a la derecha, y un
0.5 indica centrado. Puede usar cualquier otro valor que indicará la proporción
de texto que aparece a la izquierda de la posición de dibujo, por tanto un valor
de -0.1 dejará un 10% de la anchura del texto entre el mismo y la posición de
dibujo.
cex=1.5 Es el carácter de expansión. Su valor indica el tamaño de los caracteres de
texto (incluidos los caracteres de dibujo) respecto del tamaño predeterminado.
Capı́tulo 12: Procedimientos gráficos 78

12.5.2 Ejes y marcas de división


Muchos de los gráficos de nivel alto en R tienen ejes, pero, además, siempre es posible
construirlos con la función gráfica de nivel bajo, axis(). Los ejes tienen tres componentes
principales: La lı́nea del eje, cuyo estilo lo controla el parámetro lty, las marcas de división,
que indican las unidades de división del eje, y las etiquetas de división, que indican las
unidades de las marcas de división. Estas componentes pueden modificarse con los siguientes
parámetros gráficos:
lab=c(5, 7, 12)
Los dos primeros números indican el número de marcas del eje x y del eje y
respectivamente, y el tercer número es la longitud de las etiquetas de los ejes
medida en caracteres (incluido el punto decimal). Atención: Si elige un número
muy pequeño puede que todas las etiquetas de división se redondeen al mismo
número.
las=1 Corresponde a la orientación de las etiquetas de los ejes. Un 0 indica paralelo
al eje, un 1 indica horizontal, y un 2 indica perpendicular al eje.
mgp=c(3, 1, 0)
Son las posiciones de las componentes de los ejes. La primera es la distancia
desde la etiqueta del eje al propio eje, medida en lı́neas de texto. La segunda es
la distancia hasta las etiquetas de división. La tercera y última es la distancia
desde el eje a la lı́nea del eje (normalmente cero). Los valores positivos indican
que está fuera de la zona de dibujo, y los negativos indican que está dentro.
tck=0.01 Es la longitud de las marcas de división, dada como una fracción de la zona de
dibujo. Cuando tck es pequeño (menos de 0.5) las marcas de división de ambos
ejes serán del mismo tamaño. Un valor de 1 hará que aparezca una rejilla. Si
el valor es negativo, las marcas de división se harán por la parte exterior de la
zona de dibujo. Utilice tck=0.01 y mgp=c(1,-1.5,0) para obtener marcas de
división internas.
xaxs="s"
yaxs="d" Estilo de eje de los ejes x e y respectivamente. Con los estilos "s" (estándar) y
"e" (extendido) tanto la mayor marca como la menor caen fuera del intervalo de
los datos. Los ejes extendidos pueden ampliarse levemente si hay algún punto
muy próximo al borde. Este estilo de ejes puede dejar a veces grandes zonas en
blanco cerca de los bordes. Con los estilos "i" (interno) y "r" (predeterminado)
las marcas de división siempre caen dentro del rango de los datos, aunque el
estilo "r" deja un pequeño espacio en los bordes.
Si selecciona el estilo "d" (directo) se procede a bloquear los ejes actuales y
los utiliza para los siguientes gráficos hasta que el parámetro se cambie a otro
de los valores. Este procedimiento es útil para generar series de gráficos con
escalas fijas.

12.5.3 Márgenes de las figuras


Un gráfico de R se denomina figura y comprende una zona de dibujo rodeada de
márgenes (que posiblemente contendrán etiquetas de ejes, tı́tulos, etc.) y, normalmente,
acotada por los propios ejes.
Capı́tulo 12: Procedimientos gráficos 79

Una figura tı́pica es


−−−−−−−−−−−−−−−−−−
−−−−−−−−−−−−−−−−−−
−−−−−−−−−−−−−−−−−−
−−−−−−−−−−−−−−−−−− mar[3]
−−−−−−−−−−−−−−−−−−
−−−−−−−−−−−−−−−−−−

3.0
Plot region

1.5
0.0
y
mai[2]
−1.5
−3.0

−3.0 −1.5 0.0 1.5 3.0

mai[1] x
Margin

Los parámetros gráficos que controlan la disposición de la figura incluyen:

mai=c(1, 0.5, 0.5, 0)


Anchuras de los márgenes inferior, izquierdo, superior y derecho respectiva-
mente, medidos en pulgadas.

mar=c(4, 2, 2, 1)
Similar a mai, pero medido en lı́neas de texto.

Los parámetros mar y mai están relacionados en el sentido de que un cambio en uno se
refleja en el otro. Los valores predeterminados son a menudo demasiado grandes, el margen
derecho se necesita raramente, igual que el superior si no se incluye tı́tulo. Los márgenes
inferior e izquierdo sólo necesitan el tamaño preciso para incluir las etiquetas de ejes y de
división. Además el valor predeterminado no tiene en cuenta la superficie del dispositivo
gráfico. Ası́, si utiliza el dispositivo postscript() con el argumento height=4 obtendrá
un gráfico en que la mitad del mismo son márgenes, salvo que explı́citamente cambie mar
o mai. Cuando hay figuras múltiples, como veremos después, los márgenes se reducen a la
mitad, aunque suele ser insuficiente cuando varias figuras comparten la misma página.

12.5.4 Figuras múltiples

R permite la creación de una matriz de n × m figuras en una sola página. Cada figura
tiene sus propios márgenes, y la matriz de figuras puede estar opcionalmente rodeada de
un margen exterior , tal como se muestra en la siguiente figura:
Capı́tulo 12: Procedimientos gráficos 80
−−−−−−−−−−−−−−−
−−−−−−−−−−−−−−−
−−−−−−−−−−−−−−− oma[3]
−−−−−−−−−−−−−−−
−−−−−−−−−−−−−−−

omi[4]
mfg=c(3,2,3,2)

omi[1]
mfrow=c(3,2)

Los parámetros gráficos relacionados con las figuras múltiples son los siguientes:
mfcol=c(3, 2)
mfrow=c(2, 4)
Definen el tamaño de la matriz de figuras múltiples. En ambos, el primer valor
es el número de filas, el segundo el de columnas. La diferencia entre los dos, es
que con el primero, mfcol, la matriz se rellena por columnas, en tanto que con
el segundo, mfrow, lo hace por filas. La distribución en la figura del ejemplo se
ha creado con mfrow=c(3,2) y se ha representado la página en el momento en
que se han realizado los cuatro primeros gráficos.
mfg=c(2, 2, 3, 2)
Definen la posición de la figura actual dentro de la matriz de figuras múltiples.
Los dos primeros valores indican la fila y columna de la figura actual, en tanto
que los dos últimos son el número de filas y columnas de la matriz de figuras
múltiples. Estos parámetros se utilizan para seleccionar cada una de las dife-
rentes figuras de la matriz. Incluso, los dos últimos valores pueden ser distintos
de los verdaderos valores, para poder obtener figuras de tamaños distintos en
la misma página.
fig=c(4, 9, 1, 4)/10
Definen la posición de la figura actual en la página. Los valores son las posiciones
de los bordes izquierdo, derecho, inferior y superior respectivamente, medidos
como la proporción de página desde la esquina inferior izquierda. El ejemplo
corresponderı́a a una figura en la parte inferior derecha de la página. Este
parámetro permite colocar una figura en cualquier lugar de la página.
oma=c(2, 0, 3, 0)
omi=c(0, 0, 0.8, 0)
Definen el tamaño de los márgenes exteriores. De modo similar a mar y mai, el
primero está expresado en lı́neas de texto y el segundo en pulgadas, correspon-
den a los márgenes inferior, izquierdo, superior y derecho respectivamente.
Los márgenes exteriores son particularmente útiles para ajustar convenientemente los
tı́tulos, etc. Puede añadir texto en estos márgenes con la función mtext() sin más que
Capı́tulo 12: Procedimientos gráficos 81

utilizar el argumento outer=T. R no crea márgenes exteriores predeterminadamente, pero


pueden crearse utilizando las funciones oma y omi.
Es posible crear disposiciones de figuras múltiples más complejas utilizando las funciones
[Link]() y layout().

12.6 Dispositivos gráficos


R puede generar gráficos (con niveles de calidad diversos) en casi todos los tipos de
pantalla o dispositivos de impresión. Ahora bien, en primer lugar es necesario informarle
del tipo de dispositivo de que se trata. Esta acción se realiza iniciando un controlador
de dispositivo. El propósito de un controlador de dispositivo es convertir las instrucciones
gráficas de R (por ejemplo, ‘dibuja una lı́nea’) en una forma que el dispositivo concreto
pueda comprender.
Los controladores de dispositivo se inician llamando a una función de controlador de
dispositivo. Existe una función para cada controlador de dispositivo y la lista completa
puede conseguirse con help(Devices). Por ejemplo, la orden
> postscript()
dirige cualquier salida gráfica a la impresora en formato PostScript. Algunos controladores
de dispositivo habituales son:
X11() Para uso con los sistemas de ventanas X11 y Microsoft Windows.
postscript()
Para imprimir en impresoras PostScript o crear archivos con este formato.
pictex() Crea un archivo para LaTEX.
Al terminar de utilizar un dispositivo, asegúrese de finalizar el mismo utilizando la orden
> [Link]()
Esta orden asegura que el dispositivo finaliza correctamente; por ejemplo en el caso de
una impresora asegura que cada página sea completada y enviada a la impresora.

12.6.1 Inclusión de gráficos PostScript en documentos


Si utiliza el argumento file en la función postscript(), almacenará los gráficos, en
formato PostScript, en el archivo que desee. Tenga en cuenta que si el archivo ya existe,
será previamente borrado. Ello ocurre aunque el archivo se haya creado previamente en la
misma sesión de R. El gráfico tendrá orientación apaisada salvo que especifique el argumento
horizontal=FALSE. El tamaño del gráfico se controla con los argumentos width (anchura) y
height (altura) que se utilizan como factores de escala para ajustarlo a dichas dimensiones.
Por ejemplo, la orden
> postscript("[Link]", horizontal=FALSE, height=5, pointsize=10)
producirá un archivo que contiene el código PostScript para una figura de cinco pulgadas
de alto, que podrá ser incluido en un documento. A menudo dicha inclusión requiere que
el archivo se almacene en formato EPS1 . El archivo que produce R es de este tipo, pero
sólo lo indicará expresamente si se utiliza el argumento onefile=FALSE. Esta notación es
1
Acrónimo en inglés de Encapsulated PostScript
Capı́tulo 12: Procedimientos gráficos 82

consecuencia de la compatibilidad con S e indica que la salida está constituida por una sola
página. Por tanto para crear un gráfico que se pueda incluir sin problema en cualquier
procesador, deberá utilizar una orden análoga a la siguiente:
> postscript("[Link]", horizontal=FALSE, onefile=FALSE,
height=8, width=6, pointsize=10)

12.6.2 Dispositivos gráficos múltiples


La utilización avanzada de R implica a menudo tener varios dispositivos gráficos en
uso simultáneamente. Naturalmente, sólo un dispositivo gráfico acepta las órdenes gráficas
en cada momento, y se denomina dispositivo actual. Cuando se abren varios dispositivos,
forman una sucesión numerada cuyos nombres determinan el tipo de dispositivo en cada
posición.
Las principales órdenes utilizadas para trabajar con varios dispositivos y sus significados
son las siguientes:

X11() Abre una ventana en Unix y Microsoft Windows

windows()
Abre una ventana en Microsoft Windows

postscript()
pictex()
... Cada llamada a una función de controlador de dispositivo abre un nuevo dis-
positivo gráfico y, por tanto, añade un elemento a la lista de dispositivos, al
tiempo que este dispositivo pasa a ser el dispositivo actual al que se enviarán
los resultados gráficos. (En algunas plataformas es posible que existan otros
dispositivos disponibles.)

[Link]()
Devuelve el número y nombre de todos los dispositivos activos. El dispositivo
de la posición 1 es siempre el dispositivo nulo que no acepta ninguna orden
gráfica.

[Link]()
[Link]()
Devuelve el número y nombre del dispositivo gráfico siguiente o anterior, re-
spectivamente, al dispositivo actual.

[Link](which=k)
Puede usarse para hacer que el dispositivo que ocupa la posición k en la lista
de dispositivos sea el actual. Devuelve el número y nombre del dispositivo.

[Link](k)
Cierra el dispositivo gráfico que ocupa la posición k de la lista de dispositivos.
Para algunos dispositivos, como los postscript, finalizará el gráfico, bien im-
primiéndolo inmediatamente, bien completando la escritura en el archivo, de-
pendiendo de cómo se ha iniciado el dispositivo.
Capı́tulo 12: Procedimientos gráficos 83

[Link](device, ..., which=k)


[Link](device, ..., which=k)
realiza una copia del dispositivo k. Aquı́, device, es una función de disposi-
tivo, como postscript, con argumentos adicionales si es necesario, especifica-
dos por .... La función [Link] es similar, pero el dispositivo copiado se
cierra inmediatamente, lo que finaliza las acciones pendientes que se realizan
inmediatamente.
[Link]()
Cierra todos los dispositivos gráficos de la lista, excepto el dispositivo nulo.

12.7 Gráficos dinámicos


R no dispone (en este momento) de ninguna función de gráficos dinámicos, por ejemplo
para rotar una nube de puntos o activar y desactivar puntos interactivamente. Sin embargo
existen muchas de estas posibilidades disponibles en el sistema XGobi de Swayne, Cook y
Buja, disponible en
[Link]
a las que puede acceder desde R a través de la biblioteca xgobi.
XGobi está disponible actualmente para X Windows, tanto en Unix como en Microsoft
Windows, y existen conexiones con R disponibles en ambos sistemas.
Apendice A: Primera sesión con R 84

Apendice A Primera sesión con R


La siguiente sesión pretende presentar algunos aspectos del entorno R, utilizándolos.
Muchos de estos aspectos le serán desconocidos e incluso enigmáticos al principio, pero esta
sensación desaparecerá rápidamente. La sesión está escrita para el sistema UNIX, por lo
que es posible que los usuarios de Microsoft Windows deban realizar pequeños cambios.
Conéctese e inicie el sistema de ventanas. Es necesario que disponga del archivo
‘[Link]’ en su directorio de trabajo, por lo que, si es necesario, deberá
copiarlo. Si no sabe hacerlo, consulte con un experto local. Si ya lo ha copiado,
continúe.
$R Ejecute R.
Comienza R y aparece el mensaje inicial.
(Dentro de R no mostraremos el sı́mbolo de ‘preparado’ en la parte izquierda
para evitar confusiones.)
[Link]()
Muestra la ayuda interactiva, que está escrita en formato html, utilizando el
lector WEB disponible en el ordenador. Si utiliza esta ayuda comprenderá
mejor el funcionamiento de R.
Minimice la ventana de ayuda y continúe la sesión.
x <- rnorm(50)
y <- rnorm(x)
Genera dos vectores que contienen cada uno 50 valores pseudoaleatorios
obtenidos de una distribución normal (0,1) y los almacena en x e y.
plot(x, y)
Aparecerá una ventana gráfica automáticamente. En ella se representan los
puntos antes generados, tomando cada componente de x y de y como las coor-
denadas de un punto del plano.
ls() Presenta los nombres de los objetos existentes en ese momento en el espacio de
trabajo de R.
rm(x, y) Elimina los objetos x e y.
x <- 1:20 Almacena en x el vector (1, 2, . . . , 20).
w <- 1 + sqrt(x)/2
A partir del vector x, crea un vector ponderado de desviaciones tı́picas, y lo
almacena en w.
[Link] <- [Link](x=x, y= x + rnorm(x)*w)
[Link]
Crea una hoja de datos de dos columnas, llamadas x e y, y la almacena en
[Link]. A continuación la presenta en pantalla.
regr <- lm(y ∼ x, data=[Link])
summary(regr)
Realiza el ajuste de un modelo de regresión lineal de y sobre x, lo almacena en
regr, y presenta en pantalla un resumen del análisis.
Apendice A: Primera sesión con R 85

[Link] <- lm(y ∼ x, data=[Link], weight=1/w^2)


summary([Link])
Puesto que se conocen las desviaciones tı́picas, puede realizarse una regresión
ponderada.
attach([Link])
Conecta la hoja de datos, de tal modo que sus columnas aparecen como varia-
bles.
[Link] <- lowess(x, y)
Realiza una regresión local no paramétrica.
plot(x, y)
Representa el gráfico bidimensional estándar.
lines(x, [Link]$y)
Le añade la regresión local.
abline(0, 1, lty=3)
Le añade la verdadera recta de regresión (punto de corte 0, pendiente 1).
abline(coef(regr))
Le añade la recta de regresión no ponderada.
abline(coef([Link]), col = "red")
Le añade la recta de regresión ponderada.
detach() Desconecta la hoja de datos, eliminándola de la trayectoria de búsqueda.
plot(fitted(regr), resid(regr),
xlab="Predichos",
ylab="Residuos",
main="Residuos / Predichos")
Un gráfico diagnóstico de regresión para investigar la posible heteroscedastici-
dad.
qqnorm(resid(regr), main="Residuos por rangos")
Gráfico en papel probabilı́stico normal para comprobar asimetrı́a, aplastamiento
y datos anómalos. (No es muy útil en este caso)
rm(x,w,[Link],regr,[Link],[Link])
Elimina los objetos creados.
[Link]("[Link]")
Presenta el contenido del archivo.
mm <- [Link]("[Link]")
mm Lee los datos de Michaelson y Morley y los almacena en la hoja de datos mm, y
la muestra en pantalla a continuación. Hay cinco experimentos (columna Expt)
y cada uno contiene 20 series (columna Run) y la columna Speed contiene la
velocidad de la luz medida en cada caso, codificada apropiadamente.
mm$Expt <- factor(mm$Expt)
mm$Run <- factor(mm$Run)
Transforma Expt y Run en factores.
Apendice A: Primera sesión con R 86

attach(mm)
Conecta la hoja de datos en la posición predeterminada: la 2.
plot(Expt, Speed, main="Velocidad de la luz", xlab="No. Experimento")
Compara los cinco experimentos mediante diagramas de cajas.
fm <- aov(Speed ∼ Run + Expt, data=mm)
summary(fm)
Analiza los datos como un diseño en bloques aleatorizados, tomando las ‘series’
y los ‘experimentos’ como factores.
fm0 <- update(fm, . ∼ . - Run)
anova(fm0,fm)
Ajusta el submodelo eliminando las ‘series’, y lo compara utilizando un análisis
de la varianza.
detach()
rm(fm, fm0)
Desconecta la hoja de datos y elimina los objetos creados, antes de seguir ade-
lante.
Consideraremos ahora nuevas posibilidades gráficas.
x <- seq(-pi, pi, len=50)
y <- x x es un vector con 50 valores equiespaciados en el intervalo −π ≤ x ≤ π. El
vector y es idéntico a x.
f <- outer(x, y, function(x, y) cos(y)/(1 + x^2))
f es una matriz cuadrada, cuyas filas y columnas están indexadas por x e y
respectivamente, formada por los valores de la función cos(y)/(1 + x2 ).
oldpar <- par([Link] = TRUE)
par(pty="s")
Almacena los parámetros gráficos y modifica el parámetro pty (zona de dibujo)
para que valga “s” (cuadrado).
contour(x, y, f)
contour(x, y, f, nlevels=15, add=TRUE)
Dibuja un mapa de curvas de nivel de f ; y después le añade más lı́neas para
obtener más detalle.
fa <- (f-t(f))/2
fa es la “parte asimétrica” de f . (t(f) es la traspuesta de f).
contour(x, y, fa, nint=15)
Dibuja un mapa de curvas de nivel,. . .
par(oldpar)
. . . y recupera los parámetros gráficos originales.
image(x, y, f)
image(x, y, fa)
Dibuja dos gráficos de densidad
Apendice A: Primera sesión con R 87

objects(); rm(x, y, f, fa)


Presenta los objetos existentes y elimina los objetos creados, antes de seguir
adelante.
Con R también puede utilizar números complejos. 1i se utiliza para representar la
unidad imaginaria, i.
th <- seq(-pi, pi, len=100)
z <- exp(1i*th)
par(pty="s")
plot(z, type="l")
La representación gráfica de un argumento complejo consiste en dibujar la parte
imaginaria frente a la real. En este caso se obtiene un cı́rculo.
w <- rnorm(100) + rnorm(100)*1i
Suponga que desea generar puntos pseudoaleatorios dentro del cı́rculo unidad.
Un primer intento consiste en generar puntos complejos cuyas partes real e
imaginaria, respectivamente, procedan de una normal (0,1) . . .
w <- ifelse(Mod(w) > 1, 1/w, w)
. . . y sustituir los que caen fuera del cı́rculo por sus inversos.
plot(w, xlim=c(-1,1), ylim=c(-1,1), pch="+",xlab="x", ylab="y")
lines(z)
Todos los puntos están dentro del cı́rculo unidad, pero la distribución no es
uniforme.
w <- sqrt(runif(100))*exp(2*pi*runif(100)*1i)
plot(w, xlim=c(-1,1), ylim=c(-1,1), pch="+", xlab="x", ylab="y")
lines(z)
Este segundo método utiliza la distribución uniforme. En este caso, los puntos
presentan una apariencia más uniformemente espaciada sobre el cı́rculo.
rm(th, w, z)
De nuevo elimina los objetos creados, antes de seguir adelante.
q() Termina el programa R. Se le preguntará si desea salvar el espacio de trabajo.
Puesto que esta sesión ha sido solamente de presentación, probablemente de-
berı́a contestar que no.
Apendice B: Ejecución de R 88

Apendice B Ejecución de R

B.1 Ejecución de R en UNIX


La orden ‘R’ se utiliza para ejecutar R con diferentes opciones, de la forma
R [opciones] [<archivoentrada] [>archivosalida],
o, a través del interfaz R CMD, para acceder a varias herramientas de R (por ejemplo, para
procesar archivos de formato de documentación de R o manipular bibliotecas) que no están
diseñadas para ser usadas “directamente”.
Muchas opciones controlan lo que ocurre al comenzar y al terminar una sesión de R. El
mecanismo de inicio (Utilice ‘help(Startup)’ para información actualizada) es el siguiente:
• Salvo que se especifique la opción ‘--no-environ’, R busca el archivo ‘.Renviron’ en el
directorio actual; si no lo encuentra, busca el archivo indicado en la variable de entorno
R_ENVIRON, y si esta variable no existe, busca el archivo ‘.Renviron’ en el directorio
inicial del usuario. Si encuentra alguno de estos archivos, se ejecuta (como si se lo diese
como argumento a la función source dentro de R) para dar valores a las variables de
entorno. Las variables se exportan automáticamente, supuesto que se les dé valor en
lı́neas de la forma ‘nombre=valor’. Algunas de estas variables son:
R_PAPERSIZE (tamaño predeterminado del papel),
R_PRIMTCMD (orden predeterminada de impresión),
R_LIBS (lista de bibliotecas que deben buscarse), y
R_VSIZE y R_NSIZE que describiremos después.
• A continuación, R busca el perfil de inicio global (para todos los usuarios) salvo que
se haya especificado la opción ‘--no-site-file’. El nombre de este archivo se toma
del valor de la variable de entorno R_PROFILE. Si esta variable no existe, el valor
predeterminado es ‘$R_HOME/etc/Rprofile’.
• Una vez acabado el paso anterior, si no se ha especificado la opción ‘--no-init-file’,
R busca un archivo llamado ‘.Rprofile’ en el directorio actual, y si no lo encuentra
allı́, lo busca en el directorio inicial del usuario. Si encuentra el archivo, lo ejecuta.
• Si existe un archivo llamado ‘.RData’, que contenga una imagen de espacio de trabajo,
lo utiliza para restaurar el espacio de trabajo, salvo que se haya especificado la opción
‘--no-restore’.
• Por último, si existe una función llamada .First, la ejecuta. (Del mismo modo, si
existe una función llamada .Last será ejecutada al finalizar la sesión de R) La función
puede definirse en los perfiles de inicio o residir en el archivo de imagen ‘.RData’.
Además de todo lo indicado, existen opciones para controlar la memoria disponible en
una sesión de R (Utilice ‘help(Memory)’ para disponer de información actualizada). R
utiliza un modelo de memoria estático. Esto es, en el momento de iniciarse, el sistema
operativo le reserva una cantidad fija de memoria, que no puede alterarse durante la sesión.
Por tanto, pudiera ocurrir que no exista memoria suficiente en algún momento para realizar
una acción, por ejemplo, para leer un archivo de datos muy grande. Las opciones ‘--nsize’
y ‘--vsize’ (o las variables de entorno R_NSIZE y R_VSIZE) controlan la memoria disponible
para objetos de tamaños fijo y variable, respectivamente.
Las opciones que pueden darse al ejecutar R son las siguientes:
Apendice B: Ejecución de R 89

--help
-h Muestra un pequeño mensaje de ayuda y termina correctamente.
--version
Muestra la información de la versión y termina correctamente.
RHOME Muestra la trayectoria al “directorio inicial” de R y termina correctamente.
Salvo la página de ayuda en UNIX y el archivo de ejecución, la instalación de
R pone todos los archivos (ejecutables, bibliotecas, etc.) en este directorio.
--save
--no-save
Indica si debe salvar la imagen del entorno al terminar la sesión. En modo in-
teractivo, si no ha especificado ninguna, le preguntará. En modo no interactivo
es obligatorio usar una.
--no-environ
No lee ningún archivo para dar valor a las variables de entorno.
--no-site-file
No lee el perfil de inicio global al iniciar el programa.
--no-init-file
No lee el perfil de inicio de usuario al iniciar el programa.
--restore
--no-restore
Indica si la imagen salvada (archivo ‘.Rdata’ en el directorio en que R se haya
iniciado) debe ser recuperada o no. El valor predeterminado es recuperarla.
--vanilla
Combina las opciones ‘--no-save’, ‘--no-environ’ ‘--no-site-file’,
‘--no-init-file’, y ‘--no-restore’.
--no-readline
Desactiva la edición de órdenes a través de readline. Esta opción suele utilizarse
cuando se ejecuta R desde Emacs utilizando la biblioteca ESS (“Emacs Speaks
Statistics”). Véase Apendice C [El editor de ordenes], página 93, para ampliar
esta información.
--vsize=N
Indica la cantidad de memoria utilizada para objetos de tamaño variable. N
debe ser un entero, en cuyo caso la unidad de medida es el octeto o byte, o
un entero seguido de una letra que indica la unidad de medida en octetos: ‘M’,
‘K’, o ‘k’, que corresponden respectivamente a ‘Mega’ (2^20), ‘Kilo’ (2^10) (de
ordenador), o ‘kilo’decimal (1000).
--nsize=N
Indica la cantidad de memoria utilizada para objetos de tamaño fijo. Las con-
sideraciones realizadas en el apartado anterior son válidas para N.
--quiet
--silent
-q No muestra ni el mensaje de copyright ni los mensajes de inicio.
Apendice B: Ejecución de R 90

--slave Ejecuta R con el mı́nimo de salidas posible. Esta opción se utiliza con programas
que se sirven de R para realizar cálculos para ellos mismos.
--verbose
Muestra el máximo de salidas posible. Además modifica la opción verbose a
TRUE. R utiliza esta opción para controlar si debe mostrar mensajes de diag-
nóstico.
--debugger=depurador
-d depurador
Ejecuta R desde el programa de depuración (debugger) depurador. En este
caso, si existen otras opciones, se descartan. Cualquier otra opción debe darse
al iniciar R desde el programa de depuración.
--gui=tipo
Utiliza tipo como interfaz gráfico (advierta que esto también incluye los gráficos
interactivos). Los valores posibles de tipo son X11 (predeterminado) y GNOME,
supuesto que GNOME esté disponible.
La entrada y la salida pueden redirigirse de la manera habitual, utilizando ‘<’ and ‘>’.
R CMD permite utilizar varias herramientas que son útiles en conjunción con R, pero que
no están diseñadas para usarlas “directamente”. La forma general es
R CMD orden argumentos
donde orden es el nombre de la herramienta y argumentos son los argumentos que se pasan
a la misma.
Las herramientas disponibles son:
BATCH Ejecuta R en modo no interactivo.
COMPILE Compila archivos para uso con R.
SHLIB Construye bibliotecas compartidas del sistema operativo para carga dinámica.
INSTALL Instala bibliotecas añadidas.
REMOVE Elimina bibliotecas añadidas.
build Construye bibliotecas añadidas.
check Comprueba bibliotecas añadidas.
Rdconv Convierte desde formato Rd a otros formatos, incluyendo html, Nroff, LaTEX,
texto ASCII sin formato, y formato de documentación S.
Rd2dvi Convierte desde formato Rd a DVI/PDF.
Rd2txt Convierte desde formato Rd a texto con formato.
Rdindex Extrae información de ı́ndices de archivos Rd.
Sd2Rd Convierte desde formato de documentación S a formato Rd.
Las cinco primeras herramientas (BATCH, COMPILE, SHLIB, INSTALL, y REMOVE) pueden
ejecutarse “directamente” sin la opción CMD, esto es, en la forma R orden argumentos.
Utilice la orden
R CMD herramienta --help
para obtener información sobre cada una de las herramientas descritas.
Apendice B: Ejecución de R 91

B.2 Ejecución de R en Microsoft Windows


El procedimiento de inicio en Microsoft Windows es muy similar al de UNIX, pero no
necesariamente idéntico. Existen dos versiones de R en este sistema operativo: Una basada
en ventanas MDI, [Link], y otra en ventanas SDI, [Link], pensada especialmente
para uso no interactivo.
Muchas opciones controlan lo que ocurre al comenzar y al terminar una sesión de R. El
mecanismo de inicio (Utilice ‘help(Startup)’ para información actualizada) es el siguiente.
Las referencias al “directorio inicial” deben ser aclaradas, ya que éste no siempre está
definido en Microsoft Windows. Si se ha definido la variable de entorno R_USER, entonces
su valor es el directorio inicial. En caso contrario, lo define la variable de entorno HOME; y si
tampoco está definida, lo harán las variables HOMEDRIVE y HOMEPATH (que normalmente están
definidas en Windows NT). Si ninguna de las variables mencionadas existe, el directorio
inicial será el directorio de trabajo.
• Salvo que se especifique la opción ‘--no-environ’, R busca el archivo ‘.Renviron’ en
el directorio actual; si no lo encuentra, busca el archivo ‘.Renviron’ en el directorio
inicial del usuario. Si encuentra alguno de estos archivos, se ejecuta (como si se lo diese
como argumento a la función source dentro de R) para dar valores a las variables de
entorno. Se asigna valor a las variables en lı́neas de la forma ‘nombre=valor’. Algunas
de estas variables son
R_PAPERSIZE (tamaño predeterminado del papel),
R_PRIMTCMD (orden predeterminada de impresión),
R_LIBS (lista de bibliotecas que deben buscarse), y
R_VSIZE y R_NSIZE que describiremos después.
Las variables de entorno también pueden indicarse como parejas de la forma ‘nom-
bre=valor’ en la propia lı́nea de órdenes.
• A continuación, R busca el archivo de perfil de inicio en el ordenador, salvo que se
indique la opción ‘--no-site-file’. El nombre del archivo se almacena en la variable
de entorno R_PROFILE y si no existe se toma ‘$R_HOME/etc/Rprofile’.
• Si no se ha indicado ‘--no-init-file’, R busca el archivo ‘.Rprofile’ en el directorio
actual o en el de inicio del usuario y ejecuta las órdenes en él contenidas.
• A continuación carga el archivo de imagen ‘.RData’, si existe, salvo que se indique la
opción ‘--no-restore’.
• Si existe la función .First, la ejecuta. Esta función, ası́ como la función .Last que se
ejecuta la finalizar la sesión de R, pueden definirse en los perfiles de inicio adecuados,
o residir en el archivo ‘.RData’.
Además de todo lo indicado, existen opciones para controlar la memoria disponible en
una sesión de R (Utilice ‘help(Memory)’ para disponer de información actualizada). R
utiliza un modelo de memoria estático. Esto es, en el momento de iniciarse, el sistema
operativo le reserva una cantidad fija de memoria, que no puede alterarse durante la sesión.
Por tanto, pudiera ocurrir que no exista memoria suficiente en algún momento para realizar
una acción, por ejemplo, para leer un archivo de datos muy grande. Las opciones ‘--nsize’
y ‘--vsize’ (o las variables de entorno R_NSIZE y R_VSIZE) controlan la memoria disponible
para objetos de tamaños fijo y variable, respectivamente.
Apendice B: Ejecución de R 92

Las opciones que pueden darse al ejecutar R en Microsoft Windows son las siguientes:
--version
Muestra la información de la versión y termina correctamente.
--mdi
--sdi
--no-mdi Inidica si Rgui se comportará como un programa MDI (predeterminado), donde
cada nueva ventana está contenida dentro de la ventana principal, o como un
programa SDI, donde cada ventana aparece de modo independiente en el es-
critorio.
--save
--no-save
Indica si debe salvar la imagen del entorno al terminar la sesión. En modo in-
teractivo, si no ha especificado ninguna, le preguntará. En modo no interactivo
es obligatorio usar una.
--restore
--no-restore
Indica si la imagen salvada (archivo ‘.Rdata’ en el directorio en que R se haya
iniciado) debe ser recuperada o no. El valor predeterminado es recuperarla.
--no-site-file
No lee el perfil de inicio global al iniciar el programa.
--no-init-file
No lee el archivo ‘.Rprofile’ del directorio del usuario (perfil de inicio de
usuario) al iniciar el programa.
--no-environ
No lee el archivo ‘.Renviron’.
--vanilla
Combina las opciones --no-save, --no-restore, --no-site-file,
--no-init-file y --no-environ.
-q
--quiet
--silent No muestra el mensaje de inicio.
--slave Ejecuta R con el mı́nimo de salidas posible.
--verbose
Muestra el máximo de salidas posible.
--ess Prepara Rterm para uso en modo R-inferior en ESS.
Apendice C: El editor de órdenes 93

Apendice C El editor de órdenes

C.1 Preliminares
Si la biblioteca de GNU, ‘readline’, está disponible cuando se compila R en UNIX, se
puede utilizar un editor interno de lı́neas de órdenes que permite recuperar, editar y volver
a ejecutar las órdenes utilizadas previamente.
Este editor puede desactivarse (lo que permite utilizar ESS1 ) dando la opción de inicio
‘--no-readline’.
La versión de Microsoft Windows dispone de un editor más sencillo. Vea la opción
‘Console’ en el menú ‘Help’.
Cuando use R con las posibilidades de readline, estarán disponibles las opciones que
posteriormente se indican.
Tenga en cuenta las siguientes convenciones tipográficas usuales:
Muchas de las órdenes utilizan caracteres caracteres Control y Meta. Los caracteres
Control, tales como Control-m, se obtienen pulsando la tecla hCTRLi y, sin soltarla, pulsando
la tecla hmi, y en la tabla los escribiremos como C-m. Los caracteres Meta, tales como Meta-
b, se obtienen pulsando la tecla hMETAi y, después de soltarla, pulsando la tecla hbi, y en la
tabla los escribiremos como M-b. Si su teclado no tiene la tecla hMETAi, puede obtenerlos
mediante una secuencia de dos caracteres que comienza con ESC. Esto es, para obtener M-b,
deberá escribir hESCihbi. Estas secuencias ESC también puede utilizarlas aunque su teclado sı́
disponga de la tecla hMETAi. Debe tener en cuenta que en los caracteres Meta se distingue
entre mayúsculas y minúsculas, por lo que puede que sea distinto el resultado si se pulsa
M-b o M-B.

C.2 Edición de acciones


R conserva un historial de las órdenes que se teclean, incluyendo las lı́neas erróneas,
lo que permite recuperar las lı́neas del historial, modificarlas si es necesario, y volver a
ejecutarlas como nuevas órdenes. Si el estilo de edición de órdenes es emacs cualquier
carácter que teclee se inserta en la orden que se está editando, desplazando los caracteres
que estén a la derecha del cursor. En el estilo vi el modo de inserción de caracteres se inicia
con M-i o M-a, y se finaliza con hESCi.
Cuando pulse la tecla hRETi, la orden completa será ejecutada.
Otras acciones posibles de edición se resumen en la tabla siguiente.

C.3 Resumen del editor de lı́neas de órdenes

1
Corresponde al acrónimo del editor de textos, ‘Emacs Speaks Statistics’; vea la dirección
[Link]
Apendice C: El editor de órdenes 94

Recuperación de órdenes y movimiento vertical


C-p Recupera la orden anterior (retrocede en el historial).
C-n Recupera la orden posterior (avanza en el historial).
C-r text Recupera la última orden que contenga la cadena texto.
En muchos terminales, es posible utilizar las teclas de flecha hacia arriba y flecha hacia
abajo, respectivamente, en vez de C-p y C-n.

Movimiento horizontal del cursor


C-a Va al principio de la lı́nea.
C-e Va al final de la lı́nea.
M-b Retrocede una palabra.
M-f Avanza una palabra.
C-b Retrocede un carácter.
C-f Avanza un carácter.
En muchos terminales, es posible utilizar las teclas de flecha hacia la izquierda y flecha
hacia la derecha, respectivamente, en vez de C-b y C-f.

Edición
text Inserta texto en el cursor.
C-f text Añade texto tras el cursor.
hDELi Borra el carácter a la izquierda del cursor.
C-d Borra el carácter bajo el cursor.
M-d Borra el resto de la palabra bajo el cursor, y la guarda.
C-k Borra el resto de la lı́nea desde el cursor, y lo guarda.
C-y Inserta el último texto guardado.
C-t Intercambia el carácter bajo el cursor con el siguiente.
M-l Cambia el resto de la palabra a minúsculas.
M-c Cambia el resto de la palabra a mayúsculas.
hRETi Vuelve a ejecutar la lı́nea.
Al pulsar hRETi, se termina la edición de la lı́nea.
Apendice D: Índice de funciones y variables 95

Apendice D Índice de funciones y variables

! +
! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
+ ............................................ 8
!= . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

% >
%*% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
%o% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 > . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
>= . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
&
& . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
&& . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 ^
^ ............................................ 8
*
* ............................................ 8
<
-
< . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
- ............................................ 8
<= . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
<<- . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
.First . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
.Last . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
A
abline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
/ ace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
/ ............................................ 8 add1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
anova . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59, 60
aov . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
:
aperm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
: ............................................ 9
array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
[Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
= [Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
== . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 attach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
attr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
? attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
? ............................................ 4 avas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
axis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

|
| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
|| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 B
boxplot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
~ break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
~ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 bruto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Apendice D: Índice de funciones y variables 96

C G
c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7, 10, 27, 29 glm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
cbind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
coef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
H
coefficients . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
contour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 hist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38, 71
Contrastes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
coplot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
cos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
I
crossprod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22, 24 identify. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
cut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
ifelse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
D [Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
[Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
[Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
[Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 K
density . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 [Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
detach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
[Link]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
[Link]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 L
[Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 legend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
[Link]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8, 14
[Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 levels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
deviance. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 lines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
diag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
dim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 lm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
dotplot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 lme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
drop1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 locator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
loess . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67, 68
log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
lqs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
E lsfit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
ecdf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
eigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 M
Error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 mars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
exp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 max . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
mean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
min . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
F mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

F ............................................ 9
factor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 N
FALSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 NA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
fivenum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 NaN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 ncol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
formula . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 next . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
function. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 nlm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65, 66, 67
Apendice D: Índice de funciones y variables 97

nlme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 S
nrow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 scan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
seq . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
O [Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 sin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
ordered . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 sink . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
outer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
split . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
sqrt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
P stem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
pairs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59, 61
par . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 sum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
paste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38, 59
persp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 svd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
pictex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
plot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59, 69 T
pmax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
pmin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
T ............................................ 9
points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
[Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
polygon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22, 27
postscript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 tan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
predict . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 tapply . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
print . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
prod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
TRUE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Q
qqline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39, 71 U
qqnorm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39, 71 unclass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
qqplot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 update . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
qr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

V
R var . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
range . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 [Link]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
rbind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
[Link]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
[Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 W
rep . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
repeat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 [Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
resid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
residuals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
rlm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 X
rm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 X11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Apendice E: Índice de conceptos 98

Apendice E Índice de conceptos

A G
Acceso a datos internos . . . . . . . . . . . . . . . . . . . . . . 35 Gráficos cuantil-cuantil . . . . . . . . . . . . . . . . . . . . . . . 39
Actualización de modelos ajustados . . . . . . . . . . . 61 Gráficos dinámicos . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Ajuste por mı́nimos cuadrados . . . . . . . . . . . . . . . . 25
Ámbito . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Análisis de varianza . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Argumentos con nombre. . . . . . . . . . . . . . . . . . . . . . 47
H
Asignación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Hojas de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Autovalores y autovectores . . . . . . . . . . . . . . . . . . . 25
I
B Indexación de vectores . . . . . . . . . . . . . . . . . . . . . . . 11
Bibliotecas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Indexación de y mediante variables indexadas . . 20

C K
Ciclos y ejecución condicional . . . . . . . . . . . . . . . . . 44
Kolmogorov-Smirnov, contraste de . . . . . . . . . . . . 40
Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15, 54
Cómo definir un operador binario . . . . . . . . . . . . . 47
Cómo importar datos . . . . . . . . . . . . . . . . . . . . . . . . 36
Concatenación de listas . . . . . . . . . . . . . . . . . . . . . . 29 L
Contrastes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Lectura de datos desde un archivo . . . . . . . . . . . . 33
Contrastes de una y de dos muestras . . . . . . . . . . 41 Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Controladores de dispositivos gráficos . . . . . . . . . 81

D M
Descomposición en valores singulares . . . . . . . . . . 25 Máxima verosimilitud . . . . . . . . . . . . . . . . . . . . . . . . 67
Descomposición QR . . . . . . . . . . . . . . . . . . . . . . . . . . 25 Mı́nimos cuadrados no lineales . . . . . . . . . . . . . . . . 65
Determinantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 Modelos aditivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Diagrama de cajas . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Modelos basados en árboles . . . . . . . . . . . . . . . . . . . 68
Distribuciones de probabilidad . . . . . . . . . . . . . . . . 37 Modelos estadı́sticos . . . . . . . . . . . . . . . . . . . . . . . . . 55
Modelos lineales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Modelos lineales generalizados . . . . . . . . . . . . . . . . 61
E Modelos mezclados. . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Eliminar objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Escritura de funciones . . . . . . . . . . . . . . . . . . . . . . . . 46
Espacio de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Estimación de la densidad . . . . . . . . . . . . . . . . . . . . 38
O
Expresiones agrupadas . . . . . . . . . . . . . . . . . . . . . . . 44 Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Órdenes de control . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Orientación a objetos . . . . . . . . . . . . . . . . . . . . . . . . 54
F
Factores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17, 58
Factores ordinales . . . . . . . . . . . . . . . . . . . . . . . . 17, 58 P
Familias. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
Fórmulas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Parámetros gráficos . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Función de distribución empı́rica . . . . . . . . . . . . . . 39 Personalización del entorno . . . . . . . . . . . . . . . . . . . 53
Funciones genéricas . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Producto exterior de variables indexadas . . . . . . 23
Funciones y operadores aritméticos . . . . . . . . . . . . . 8 Producto matricial . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Apendice E: Índice de conceptos 99

R Tabulación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Redirección de la entrada y la salida . . . . . . . . . . . 5 Traspuesta generalizada de una variable indexada
Regla de reciclado . . . . . . . . . . . . . . . . . . . . . . . . . 8, 22 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Regresión con aproximación local . . . . . . . . . . . . . 67 Trayectoria de búsqueda. . . . . . . . . . . . . . . . . . . . . . 31
Regresión robusta. . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
V
S Valores faltantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Shapiro-Wilk, contraste de . . . . . . . . . . . . . . . . . . . 40 Valores predeterminados . . . . . . . . . . . . . . . . . . . . . 47
Student, contraste t de . . . . . . . . . . . . . . . . . . . . . . . 41 Vectores de caracteres . . . . . . . . . . . . . . . . . . . . . . . . 10
Sucesiones regulares . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
W
T Wilcoxon, contraste de . . . . . . . . . . . . . . . . . . . . . . . 42
Apendice F: Referencias 100

Apendice F Referencias
D. M. Bates and D. G. Watts (1988), Nonlinear Regression Analysis and Its Applications.
John Wiley & Sons, New York.
Richard A. Becker, John M. Chambers and Allan R. Wilks (1988), The New S Language.
Chapman & Hall, New York. This book is often called the “Blue Book ”.
John M. Chambers and Trevor J. Hastie eds. (1992), Statistical Models in S. Chapman
& Hall, New York. This is also called the “White Book ”.
Annette J. Dobson (1990), An Introduction to Generalized Linear Models, Chapman and
Hall, London.
Peter McCullagh and John A. Nelder (1989), Generalized Linear Models. Second edition,
Chapman and Hall, London.
John A. Rice (1995), Mathematical Statistics and Data Analysis. Second edition.
Duxbury Press, Belmont, CA.
S. D. Silvey (1970), Statistical Inference. Penguin, London.
Econometría básica con Python .

Fabián Alejandro Triana Alarcón

Universidad Nacional de Colombia


Facultad de Ciencias Económicas
2019

Esta obra está bajo la licencia Creative Commons Atribución-NoComercial 4.0 Internacional
Prefacio
Esta obra constituye un manual para la realización de análisis econométrico básico con el uso del lenguaje de
programación Python; no comprende, como parte de su contenido, un cuerpo teórico sólido que pueda tomarse
como base adecuada para la enseñanza de la Econometría y tan solo se limita a ser un referente práctico de la
aplicación de las técnicas y modelos propios de la materia. Dado el enfoque aplicado de este trabajo, el lector
no encontrará en éste un texto que le sirva de guía teórica y le permita fortalecer sus conocimientos sobre la
Econometría, pues no se trata del objetivo aquí planteado; para tal propósito, se sugiere recurrir a los manuales
especializados generalmente empleados como material de referencia en los cursos de pregrado en Economía.
El contenido que se presenta en esta obra está especialmente dirigido a estudiantes de Economía, Estadís-
tica y carreras afines, a los profesionales cuyas labores se vean asociadas al ámbito de la Econometría y a toda
persona, con cierto conocimiento previo, que sienta afinidad por la materia y esté interesada en mejorar sus ha-
bilidades en el uso de herramientas informáticas útiles en el desarrollo de ejercicios de econometría aplicada.
Dado el mínimo nivel de profundidad con el que se abordan los fundamentos teóricos sobre los que se estruc-
tura el análisis econométrico en esta obra, y su marcado enfoque práctico, se espera que el lector posea unos
buenos conocimientos previos (a nivel introductorio) sobre la materia, de modo que no le resulte complejo
seguir los ejercicios expuestos y pueda alcanzar sus objetivos de aprendizaje.
Además de un conocimiento básico previo sobre Econometría, se espera que el lector esté familiarizado
con el uso de herramientas informáticas, de forma que comprenda, sin mayores problemas, las indicaciones
dadas para el desarrollo de los ejercicios planteados y las instrucciones brindadas para la instalación y uso
de los programas empleados. Asimismo, resulta necesario que el lector posea cierto conocimiento del idioma
inglés, en cuanto la mayoría de funciones, métodos y atributos empleados son representados por medio de
palabras en inglés, las cuales, dado el muy alto nivel del lenguaje de programación que se empleará, resultan
muy informativas y facilitan enormemente la comprensión de las tareas que se llevan a cabo.
Deseo resaltar mi interés en que este trabajo se constituya como referente no teórico y facilite el aprendiza-
je de la Econometría dado el tratamiento práctico que pretende lograr; asimismo, que permita el acercamiento
de toda persona interesada en la materia, con cierto nivel de conocimiento, sin importar el ámbito de su for-
mación, y que promueva el aprovechamiento de herramientas tecnológicas por parte de todos. Aunque cierto
conocimiento o experiencia previa con lenguajes de programación son bienvenidos, y resultarán muy útiles para
la comprensión de la obra por parte del lector, estos no son indispensables pues, con el tratamiento dado a la
información en esta obra, pretendo que las exposiciones presentadas sean lo más ilustrativas y claras posibles,
de modo que todo lector se sienta cómodo con el estudio de este trabajo y alcance sus metas de aprendizaje
satisfactoriamente.

El Autor,
Fabián Alejandro Triana Alarcón
Índice general

1. Instalación de los programas y preparación del entorno 1


Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Anaconda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Jupyter Notebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Instalación y primeros pasos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

2. Exploración de los datos 13


Preparación del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Importación de los datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Estadística descriptiva . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3. Regresión lineal por Mínimos Cuadrados Ordinarios 21


Regresión lineal simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Regresión lineal múltiple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

4. Verificación de supuestos 39
Supuestos sobre la estructura del modelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Preparación del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Importación de los datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Número de observaciones mayor a número de parámetros . . . . . . . . . . . . . . . . . . . . 41
Variación en las variables explicativas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
No multicolinealidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
No sesgo de especificación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Supuestos sobre el término de error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Valor medio igual a cero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Homoscedasticidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
No autocorrelación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Normalidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

5. Mínimos Cuadrados en 2 Etapas 66


Preparación del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Importación de los datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Proceso manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Primera etapa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Segunda etapa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Proceso automático . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
MCO vs. MC2E . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Comparación de modelos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6. Variable dependiente discreta - Caso binario 82
Modelo lineal de probabilidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Preparación del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Importación de los datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Preparación de los datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Modelo Logit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Modelo Logit en Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Modelo Probit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Modelo Probit en Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
Comparación de modelos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

7. Variable dependiente discreta - Caso no binario 99


Modelo Logit Multinomial en Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Preparación del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Importación de los datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Preparación de los datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Construcción y estimación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

Referencias 111
Capítulo 1

Instalación de los programas y preparación del


entorno

A lo largo de este trabajo se pretende desarrollar ejercicios prácticos, con propósitos ilustrativos, que sirvan
de referente para el uso del lenguaje de programación Python como herramienta útil para el análisis economé-
trico y que permitan al lector familiarizarse con el mismo y fortalecer sus habilidades en el manejo de éste.
Esta obra se basa en el uso de Python 3, con distribución Anaconda y trabajando sobre Jupyter Notebook;
aunque estos nombres resulten extraños para el lector, especialmente el novato, es adecuado señalar que éste
no debería preocuparse, ya que se trata de conceptos sin mayor complejidad (para nuestros propósitos) y que,
a continuación, se pretenden aclarar, de modo que sienta entera confianza en el estudio de esta obra.

Python
Python es un lenguaje de programación orientado a objetos, interpretado, de alto nivel y con tipado di-
námico (Python Software Foundation,s.f), creado por Guido van Rossum, un científico de la computación y
programador holandés, y que apareció en su versión inicial en 1991. Python es de muy alto nivel, lo que sugiere
que su sintaxis resulta relativamente fácil de comprender y lo que se refleja en el hecho de que su proceso de
aprendizaje es más bien sencillo en comparación con el que corresponde a otros lenguajes de programación.
Se ha señalado que trabajaremos con Python 3, sin embargo, no se ha informado al lector sobre la diferencia
entre Python, a secas, y Python 3. Esto no debería causarle intranquilidad, pues la distinción es bien simple:
Python es el lenguaje de programación, mientras que Python 3 es una versión de dicho lenguaje (el cual también
está disponible en la versión Python 2). En este trabajo, se hará uso de Python 3 ya que es la versión que
evidencia una tendencia creciente a la adopción generalizada y se caracteriza por un desarrollo activo.
El uso que se hace de Python a lo largo de esta obra es aplicado y específico, es decir, se emplea con propósitos
muy claramente delineados, ejecutando tareas útiles para abordar el análisis econométrico que se pretende
llevar a cabo; en ningún momento este trabajo trata de ser un manual de programación o algo que se le parezca,
por lo cual el lector no debe considerar que con el estudio de esta obra aprenderá a programar, pues tal objetivo
excede, y por mucho, el alcance de esta guía. Al lector interesado en la programación como tal, se sugiere
consultar otro tipo de recursos que le resulten útiles a tal propósito, en cuanto en esta obra se recurre a Python
únicamente como herramienta y no como el tema central a abordar.
Tal y como apenas se ha señalado, Python es una herramienta que utilizaremos para desarrollar análisis
econométrico; sin embargo, puede que tal planteamiento no resulte del todo claro al lector, quien es natural
que se cuestione ¿cómo se hará uso de Python para llevar a cabo análisis econométrico? La respuesta a tal
interrogante no implica mayor complejidad: tan solo se hará uso de las funciones, métodos y atributos que
nos ofrecen Python y sus librerías especializadas (las cuales abordaremos posteriormente) para manejar los

1
datos, extraer la información requerida, construir los modelos, realizar las estimaciones y ejecutar las pruebas
estadísticas necesarias. Aunque tales tareas sí comprenden operaciones con cierto nivel de dificultad e implican
cálculos elaborados, el usuario no tendrá que realizar un esfuerzo extraordinario, de hecho, ni siquiera tendrá
que definir una función de dificultad significativa por su propia cuenta o escribir código “complejo”, en cuanto
Python y sus librerías especializadas brindarán los instrumentos que requeriremos a lo largo de nuestra labor.

Anaconda
Aunque es posible instalar Python usando la distribución oficial del sitio web de Python Software Founda-
tion, [Link] no se recurrirá a tal medio, en cuanto se hará uso de la distribución Anaconda.
La explicación para tal decisión se encuentra en el hecho de que la distribución Anaconda instala automáti-
camente múltiples librerías (muy útiles para las labores que se adelantarán), incluye un gestor de paquetes
propio, conda, el sistema de gestión de paquetes estándar de Python (pip) y, además, nos da acceso a Anacon-
da Navigator, una interfaz gráfica con la que podemos ingresar a Jupyter Notebook, que es donde, en último
término, escribiremos el código Python con el que llevaremos a cabo nuestro análisis econométrico.
Anaconda Distribution es un producto de la empresa estadounidense Anaconda, Inc., (anteriormente cono-
cida como Continuum Analytics) y consiste en una distribución Python gratuita y de código abierto ampliamente
utilizada en el campo de la ciencia de datos y machine learning. Para mayor información, se sugiere consultar el
sitio web de la documentación oficial [Link] aunque, para nuestros propósi-
tos, esto no resultará ni siquiera necesario, en cuanto aquí se darán las instrucciones requeridas por el lector
para seguir correctamente el desarrollo de los temas tratados.

Jupyter Notebook
Jupyter Notebook es una aplicación web de código abierto que permite crear y compartir documentos que
contienen código, gráficos, ecuaciones y texto (Project Jupyter, 2019). Soporta varios lenguajes de programa-
ción, entre ellos Julia, Python y R, cuenta con una interfaz atractiva, de fácil manejo y muy amigable con el
usuario, y hace parte de Project Jupyter, una iniciativa surgida en 2014 a partir de IPython y dirigida por Fernan-
do Pérez, físico colombiano de la Universidad de Antioquia, profesor asistente del Departamento de Estadística
de UC Berkeley e investigador del Berkeley Institute for Data Science.
La elección de Jupyter Notebook para ser la aplicación en la que se desarrollará nuestro análisis economé-
trico radica en lo agradable de su interfaz y su manejo extremadamente simple e intuitivo, lo que nos permitirá
escribir código, visualizar resultados, elaborar gráficos y realizar comentarios en un mismo documento, enri-
queciendo vastamente nuestro análisis y facilitando la comprensión del contenido creado (de hecho, este libro
ha sido escrito en Jupyter Notebook, sobre LATEXy markdown, con edición posterior en Overleaf). Para mayor
información sobre Project Jupyter, se invita a consultar el sitio web oficial del proyecto: [Link]

Instalación y primeros pasos


Al comienzo de este capítulo se señaló que “esta obra se basa en el uso de Python 3, con distribución
Anaconda y trabajando sobre Jupyter Notebook”; ya se ha dado al lector una breve descripción de cada uno
de estos términos, sin embargo, aún no se le ha brindado un conjunto de instrucciones concretas para poder
acceder al “material” con el cual trabajaremos. Por lo tanto, en esta sección, se procederá a presentar una
descripción detallada de los pasos a seguir para instalar los programas requeridos y preparar el entorno, de
modo que se pueda dar inicio efectivo al contenido principal de esta guía práctica de econometría básica con
Python.

2
Lo primero que debemos hacer es contar con la distribución Anaconda. En caso de no tenerla instalada
previamente (lo que es muy probable, especialmente para un usuario no familiarizado con el tema), debemos
acceder al sitio web oficial [Link] Al ingresar a tal dirección, nos encontra-
remos con una página como la siguiente:

Podemos observar que en la parte inferior se encuentra una sección en la que está escrito Windows | macOS
| Linux. En este punto, debemos hacer clic sobre el nombre correspondiente al sistema operativo de nuestro
computador; esto, con el propósito de acceder al Instalador Anaconda para nuestro sistema operativo. En es-
te caso particular, seleccionaremos Windows, sin embargo, el lector tiene que ser cuidadoso, en cuanto debe
seleccionar el sistema operativo de su computador (el cual, puede no ser Windows). Al seleccionar el sistema
operativo correspondiente, podremos visualizar lo siguiente:

Observamos que hay dos instaladores: uno para Python 3 (versión 3.71 específicamente) y otro para Python
2 (versión 2.72 específicamente); el que debemos seleccionar, como se ha señalado con anterioridad, es el de
1 La versión más reciente al momento de publicación de esta obra.
2 La versión más reciente al momento de publicación de esta obra.

3
Python 3. En cuanto al Graphical Installer específico, éste depende del procesador de nuestro computador: si
se trata de un procesador de 64 bits, naturalmente descargaremos el 64-Bit Graphical Installer, y si se trata de
un procesador de 32 bits, nuestra elección será el 32-Bit Graphical Installer. Para el lector que desconozca el
sistema de su computador, éste puede ser consultado, en Windows, en las propiedades del equipo o en:

Allí, encontrará, entre otros datos, la información correspondiente al sistema; información similar a la que
se presenta a continuación:

Una vez seleccionado el Graphical Installer correspondiente, solo se debe guardar el archivo y, una vez descarga-
do por completo, se debe ejecutar. Este proceso es muy simple (pues tan solo consiste en seguir instrucciones
muy concisas), por lo cual no se expondrá paso a paso, en cuanto hacerlo resultaría superfluo. Una vez des-
cargado e instalado el programa, accederemos a Anaconda Navigator; para tal propósito, recurriremos a una
búsqueda desde el botón de inicio de Windows, tal y como se señala a continuación:

4
Haremos clic sobre Anaconda Navigator (Aplicación de escritorio) y esperaremos que sea cargada por com-
pleto (esto puede llegar a tomar un poco de tiempo, por lo que el usuario no debe inquietarse). Recibiremos
entonces el siguiente mensaje:

5
Sobre este mensaje, simplemente debemos pulsar en Ok o en Ok, and don’t show again. Una vez rea-
lizado este paso, tendremos acceso a Anaconda Navigator, donde encontraremos una pantalla de inicio similar
a la que se presenta a continuación:

El lector recordará que usaremos Python 3, con distribución Anaconda, trabajando sobre Jupyter Notebook. En
este punto, si se han seguido las instrucciones dadas, ya contamos con la distribución Anaconda para Python 3
en nuestro computador; lo único que hace falta es tener acceso a Jupyter Notebook, lo que es, a decir verdad,
el paso más simple de todos: tan solo consiste en hacer clic.
El lector puede observar que Jupyter Notebook es una de las aplicaciones presentes en la pantalla de inicio
de Anaconda Navigator; para ingresar a ésta, tan solo debe pulsar en Launch:

6
Una vez pulsado, seremos automáticamente redirigidos a localhost. El proceso de redirección puede demorar
un poco, por lo cual, se recomienda al usuario ser paciente; una vez culminado este proceso, estaremos en
Jupyter Notebook, donde finalmente podremos iniciar con nuestro análisis econométrico y entrar en contacto
efectivo con Python.
La descripción dada hasta ahora acerca del proceso a seguir para acceder a Jupyter Notebook se ha pre-
sentado teniendo en consideración la instalación inmediatamente previa de Anaconda; sin embargo, una vez se
cuenta con ésta, no es necesario ingresar a Jupyter Notebook a través de Anaconda Navigator. Una ruta más
breve, y con una ejecución más veloz, es el acceso desde Anaconda Prompt; basta con hacer la búsqueda y
pulsar sobre la aplicación correspondiente:

7
Una vez se ha hecho clic, se debe escribir jupyter notebook en Anaconda Prompt y pulsar la tecla Enter
(Intro), después de lo cual se abrirá Jupyter Notebook y aparecerá en Anaconda Prompt información adicional
sobre el proceso ejecutado.
Por último, el modo más sencillo (una vez se tiene instalada Anaconda) y, naturalmente, el más evidente
para acceder a Jupyter Notebook es buscando directamente la aplicación y pulsando sobre el ícono correspon-
diente:

8
Cualquiera de los 3 métodos de acceso señalados anteriormente es completamente válido y debe permitir
al usuario ingresar a Jupyter Notebook; estando en esta aplicación, se debe hacer clic en la pestaña New que se
encuentra en la sección superior derecha, y pulsar entonces sobre Python 3, como se indica a continuación:

Una vez hemos hecho clic sobre Python 3, se abrirá una nueva pestaña en el navegador, que tendrá una
apariencia como la siguiente:

9
Es en este punto donde empieza nuestro verdadero trabajo, pues es ahora cuando podremos comenzar a
escribir código Python y llevar a cabo el análisis econométrico que tenemos planeado.
El lector observará que la apariencia del notebook de Jupyter es bastante agradable, con cierta similitud
a un editor de texto tradicional o un programa ofimático común. En realidad, la sensación de seguridad que
transmite esta apariencia ’familiar’ efectivamente se ve reflejada en facilidad de manejo: vemos un entorno
no sobrecargado y la mayoría de comandos se explican por sí mismos; al usuario no completamente ajeno a
la informática le resultará verdaderamente sencillo el uso del notebook de Jupyter. Tal vez el lector ha notado
que el logo de Python se encuentra en la parte superior derecha del notebook, y que cerca de éste está escrito
Python 3: esto nos indica que el código escrito en el notebook corresponde a código Python. Asimismo,
observamos que, en la parte superior del notebook, al lado del logo de Jupyter Notebook, se encuentra la
palabra Untitled; éste es el nombre que ha sido asignado por defecto a nuestro notebook, el cual, si lo
deseamos, podemos cambiar por el de nuestra preferencia haciendo clic sobre Untitled, o recurriendo a las
opciones Save as y Rename del menú File:

Modificaremos el nombre de nuestro notebook y lo llamaremos, con fines ilustrativos, “Mi primer note-
book”:

Ahora, procederemos a escribir nuestra primera línea de código; para esto, emplearemos la función prede-
finida print(...). El lector puede observar que en el notebook hay una celda sin ningún contenido; es aquí
donde debemos escribir nuestro código. Para este ejemplo específico, el argumento de la función print(...)
será “Esta es la primera línea de código de mi notebook escrito en Python”:

A esta altura, el usuario ha escrito su primera línea de código Python en el notebook, pero, ¿este código ha
tenido algún efecto? ¿se ha ejecutado alguna acción? La respuesta es simplemente no. Nuestra línea de código

10
no ha generado ningún resultado ya que tan solo la hemos escrito; aún no la hemos ejecutado. Para ejecutar
el código, debemos pulsar el botón Run, ubicado en la parte superior, debajo de la ficha Cell, o, de forma
alternativa y más rápida, emplear la combinación de teclas Ctrl + Enter.
Al ejecutar el código, obtendremos lo siguiente:

Podemos observar que debajo de nuestra celda ha sido impreso el mensaje contenido en el código y que
en los corchetes que se encuentran al lado de la celda de código, los cuales estaban vacíos, ha aparecido el
número uno. Este número indica que es la primera ejecución de código que hemos realizado en el notebook; si
ingresamos de nuevo a la celda y ejecutamos otra vez el código (con Run o la combinación Ctrl + Enter),
observaremos que ya no está el número uno sino el número dos (se invita al lector a comprobarlo por su propia
cuenta).
Ya hemos creado un notebook de Jupyter, le hemos asignado un nombre y hemos escrito y ejecutado algo
de código dentro de éste. Estas son algunas de las instrucciones básicas que el usuario, sin excusa alguna, debe
conocer; por último, guardaremos los cambios realizados en el notebook y procederemos a detenerlo y cerrarlo.
Conociendo este conjunto de operaciones, el usuario tendrá la preparación fundamental requerida para abordar
exitosamente el verdadero contenido en el que se especializa esta obra.
El proceso empleado para guardar los cambios efectuados en el notebook es muy sencillo y prácticamente
idéntico al utilizado con cualquier programa ofimático común, por lo que no representará dificultad alguna a
cualquier usuario con conocimiento básico de informática. Basta con usar la combinación de teclas Ctrl + S,
o hacer clic sobre la ficha de Save and Checkpoint (identificada con la forma de un disquete) de la cinta de
opciones.
Aunque el notebook puede cerrarse cerrando la pestaña del navegador en la que se encuentra, tal proce-
dimiento resulta no recomendable. Para cerrar correctamente el notebook, se debe pulsar sobre Close and
Halt del menú File, como se indica a continuación:

Habiendo realizado tal proceso, la pestaña del navegador se cerrará automáticamente y seremos redirigidos
a la pantalla de inicio de Jupyter Notebook, donde debemos encontrar el notebook que hemos apenas creado,
guardado y cerrado:
Podemos observar que nuestro notebook “Mi primer notebook”tiene un tamaño de 814 B y ha sido guar-
dado con extensión .ipynb; esto se debe a que los notebooks de Jupyter son archivos que se almacenan auto-
máticamente con dicha extensión. Para acceder de nuevo al notebook basta con hacer clic sobre su nombre;
de este modo, se abrirá una nueva pestaña en el navegador en la cual se encontrará el notebook.

11
Es importante resaltar que cada vez que se abre el notebook se está “iniciando una nueva sesión”, por lo que
ninguna línea de código ha sido ejecutada y, aunque es posible visualizar el output de las líneas de código, el
output que se visualiza no es el resultado de ejecuciones de la “sesión” actual. Esto es importante ya que, si se
pretende ejecutar una línea de código particular que requiere la ejecución previa de otra línea específica y esta
última no se ha ejecutado, se obtendrá un error.
Ya se ha señalado el proceso para ingresar a Anaconda Navigator y a Jupyter Notebook (mencionando 3
alternativas), ahora es momento de abordar el proceso contrario: cómo salir. Aunque es posible salir de Jupyter
Notebook con tan solo cerrar el navegador en el que se encuentra, tal proceder resulta inadecuado. El método
correcto para salir de Jupyter Notebook es cerrar (apropiadamente) los notebooks activos y, en seguida, cerrar
Jupyter Notebook haciendo clic sobre la ficha Quit que se encuentra en la sección superior derecha:

Una vez pulsada, obtendremos el siguiente mensaje:

Ahora sí, podemos cerrar con entera tranquilidad el navegador. En cuanto a Anaconda Navigator, después
de haber realizado el proceso apenas descrito, si está abierta, basta con cerrarla como si fuera un programa
ofimático cualquiera, con tan solo pulsar la ficha con la x en el extremo superior derecho.
Para volver a acceder a Jupyter Notebook tan solo se requiere repetir las instrucciones necesarias que se
han indicado a lo largo de este capítulo, las cuales, en realidad, no son más que acceder a Anaconda Navigator y,
desde esta aplicación, ingresar a Jupyter Notebook (o simplemente acceder desde Anaconda Prompt o Jupyter
Notebook directamente) siendo redirigido a localhost.
Con este capítulo se cierra el tratamiento de los programas a emplear; ya es hora de entrar en la verdadera
materia que aborda esta obra, por lo que, a partir del siguiente capítulo, se desarrollará el propósito de esta
guía práctica: análisis econométrico con Python.

12
Capítulo 2

Exploración de los datos

Resulta imposible llevar a cabo un análisis sin tener datos que analizar; esto no es más que un absurdo. Así,
si queremos realizar un ejercicio de análisis econométrico, debemos contar con la información requerida para
poder llevarlo a cabo; el lector no debe preocuparse por este comentario, en cuanto, además de la guía práctica
que se le ofrece, también se le brinda la información con la cual se construye los modelos y se estructura el
análisis.
Para cada uno de los ejercicios que se desarrollarán en esta obra se indicará la fuente de la cual se ha ob-
tenido la información empleada en el análisis econométrico correspondiente. Asimismo, es posible obtener los
datasets utilizados, en los mismos formatos que se emplean en esta obra, contactando al autor de la misma
(basta con enviar un correo a la dirección fatrianaa@[Link]), quien le dará acceso a la información reque-
rida, o, alternativamente, acceder al sitio web de la Unidad de Informática y Comunicaciones de la Facultad de
Ciencias Económicas de la Universidad Nacional de Colombia.
Esta obra, dado su carácter práctico y la utilidad que puede llegar a significar para los estudiantes de pre-
grado en Economía, se apoya, principalmente, en Econometría de Gujarati y Porter (2010) e Introducción a la
econometría. Un enfoque moderno de Wooldridge (2010), textos muy sencillos ampliamente empleados como
guía en los cursos básicos de Econometría, a los que en este trabajo se recurre, en múltiples ocasiones, pa-
ra desarrollar los ejercicios ilustrativos planteados y con los que se pretende que el lector pueda contrastar
resultados, de modo que evidencie que los procedimientos realizados son correctos y llegan a las soluciones
esperadas.
El dataset que se utilizará para la regresión lineal simple por Mínimos Cuadrados Ordinarios que se tratará
en el próximo capítulo es el empleado en el Ejemplo 7.1, Mortalidad infantil en relación con el PIB per cápita y
la tasa de alfabetización de las mujeres, de Gujarati y Porter (2010). Se invita al lector a consultar el ejemplo, de
modo que pueda verificar los resultados obtenidos.
La fuente de la información usada por Gujarati y Porter (2010) es “Chandan Mukherjee, Howard White y
Marc Whyte, Econometrics and Data Analysis for Developing Countries, Routledge, Londres, 1998, p. 456”. Los
datos usados para el ejercicio en Python que se lleva a cabo en esta obra se obtuvieron del fichero de datos de
Gujarati en gretl.
En este punto empieza nuestro trabajo con Python. Lo primero que debe hacer el usuario es acceder a
Jupyter Notebook (a través de Anaconda Navigator o por el medio de su preferencia) y crear un nuevo notebook.
En este notebook es donde se escribirá el código requerido y se realizará el análisis econométrico planteado.

Preparación del entorno


La primera celda en la que el usuario escribirá y ejecutará código tendrá el siguiente contenido:

13
In [1]: # Importamos las librerías requeridas:
import numpy as np
import pandas as pd
import [Link] as sm
import [Link] as plt
import seaborn as sns
%matplotlib inline
[Link]("seaborn-white")

¿De qué tratan estas líneas de código? El lector notará que lo primero que se ha escrito, a modo de co-
mentario (los comentarios van precedidos de #), es “Importamos las librerías requeridas”; esto es lo que, efec-
tivamente, se consigue al ejecutar esta celda de códigos. Básicamente, lo que logramos es preparar nuestro
entorno de trabajo al obtener acceso al conjunto de herramientas que requeriremos para nuestro análisis; tales
herramientas hacen parte de librerías.
Una librería simplemente es un conjunto de funciones y métodos diseñados para realizar tareas específi-
cas, agrupados en un único espacio (la librería), y que se agrega a Python “básico” con el objetivo de alcanzar
resultados que con sus funciones integradas (nativas) no pueden lograrse fácilmente. Al lector familiarizado
con el lenguaje de programación R basta con decir que una librería Python es, esencialmente, equivalente a un
paquete de R.
Las librerías que importamos son NumPy, pandas, Statsmodels, Matplotlib y Seaborn; cada una de estas tiene
un rol particular a desempeñar en nuestro trabajo, el cual se presentará, junto a breves descripciones, en un
momento. Algo que debe notar el lector es que la importación de las librerías no consiste tan solo en “importar
las librerías”, sin especificar algún tipo de instrucción adicional: la librería NumPy no se importa simplemente
como import numpy sino que se importa como import numpy as np. ¿A qué se debe esto?, ¿qué efecto
genera?
El proceso de importación con as, que es una palabra reservada de Python, se lleva a cabo con el propósito
de asignar un alias a las librerías importadas. El lector puede estar preguntándose: si la librería ya tiene un
nombre, ¿para qué se le quiere asignar un alias? La respuesta al interrogante es muy sencilla: tan solo por
practicidad. Al importar la librería NumPy con el alias de np, tal librería queda identificada con este nombre
(np); por lo tanto, cada vez que se necesite emplear una función o método de NumPy basta con hacer referencia
a np y no a numpy.
Tal vez la utilidad de as resulte más evidente en el caso de la librería Matplotlib: en vez de hacer referencia
a [Link] en cada ocasión que se requiera una función o método proveniente de éste, basta con
hacer referencia a plt. Así, en vez de usar 17 caracteres, tan solo se requiere emplear 3; aunque al lector esto le
parezca trivial en este momento, con el tiempo verá que la importación con as resulta tremendamente práctica
y facilita enormemente el trabajo.
Las librerías importadas son NumPy, pandas, Statsmodels, Matplotlib y Seaborn. ¿Por qué se importa este grupo
específico de librerías y no otras? Para comprenderlo, se ofrece una breve descripción de cada una de estas, de
modo que el lector tenga claridad sobre el propósito de las mismas.

NumPy es una librería Python para computación científica que provee un conjunto de rutinas para la
ejecución de procedimientos sobre objetos, como vectores y matrices, los cuales incluyen, entre otros,
operaciones matemáticas y lógicas, álgebra lineal básica, simulación aleatoria y estadística fundamental
(The SciPy community, 2019).
pandas es una librería de código abierto que ofrece herramientas de alto desempeño y fáciles de usar
para el manejo y análisis de datos en Python. Se trata de una librería que permite llevar a cabo un proceso
óptimo de análisis de información directamente en Python, sin tener que recurrir a un lenguaje con mayor
especificidad, como R. pandas es empleada tanto en ámbitos académicos como comerciales, en campos

14
diversos que incluyen las finanzas, la neurociencia, la estadística y la economía (Pandas, s.f). El creador
y Benevolent Dictator for Life de pandas es el matemático y desarrollador estadounidense Wes McKinney.

Statsmodels es una librería para análisis econométrico y estadístico en Python que provee funciones para
la estimación de múltiples modelos estadísticos, así como la realización de pruebas y análisis de infor-
mación estadística. Los resultados generados por las funciones de la librería son contrastados con los
de paquetes estadísticos reconocidos, de modo que se pueda garantizar su exactitud. La librería tiene
su origen en el módulo models de [Link] y en 2009, tras un proceso de corrección, prueba y
mejora, se lanzó Statsmodels de forma independiente (Perktold, Seabold, y Taylor, 2018).

Matplotlib es una librería de gráficos 2D usada en Python para la generación de imágenes de alta cali-
dad. Con Matplotlib es posible generar histogramas, gráficos de barras, diagramas de dispersión, entre
otros, mediante el uso de tan solo unas cuantas líneas de código. El módulo pyplot provee una interfaz
similar a MATLAB, útil para gráficos sencillos, que permite un control completo de las distintas propieda-
des. Matplotlib es una contribución de John Hunter, un neurobiólogo estadounidense, y de sus múltiples
colaboradores (The Matplotlib development team, 2018).

Seaborn es una librería para la elaboración de gráficos estadísticos en Python. Está basada en Matplotlib
y sus funciones de construcción de gráficos se orientan a datasets, por lo que está estrechamente inte-
grada con las estructuras de datos de pandas. El propósito de Seaborn es constituir la visualización en un
elemento clave en la exploración y comprensión de información (Waskom, 2018).

El lector, teniendo en consideración las breves descripciones apenas presentadas, ya debería tener una idea
del papel que desempeña cada una de las librerías mencionadas en el análisis que se pretende llevar a cabo; sin
embargo, si éste no es el caso y la información previamente expuesta le resulta un poco confusa, en palabras
sencillas la utilidad de cada una de las librerías es la siguiente:

NumPy: Análisis numérico.


pandas: Manejo de datos.
Statsmodels: Construcción de los modelos y realización de las estimaciones.
Matplotlib: Elaboración de gráficos (generales).
Seaborn: Funciones adicionales para la elaboración de gráficos (especializados).

Una vez ejecutadas las líneas de código de la primera celda, se habrán importado las librerías requeridas y
tendremos a disposición el conjunto de herramientas de las cuales haremos uso para llevar a cabo el análisis
econométrico. Ahora, procederemos a cargar la información con la que trabajaremos.

Importación de los datos


La importación de los datos se lleva a cabo con funciones de la librería pandas. Dependiendo del tipo de
archivo en el cual se encuentre almacenada la información del dataset, deberemos recurrir a una función parti-
cular y un conjunto de parámetros específicos.
El dataset que usaremos corresponde a un archivo .txt, por lo que se recurrirá a la función
read_csv(...) de pandas y se señalarán los valores correspondientes al parámetro delimiter.

Nota: El parámetro obligatorio de la función read_csv(...) es el filepath_or_buffer y corresponde a la


ruta del archivo, es decir, la ubicación del archivo que contiene el dataset, expresada como string.

Para nuestro caso en particular, el siguiente código es el que importa el dataset:

15
In [2]: data = pd.read_csv("C:/Users/FCE/Documents/Econometrics/[Link]",
sep = " ", delimiter="\t")

El lector debe notar que no se ha escrito read_csv(...) sino pd.read_csv(...). Esto se debe a que
read_csv(...) no es una función “nativa” de Python sino que pertenece a pandas, por lo cual es necesa-
rio especificar de dónde proviene. Dado que la importación se realizó con import pandas as pd, el código
pd.read_csv(...) informa que la función read_csv(...) pertenece a pandas. Si la importación no se hu-
biera llevado a cabo con el uso de un alias, se debería haber recurrido a pandas.read_csv(...) para conse-
guir el resultado.

Nota: El usuario debe tener en cuenta que el valor que asignará al parámetro filepath_or_buffer no será el
mismo que el del código del ejemplo anterior, pues éste depende de la ubicación del archivo GujaratiPor-
[Link] (o el archivo en el que haya guardado el dataset) en su computador.

Una vez ejecutada la línea de código de esta celda, el dataset debería haber sido importado correctamente.
Para comprobarlo, se recurre al método .head(), aplicado al objeto que contiene el dataset (en este caso se
le ha asignado el nombre data):

In [3]: [Link]()

Out[3]: CM FLR PGNP TFR


0 128 37 1870 6.66
1 204 22 130 6.15
2 202 16 310 7.00
3 197 65 570 6.25
4 96 76 2050 3.81

Podemos observar que el proceso de importación ha sido exitoso y la información del dataset ha sido alma-
cenada correctamente en un DataFrame de pandas. Ahora, podemos continuar tranquilamente con el desarrollo
de nuestro análisis, en cuanto hemos concluido satisfactoriamente el primer paso.

Estadística descriptiva
Antes de empezar a examinar los valores de las variables del dataset e identificar las posibles relaciones que
hay entre estas, es recomendable hacer un reconocimiento del propio dataset, es decir, obtener información
sobre sus dimensiones, el tipo de datos que contiene, etc. Para conocer las dimensiones del dataset puede
recurrirse al atributo .shape:

In [4]: [Link]

Out[4]: (64, 4)

El resultado de la aplicación del atributo .shape es una tupla Python (...) en la que se informa el número de
filas y de columnas (en ese orden) que tiene el DataFrame; así, nuestro dataset contiene 64 filas y 4 columnas.
Ahora, sabemos que son 4 columnas, pero ¿cómo se llaman? ¿cuál es el nombre de cada una de estas 4 co-
lumnas? Cuando se ha aplicado el método .head() se han visualizado las primeras observaciones del dataset,
incluyendo el encabezado en el que se encuentran los nombres de las columnas; sin embargo, si específicamente
se quiere saber el nombre de las columnas, puede emplearse el atributo .columns:

16
In [5]: [Link]

Out[5]: Index(['CM', 'FLR', 'PGNP', 'TFR'], dtype='object')

Con toda la información obtenida se sabe que el dataset con el que se trabajará tiene 64 filas y 4 columnas,
y que los nombres de las columnas son ’CM’, ’FLR’, ’PGNP’ y ’TFR’. Sin embargo, aún se desconoce qué tipo de
datos contienen dichas columnas, ¿se trata de palabras? ¿de números? ¿solo números enteros? Para descubrirlo,
puede emplearse el atributo .dtypes:

In [6]: [Link]

Out[6]: CM int64
FLR int64
PGNP int64
TFR float64
dtype: object

La información generada por el atributo .dtypes indica que tres variables (columnas) contienen datos de
tipo int64 (número entero) y una de tipo float64 (número decimal).
Finalmente, una manera de obtener la información que se halla con la aplicación de los tres atributos apenas
descritos es el método .info(), el cual permite visualizarlos en un único espacio:

In [7]: [Link]()

<class '[Link]'>
RangeIndex: 64 entries, 0 to 63
Data columns (total 4 columns):
CM 64 non-null int64
FLR 64 non-null int64
PGNP 64 non-null int64
TFR 64 non-null float64
dtypes: float64(1), int64(3)
memory usage: 2.1 KB

El resultado de la aplicación de este método es tan solo un resumen de la información que hemos obtenido
previamente. Adicionalmente, se presenta el dato sobre el uso de memoria asociado, que en este caso es de 2.1
Kb; el tipo de objeto al que corresponde el dataset, el cual es un DataFrame de pandas; y el rango del índice,
el cual corresponde al intervalo [0, 63] (es muy importante tener en cuenta que la indización en Python inicia
desde 0 y no desde 1).
Para obtener estadística descriptiva de las variables numéricas del dataset se puede recurrir al método
.describe():

In [8]: [Link]()

Out[8]: CM FLR PGNP TFR


count 64.000000 64.000000 64.000000 64.000000
mean 141.500000 51.187500 1401.250000 5.549687
std 75.978067 26.007859 2725.695775 1.508993

17
min 12.000000 9.000000 120.000000 1.690000
25 % 82.000000 29.000000 300.000000 4.607500
50 % 138.500000 48.000000 620.000000 6.040000
75 % 192.500000 77.250000 1317.500000 6.615000
max 312.000000 95.000000 19830.000000 8.490000

Tal y como observamos, el método .describe() nos permite obtener información estadística básica sobre
las variables numéricas, lo que incluye, por ejemplo, el promedio, los valores máximo y mínimo y la desviación
estándar. Así, se puede evidenciar, por ejemplo, que el valor mínimo de la variable ’CM’ es 12, el valor máximo
de la variable ’PGNP’ es 19,830 y el promedio de la variable ’TFR’ corresponde a 5.55.
Al aplicar el método .describe() sobre el DataFrame se obtiene, por defecto, información estadística
básica de todas las variables numéricas presentes en éste. Si solo se está interesado en la información de una
variable en particular, esta puede ser seleccionada por medio del uso de los corchetes [ ], indicando su nombre
(entre comillas) dentro de estos.
Así, si, por ejemplo, solo nos interesa la variable ’CM’, el código a emplear será el siguiente:

In [9]: data["CM"].describe()

Out[9]: count 64.000000


mean 141.500000
std 75.978067
min 12.000000
25 % 82.000000
50 % 138.500000
75 % 192.500000
max 312.000000
Name: CM, dtype: float64

Si, por el contrario, lo que nos interesa es un grupo de variables y no solo una, podemos seleccionarlo
haciendo uso de los corchetes [ ] e incluyendo los nombres de las variables de interés (entre comillas, separados
por comas) dentro de una lista Python […]:

In [10]: data[["CM", "PGNP", "FLR"]].describe()

Out[10]: CM PGNP FLR


count 64.000000 64.000000 64.000000
mean 141.500000 1401.250000 51.187500
std 75.978067 2725.695775 26.007859
min 12.000000 120.000000 9.000000
25 % 82.000000 300.000000 29.000000
50 % 138.500000 620.000000 48.000000
75 % 192.500000 1317.500000 77.250000
max 312.000000 19830.000000 95.000000

A esta altura se sabe que el dataset contiene información sobre 4 variables numéricas (3 de valores enteros
y 1 de valores no enteros), contando con datos de 641 individuos (entidades) para cada una de estas. Ya se ha
1 Eneste caso, la información de cada individuo (entidad) corresponde a una única fila, por lo que, como el dataset tiene 64 filas,
sabemos que hay información sobre 64 individuos (entidades). Sin embargo, en otros casos, como los paneles de datos, la información
del mismo individuo (entidad) no corresponde a una única fila, por lo que hay que ser cuidadosos en tales situaciones.

18
realizado un muy breve reconocimiento de los datos, por lo que se tiene un mayor nivel de familiarización con
los mismos y es posible identificar potenciales relaciones a examinar.
Hasta el momento, se ha llevado a cabo un proceso de reconocimiento de los datos por medio del cual se
ha obtenido información puramente numérica: el número de filas y columnas, la media y la desviación estándar
de cada variable, etc. Esto nos da cierta idea sobre los datos, sin embargo, con frecuencia, “una imagen vale más
que mil palabras”2 ; es posible que un gráfico tenga un inmenso poder comunicativo y contribuya enormemente
a obtener una mejor comprensión acerca de los datos con los que se trabajará.
La función pairplot(...) de Seaborn resulta particularmente útil para el contexto en el que nos encontra-
mos; esta función crea una cuadrilla en la que podremos visualizar la relación que existe entre pares de variables
(por medio de diagramas de dispersión) y la distribución de cada una de estas (por medio de histogramas):

In [11]: g = [Link](data, plot_kws = {"color": "darkblue"},


diag_kws = {"color":"darkblue"})
[Link]("Relación entre pares de variables",
fontsize = 25,
fontweight = "bold")
plt.subplots_adjust(top=0.9)
[Link](1,-.02,
"Elaboración:",
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](1,-.05,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()
2 O, en vez de palabras, más bien números en este caso.

19
En la diagonal se observa la distribución de cada una de las variables, mientras que en los diagramas de
dispersión se encuentra la relación entre pares de estas; así, se puede observar que, por ejemplo, el ingreso
se concentra en valores inferiores a 5000 y existe una relación negativa entre la tasa de alfabetización de las
mujeres y la mortalidad infantil.
En estos momentos ya se cuenta con información básica sobre las variables del dataset y hemos adelantado
una breve inspección visual de las relaciones que se presentan entre estas; ahora, se puede hacer uso de este
conocimiento para obtener expresiones más concretas de dichas relaciones, examinándolas desde un punto de
vista un tanto más técnico: en el siguiente capítulo se abordará el tema de la regresión lineal por el método de
Mínimos Cuadrados Ordinarios.

20
Capítulo 3

Regresión lineal por Mínimos Cuadrados


Ordinarios

La regresión lineal es el tema introductorio que generalmente se aborda en los cursos de Econometría de
pregrado en Economía; de acuerdo a Greene (2003), “el modelo de regresión lineal es la herramienta más útil del
kit de herramientas de los econometristas” (p. 7). Este tema suele ser el primer contacto que tiene el estudiante
con materia de contenido propiamente econométrico y tiende a ser abordada con cierto detalle, de modo que
éste tenga claridad suficiente sobre la misma. Es común que en los cursos se presente la regresión lineal, junto
a los fundamentos teóricos aplicables a la misma, con el apoyo de un texto guía de un autor reconocido y
que se haga énfasis apreciable en los supuestos asociados, de modo que el estudiante adquiera un nivel de
conocimiento adecuado sobre ésta.
En esta obra, considerando su carácter práctico y teniendo en cuenta la advertencia realizada en el prefacio
de que los fundamentos teóricos se abordarán con un nivel de profundidad mínimo, se estará limitado a exponer
la idea general de la regresión lineal en términos muy superficiales; esto, con el propósito de enfocarse en el
objetivo planteado y de no hacer sentir incómodo al lector proveniente de otras ramas de estudio o con una
formación econométrica no tan sólida, al no ahondar en las bases técnicas y las formalidades matemáticas sobre
las que se cimienta la regresión lineal y sus métodos de estimación.
El análisis de regresión trata del estudio de la dependencia de una variable (variable dependiente)
respecto de una o más variables (variables explicativas) con el objetivo de estimar o predecir la
media o valor promedio poblacional de la primera en términos de los valores conocidos o fijos (en
muestras repetidas) de las segundas. (Gujarati y Porter, 2010, p.15)
En términos simples, el análisis de regresión estudia la relación que existe entre la variable dependiente y
la(s) variable(s) explicativa(s). Para el caso de la regresión lineal, tal relación puede ser expresada como una
función lineal en los parámetros, pero ¿de qué parámetros se está hablando?
Para resolver el anterior interrogante, lo primero que se hará será considerar una regresión bivariada, en la
cual la variable dependiente (que se identifica con y) puede ser expresada como una función1 (con linealidad
en los parámetros) de la variable explicativa (que se identifica con x). Concretamente:

yi = β 0 + β 1 xi + ui
Esta expresión corresponde a la Función de Regresión Poblacional (FRP) (Gujarati y Porter, 2010) y los
parámetros a los que se ha hecho referencia son simplemente los β; como podemos ver, estos β no se ven
afectados por algún tipo de transformación que modifique su relación lineal con la variable dependiente: a esto
1 No se trata exactamente de una función, pues, como lo señalan Fahrmeier, Kneib, y Lang (2007), una característica fundamental

de los análisis de regresión es que la relación entre la variable dependiente y las variables explicativas no corresponde a una función en
sentido estricto, dado que se ve afectada por perturbaciones aleatorias (p. 19).

21
es que se hace referencia con linealidad en los parámetros, la cual, no necesariamente debe aplicar a las variables
explicativas (Greene, 2003; Gujarati y Porter, 2010). En cuanto a u, ésta corresponde al término de error, el cual
recoge el efecto de las variables no incluidas explícitamente en el modelo y que afectan y.
Ahora, en la práctica se desconoce la Función de Regresión Poblacional, por lo que se recurre a la Función
de Regresión Muestral (FRM) como una aproximación a esta. Al lector interesado en la cuestión y que desee
conocer una explicación al respecto, se sugiere consultar el Capítulo 2, Análisis de regresión con dos variables:
algunas ideas básicas, de “Gujarati, D.N. y Porter, D.C. (2010). Econometría”.
La Función de Regresión Muestral es:

yi = β̂ 0 + β̂ 1 xi + ûi
Esta expresión es muy parecida a la de la Función de Regresión Poblacional, pero presenta una diferencia
fundamental: los términos de la Función de Regresión Muestral tienen correspondencia directa con los términos
incluidos en la expresión de la Función de Regresión Poblacional, pero no son exactamente los mismos, en
cuanto se trata de sus estimadores. Así, el estimador del parámetro k se identifica como k̂.
Tal y como lo señalan Gujarati y Porter (2010), “el objetivo principal del análisis de regresión es estimar la
Función de Regresión Poblacional con base en la Función de Regresión Muestral” (p. 44). En este sentido, lo
que se busca es hallar los valores de los β̂ que sean más cercanos a los verdaderos valores de los β.
Para realizar la estimación, es frecuente el uso de dos métodos: Mínimos Cuadrados Ordinarios y Máxima
Verosimilitud. El método de Mínimos Cuadrados Ordinarios, o MCO, es el más común y tiende a ser abordado
como tema fundamental en los cursos de econometría a nivel introductorio.
Resaltando, una vez más, que el nivel de profundidad con el que se abordan las bases teóricas en esta obra
es ínfimo, tan solo se presentará (con el perdón de los especialistas, al no tratar con el detalle justo la cuestión)
la idea general del método de MCO: el objetivo es, básicamente, minimizar la suma de los residuos (termino û
en la Función de Regresión Muestral) cuadrados.
Para el caso de una regresión bivariada, lo que se busca con el método MCO es el menor valor posible de
n
∑i=1 û2i , teniendo en cuenta que:
n n
∑ û2i = ∑ (yi − β̂0 − β̂1 xi )2
i =1 i =1

Así, los estimadores β̂ 0 y β̂ 1 con los que se halla la menor suma de los residuos al cuadrado son los estima-
dores MCO, los cuales, con el cumplimiento de un conjunto de supuestos específicos, evidencian propiedades
estadísticas muy atractivas.
Al lector interesado en conocer el proceso por medio del cual se obtienen los valores de los β̂ y familiarizarse
con las bases matemáticas correspondientes, se le invita a consultar el Capítulo 3, Modelo de regresión con dos
variables: problema de estimación, de “Gujarati, D.N. y Porter, D.C. (2010). Econometría” y el Capítulo 2, El modelo
de regresión simple, de “Wooldridge, J.M. (2010). Introducción a la econometría. Un enfoque moderno”.
Ya se ha presentado, muy brevemente, una idea general de la regresión lineal y el método de MCO. Ahora, es
momento de iniciar de forma efectiva con la labor práctica; a saber, análisis econométrico con el uso de Python.
Lo primero que se hará es llevar a cabo una estimación, por Mínimos Cuadrados Ordinarios, de un modelo
de regresión lineal simple. El modelo de regresión lineal simple no es más que una regresión lineal bivariada,
es decir, en la que solo intervienen dos variables explícitas: la variable dependiente como función lineal en los
parámetros de la variable explicativa. Esto es, simplemente, un modelo al que corresponde una FRM de la forma
yi = β̂ 0 + β̂ 1 xi + ûi , la cual debe resultar familiar al lector, pues es la que se ha tratado previamente.

22
Regresión lineal simple
Para llevar a cabo la regresión lineal simple, utilizaremos el mismo dataset del capítulo previo y reque-
riremos de las mismas herramientas empleadas en éste, por lo que procederemos a importar las respectivas
librerías y los datos:

In [1]: import numpy as np


import pandas as pd
import [Link] as sm
import [Link] as plt
import seaborn as sns
%matplotlib inline
[Link]("seaborn-white")

In [2]: data = pd.read_csv("[Link]",


sep = " ", delimiter="\t")

En el capítulo previo se llevó a cabo un breve reconocimiento de los datos. Ahora, se procederá a la reali-
zación de una Regresión Lineal Simple por el método de Mínimos Cuadrados Ordinarios, teniendo en conside-
ración lo siguiente:

El DataFrame contiene información de las siguientes variables:


’CM’. Esta variable hace referencia a Child Mortality y corresponde a la mortalidad infantil. Se trata
del número de fallecimientos, en un año, de niños con una edad inferior a 5 años por cada 1000 nacidos
vivos.
’FLR’. Esta variable hace referencia a Female Literacy Rate y corresponde a la tasa de alfabetización de
las mujeres.
’PGNP’ hace referencia a Per cápita Gross National Product y corresponde al PIB per cápita en 1980.
’TFR’ hace referencia a Total Fertility Rate y corresponde a la tasa de fecundidad total.

En la regresión lineal simple que se adelantará, la variable dependiente será ’CM’ (mortalidad infantil) y
la variable explicativa será ’PGNP’ (PIB per cápita).

Se procederá a examinar visualmente la relación que existe entre las variables de interés. Para esto, se
construirá un diagrama de dispersión (scatterplot) con el uso del método .scatter(), aplicado a un Axes de
Matplotlib. El código a emplear es el siguiente:

In [3]: fig, ax = [Link](figsize = (10, 5))


[Link]("PIB per cápita vs. Mortalidad infantil", fontsize = 18,
fontweight = "bold")
[Link](x = data["PGNP"], y = data["CM"], s = 50, color = "darkblue")
ax.set_xlabel("PIB per cápita", fontsize = 15)
ax.set_ylabel("Mortalidad infantil\n(por cada 1000 nacidos vivos)",
fontsize = 15)
plt.subplots_adjust(top=0.9)
plt.tick_params(labelsize = 15)
[Link](.9,-.02,
"Elaboración:",

23
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.08,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()

Aunque es un bloque de código algo extenso, el lector debe centrar su atención tan solo en la primera,
especialmente la tercera, y la última línea de código de la celda, pues son las que contienen la “esencia” del
gráfico; las demás, solamente hacen referencia a detalles de los cuales se puede prescindir. No se hará énfasis
en la explicación de este código, en cuanto no es el asunto principal que busca tratarse y la mayoría de líneas “se
explican por sí mismas”. Al lector que, sin embargo, esté interesado en este asunto, se recomienda el estudio
detallado de Matplotlib.
A partir del gráfico se puede observar que la relación entre las variables es aparentemente negativa. Sin
embargo, esto tan solo es una impresión que se genera a partir de la observación y puede ser errada, razón por
la cual se examinará cuantitativamente dicha relación buscando comprobar si la conjetura es correcta; para tal
propósito se llevará a cabo una regresión lineal por el método de MCO.
Para crear el modelo, se emplea la función OLS(...) de Statsmodels. Los argumentos que se incluyen co-
rresponden, respectivamente, a la variable dependiente y a la variable explicativa. El resultado será asignado a
un objeto que se denominará MiModeloSimple:

In [4]: MiModeloSimple = [Link](data["CM"], data["PGNP"])

El modelo ya ha sido creado, es decir, ya se ha definido su estructura; sin embargo, aún no se ha llevado
a cabo ninguna estimación a partir de éste. Para efectuar la estimación se recurrirá al método .fit() y se
almacenarán los resultados generados en un objeto al que se denominará ResultadosSimple:

24
In [5]: ResultadosSimple = [Link]()
Al ejecutar esta línea de código no se obtiene algún resultado visible. Esto se debe a que lo que se ha
conseguido con la ejecución es guardar los resultados generados en un objeto, sin indicar la realización de
alguna acción en particular con dicho objeto. Ahora, si se desea visualizar los resultados del modelo, puede
simplemente usarse la función integrada print(...), empleando como argumento la aplicación del método
.summary() sobre el objeto que almacena los resultados de dicho modelo. Así:
In [6]: print([Link]())
OLS Regression Results
==============================================================================
Dep. Variable: CM R-squared: 0.056
Model: OLS Adj. R-squared: 0.041
Method: Least Squares F-statistic: 3.710
Date: xxx, xx xxx xxxx Prob (F-statistic): 0.0586
Time: xx:xx:xx Log-Likelihood: -413.92
No. Observations: 64 AIC: 829.8
Df Residuals: 63 BIC: 832.0
Df Model: 1
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
PGNP 0.0124 0.006 1.926 0.059 -0.000 0.025
==============================================================================
Omnibus: 7.668 Durbin-Watson: 0.755
Prob(Omnibus): 0.022 Jarque-Bera (JB): 7.941
Skew: -0.561 Prob(JB): 0.0189
Kurtosis: 4.312 Cond. No. 1.00
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
specified.

Lo que se obtiene con la ejecución del código es un resumen de la instancia de resultados en el que se
presenta la información más relevante acerca de estos: en la parte superior se incluye el método de estimación
empleado, el número de observaciones, los grados de libertad (de los residuos y del modelo) y el R2 (estándar
y ajustado), entre otros datos relevantes. En la sección del medio se encuentran los coeficientes, junto a sus
respectivos errores estándar, estadísticos t e intervalos de confianza. En la última sección están las advertencias,
las cuales resultan, frecuentemente, muy útiles y pueden ayudar a identificar potenciales problemas.
Se puede observar que el coeficiente de la variable explicativa es positivo, lo que contradice la conclusión
visual de que la relación, aparentemente, era negativa; algo que resulta, además, poco lógico, pues se esperaría
que los países con ingreso per cápita más alto tuvieran una tasa de mortalidad infantil menor. Ante esta situa-
ción extraña vale la pena preguntarse sobre su causa: ¿cómo puede explicarse tal inconsistencia? La respuesta
es extremadamente simple: el lector debe notar que para este modelo solo se ha calculado el coeficiente co-
rrespondiente a la variable ’PGNP’ y no se ha considerado la existencia del parámetro de intercepto, es decir, se
ha llevado a cabo una regresión a través del origen.

25
En vez de considerar yi = β 0 + β 1 xi + ui , se ha asumido que la FRP es de la forma yi = β 1 xi + ui .
La función OLS(...) de Statsmodels no asume por defecto que el modelo a estimar incluye una constante
como variable explicativa y, por tanto, no realiza la estimación del coeficiente correspondiente al parámetro de
intercepto. Para conseguir la estimación de dicho parámetro es necesario especificar que una constante debe
añadirse al conjunto de variables explicativas del modelo; esto se logra, por ejemplo, empleando la función
add_constant(...) de Statsmodels, tomando como argumento el objeto correspondiente a las variables ex-
plicativas incluidas. Así:

In [7]: MiModeloSimple2 = [Link](data["CM"], sm.add_constant(data["PGNP"]))

Para realizar la estimación y visualizar los resultados correspondientes, basta con repetir la misma estruc-
tura de los códigos empleados previamente (en el caso del modelo sin término del intercepto). Así:

In [8]: ResultadosSimple2 = [Link]()


print([Link]())

OLS Regression Results


==============================================================================
Dep. Variable: CM R-squared: 0.166
Model: OLS Adj. R-squared: 0.153
Method: Least Squares F-statistic: 12.36
Date: xxx, xx xxx xxxx Prob (F-statistic): 0.000826
Time: xx:xx:xx Log-Likelihood: -361.64
No. Observations: 64 AIC: 727.3
Df Residuals: 62 BIC: 731.6
Df Model: 1
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 157.4244 9.846 15.989 0.000 137.743 177.105
PGNP -0.0114 0.003 -3.516 0.001 -0.018 -0.005
==============================================================================
Omnibus: 3.321 Durbin-Watson: 1.931
Prob(Omnibus): 0.190 Jarque-Bera (JB): 2.545
Skew: 0.345 Prob(JB): 0.280
Kurtosis: 2.309 Cond. No. 3.43e+03
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
specified.
[2] The condition number is large, 3.43e+03. This might indicate that there are
strong multicollinearity or other numerical problems.

Ahora, se puede observar que el coeficiente correspondiente a la variable explicativa es negativo, tal y como
se había presumido en el examen visual, y que ésta es estadísticamente significativa. Lo que se hará enseguida
es graficar los valores originales y los valores estimados por el modelo, de modo que puedan contrastarse y

26
sea posible un examen visual de la bondad de ajuste de éste. Para esto, nuevamente se hará uso del método
.scatter(...) aplicado sobre un Axes de Matplotlib.
In [9]: fig, ax = [Link](figsize = (10,5))
[Link]("Regresión lineal simple (MCO)", fontsize = 18,
fontweight = "bold")
[Link](data["PGNP"], data["CM"], s = 50,
label = "Valores originales",
color = "darkblue")
[Link](data["PGNP"], [Link](), s = 50,
label = "Valores estimados",
color = "red")
ax.set_xlabel("PIB per cápita", fontsize = 15)
ax.set_ylabel("Mortalidad infantil\n(por cada 1000 nacidos vivos)",
fontsize = 15)
[Link](frameon = True, loc = "lower left")
plt.subplots_adjust(top=0.9)
plt.tick_params(labelsize = 15)
[Link](.9,-.02,
"Elaboración:",
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.08,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()

27
Se observa que la bondad de ajuste del modelo no es la mejor. ¿A qué se debe este resultado desalentador?
Si el lector examina el gráfico anterior podrá notar que la parte correspondiente a los datos originales presenta
un muy ligero parecido con el gráfico de una función de la forma y = f ( x ) = xa , con a > 0, x > 0 y
representación del valor de x en el eje de las abscisas y del valor de y en el eje de las ordenadas. Este parecido
sugiere que la forma funcional yi = β 0 + β 1 xi + ui tal vez no sea la más adecuada para modelar una relación
con estos datos, en cuanto y puede asemejarse más a una función de x de la forma f ( x ) = xa ( a 6= 0) que a
una de la forma f ( x ) = ax ( a 6= 0). Teniendo en cuenta esto, tal vez la forma yi = β 0 + β 1 1x + ui genere


mejores resultados.
Nota: Un modelo con forma funcional yi = β 0 + β 1 1x + ui es denominado modelo recíproco. (Se in-


vita al lector a consultar el Capítulo 6, Extensiones del modelo de regresión lineal con dos variables, de Gujarati
y Porter (2010), para obtener mayor información al respecto).
Para trabajar con un modelo recíproco se debe tener en cuenta que la estimación no se hará a partir de los
valores de x sino de los valores de 1x . Evidentemente, esto implica que el modelo es no lineal en la variable
x, pero, ¿esto significa que lo que se llevará a cabo es una regresión no lineal? La respuesta es no. Aunque el
modelo ya no es lineal en la variable x, el lector debe recordar que la linealidad considerada hace referencia a
los parámetros y no a las variables, por lo cual, a pesar de la no linealidad de la variable, sigue tratándose de un
modelo de regresión lineal, en cuanto la linealidad en los parámetros no se ha visto afectada.
Se ha creado el modelo tomando ’PGNP’ como variable explicativa, ahora, ésta no será incluida, sino que se
recurrirá a su recíproco: ’1/PGNP’. Los valores de ’PGNP’ están incluidos en el dataset que se ha importado, sin
embargo, los valores de ’1/PGNP’ son desconocidos. ¿Cómo se puede emplear una variable de cuyos valores no
se tiene información?
Aunque se desconocen los valores de ’1/PGNP’, aún se tiene la posibilidad de obtenerlos en tanto los valores
de ’PGNP’ son conocidos y tan solo se debe realizar una operación matemática sencilla. El cálculo de estos
valores, a pesar de ser simple, puede resultar tedioso dada la cantidad de los mismos y puede llevar a cometer
errores si se ejecuta de forma “manual”; por fortuna, pandas resulta muy útil en este contexto, pues evita la
realización de tal labor “manual” para el cálculo de cada uno de los valores, al permitir aplicar operaciones
matemáticas básicas de Python que permiten obtener el resultado fácilmente.
Se creará la variable correspondiente al recíproco de la variable original, ya que ésta no hace parte del
dataset. Para crear dicha variable, simplemente se realiza una asignación a una Series de pandas, señalando
el nombre del DataFrame correspondiente y, a continuación, especificando el nombre que quiere darse a la
variable nueva (escrito entre comillas) dentro de corchetes [ ]. Así:
In [10]: data["1/PGNP"] = 1/data["PGNP"]
Se verifica la correcta ejecución del proceso, recurriendo al método .head():
In [11]: [Link]()
Out[11]: CM FLR PGNP TFR 1/PGNP
0 128 37 1870 6.66 0.000535
1 204 22 130 6.15 0.007692
2 202 16 310 7.00 0.003226
3 197 65 570 6.25 0.001754
4 96 76 2050 3.81 0.000488
Como el lector puede observar, la última columna lleva el nombre que se ha asignado a la nueva variable
(“1/PGNP”) cuyos valores son los resultados de tomar, fila por fila, el número uno y dividirlo por el valor corres-
pondiente de la columna ’PGNP’. Para comprobar la exactitud de la operación realizada, siéntase en la libertad
de tomar la información de una fila cualquiera y ejecutar el cálculo correspondiente.

28
 
Ahora, estimaremos el modelo yi = β 0 + β 1 x1i + ui , teniendo en cuenta que 1x corresponde a la variable
recién creada ’1/PGNP’, y observaremos si se genera una mejora en el ajuste respecto al obtenido con la estima-
ción de yi = β 0 + β 1 xi + ui . Para la construcción y estimación del modelo y visualización de los resultados,
emplearemos, nuevamente, la función OLS(...) de Statsmodels y los métodos .fit() y .summary():

In [12]: MiModeloSimple3 = [Link](data["CM"], sm.add_constant(data[["1/PGNP"]]))


ResultadosSimple3 = [Link]()
print([Link]())

OLS Regression Results


==============================================================================
Dep. Variable: CM R-squared: 0.459
Model: OLS Adj. R-squared: 0.450
Method: Least Squares F-statistic: 52.61
Date: xxx, xx xxx xxxx Prob (F-statistic): 7.82e-10
Time: xx:xx:xx Log-Likelihood: -347.79
No. Observations: 64 AIC: 699.6
Df Residuals: 62 BIC: 703.9
Df Model: 1
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 81.7944 10.832 7.551 0.000 60.141 103.447
1/PGNP 2.727e+04 3759.999 7.254 0.000 1.98e+04 3.48e+04
==============================================================================
Omnibus: 0.147 Durbin-Watson: 1.959
Prob(Omnibus): 0.929 Jarque-Bera (JB): 0.334
Skew: 0.065 Prob(JB): 0.846
Kurtosis: 2.671 Cond. No. 534.
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
specified.

Ahora, procedemos a graficar los valores originales y los valores estimados, usando el método
.scatter(...) de un Axes de Matplotlib, de modo que podamos observar si efectivamente se genera una
mejora en el ajuste:

In [13]: fig, ax = [Link](figsize = (10,5))


[Link]("Regresión lineal simple (MCO)", fontsize = 18,
fontweight = "bold")
[Link](data["PGNP"], data["CM"], s = 50,
label = "Valores originales",
color = "darkblue")
[Link](data["PGNP"], [Link](), s = 50,

29
label = "Valores estimados",
color = "red")
ax.set_xlabel("PIB per cápita", fontsize = 15)
ax.set_ylabel("Mortalidad infantil\n(por cada 1000 nacidos vivos)",
fontsize = 15)
[Link](frameon = True, loc = "lower right")
plt.subplots_adjust(top=0.9)
plt.tick_params(labelsize = 15)
[Link](.9,-.02,
"Elaboración:",
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.08,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()

Como podemos observar, se presenta una mejora clara, en cuanto el ajuste parece ser más adecuado; la me-
jora es visualmente evidente, sin embargo, también se ha presentado un incremento apreciable del coeficiente
de determinación, o R2 , el cual ha pasado de 0.166 a 0.459, como el lector puede comprobar en las tablas de
resumen de cada regresión. Así, para este caso particular, la forma funcional del modelo recíproco parece ser
más apropiada que la forma funcional tradicional.

30
Regresión lineal múltiple
Ya hemos realizado una regresión lineal usando una única variable explicativa; ahora, emplearemos varias
explicativas. En un modelo de regresión lineal múltiple se tiene que la FRP es de la forma yi = β 0 + β 1 x1i +
... + β k xki + ui , de modo que se incluyen k variables explicativas (sin contar la constante de β 0 ) y se deben
estimar k + 1 parámetros (los de las variables y el término del intercepto).
En el modelo de regresión lineal múltiple que emplearemos, la variable dependiente será ’CM’ (mortalidad
infantil) y las variables explicativas serán ’PGNP’ (PIB per cápita) y ’FLR’ (tasa de alfabetización de las muje-
res). Lo primero que haremos es examinar gráficamente la relación que existe entre las variables explicativas
y la variable dependiente; esta vez, recurriremos a la función pairplot(...) de Seaborn, en cuanto esta nos
permite graficar relaciones de pares en conjuntos de datos.
La función pairplot(...) de Seaborn incluye múltiples argumentos, sin embargo, para nuestros propósi-
tos, además de (obviamente) el parámetro obligatorio data, los argumentos importantes son x_vars y y_vars; el
primero permite especificar las variables que se quiere graficar en el eje de las abscisas y el segundo las que se
quiere graficar en el eje de las ordenadas. Para nuestro caso particular, y_vars = 'CM' y x_vars =['PGNP',
'FLR'] (el lector debe notar que, dado que se trata de varias variables, deben incluirse dentro de una lista Pyt-
hon [...]).

In [14]: g = [Link](data, x_vars = ["PGNP", "FLR"],


y_vars = "CM", height = 5,
plot_kws = {"color": "darkblue", "s": 50})
[Link]("Correlación entre variables", fontsize = 18,
fontweight = "bold", y = 1.08)
[Link][0].set_xlabel("PGNP\n(PIB per cápita)", fontsize = 15)
[Link][0].set_ylabel("CM\n(Mortalidad infantil)", fontsize = 15)
[Link][1].set_xlabel("FLR\n(Tasa de alfabetización de las mujeres)",
fontsize = 15)
for ax in [Link]:
ax.tick_params(labelsize = 15)
[Link](1,-.09,
"Elaboración:",
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](1,-.15,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()

31
A partir de nuestro gráfico podemos observar que la relación que existe entre las variables explicativas
y la variable dependiente es, aparentemente, negativa para ambas. Ahora, es momento de cuantificar tales
relaciones. Debemos asignar las variables a objetos, de modo que resulte menos tediosa la construcción del
modelo y contemos con un código más “limpio”. Para seleccionar variables de un DataFrame se deben emplear
los corchetes ’[ ]’, teniendo en consideración lo siguiente:

Cuando se trata de una sola variable, basta con escribir su nombre (entre comillas) dentro de los corche-
tes.

Cuando se trata de varias variables, se debe escribir sus nombres (entre comillas, separados por comas)
dentro de una lista Python [...] y emplear tal lista dentro de los corchetes.

La variable dependiente será asignada al objeto Y y las variables explicativas serán asignadas al objeto X,
así:

In [15]: Y = data["CM"]
X = data[["PGNP", "FLR"]]

Como ya hemos asignado las variables a los objetos correspondientes, ahora podemos emplear tales objetos
como argumentos de la función OLS(...) de Statsmodels para la construcción del modelo, recordando que sus
argumentos son, respectivamente, la variable dependiente y las variables explicativas:

In [16]: MiModelo = [Link](Y, X)

Llevamos a cabo la estimación, con el método .fit(), y guardamos los resultados generados en un objeto
que se denominará Resultados:

In [17]: Resultados = [Link]()

32
Finalmente, visualizamos los resultados empleando la función integrada (nativa) print(...), usando co-
mo argumento la aplicación del método .summary():

In [18]: print([Link]())

OLS Regression Results


==============================================================================
Dep. Variable: CM R-squared: 0.387
Model: OLS Adj. R-squared: 0.368
Method: Least Squares F-statistic: 19.61
Date: xxx, xx xxx xxxx Prob (F-statistic): 2.52e-07
Time: xx:xx:xx Log-Likelihood: -400.07
No. Observations: 64 AIC: 804.1
Df Residuals: 62 BIC: 808.5
Df Model: 2
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
PGNP -0.0060 0.006 -0.974 0.334 -0.018 0.006
FLR 1.8838 0.325 5.796 0.000 1.234 2.534
==============================================================================
Omnibus: 7.742 Durbin-Watson: 1.601
Prob(Omnibus): 0.021 Jarque-Bera (JB): 2.919
Skew: 0.161 Prob(JB): 0.232
Kurtosis: 2.005 Cond. No. 62.1
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
specified.

Se puede evidenciar que no se ha estimado el término del intercepto, es decir, se ha efectuado una regresión
a través del origen. Esto, como se mencionó con anterioridad, se debe a que la función OLS(...) de Statsmodels
no asume por defecto que se debe incluir una constante en el conjunto de variables explicativas. Para que la
estimación se realice con un intercepto es necesario especificar que una constante debe ser empleada como re-
gresora; para tal propósito, se puede emplear la función add_constant(...) de Statsmodels, tomando como
argumento el objeto que contiene las demás variables explicativas.

In [19]: MiModelo2 = [Link](Y, sm.add_constant(X))

La estimación y la visualización de resultados se llevan a cabo empleando los métodos que se han usado
con anterioridad (.fit() y .summary()):

In [20]: Resultados2 = [Link]()


print([Link]())

33
OLS Regression Results
==============================================================================
Dep. Variable: CM R-squared: 0.708
Model: OLS Adj. R-squared: 0.698
Method: Least Squares F-statistic: 73.83
Date: xxx, xx xxx xxxx Prob (F-statistic): 5.12e-17
Time: xx:xx:xx Log-Likelihood: -328.10
No. Observations: 64 AIC: 662.2
Df Residuals: 61 BIC: 668.7
Df Model: 2
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 263.6416 11.593 22.741 0.000 240.460 286.824
PGNP -0.0056 0.002 -2.819 0.006 -0.010 -0.002
FLR -2.2316 0.210 -10.629 0.000 -2.651 -1.812
==============================================================================
Omnibus: 0.732 Durbin-Watson: 2.186
Prob(Omnibus): 0.693 Jarque-Bera (JB): 0.559
Skew: 0.228 Prob(JB): 0.756
Kurtosis: 2.949 Cond. No. 6.77e+03
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
specified.
[2] The condition number is large, 6.77e+03. This might indicate that there are
strong multicollinearity or other numerical problems.

El lector puede observar que los resultados que hemos alcanzado han sido obtenidos con el empleo de
métodos, los cuales son, esencialmente, funciones asociadas a un objeto específico, aplicables directamente
sobre éste.
.fit() y .summary() son métodos aplicables a objetos generados por la función OLS(...), sin embargo,
estos no son los únicos métodos propios de tal tipo de objetos. Para conocer los métodos y atributos de un
objeto en Python, se emplea la función integrada (nativa) dir(...), la cual toma como argumento un objeto
de Python y devuelve sus atributos y métodos dentro de una lista Python ’[…]’.
Para conocer los métodos y atributos del objeto Resultados2, el código a emplear es el siguiente:

In [21]: dir(Resultados2)

Out[21]: ['HC0_se',
'HC1_se',
'HC2_se',
'HC3_se',
'_HCCM',
'__class__',

34
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'_cache',
'_data_attr',
'_get_robustcov_results',
'_is_nested',
'_wexog_singular_values',
'aic',
'bic',
'bse',
'centered_tss',
'compare_f_test',
'compare_lm_test',
'compare_lr_test',
'condition_number',
'conf_int',
'conf_int_el',
'cov_HC0',
'cov_HC1',
'cov_HC2',
'cov_HC3',
'cov_kwds',
'cov_params',
'cov_type',
'df_model',

35
'df_resid',
'diagn',
'eigenvals',
'el_test',
'ess',
'f_pvalue',
'f_test',
'fittedvalues',
'fvalue',
'get_influence',
'get_prediction',
'get_robustcov_results',
'initialize',
'k_constant',
'llf',
'load',
'model',
'mse_model',
'mse_resid',
'mse_total',
'nobs',
'normalized_cov_params',
'outlier_test',
'params',
'predict',
'pvalues',
'remove_data',
'resid',
'resid_pearson',
'rsquared',
'rsquared_adj',
'save',
'scale',
'ssr',
'summary',
'summary2',
't_test',
't_test_pairwise',
'tvalues',
'uncentered_tss',
'use_t',
'wald_test',
'wald_test_terms',
'wresid']

Cada uno de los objetos de esta lista Python [...] es un método o atributo del modelo que hemos construido.
Así, si deseamos conocer el coeficiente de determinación múltiple o R2 , el cual se identifica con el atributo
.rsquared, podemos emplear el siguiente código:

36
In [22]: print("El R2 del modelo es:", [Link])

El R2 del modelo es: 0.7076654981900712

Si estamos interesados en los coeficientes estimados, podemos recurrir al atributo .params, así:

In [23]: print("Los coeficientes del modelo son:\n", [Link])

Los coeficientes del modelo son:


const 263.641586
PGNP -0.005647
FLR -2.231586
dtype: float64

Asimismo, los métodos permiten ejecutar acciones específicas sobre el objeto. A modo de ejemplo, el méto-
do .get_robustcov_results() permite obtener una nueva instancia de resultados asumiendo por defecto
covarianza robusta.

In [24]: Resultados2_robustos = Resultados2.get_robustcov_results()


print(Resultados2_robustos.summary())

OLS Regression Results


==============================================================================
Dep. Variable: CM R-squared: 0.708
Model: OLS Adj. R-squared: 0.698
Method: Least Squares F-statistic: 93.06
Date: xxx, xx xxx xxxx Prob (F-statistic): 2.94e-19
Time: 17:34:52 Log-Likelihood: -328.10
No. Observations: 64 AIC: 662.2
Df Residuals: 61 BIC: 668.7
Df Model: 2
Covariance Type: HC1
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 263.6416 11.983 22.002 0.000 239.681 287.602
PGNP -0.0056 0.001 -4.788 0.000 -0.008 -0.003
FLR -2.2316 0.194 -11.516 0.000 -2.619 -1.844
==============================================================================
Omnibus: 0.732 Durbin-Watson: 2.186
Prob(Omnibus): 0.693 Jarque-Bera (JB): 0.559
Skew: 0.228 Prob(JB): 0.756
Kurtosis: 2.949 Cond. No. 6.77e+03
==============================================================================

Warnings:
[1] Standard Errors are heteroscedasticity robust (HC1)

37
[2] The condition number is large, 6.77e+03. This might indicate that there are
strong multicollinearity or other numerical problems.

Se invita al lector a explorar los demás métodos y atributos disponibles, de modo que pueda conocer toda
la información adicional con la que puede profundizar su análisis.
En este punto se cierra el tercer capítulo de esta obra; el lector ya ha tenido un acercamiento a la regresión
lineal simple y múltiple en Python con estimación por Mínimos Cuadrados Ordinarios; asimismo, ha observado
cómo llevar a cabo un análisis exploratorio mínimo y cómo elaborar gráficos básicos. Ahora, tal y como se
mencionó en este capítulo, dados ciertos supuestos, los estimadores de MCO exhiben propiedades estadísticas
atractivas; tales supuestos corresponden al tema abordado en el siguiente capítulo.

38
Capítulo 4

Verificación de supuestos

Los estimadores de MCO presentan una serie de propiedades estadísticas atractivas dados unos supuestos;
tal afirmación se sustenta en el Teorema de Gauss-Markov, el cual, siguiendo a Gujarati y Porter (2010), puede
expresarse como: “Dados los supuestos del modelo clásico de regresión lineal, los estimadores de mínimos
cuadrados, dentro de la clase de estimadores lineales insesgados, tienen varianza mínima, es decir, son MELI”
(p. 72).
MELI (BLUE en inglés) hace referencia a “Mejores Estimadores Lineales Insesgados” y es la calificación que
adquieren los estimadores de MCO cuando se cumple con los supuestos del modelo clásico de regresión lineal,
también conocido como modelo de Gauss o modelo estándar de regresión lineal.
El modelo clásico de regresión lineal plantea, de acuerdo a Gujarati y Porter (2010), los siguientes supues-
tos:

1. Se trata de un modelo de regresión lineal, es decir, lineal en los parámetros.


2. Valores fijos de x o valores de x independientes del término de error. Esto significa que se requiere cova-
rianza 0 entre ui y cada variable x ji . Es decir cov(ui , x ji ) = 0 ∀ i, j
3. Valor medio de la perturbación ui igual a 0. E(ui ) = 0 ∀ i
4. Homoscedasticidad o varianza constante de ui . Esto es, var (ui ) = σ2 ∀ i
5. No autocorrelación, o correlación serial, entre las perturbaciones. Es decir, cov(ui , u j ) = 0 ∀ i 6= j
6. El número de observaciones (n) debe ser mayor que el de parámetros por estimar. Si se tiene k variables
explicativas (sin incluir el intercepto), entonces debe estimarse k + 1 parámetros (para una regresión
con término del intercepto), por lo tanto, en tal caso, n > k + 1.
7. Debe haber variación en los valores de las variables x.
8. No debe haber colinealidad exacta entre las variables x.
9. No hay sesgo de especificación, es decir, el modelo está correctamente especificado.

En este capítulo se verifica el cumplimiento de algunos de estos supuestos con el uso de diversas herramien-
tas. Algo que debe tener en consideración el lector es que algunos de los supuestos anteriormente señalados
corresponden específicamente a la estructura (ecuación) del modelo (por ejemplo, el supuesto 1) mientras que
otros hacen referencia explícita a las características del término de error (por ejemplo, el supuesto 4 y el su-
puesto 5). Así, para tener mayor claridad, primero examinaremos los supuestos sobre la estructura del modelo
y posteriormente abordaremos los supuestos que se refieren al término de error, por lo que no se seguirá el
mismo orden en el que los supuestos han sido enunciados.

39
Supuestos sobre la estructura del modelo
El modelo clásico de regresión lineal asume el cumplimiento de una serie de supuestos sobre la estructura
(ecuación) del modelo. Es posible verificar el cumplimiento de algunos de estos supuestos en Python utilizando
funciones propias de algunas librerías especializadas como Statsmodels.
Estimaremos un modelo de regresión lineal múltiple y verificaremos el cumplimiento de los supuestos sobre
la estructura del mismo. El dataset que utilizaremos será, al igual que en el anterior capítulo, el del Ejemplo
7.1, Mortalidad infantil en relación con el PIB per cápita y la tasa de alfabetización de las mujeres, de Gujarati y Porter
(2010).

Preparación del entorno


La primera celda en la que el usuario escribirá y ejecutará código tendrá el siguiente contenido:

In [1]: import numpy as np


import pandas as pd
import [Link] as sm
import [Link] as sms
import [Link] as plt
import seaborn as sns
%matplotlib inline
[Link]("seaborn-white")

El lector ya debe saber que el anterior bloque de código permite cargar las herramientas necesarias para
llevar a cabo las acciones requeridas en el análisis econométrico planteado; en caso de que no tenga claridad
al respecto, se le pide consultar la sección “Preparación del entorno” del anterior capítulo, en donde se brinda
una explicación sobre tal cuestión.
El código de esta celda es exactamente igual al empleado en la preparación del entorno del capítulo anterior,
exceptuando por lo siguiente: utilizaremos, adicionalmente, el módulo stats de la librería Statsmodels, el cual
se importará con el alias de sms.
La importación de [Link] es fundamental para nuestra labor, pues es donde están conte-
nidas la mayoría de funciones que emplearemos, sin las cuales no sería posible verificar fácilmente el cumpli-
miento de los supuestos del modelo clásico de regresión lineal. El usuario debe ser cuidadoso de, en caso de
reutilizar el bloque de código de preparación del entorno del capítulo anterior, incluir correctamente la línea de
importación de [Link].

Importación de los datos


La importación de los datos se lleva a cabo con funciones de la librería pandas. Dependiendo del tipo de
archivo en el cual se encuentre almacenada la información del dataset, deberemos recurrir a una función parti-
cular y un conjunto de parámetros específicos.
Para nuestro caso concreto, el siguiente código es el que importa el dataset:

In [2]: data = pd.read_csv("[Link]",


delimiter="\t")

Al lector que no entienda esta línea de código, se sugiere, nuevamente, dirigirse al Capítulo 2, en donde
encontrará una explicación que seguramente aclarará sus dudas, o al Capítulo 1 de Triana y Galindo (2019).

40
Una vez ejecutada la línea de código de esta celda, el dataset debería haber sido importado correctamente.
Para comprobarlo, recurrimos al método .head(), aplicado al objeto que contiene el dataset (en este caso le
hemos asignado el nombre data):

In [3]: [Link]()

Out[3]: CM FLR PGNP TFR


0 128 37 1870 6.66
1 204 22 130 6.15
2 202 16 310 7.00
3 197 65 570 6.25
4 96 76 2050 3.81

Podemos observar que el proceso de importación ha sido exitoso y la información del dataset ha sido alma-
cenada correctamente en un DataFrame de pandas. Ahora, podemos continuar tranquilamente con el desarrollo
de nuestro análisis, en cuanto hemos concluido satisfactoriamente el primer paso. Procederemos a verificar el
cumplimiento de algunos de los supuestos sobre la estructura del modelo.

Supuesto de número de observaciones mayor a número de parámetros (Supuesto #6)


Uno de los supuestos sobre la estructura del modelo es que el número de observaciones con el cual se realiza
la estimación debe superar el número de parámetros (β) a ser estimados. Si pretendemos trabajar con todas
las observaciones del DataFrame, basta con examinar sus dimensiones para determinar la cantidad máxima
de parámetros que podrían estimarse. Las dimensiones de un DataFrame corresponden al número de filas y
columnas por los cuales está formado y pueden conocerse muy fácilmente (recuerde que ya se ha realizado la
misma operación en el segundo capítulo): basta con emplear el atributo .shape, así:

In [4]: [Link]

Out[4]: (64, 4)

El resultado que obtenemos es una tupla Python (...) que informa que nuestro DataFrame es de tamaño
64 × 4, por lo que posee 64 filas y 4 columnas. Considerando que cada fila corresponde a una observación y
cada columna a una variable, el número máximo de parámetros a estimar es 63.
¿Qué sucede si el número de observaciones es inferior al número de parámetros a estimar? “Si hay menos
de k observaciones entonces X no puede ser de rango completo” (Greene, 2003, p. 14). Debido a esto, cuando
el número de parámetros a estimar excede el número de observaciones disponibles para hacer la estimación,
se obtiene que la varianza es infinita, por lo cual el método MCO no puede emplearse (James, Witten, Hastie, y
Tibshirani, 2013).
A modo de ejemplo, se realizará una regresión de prueba, con fines puramente ilustrativos, en la que se
usarán solo 3 observaciones para estimar 4 parámetros. Para esto, se seleccionarán únicamente las tres primeras
observaciones del DataFrame recurriendo a los corchetes [ ]:

In [5]: Modelo_de_Prueba = [Link](data["CM"][0:3],


sm.add_constant(data[["FLR", "PGNP", "TFR"]][0:3]))
Resultados_de_Prueba = Modelo_de_Prueba.fit()
print(Resultados_de_Prueba.summary())

41
OLS Regression Results
==============================================================================
Dep. Variable: CM R-squared: 1.000
Model: OLS Adj. R-squared: nan
Method: Least Squares F-statistic: 0.000
Date: xxx, xx xxx xxxx Prob (F-statistic): nan
Time: xx:xx:xx Log-Likelihood: 66.160
No. Observations: 3 AIC: -126.3
Df Residuals: 0 BIC: -129.0
Df Model: 2
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 3.2453 inf 0 nan nan nan
FLR 2.0501 inf 0 nan nan nan
PGNP -0.0692 inf -0 nan nan nan
TFR 26.7721 inf 0 nan nan nan
==============================================================================
Omnibus: nan Durbin-Watson: 0.854
Prob(Omnibus): nan Jarque-Bera (JB): 0.511
Skew: -0.678 Prob(JB): 0.774
Kurtosis: 1.500 Cond. No. 753.
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
specified.
[2] The input rank is higher than the number of observations.

Como el lector puede observar, los errores estándar son infinitos, y los p-values e intervalos de confianza no
están definidos, por lo que no resulta útil emplear los estimadores de MCO cuando el número de observaciones
es inferior al número de parámetros que se pretende estimar.
Ciertamente, el ejemplo que se acaba de presentar es muy sencillo; sin embargo, es posible que el dataset
a emplear tenga un gran número de variables explicativas, incluso muy superior a la cantidad de observaciones
disponibles, por lo cual el Supuesto #6, a pesar de su apariencia inocente, no debe ser tomado a la ligera.
Cuando existen muchas más variables explicativas que observaciones, se puede recurrir a diversas técnicas
y se pueden llevar a cabo procesos de selección de variables; sin embargo, tales procedimientos exceden el
alcance de esta obra y no serán abordados en la misma.

Supuesto de variación en los valores de las variables explicativas (Supuesto #7)


El supuesto #7 indica que las variables explicativas deben ser “variables”; es decir, que los valores de las
variables explicativas deben cambiar y no pueden corresponder a la misma constante. Intuitivamente, para que
una variable explicativa sea “explicativa” sus cambios deben asociarse1 a cambios en la variable dependiente;
1 Esto no quiere decir que haya una relación causal. El hecho de que el movimiento de una variable se asocie al movimiento de otra

no significa que ésta esté causando el cambio de la otra.

42
si el valor de una variable explicativa no cambia, resulta difícil identificar la manera como está asociada con la
variable dependiente.
Identificar el cumplimiento de este supuesto es bastante sencillo: “si la desviación estándar muestral de las
xi es cero, entonces el supuesto no se satisface; si no es así, este supuesto se satisface” (Wooldridge, 2010, p.
48).
Para verificar en Python el cumplimiento del supuesto de variabilidad en las variables, se puede emplear,
por ejemplo, el método .apply(), utilizando como argumento la función std(...) de NumPy:

In [6]: [Link]([Link]([Link], axis = 0),


columns = ["Desviación estándar"])

Out[6]: Desviación estándar


CM 75.382151
FLR 25.803873
PGNP 2704.317439
TFR 1.497158

Nota: La función DataFrame(...) de pandas se usa en este código con fines puramente estéticos, para
obtener una presentación más agradable del resultado. La parte realmente importante, que genera el
contenido en el que se está interesado, es [Link]([Link], axis = 0).

Como se evidencia, todas las desviaciones estándar son diferentes de 0, por lo que hay variabilidad en
las variables (si la desviación estándar de alguna variable fuese 0, no se trataría de una variable, sino de una
constante).

Supuesto de No Multicolinealidad (Supuesto #8)


Uno de los supuestos sobre la estructura del modelo es que no se presenta colinealidad exacta entre las
variables explicativas. Al lector que desconozca la definición de colinealidad, se señala que ésta, de acuerdo a
Dormann et al. (2013), “describe la situación en la cual dos o más variables predictoras en un modelo estadístico
están linealmente relacionadas” (p. 28).
Un método para detectar colinealidad entre variables es calcular el coeficiente de correlación simple entre
pares de estas; si alguno de estos coeficientes es mayor, en valor absoluto, a 0.9, entonces hay síntoma de
colinealidad muy fuerte.
Para examinar la correlación entre las variables numéricas de un DataFrame de pandas, es posible emplear
el método .corr(). Así:

In [7]: [Link]()

Out[7]: CM FLR PGNP TFR


CM 1.000000 -0.818285 -0.407697 0.671135
FLR -0.818285 1.000000 0.268530 -0.625954
PGNP -0.407697 0.268530 1.000000 -0.185718
TFR 0.671135 -0.625954 -0.185718 1.000000

Si el usuario prefiere una representación un tanto más “rica” visualmente, puede recurrir a la función
heatmap(...) de Seaborn, la cual dibuja una matriz codificada por color. El argumento a pasar a la función,
para este caso específico, es la matriz de correlaciones, la cual, tal y como hemos apenas visto, es obtenida con
la aplicación del método .corr() sobre el DataFrame.
El código a emplear es el siguiente:

43
In [8]: fig, ax = [Link](figsize = (8, 6))
[Link]("Correlación simple\nentre variables explicativas",
fontsize = 18,
fontweight = "bold", x = 0.43)
[Link]([Link](), ax = ax)
plt.subplots_adjust(top = 0.85)
[Link](.9,-.02,
"Elaboración:",
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.07,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()

44
Nota: El lector debe notar que, dado que Seaborn es una librería basada en Matplotlib y que la función
heatmap(...) de Seaborn devuelve un Axes de una figura de Matplotlib, el código empleado en el blo-
que anterior es código común de esta última librería. Dado que Matplotlib desempeña un rol auxiliar en
este trabajo, no se dedicarán esfuerzos a examinar detalladamente la estructura de su código, por lo que
al lector interesado en ésta se sugiere consultar recursos adicionales.

Podemos observar que la escala de color usada por defecto por la función heatmap(...) de Seaborn no
parece ser la más adecuada. Es posible modificar esto incluyendo un valor específico para el parámetro cmap,
asignando la escala que nos parezca la más indicada. A modo de ejemplo, en el siguiente código se empleará la
escala ’RdYlGn’ (rojos, amarillos, verdes):

In [9]: fig, ax = [Link](figsize = (8, 6))


[Link]("Correlación simple\nentre variables explicativas",
fontsize = 18,
fontweight = "bold", x = 0.43)
[Link]([Link](), ax = ax, cmap = "RdYlGn")
plt.subplots_adjust(top = 0.85)
[Link](.9,-.02,
"Elaboración:",
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.07,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()

45
Si consideramos que nuestro gráfico aún no es lo suficientemente informativo, podemos agregar el coefi-
ciente de correlación simple correspondiente a cada una de las celdas. Para alcanzar tal objetivo, debemos
incluir el parámetro annot en la función heatmap(...) de Seaborn y asignarle el valor de True (annot es un
parámetro de tipo booleano). Así:

In [10]: fig, ax = [Link](figsize = (8, 6))


[Link]("Correlación simple\nentre variables explicativas",
fontsize = 18,
fontweight = "bold", x = 0.43)
[Link]([Link](), ax = ax, cmap = "RdYlGn",
annot = True)
plt.subplots_adjust(top = 0.85)
[Link](.9,-.02,
"Elaboración:",

46
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.07,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()

Así, notamos que la correlación más fuerte se presenta entre las variables ’CM’ y ’FLR’, con un coeficiente
superior a 0.8 (en valor absoluto); sin embargo, tal magnitud del coeficiente no debe ser una preocupación
mayor, en cuanto el Supuesto #8 se refiere a colinealidad entre variables explicativas y la variable ’CM’ es la
variable dependiente, mientras que ’FLR’ es una variable explicativa.
El Supuesto #8 hace referencia a colinealidad exacta; esto es cuando una variable explicativa tiene una re-
lación lineal exacta con otra. Cuando se presenta colinealidad exacta, los coeficientes de regresión son indeter-
minados y los errores estándar son infinitos (Gujarati y Porter, 2010).

47
En nuestro caso específico, ninguna de las variables explicativas presenta una correlación muy fuerte con
alguna otra variable explicativa distinta a ella misma, por lo tanto, no se presenta multicolinealidad perfecta y
es posible estimar los parámetros.
Otra técnica útil para detectar multicolinealidad es por medio del Factor de Inflación de Varianza (VIF,
por sus siglas en inglés). “VIFs altos reflejan un incremento en las varianzas de los coeficientes de regresión
estimados debido a colinealidad entre variables predictoras, en comparación con las que se obtendrían cuando
las predictoras son ortogonales” (Murray, Nguyen, Lee, Remmenga, y Smith, 2012).
Así, en caso de que no haya multicolinealidad, o ésta sea leve, se espera que la magnitud de los VIFs sea
pequeña y entre más fuerte sea la colinealidad más alto será el VIF; Gujarati y Porter (2010), basados en el
trabajo de Kleinbaum, Kupper y Muller (1988), sugieren una regla práctica: “si el FIV [VIF] de una variable es
superior a 10 (esto sucede si R2j excede 0.90), se dice que esa variable es muy colineal” (p.340).
¿Cuáles son los VIFs de las variables del dataset con el que se está trabajando? Para obtener estos valores,
se hará uso de la función variance_inflation_factor(...) del módulo stats.outliers_influence
de la librería Statsmodels.
La función variance_inflation_factor(...) recibe dos argumentos: la matriz que contiene las va-
riables explicativas (exog) y un índice (exog_idx) que señala qué variable es a la que corresponde el VIF calculado.
Esta función calcula el VIF para la variable señalada; sin embargo, es de interés conocer el VIF de cada una de
las variables explicativas.
Es posible aplicar la función variance_inflation_factor(...) de forma “manual” tantas veces como
variables de interés haya; para nuestro caso, hay 3 variables explicativas, por lo que la función solo debe apli-
carse 3 veces, una operación que no es excesivamente tediosa. Sin embargo, si se tuviera un número apreciable
de variables explicativas, resultaría molesto repetir la operación en múltiples ocasiones, por lo que resulta útil
poder “automatizar” tal tarea. Una opción para lograr esto es utilizar un ciclo for:

In [11]: from [Link].outliers_influence import variance_inflation_factor


vif = []
exog = sm.add_constant(data[[Link][1:4]])
rango = sm.add_constant(data[[Link][1:4]]).shape[1]
for i in range(rango):
[Link](variance_inflation_factor([Link], i))
[Link]({'VIF': vif[1:]}, index= [Link][1:4])

Out[11]: VIF
FLR 1.711845
PGNP 1.078306
TFR 1.645150

Así, se puede observar que ninguna de las variables explicativas exhibe un VIF elevado, por lo que no se
sugiere que alguna de estas variables sea muy colineal. Este resultado está en línea con lo que se obtuvo en la
matriz de correlaciones, en la cual se encontró que ninguno de los coeficientes de correlación simple era muy
alto.

Supuesto de No sesgo de especificación (Supuesto #9)


Uno de los supuestos sobre el modelo es que éste debe estar correctamente especificado, es decir, que la
forma funcional que adopta es la correcta y que las variables incluidas son las adecuadas.
Visualmente, es posible examinar si la forma funcional adoptada es la adecuada al graficar, por medio de
un diagrama de dispersión, los valores estimados contra los residuos; se espera que el comportamiento sea

48
relativamente estable, con los puntos distribuidos simétricamente alrededor del cero, sin presentar patrones
específicos.
Dado que aún no se ha creado el modelo ni llevado a cabo la estimación, no se cuenta con una instancia de
resultados a emplear. Por lo tanto, lo primero que se hará es realizar la construcción y estimación correspon-
dientes:

In [12]: Y = data["CM"]
X = data[["PGNP", "FLR"]]
Modelo = [Link](Y, sm.add_constant(X))
Resultados = [Link]()
print([Link]())

OLS Regression Results


==============================================================================
Dep. Variable: CM R-squared: 0.708
Model: OLS Adj. R-squared: 0.698
Method: Least Squares F-statistic: 73.83
Date: xxx, xx xxx xxxx Prob (F-statistic): 5.12e-17
Time: xx:xx:xx Log-Likelihood: -328.10
No. Observations: 64 AIC: 662.2
Df Residuals: 61 BIC: 668.7
Df Model: 2
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 263.6416 11.593 22.741 0.000 240.460 286.824
PGNP -0.0056 0.002 -2.819 0.006 -0.010 -0.002
FLR -2.2316 0.210 -10.629 0.000 -2.651 -1.812
==============================================================================
Omnibus: 0.732 Durbin-Watson: 2.186
Prob(Omnibus): 0.693 Jarque-Bera (JB): 0.559
Skew: 0.228 Prob(JB): 0.756
Kurtosis: 2.949 Cond. No. 6.77e+03
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
specified.
[2] The condition number is large, 6.77e+03. This might indicate that there are
strong multicollinearity or other numerical problems.

Ya se cuenta con el modelo y sus resultados, pero aún no hemos asignado los residuos a un objeto, por lo que
llevaremos a cabo tal asignación, de modo que se simplifique el código empleado. Asignaremos los residuos,
los cuales son un atributo de la instancia de resultados (el usuario puede verificarlo con la función dir(...)),
al objeto Residuos, así:

In [13]: Residuos = [Link]

49
Del mismo modo, es posible asignar los valores estimados, que se obtienen con la aplicación del método
.predict() a un objeto específico:

In [14]: Valores_estimados = [Link]()

In [15]: fig, ax = [Link](figsize = (8,6))


[Link]("Valores estimados vs.\nResiduos",
fontsize = 18,
fontweight = "bold")
[Link](Valores_estimados, Residuos,
color = "darkblue")
ax.set_xlabel("Valores estimados", fontsize = 15)
ax.set_ylabel("Residuos", fontsize = 15)
ax.tick_params(labelsize = 13)
[Link](.9,-.02,
"Elaboración:",
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.07,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()

50
Como se observa, no parece que se presente un patrón específico y los puntos están simétricamente distri-
buidos alrededor del cero, por lo que el análisis visual sugiere que la forma funcional adoptada no es errónea.
Otro aspecto a considerar respecto a la estructura del modelo es que ésta se mantenga a lo largo de la esti-
mación; es decir, que no sea inestable. Para verificar la estabilidad en los parámetros, o no cambio estructural,
puede emplearse la prueba CUSUM basada en residuos de MCO, la cual toma como hipótesis nula no cambio
estructural.
La función empleada en Python para la prueba CUSUM basada en residuos de MCO es
breaks_olsresid(...) de [Link]; sus argumentos incluyen los residuos del modelo esti-
mado por MCO y el parámetro ddof, con valor por defecto igual a 0 y referente al número de grados de libertad
empleados en la corrección de la varianza del error.
Ahora, emplearemos el objeto Residuos como argumento de la función breaks_olsresid(...); los
resultados obtenidos con la ejecución de la función serán almacenados en un objeto al que denominaremos
ResultadosTest, así:

In [16]: ResultadosTest = sms.breaks_cusumolsresid(Residuos)


print(ResultadosTest)

51
(0.5191974341185455, 0.9503227705917948, [(1, 1.63), (5, 1.36), (10, 1.22)])

Podemos ver que el resultado que obtenemos de nuestro código es tan solo una tupla Python (...) que
contiene un conjunto de datos; esto se debe a que [Link] no tiene integrado un modo de presentación
más “elaborado” del resultado.
Para mejorar la presentación de los resultados del Test se puede hacer uso de una Series de pandas, de
modo que podamos organizar adecuadamente la información disponible. Procederemos a crear dicha Series
asignándole como valores los componentes del resultado del test y como index una lista Python [...] con sus
respectivos nombres. Así:

In [17]: Nombres = ["Estadístico", "p-value del estadístico", "Valores críticos"]


[Link](ResultadosTest, index = Nombres)

Out[17]: Estadístico 0.519197


p-value del estadístico 0.950323
Valores críticos [(1, 1.63), (5, 1.36), (10, 1.22)]
dtype: object

Asumiendo un nivel de significancia de 5 %, como p − value > α entonces no se rechaza la hipótesis nula
y se concluye que no se presenta cambio estructural (a un nivel de confianza de 95 %).
Ya se han evaluado, muy brevemente, algunos de los supuestos del modelo clásico de regresión lineal sobre
la estructura del modelo. Ahora, se examinará el cumplimiento de los supuestos referentes al término de error.

Supuestos sobre el término de error


Se dará continuidad al modelo empleado en la sección “Supuestos sobre la estructura del modelo”, por lo
que no se llevará a cabo la “Preparación del entorno” y la “Importación de los datos”, en cuanto estas ya se han
efectuado de forma exitosa previamente. Al lector que no haya seguido la información presentada, se le sugiere
dirigirse al inicio de este capítulo y ejecutar las dos primeras celdas de código; una vez hecho esto, debería poder
continuar con la exposición presentada sin inconvenientes.

Valor medio de la perturbación igual a cero (Supuesto #3)


Los residuos del modelo pueden ser positivos o negativos, es decir, es posible que el valor estimado por
el modelo sea inferior al verdadero o superior al mismo, pero la distribución debe ser relativamente simétrica.
No se espera que todos los residuos sean positivos ni que todos sean negativos, pues se estaría realizando una
subestimación o sobreestimación sistemática; lo adecuado es que exista balance entre los residuos positivos y
negativos, de modo que su valor promedio sea cero.
Este supuesto es relativamente fácil de evaluar, pues tan solo debemos calcular el valor promedio de los
residuos, para lo que la función mean(...) de NumPy resulta indicada:

In [18]: [Link](Residuos)

Out[18]: 1.254552017826427e-13

El argumento que recibe la función mean(...) de NumPy es el vector de residuos, el cual, conveniente-
mente, hemos asignado de forma previa al objeto Residuos. El resultado obtenido es prácticamente cero, pues

52
se trata de un número, expresado en notación científica, que es muy pequeño; esto sugiere que el Supuesto #3
se cumple para este ejemplo.
Si bien hemos utilizado una función, puede resultar útil señalar al lector que también existe un método,
con el que es posible calcular el promedio, el cual, como es de esperarse, es .mean():

In [19]: [Link]()

Out[19]: 1.254552017826427e-13

Homoscedasticidad (Supuesto #4)


El supuesto de homoscedasticidad en el término de error indica, esencialmente, que éste debe exhibir va-
rianza constante. Existen pruebas estadísticas formales para verificar el cumplimiento de este supuesto, sin
embargo, lo primero que haremos será emplear métodos informales, de modo que el lector tenga una idea un
tanto más clara de la cuestión a tratar.
Examinaremos la presencia de heteroscedasticidad (esto es, varianza no constante o “no-
homoscedasticidad”) informalmente por medio de un análisis visual. Lo que haremos es graficar los
residuos del modelo, con el objetivo de observar si su comportamiento exhibe un patrón particular que pueda
señalar la presencia de heteroscedasticidad. (Véase la Sección 11.5, del Capítulo 11, Heteroscedasticidad: ¿qué
pasa si la varianza del error no es constante?, de Gujarati y Porter (2010))
Para realizar el gráfico correspondiente usaremos el método .plot() sobre un Axes creado con la función
subplots(...) de Matplotlib, empleando como argumento los residuos del modelo; estos residuos han sido
previamente asignados a un objeto, por lo que es este objeto el que se empleará como argumento. Al lector que
no haya seguido el ejercicio realizado en la sección “Supuestos sobre la estructura del modelo”, se le invita a
consultarla y ejecutar las celdas de código correspondientes, con las que podrá generar los objetos requeridos
a continuación.
El contenido de la celda de código para la generación del gráfico es el siguiente:

In [20]: fig, ax = [Link](figsize = (10,5))


[Link]("Residuos", fontsize = 18,
fontweight = "bold")
[Link](Residuos, color = "darkblue")
ax.set_xlabel("Observaciones", fontsize = 15)
ax.set_ylabel("Residuos", fontsize = 15)
ax.tick_params(labelsize = 14)
[Link](.9,-.02,
"Elaboración:",
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.08,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()

53
Como se puede observar, los residuos no exhiben un patrón particular y la amplitud de sus cambios no pare-
ce variar significativamente, por lo que, a primera vista, no hay evidencia de heteroscedasticidad. Sin embargo,
tal conclusión puede resultar apresurada.
Un análisis similar puede llevarse a cabo empleando los valores estimados y los residuos al cuadrado, lo
que permite controlar por la naturaleza de los residuos (positivos o negativos) al basarse en su magnitud y no
en su signo. Se espera que no se presente ningún patrón particular, de modo que la varianza de los residuos sea
constante y estos no dependan de la magnitud de la variable regresada.
Para ejecutar este análisis utilizaremos el método .scatter(...) empleando como argumentos el vec-
tor de valores estimados y el de residuos al cuadrado. Ya contamos con el primero, pero solo se tiene infor-
mación de los residuos y no de los residuos cuadrados; sin embargo, basta con aplicar el operador ** para
obtener dichas magnitudes, ya que éste es el que permite en Python realizar potenciación siguiendo la estruc-
tura base**exponente:

In [21]: fig, ax = [Link](figsize = (10,5))


[Link]("Valores estimados vs.\nResiduos cuadrados",
fontsize = 18,
fontweight = "bold")
[Link](Valores_estimados, Residuos**2,
color = "darkblue")
ax.set_xlabel("Valores estimados\n(variable dependiente)",
fontsize = 15)
ax.set_ylabel("Residuos cuadrados", fontsize = 15)
ax.tick_params(labelsize = 14)
[Link](.9,-.02,
"Elaboración:",

54
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.08,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
plt.subplots_adjust(top = 0.85)
[Link]()

De acuerdo al resultado obtenido, no parece que los residuos sean heteroscedásticos, aunque hay una li-
gera impresión de que van creciendo con la magnitud de la variable dependiente; para llegar a una conclusión
“válida”, el análisis visual no es suficiente: se requiere de pruebas estadísticas formales.
Una de las pruebas estadísticas generalmente empleadas para verificar la presencia de heteroscedasticidad
es el Test Breusch-Pagan de Multiplicadores de Lagrange (Greene, 2003), el cual, básicamente, toma como
hipótesis nula homoscedasticidad.
La función empleada en Python para el Test Breusch-Pagan de Multiplicadores de Lagrange para heteros-
cedasticidad es het_breuschpagan(...) de [Link]; sus argumentos incluyen los residuos del mo-
delo y el conjunto de variables que pueden causar la heteroscedasticidad.
Ya se cuenta con un objeto que contiene los residuos, sin embargo, aún no se han asignado las variables
explicativas del modelo a un objeto particular. Como las variables explicativas consisten en un atributo de la
instancia de resultados del modelo, podemos asignarlas a un objeto aplicando dicho atributo, así:

In [22]: Explicativas = [Link]

Ya que contamos con los objetos a emplear como argumentos, podemos hacer uso de la función
het_breuschpagan(...) de [Link]. Siguiendo la estructura que hemos empleado para las pruebas
de la sección anterior, el código a ejecutar es el siguiente:

55
In [23]: ResultadosTest = sms.het_breuschpagan(Residuos, Explicativas)
Nombres = ["Estadístico LM", "p-value del estadístico LM", "Estadístico F",
"p-value del estadístico F"]
[Link](ResultadosTest, index = Nombres)

Out[23]: Estadístico LM 1.591582


p-value del estadístico LM 0.451224
Estadístico F 0.777832
p-value del estadístico F 0.463904
dtype: float64

Asumiendo un nivel de significancia de 5 %, como p − value > α entonces no se rechaza la hipótesis nula
y se concluye que se presenta homoscedasticidad en el término de error (trabajando con un α de 0.05).

Nota 1: Este test equivale al generado por la función bptest(...) de R (paquete lmtest).

Nota 2: Este test equivale al generado por el código estat hettest, rhs fstat de Stata.

Otra de las pruebas estadísticas comúnmente empleadas para verificar la presencia de heteroscedasticidad
es el Test White de Multiplicadores de Lagrange, el cual hace uso de regresiones auxiliares, toma, esencialmente,
como hipótesis nula homoscedasticidad, y es capaz de identificar formas más generales de heteroscedasticidad
que el Test de Breusch-Pagan (Verbeek, 2004).
La función empleada en Python para el Test White de Multiplicadores de Lagrange para heteroscedasticidad
es het_white(...) de [Link]; sus argumentos incluyen los residuos del modelo y el conjunto de
variables a emplear en las regresiones auxiliares.
Los argumentos empleados en la función het_white(...) de [Link] corresponden a los mismos
utilizados en la función het_breuschpagan(...) de dicho módulo y, dado que estos han sido asignados
previamente a objetos específicos, basta con reutilizar tales objetos.
Siguiendo la estructura que se ha empleado para las demás pruebas estadísticas realizadas, el código a
ejecutar es el siguiente:

In [24]: ResultadosTest = sms.het_white(Residuos, Explicativas)


Nombres = ["Estadístico LM", "p-value del estadístico LM",
"Estadístico F", "p-value del estadístico F"]
[Link](ResultadosTest, index = Nombres)

Out[24]: Estadístico LM 2.356718


p-value del estadístico LM 0.797902
Estadístico F 0.443486
p-value del estadístico F 0.816252
dtype: float64

Los resultados del Test White de Multiplicadores de Lagrange para heteroscedasticidad reafirman la con-
clusión del Test Breusch-Pagan realizado con anterioridad. Asumiendo un nivel de significancia de 5 %, como
p − value > α entonces no se rechaza la hipótesis nula y se concluye que se presenta homoscedasticidad en
el término de error (a un nivel de confianza de 95 %).

Nota: Este test equivale al generado por el código estat imtest, white de Stata.

56
No autocorrelación, o correlación serial, entre las perturbaciones (Supuesto #5)
El supuesto de no autocorrelación entre las perturbaciones indica que estas no siguen patrones sistemáticos
y no están correlacionadas entre sí; tal supuesto puede justificarse para el caso de datos transversales, pero
tiende a incumplirse cuando se trabaja con series de tiempo, en cuanto las observaciones sucesivas usualmente
están fuertemente correlacionadas (Véase la Sección 3.2, del Capítulo 3, Modelos de regresión con dos variables:
problema de estimación, de Gujarati y Porter (2010)).
Este supuesto normalmente se verifica con pruebas estadísticas, algunas de las cuales se tratan a continua-
ción. Una prueba estadística generalmente empleada para verificar la presencia de autocorrelación de orden 1
es el Test Durbin-Watson, el cual toma, esencialmente, como hipótesis nula no autocorrelación (de orden 1) y
genera un estadístico cuyo valor se contrasta con los valores críticos correspondientes (Chatterjee y Simonoff,
2013) para establecer una conclusión.
La función empleada en Python para el Test Durbin-Watson es durbin_watson(...) de [Link];
sus argumentos incluyen los residuos del modelo.
Siguiendo la misma estructura de las otras pruebas estadísticas realizadas, y teniendo en cuenta que los
residuos ya se asignaron a un objeto específico, el código a ejecutar es el siguiente:

In [25]: ResultadosTest = sms.durbin_watson(Residuos)


Nombres = ["Estadístico DW"]
[Link](ResultadosTest, index = Nombres)

Out[25]: Estadístico DW 2.186159


dtype: float64

El valor obtenido es 2.1861, entonces, ¿se presenta autocorrelación de orden 1? El lector debe notar que, a
diferencia de otras pruebas estadísticas, en el caso del Test Durbin-Watson no se reporta un p-value con el cual
decidir si se rechaza o no la hipótesis nula, entonces, ¿cómo se determina la conclusión?
El estadístico Durbin-Watson es contrastado con valores críticos a partir de los cuales se establecen zonas
de autocorrelación positiva, no autocorrelación, indeterminación y autocorrelación negativa; dependiendo de
la zona en la que se ubique el valor del estadístico, se obtiene una conclusión específica.
Para este caso en concreto, asumiendo un nivel de significancia de 5 %, los valores de d L y dU para 2 variables
explicativas (excluyendo el término constante) y 65 observaciones son, respectivamente, 1.536 y 1.662. El
valor de 2.1861 pertenece al intervalo (dU , 4 − dU ), el cual corresponde a la zona de No Autocorrelación, por
lo que se concluye que no se presenta autocorrelación de primer orden (trabajando con un α de 0.05).

Nota: Este test equivale al generado por la función dwtest(...) de R (paquete lmtest). El usuario debe
tener en cuenta que, en la función de R, a diferencia de la de Python, el argumento a emplear no son los
residuos sino el propio modelo.

El Test Durbin-Watson es una prueba estadística que permite identificar autocorrelación de primer orden,
sin embargo, si se quiere examinar correlación serial de orden superior ésta ya no puede emplearse y gene-
ralmente se recurre al Test Breusch-Godfrey, el cual toma, esencialmente, no autocorrelación como hipótesis
nula y se basa en la ejecución de una regresión auxiliar en la que se incluyen rezagos del término de error como
variables explicativas (Kleiber y Zeileis, 2008).
La función empleada en Python para el Test Breusch-Godfrey de Multiplicadores de Lagrange para autoco-
rrelación de los residuos es acorr_breusch_godfrey(...) de [Link]; sus argumentos incluyen la
instancia de resultados (¡no los residuos!) del modelo y el número de rezagos, nlags, a incluir en la regresión
auxiliar.
Siguiendo la misma estructura de las otras pruebas estadísticas realizadas, y teniendo en cuenta que la
instancia de resultados del modelo ya se asignó a un objeto específico, el código a ejecutar es el siguiente:

57
In [26]: ResultadosTest = sms.acorr_breusch_godfrey(Resultados, nlags = 1)
Nombres = ["Estadístico LM", "p-value del estadístico LM", "Estadístico F",
"p-value del estadístico F"]
[Link](ResultadosTest, index = Nombres)

Out[26]: Estadístico LM 0.728605


p-value del estadístico LM 0.393336
Estadístico F 0.690933
p-value del estadístico F 0.409143
dtype: float64

Los resultados del Test Breusch-Godfrey de Multiplicadores de Lagrange para autocorrelación de los resi-
duos reafirman la conclusión del Test Durbin-Watson realizado con anterioridad. Asumiendo un nivel de signi-
ficancia de 5 %, como p − value > α entonces no se rechaza la hipótesis nula y se concluye que no se presenta
correlación serial de primer orden en el término de error (trabajando con un nivel de significancia de 5 %).
Ya se ha indicado que el Test Breusch-Godfrey es una prueba general de autocorrelación, en cuanto permite
identificar correlación serial de orden superior a 1. En el código anterior hemos asignado el valor de 1 al pará-
metro nlags de la función acorr_breusch_godfrey(...) de [Link], por lo que hemos examinado
la presencia de autocorrelación de primer orden, al igual que en el Test Durbin-Watson. Ahora, procederemos
a examinar la presencia de autocorrelación hasta de orden 4, por lo que nlags tomará el valor de 4. Basta con
reutilizar la última celda de código que hemos empleado y modificar el valor del parámetro nlags, así:

In [27]: ResultadosTest = sms.acorr_breusch_godfrey(Resultados, nlags = 4)


Nombres = ["Estadístico LM", "p-value del estadístico LM", "Estadístico F",
"p-value del estadístico F"]
[Link](ResultadosTest, index = Nombres)

Out[27]: Estadístico LM 1.211387


p-value del estadístico LM 0.876220
Estadístico F 0.274927
p-value del estadístico F 0.892978
dtype: float64

Para el caso de autocorrelación hasta de orden 4, asumiendo un nivel de significancia de 5 %, como p −


value > α entonces no se rechaza la hipótesis nula y se concluye que no hay correlación serial hasta de orden
4 (trabajando con un α de 0.05).

Nota: Este test equivale al generado por la función bgtest(...) de R (paquete lmtest).

Otras pruebas estadísticas comúnmente empleadas para examinar la presencia de autocorrelación en el


término de error son el Test Box-Pierce y el Test Ljung-Box, los cuales están estrechamente relacionados, toman
como hipótesis nula, básicamente, no autocorrelación y, al igual que el Test Breusch-Godfrey, son útiles para
determinar la existencia de autocorrelación hasta de orden p.
La función empleada en Python para el Test Ljung-Box es la misma que para el Test Box-Pierce. Se trata
de acorr_ljungbox(...) de [Link]; sus argumentos incluyen los residuos del modelo, el número
de rezagos deseados (lags) y una variable booleana para indicar si se quiere obtener únicamente los resultados
del Test Ljung-Box (efectuado por defecto) o si también se desea visualizar los resultados del Test Box-Pierce
(boxpierce = True o False).
Siguiendo la misma estructura de las otras pruebas estadísticas realizadas, y teniendo en cuenta que los
residuos ya se asignaron a un objeto específico, el código a ejecutar es el siguiente:

58
In [28]: ResultadosTest = sms.acorr_ljungbox(Residuos, lags = 1, boxpierce = True)
Nombres = ["Estadístico LB", "p-value del estadístico LB", "Estadístico BP",
"p-value del estadístico BP"]
[Link](ResultadosTest, index = Nombres)

Out[28]: Estadístico LB [0.7549153999366602]


p-value del estadístico LB [0.3849244343516546]
Estadístico BP [0.7206010635759029]
p-value del estadístico BP [0.3959468201677663]
dtype: object

Tanto el p-value correspondiente al estadístico del Test Ljung-Box como el correspondiente al Test Box-
Pierce son superiores a 0.05, por lo que no se rechaza la hipótesis nula y se concluye que no se presenta corre-
lación serial de primer orden (observe que lags = 1).
Al igual que en el Test Breusch-Godfrey, el Test Ljung-Box y el Test Box-Pierce pueden emplearse para
examinar la existencia de correlación serial hasta de orden p. Para examinar hasta p orden de autocorrelación,
basta con asignar el valor de p al parámetro lags de la función acorr_ljungbox(...) de [Link].
Siguiendo la misma estructura de las otras pruebas estadísticas realizadas, y teniendo en cuenta que los
residuos ya se asignaron a un objeto específico, el código a ejecutar para evaluar correlación serial hasta de
orden 2 (por ejemplo) es el siguiente:

In [29]: ResultadosTest = sms.acorr_ljungbox(Residuos, lags = 2, boxpierce = True)


Nombres = ["Estadístico LB", "p-value del estadístico LB", "Estadístico BP",
"p-value del estadístico BP"]
[Link](ResultadosTest, index = Nombres)

Out[29]: Estadístico LB [0.7549153999366602, 1.080970233422208]


p-value del estadístico LB [0.3849244343516546, 0.5824656200187786]
Estadístico BP [0.7206010635759029, 1.0268949980623265]
p-value del estadístico BP [0.3959468201677663, 0.5984289353087118]
dtype: object

El lector debe notar que la función acorr_ljungbox(...) de [Link] genera p valores para el
estadístico y p-value correspondientes, los cuales presenta dentro de una lista Python “[...]”, en cuanto ejecuta
la prueba respectiva para cada uno de los rezagos incluidos.

Nota: Este test equivale al generado por la función [Link](...) de R (paquete stats). A diferencia
de Python, en R la prueba que se ejecuta por defecto es la de Box-Pierce, por lo que si se quiere llevar a
cabo el Test Ljung-Box, tal preferencia debe señalarse explícitamente en el parámetro type de la función.

Supuesto de Normalidad
Uno de los supuestos sobre el término de error es que tiene distribución de probabilidad normal. Es posible
que el lector cuidadoso haya notado que este supuesto no se incluyó en el listado de supuestos del modelo
clásico de regresión lineal, presentado en la primera parte de este capítulo. Esto se debe a que el supuesto de
normalidad en la distribución del término de error no hace parte del modelo clásico de regresión lineal, por
lo que no es necesario para garantizar las propiedades de los estimadores de MCO de linealidad, insesgadez y
varianza mínima. Entonces, ¿por qué se menciona?

59
El supuesto de normalidad en la distribución del término de error hace parte del modelo clásico de regresión
lineal normal, y ve justificada su existencia por la necesidad de llevar a cabo inferencia estadística. Mientras los
demás supuestos presentados a inicio de este capítulo son requeridos para que los estimadores de MCO sean
MELI (o BLUE en inglés), tales supuestos no son suficientes para garantizar que la inferencia estadística sea
válida.

Debido a que el método de MCO no hace ninguna suposición respecto de la naturaleza probabilís-
tica de ui , resulta de poca ayuda para el propósito de hacer inferencias sobre la FRP mediante la
FRM, a pesar del teorema de Gauss-Markov. Este vacío puede llenarse si se supone que las u siguen
una determinada distribución de probabilidad. Por razones que mencionaremos en seguida, en el
contexto de regresión se supone, por lo general, que las u tienen la distribución de probabilidad
normal. (Gujarati y Porter, 2010, p. 98)

El supuesto indica que el término de error tiene distribución normal con media cero y varianza constante
y que, además, la covarianza entre ui y u j (i 6= j) es cero. Así, el supuesto puede resumirse en que ui ∼
N ID (0, σ2 ), en otras palabras: el término de error es normal e independientemente distribuido.
Una de las pruebas estadísticas más populares empleadas para verificar el cumplimiento de normalidad en
la distribución del término de error es el Test Jarque-Bera, el cual se basa en la asimetría y la curtosis (Würtz y
Katzgraber, 2009) y toma como hipótesis nula, básicamente, distribución normal.
La función empleada en Python para el Test Jarque-Bera para normalidad es jarque_bera(...) de stats-
[Link]; sus argumentos incluyen los residuos del modelo.
Siguiendo la misma estructura de las otras pruebas estadísticas realizadas, y teniendo en cuenta que los
residuos ya se asignaron a un objeto específico, el código a ejecutar para evaluar normalidad en la distribución
del término de error es el siguiente:

In [30]: ResultadosTest = sms.jarque_bera(Residuos)


Nombres = ["Jarque-Bera", "p-value", "Asimetría", "Curtosis"]
[Link](ResultadosTest, index = Nombres)

Out[30]: Jarque-Bera 0.559405


p-value 0.756009
Asimetría 0.227575
Curtosis 2.948855
dtype: float64

Asumiendo un nivel de significancia de 5 %, como p − value > α entonces no se rechaza la hipótesis nula
y se concluye que se presenta normalidad en la distribución del término de error (trabajando con un α de 0.05).

Nota: Este test equivale al generado por la función [Link](...) de R (paquete tseries). El
usuario debe tener en cuenta que el argumento empleado en la función de R, al igual que la de Python,
corresponde a los residuos del modelo.

Otras pruebas ampliamente utilizadas para evaluar normalidad son la de Anderson-Darling, la cual perte-
nece a la clase cuadrática de los estadísticos basados en Función de Distribución Empírica, y la de Kolmogorov-
Smirnov, también construida a partir de dicha función (Razali y Wah, 2011). La primera prueba es implementada
en Python con la función normal_ad(...) de [Link]:

In [31]: ResultadosTest = sms.normal_ad(Residuos)


Nombres = ["Anderson-Darling", "p-value"]
[Link](ResultadosTest, index = Nombres)

60
C:\Users\FCE\Anaconda3\lib\site-packages\statsmodels\stats\_adnorm.py:66:
FutureWarning: Using a non-tuple sequence for multidimensional indexing
is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`.
In the future this will be interpreted as an array index, `arr[[Link](seq)]`,
which will result either in an error or a different result.
S = [Link]((2*i[sl1]-1.0)/N*([Link](z)+[Link](1-z[sl2])), axis=axis)

Out[31]: Anderson-Darling 0.329856


p-value 0.508773
dtype: float64

Reforzando la conclusión del Test Jarque Bera, el resultado de Anderson-Darling sugiere que la distribución
de los residuos es normal.
Respecto a la otra prueba, la de Kolmogorov-Smirnov, esta es implementada con la función
kstest_normal(...) del módulo diagnostic de [Link]. La estructura a emplear para presen-
tar los resultados es la misma utilizada en las demás pruebas estadísticas que se han realizado:

In [32]: ResultadosTest = [Link].kstest_normal(Residuos)


Nombres = ["Estadístico KS", "p-value"]
[Link](ResultadosTest, index = Nombres)

Out[32]: Estadístico KS 0.081275


p-value 0.200000
dtype: float64

Al igual que con Jarque-Bera y Anderson-Darling, los resultados del test de Kolmogorov-Smirnov indican
que no se rechaza la hipótesis nula y, por tanto, no se rechaza que la distribución del término de error sea
normal.
Las dos últimas pruebas realizadas se basan en la Función de Distribución Empírica, la cual, de acuerdo a
Vaart (1998) es el estimador natural de la distribución subyacente, F, si esta no es conocida. Para visualizar
la distribución empírica en Python podemos hacer uso de funciones de Matplotlib y para obtener los valores
correspondientes a dicha función basta con hacer unas cuantas operaciones sencillas.
Vaart (1998) define, para una muestra aleatoria X1 , ..., Xn , la Función de Distribución Empírica de la si-
guiente manera:

1 n
n i∑
Fn (t) = 1 { Xi ≤ t }
=1
A partir de esta definición es muy simple determinar los puntos con los que se calculará la Función de
Distribución Empírica. En primer lugar, debemos organizar los valores de la variable de interés de menor a
mayor, para lo que se utiliza la función sort(...) de NumPy:

In [33]: x = [Link](Residuos)

A cada uno de estos valores x corresponde una “probabilidad” de ser menor o igual; es decir, si tenemos el
conjunto {1,2,2,3,5,7,8,9,10,10} la probabilidad de que un elemento sea menor o igual a 2 es 30 % (pues tres
de los diez elementos son menores o iguales a dos), la probabilidad de que un elemento sea menor o igual a 7
es 60 % (pues seis de los diez elementos son menores o iguales a siete). Esta lógica es muy fácil de implementar
en el código: tan solo se debe determinar el número de elementos, n, y dividir una secuencia 1, ..., n entre este
número, de modo que se obtengan las probabilidades correspondientes:

61
In [34]: n = [Link]
y = [Link](1, n+1) / n

Ya se tienen los pares ( x, y) para graficar la Función de Distribución Empírica, pero ésta solo nos resulta
útil si podemos compararla con algún referente: una distribución teórica. La distribución normal se construye
con dos parámetros: la media y la desviación estándar. Si se generan datos a partir de una distribución normal
con media igual al promedio de los residuos y desviación estándar igual a la de estos, dichos datos sirven para
graficar la distribución téorica (pues se sabe que efectivamente se trata de una distribución normal), la cual
puede usarse para comparar con la distribución empírica; si el comportamiento es muy similar, visualmente se
sugiere que la distribución de los residuos es normal.
Veamos lo anteriormente descrito en la práctica. Para generar valores aleatorios provenientes de una dis-
tribución normal se puede usar la función normal(...) del módulo random de NumPy. A esta función se debe
especificar la media (loc), la desviación estándar (scale) y el número de valores a generar (size):

In [35]: Residuos_teoricos = [Link](loc = [Link](Residuos),


scale = [Link](Residuos),
size = 10000)

A partir de estos residuos teóricos puede definirse la función de distribución “teórica”, para lo que basta
emplear las mismas operaciones usadas en la distribución empírica:

In [36]: x_teor = [Link](Residuos_teoricos)


n_teor = x_teor.size
y_teor = [Link](1, n_teor+1) / n_teor

Finalmente, con la función subplots(...) de [Link] y los métodos .scatter() y .plot(),


puede visualizarse la relación entre las funciones previamente definidas:

In [37]: fig, ax = [Link](figsize = (8,6))


[Link]("Distribución empírica vs.\n"
"Distribución teórica",
fontsize = 18,
fontweight = "bold")
[Link](x, y, color = "darkblue",
label = "Distribución empírica")
[Link](x_teor, y_teor, color = "red",
label = "Distribución teórica")
ax.set_xlabel(r"Valores $x$",
fontsize = 15)
ax.set_ylabel("$P(X\leq x)$", fontsize = 15)
ax.tick_params(labelsize = 14)
[Link]()
[Link](.9,-.02,
"Elaboración:",
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.08,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")

62
plt.subplots_adjust(top = 0.85)
[Link]()

El lector puede observar que la distribución de los residuos no dista mucho de la apariencia que corresponde
a la distribución normal. Los puntos correspondientes a la función de distribución empírica (los azules) se sitúan
muy cerca de la línea de referencia (la distribución teórica).
Finalmente, otro método gráfico ampliamente utilizado, seguramente más común que el de la distribución
empírica, para evaluar la normalidad, es el gráfico de cuantil-cuantil, o Q-Q Plot. Este gráfico puede construirse
con la función qqplot(...) de Statsmodels, pasando como argumento el objeto que contiene los datos a partir
de los cuales se quieren establecer los cuantiles. Por defecto esta función trabaja con la distribución normal,
razón por la cual no debe introducirse alguna modificación:

In [38]: fig, ax = [Link](figsize = (8,6))


[Link]("Q-Q Plot", size = 20,
fontweight = "bold")
[Link](Residuos, ax = ax, line = "s",

63
color = "darkblue")
ax.set_xlabel("Cuantiles teóricos", fontsize = 14)
ax.set_ylabel("Cuantiles muestrales", fontsize = 14)
[Link](.9,-.02,
"Elaboración:",
fontsize = 13, fontweight = "bold",
ha = "right")
[Link](.9,-.08,
"Triana, F.\n(2019)",
fontsize = 12, ha = "right")
[Link]()

Como se observa, la mayoría de puntos se sitúa muy cerca de la línea roja de referencia, indicador de que
la distribución de los datos no dista mucho de la teórica (la normal). Este resultado concuerda con el obtenido
en el análisis de la distribución empírica y con los resultados de las tres pruebas estadísticas empleadas para
verificar el cumplimiento del supuesto de normalidad.

64
En este capítulo se abordó muy brevemente, y sin examinar detalladamente los fundamentos teóricos aso-
ciados, el cumplimiento de algunos de los supuestos del modelo clásico de regresión lineal (normal, si consi-
deramos el último supuesto planteado) usando herramientas de librerías Python especializadas. El modelo al
que corresponden tales supuestos se ilustró, por medio de un ejemplo particular, en el Capítulo 3, en el cual se
dio una ligera aproximación (práctica) al tema fundamental que generalmente se aborda en los cursos de nivel
introductorio de Econometría. Ahora, se aumentará ligeramente la dificultad procediendo a examinar un tema
un tanto más complejo (en un sentido más teórico que práctico) al que se dedicará el próximo capítulo.

65
Capítulo 5

Mínimos Cuadrados en 2 Etapas

Uno de los supuestos (el #2 específicamente) del modelo clásico de regresión lineal planteados a inicios del
capítulo previo, establece que la covarianza entre las variables explicativas y el término de error es cero, es decir,
que toda x (siguiendo la notación empleada con anterioridad) es independiente del término de error. Cuando
las variables explicativas cumplen esta condición, son denominadas exógenas y cuando violan tal supuesto se
consideran endógenas.
Cuando alguna(s) de las variables explicativas del modelo es (son) endógena(s) los estimadores obtenidos
por el método de Mínimos Cuadrados Ordinarios pierden su calidad de MELI. Para solucionar tal inconveniente
se recurre al método de Variables Instrumentales, en el cual se emplean variables no incluidas en la ecuación
original para estimar las variables explicativas endógenas incluidas en ésta y tratar su endogeneidad.
Cuando alguna(s) variable(s) explicativa(s) y el término de error se correlacionan, se violan condiciones
de los supuestos del modelo clásico de regresión lineal, por lo que los estimadores de MCO pierden algunas
de sus características atractivas. Para solucionar el inconveniente generado por la endogeneidad de algún(os)
covariante(s) se requiere de información adicional, la cual se obtiene de una variable observable exógena no
incluida.
Tomando un modelo de regresión lineal de la forma

y1i = β 0 + β 1 xi + β 2 y2i + ui
en donde y1 es la variable dependiente, x es una variable explicativa exógena (no se correlaciona con el
término de error) y y2 es una variable explicativa endógena (se correlaciona con el término de error), si se tiene
una variable z que cumple con las condiciones siguientes:

1. cov(z, u) = 0

2. cov(z, y2 ) 6= 0

entonces z es una variable instrumental (o instrumento) para y2 .


La primera condición establecida hace referencia a la exogeneidad del instrumento y la segunda a la rele-
vancia del mismo; dichas condiciones son útiles para garantizar la “calidad” de las Variables Instrumentales, las
cuales son importantes ya que permiten tratar la endogeneidad y superar el inconveniente generado por ésta.
Al lector interesado en profundizar sobre este tema se sugiere consultar el Capítulo 15, Estimación con variables
instrumentales y mínimos cuadrados en dos etapas, de Wooldridge (2010).
Hasta este punto, el lector debe saber que la correlación entre variables explicativas y el término de error
es un problema para la obtención de estimadores con las mejores propiedades por el método de Mínimos Cua-
drados Ordinarios y que las variables instrumentales pueden emplearse para tratar la endogeneidad de la(s)

66
variable(s) explicativa(s); sin embargo, aún no se ha señalado el proceso específico por medio del cual se hace
uso de los instrumentos.
Uno de los métodos empleados para obtener estimadores de Variables Instrumentales es el de Mínimos
Cuadrados en 2 Etapas (MC2E), el cual recibe tal denominación dado que el proceso que comprende está
estructurado en dos “etapas” bien definidas en las que se llevan a cabo regresiones específicas con objetivos
concretos.
Tomando el modelo de regresión lineal señalado anteriormente, y siguiendo a Wooldridge (2010), la pri-
mera etapa del método MC2E consiste en realizar la regresión ŷ2i = π̂0 + π̂1 xi + π̂2 zi , de donde se ob-
tienen los valores ajustados ŷ2 . La segunda etapa es una regresión por Mínimos Cuadrados Ordinarios de
y1i = β 0 + β 1 xi + β 2 ŷ2i + ui (nótese que en esta regresión se emplea los valores de ŷ2 en vez de los va-
lores de y2 ). Así, de lo que se trata, básicamente, es de estimar la variable explicativa endógena a partir de
variables exógenas y, posteriormente, emplear la variable estimada como variable explicativa, junto a las de-
más exógenas del modelo original (no se incluyen los instrumentos), para estimar la variable dependiente.
Ya se ha dado una muy mínima descripción de Variables Instrumentales y el método de Mínimos Cuadrados
en 2 Etapas, por lo que el lector debe tener una idea general del tema que ahora se pretende abordar de manera
práctica; labor a la cual, a continuación, se da inicio efectivo con Python.
El dataset que se utilizará para ilustrar el uso de variables instrumentales y el método MC2E es el empleado
en el Ejemplo 15.5, Rendimientos de la educación para la mujer trabajadora, de Wooldridge (2010). El dataset se
ha obtenido desde Stata, con el comando bcuse, y ha sido exportado como archivo .csv para su uso en Python.
Se invita al lector a consultar el ejemplo, de modo que pueda verificar los resultados obtenidos.

Preparación del entorno


La primera celda en la que el usuario escribirá y ejecutará código tendrá el siguiente contenido:

In [1]: import numpy as np


import pandas as pd
import [Link] as sm
from [Link] import IV2SLS
import [Link] as plt
%matplotlib inline
[Link]("seaborn-white")

El lector ya debe saber que el anterior bloque de código permite cargar las herramientas necesarias para
llevar a cabo las acciones requeridas en el análisis econométrico planteado; en caso de que no tenga claridad
al respecto, se le pide consultar la sección ’Preparación del entorno’ del segundo capítulo, en donde se brinda
una explicación sobre tal cuestión.
El código de esta celda es exactamente igual al empleado en la preparación del entorno del Capítulo 2,
exceptuando por lo siguiente: 1) no se importa la librería Seaborn y, 2) más importante aún, se importa la función
IV2SLS(...) del módulo iv de la librería linearmodels; tal función es fundamental para el tema a abordar en
este capítulo y su importación es obligatoria.
El usuario debe ser muy cuidadoso al ejecutar la celda de código anterior, pues si lo hace inmediatamente,
sin tener en cuenta la información que se presenta a continuación, el proceso no será completamente exitoso;
esto, por la razón que enseguida se presenta. La librería linearmodels no viene integrada por defecto en la Distri-
bución Anaconda (la cual es la que estamos empleando), por lo cual es necesario instalarla por nuestra propia
cuenta para poder hacer uso de las herramientas que brinda.
Para instalar una librería basta con dirigirse al Símbolo del Sistema (’cmd’); estando ahí, tan solo se debe
escribir conda install y, a continuación, el nombre de la librería en la que se está interesado. En nuestro

67
caso, el código a emplear es conda install linearmodels. De forma alternativa puede emplearse !conda
install linearmodels directamente en el notebook.

Nota: El código anterior hace uso de conda, el gestor de paquetes propio de la distribución Anaconda, el
cual recurre al repositorio Anaconda; sin embargo, es posible que el proceso de instalación de la librería
a través de conda no resulte exitoso, en cuyo caso se puede utilizar el sistema de gestión de paquetes
estándar de Python, pip, el cual trabaja directamente con Python Package Index (PyPi). El código para la
instalación de linearmodels a través de pip es pip install linearmodels en ’cmd’ o !pip install
linearmodels en el notebook; ejecutando dicho código debería llevarse a cabo de forma exitosa la ins-
talación de la librería linearmodels.

La importación de linearmodels es fundamental para nuestra labor, pues es donde están contenidas las fun-
ciones que emplearemos para hacer uso de variables instrumentales y aplicar el método de Mínimos Cuadra-
dos en 2 Etapas; sin esta librería resultaría excesivamente complejo el proceso de estimación y la realización
de las labores requeridas para llevar a cabo un análisis econométrico “aceptable”. El usuario debe verificar la
instalación previa de linearmodels (en caso de no estar instalada, llevar a cabo el proceso de instalación corres-
pondiente, sea con conda o con pip) y, en caso de reutilizar el bloque de código de preparación del entorno
del segundo capítulo, debe ser cuidadoso y asegurarse de incluir correctamente la línea de importación de la
función IV2SLS(...) del módulo iv de linearmodels.
Una vez ejecutada la celda de código de la preparación del entorno, sin la generación de algún tipo de error,
puede continuarse con entera tranquilidad a la fase siguiente, la cual tiene una importancia fundamental en
cuanto es la que permite contar con la “materia prima” para llevar a cabo el análisis: los datos.

Importación de los datos


La importación de los datos se lleva a cabo con funciones de la librería pandas. Dependiendo del tipo de
archivo en el cual se encuentre almacenada la información del dataset, deberemos recurrir a una función parti-
cular y un conjunto de parámetros específico.
Para nuestro caso concreto, el siguiente código es el que importa el dataset:

In [2]: data = pd.read_csv("[Link]")

Al lector que no entienda esta línea de código, se le sugiere dirigirse al Capítulo 2, en donde encontrará una
explicación que seguramente aclarará sus dudas.
Una vez ejecutada la línea de código de esta celda, el dataset debería haber sido importado correctamente.
Para comprobarlo, recurrimos al método .head(), aplicado al objeto que contiene el dataset (en este caso le
hemos asignado el nombre data):

In [3]: [Link]()

Out[3]: inlf hours kidslt6 kidsge6 age educ wage repwage hushrs husage \
0 1 1610 1 0 32 12 3.3540 2.65 2708 34
1 1 1656 0 2 30 12 1.3889 2.65 2310 30
2 1 1980 1 3 35 12 4.5455 4.04 3072 40
3 1 456 0 3 34 12 1.0965 3.25 1920 53
4 1 1568 1 2 31 14 4.5918 3.60 2000 32

… faminc mtr motheduc fatheduc unem city exper nwifeinc \

68
0 … 16310 0.7215 12 7 5.0 0 14 10.910060
1 … 21800 0.6615 7 7 11.0 1 5 19.499981
2 … 21040 0.6915 12 7 5.0 0 15 12.039910
3 … 7300 0.7815 7 7 5.0 0 6 6.799996
4 … 27300 0.6215 12 14 9.5 1 7 20.100060

lwage expersq
0 1.210154 196
1 0.328512 25
2 1.514138 225
3 0.092123 36
4 1.524272 49

[5 rows x 22 columns]

Podemos observar que el proceso de importación ha sido exitoso y la información del dataset ha sido al-
macenada correctamente en un DataFrame de pandas. Ahora, podemos continuar tranquilamente con el desa-
rrollo de nuestro análisis, en cuanto hemos concluido satisfactoriamente el primer paso. Procederemos a usar
variables instrumentales empleando el método de Mínimos Cuadrados en 2 Etapas, sin embargo, el usuario
debe tener en cuenta la información que se le brinda a continuación.
El dataset que acabamos de importar contiene algunas observaciones faltantes (esto se debe a la fuente
original de la información y no a una preferencia del autor de esta obra, aunque veremos que tal situación nos
resultará algo enriquecedora al mejorar nuestra capacidad exploratoria y de manejo de datos). Estos valores
faltantes pueden generar problemas posteriormente, por lo que procederemos a identificarlos y darles un tra-
tamiento apropiado.
Para examinar cuántas filas presentan algún campo con un valor faltante y conocer su ubicación, emplea-
remos los métodos .isna() y .sum(); el primero nos permite identificar valores faltantes (por medio de un
criterio booleano) y el segundo permite ejecutar una suma que, teniendo en cuenta el tipo de datos generados
por el primer método, permite conocer la cantidad de los mismos.

In [4]: [Link]().sum()

Out[4]: inlf 0
hours 0
kidslt6 0
kidsge6 0
age 0
educ 0
wage 325
repwage 0
hushrs 0
husage 0
huseduc 0
huswage 0
faminc 0
mtr 0
motheduc 0
fatheduc 0

69
unem 0
city 0
exper 0
nwifeinc 0
lwage 325
expersq 0
dtype: int64
Se puede observar que las únicas variables en las que se presentan valores faltantes son ’wage’ y ’lwage’,
con 325 datos clasificados como Na para cada una. Esta situación puede dar lugar a ciertos problemas, por lo
cual, tratando de minimizar la posibilidad de inconvenientes durante la fase de estimación, solo se empleará
los registros en los cuales existe un valor específico (no Na) para la variable ’lwage’ (la cual tiene el rol de
variable dependiente en el modelo). ¿Cómo podremos conseguir tal objetivo, seleccionar únicamente registros
con valores no faltantes?
Para obtener un DataFrame que solamente contenga registros en los que no haya valores faltantes para
la variable ’lwage’ se hará uso de la selección por medio de corchetes “[ ]” y del método .notna() (aplicado
sobre la variable de referencia); esto, con el propósito de tener en cuenta únicamente registros que cumplan la
condición señalada. El resultado obtenido se asignará al objeto data (que ya existe), con el objetivo de contar
con un único dataset y evitar posibles confusiones. El código a ejecutar es el siguiente:
In [5]: data = data[data["lwage"].notna()]
Ahora, procedemos a verificar que hemos conseguido lo que nos hemos propuesto y en nuestro DataFrame
no se presentan valores faltantes; de nuevo, emplearemos los métodos .isna() y .sum():
In [6]: [Link]().sum()
Out[6]: inlf 0
hours 0
kidslt6 0
kidsge6 0
age 0
educ 0
wage 0
repwage 0
hushrs 0
husage 0
huseduc 0
huswage 0
faminc 0
mtr 0
motheduc 0
fatheduc 0
unem 0
city 0
exper 0
nwifeinc 0
lwage 0
expersq 0
dtype: int64

70
Observamos que todas las variables contenidas en el DataFrame carecen de valores faltantes, por lo que
ningún registro está incompleto. Este DataFrame, que tiene el nombre de data, es el que emplearemos para
trabajar con variables instrumentales y aplicar el método de Mínimos Cuadrados en 2 Etapas, el cual, aborda-
remos a continuación desde dos enfoques: 1) proceso manual y 2) proceso automático.

Proceso manual
El método de Mínimos Cuadrados en 2 Etapas, tal y como su nombre lo indica, está formado por dos etapas
de estimación claramente definidas: la primera consiste en regresar la(s) variable(s) explicativa(s) endógena(s)
contra el(los) instrumentos y la(s) variable(s) explicativa(s) exógena(s); la segunda etapa consiste en emplear
la variable estimada en la primera etapa en lugar de la variable explicativa endógena en el modelo original. Al
lector que tal descripción le parezca confusa, se sugiere remitirse a la primera parte de este capítulo o consultar
el Capítulo 15, Estimación con variables instrumentales y mínimos cuadrados en dos etapas, de Wooldridge (2010),
donde este tema es tratado en detalle.
El proceso “manual” recibe tal denominación en cuanto debemos llevar a cabo las regresiones de la primera
y la segunda etapa por nuestra propia cuenta, especificando el conjunto de variables a emplear en cada una
de estas. Tal procedimiento no es recomendable y la mayoría de programas y lenguajes que soportan análisis
estadístico (Python no es la excepción) cuenta con funciones que realizan la ejecución “automáticamente”,
evitando que el usuario deba llevar a cabo las tareas por su cuenta.
A pesar de lo señalado en el párrafo anterior, dado el enfoque práctico de esta guía y la utilidad que puede
tener para el usuario el llevar a cabo el proceso paso por paso en la comprensión del tema tratado, procederemos
a aplicar el método MC2E ’manualmente’ (el proceso automático se aborda en la siguiente sección, por lo que
al lector que no esté interesado en ejecutar ambas etapas por su propia cuenta se le sugiere consultar dicha
sección directamente, sin dedicar su tiempo al estudio de la presente).
Lo primero que debemos hacer para llevar a cabo correctamente las regresiones de la primera etapa y la
segunda etapa es agrupar las variables disponibles de forma apropiada, en cuanto cada una de estas desem-
peña un rol específico. Así, procederemos a asignar la variable dependiente a un objeto específico, la variable
explicativa endógena a otro objeto en particular y, del mismo modo, las variables explicativas exógenas y los
instrumentos a otros objetos concretos. El código a ejecutar es el siguiente:

In [7]: Y1 = data["lwage"] # Variable dependiente


Y2 = data["educ"] # Variable endógena
X = data[["exper", "expersq"]] # Variables exógenas
Z = data[["motheduc", "fatheduc"]] # Instrumentos

Primera etapa
Durante la primera etapa se regresa la variable explicativa endógena contra las variables explicativas exóge-
nas y los instrumentos. El lector puede observar que las variables explicativas exógenas y los instrumentos han
sido asignados a objetos distintos, por lo que, con el propósito de simplificar el código, ahora se asignarán
conjuntamente a un único objeto. Para conseguir tal objetivo, basta con emplear la función concat(...) de
pandas, usando como argumento una lista Python “[...]” en la que se incluyan los DataFrames que contienen
las variables explicativas exógenas y los instrumentos, y asignando el valor de 1 al parámetro axis (para conca-
tenar los DataFrames “horizontalmente”). El resultado generado por la aplicación de la función sobre la lista
recibirá el nombre (algo extenso, pero muy informativo) de RegresorasPrimeraEtapa, así:

In [8]: frames = [X, Z]


RegresorasPrimeraEtapa = [Link](frames, axis = 1)

71
Verificamos que el proceso se ha llevado a cabo exitosamente, recurriendo al método .head():

In [9]: [Link]()

Out[9]: exper expersq motheduc fatheduc


0 14 196 12 7
1 5 25 7 7
2 15 225 12 7
3 6 36 7 7
4 7 49 12 14

Ahora, dado que nuestras variables están almacenadas de forma conveniente, construimos el modelo co-
rrespondiente a la primera etapa, tomando como variable dependiente la variable explicativa endógena (’educ’
en notación original, Y2 en nuestro entorno) y como variables explicativas las variables explicativas exóge-
nas (’exper’, ’expersq’ en notación original) y los instrumentos (’motheduc’, ’fatheduc’ en notación origi-
nal). Nótese que las variables explicativas exógenas y los instrumentos están contenidos en un único objeto
(RegresorasPrimeraEtapa).
El código a ejecutar el siguiente:

In [10]: PrimeraEtapa = [Link](Y2, sm.add_constant(RegresorasPrimeraEtapa))


ResultadosPrimeraEtapa = [Link]()
print([Link]())

OLS Regression Results


==============================================================================
Dep. Variable: educ R-squared: 0.211
Model: OLS Adj. R-squared: 0.204
Method: Least Squares F-statistic: 28.36
Date: xxx, xx xxx xxxx Prob (F-statistic): 6.87e-21
Time: xx:xx:xx Log-Likelihood: -909.72
No. Observations: 428 AIC: 1829.
Df Residuals: 423 BIC: 1850.
Df Model: 4
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 9.1026 0.427 21.340 0.000 8.264 9.941
exper 0.0452 0.040 1.124 0.262 -0.034 0.124
expersq -0.0010 0.001 -0.839 0.402 -0.003 0.001
motheduc 0.1576 0.036 4.391 0.000 0.087 0.228
fatheduc 0.1895 0.034 5.615 0.000 0.123 0.256
==============================================================================
Omnibus: 10.903 Durbin-Watson: 1.940
Prob(Omnibus): 0.004 Jarque-Bera (JB): 20.371
Skew: -0.013 Prob(JB): 3.77e-05
Kurtosis: 4.068 Cond. No. 1.55e+03
==============================================================================

72
Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
specified.
[2] The condition number is large, 1.55e+03. This might indicate that there are
strong multicollinearity or other numerical problems.

Al lector que ha seguido atentamente esta obra, la anterior celda de código no debería resultarle extraña,
pues no es más que una regresión por Mínimos Cuadrados Ordinarios; si el código presentado le resulta confuso,
se le sugiere consultar el tercer capítulo de este trabajo, en donde la regresión por MCO es el tema central.
Hemos empleado instrumentos como covariantes en una regresión, sin embargo, aún desconocemos si di-
chos instrumentos cumplen las condiciones para desempeñar satisfactoriamente tal papel; todavía no hemos
evaluado la “calidad” de las variables instrumentales utilizadas, por lo que procederemos a examinarla.
Para que las variables instrumentales empleadas cumplan la condición de relevancia (véase la primera parte
de este capítulo), los parámetros asociados a los instrumentos deben ser estadísticamente distintos de cero.
Como puede observarse en la instancia de resultados del modelo, tanto para la variable ’motheduc’ como para
la variable ’fatheduc’ se cumple dicha condición, pues los coeficientes asociados son estadísticamente signi-
ficativos, el p-value respectivo es inferior a 0.05 (nivel de significancia elegido) y el intervalo de confianza no
contiene el 0.
La conclusión del párrafo anterior es que los instrumentos cumplen la condición de relevancia, sin embargo,
para probar formalmente la validez de dicha conclusión puede emplearse una Prueba F para significancia de un
subconjunto de parámetros. La Prueba F se ejecuta en Python por medio del método .f_test(), aplicado sobre
la instancia de resultados del modelo y empleando como argumento las condiciones a evaluar (que pueden
expresarse como un ’string’).
En este caso, asignaremos la hipótesis a un objeto que se empleará como argumento en el método
.f_test(); esto, con el propósito de simplificar el código. Así:

In [11]: hipotesis = "(fatheduc = 0), (motheduc = 0)"


Prueba_F = ResultadosPrimeraEtapa.f_test(hipotesis)
print(Prueba_F)

<F test: F=array([[55.40030043]]), p=4.268908724630835e-22, df_denom=423, df_num=2>

El resultado generado por el método .f_test() es un objeto de tipo ContrastResults(), el cual cuenta
con sus propios atributos. Algunos de estos atributos corresponden al estadístico y al p-value, los cuales son
los valores de interés para nuestros propósitos. Estos atributos son objetos de tipo ndarray de NumPy, sin
embargo, lo que nos interesa no es el objeto como tal sino su contenido, el cual debe expresarse como un dato
de tipo float.
Para realizar la extracción de los valores correspondientes, se recurre al uso de la selección por medio de
corchetes “[ ]” y del método .item(), indicando la posición correspondiente al elemento de interés; asimismo,
emplearemos una Series de pandas para obtener una presentación un tanto más “agradable” del resultado. El
código a ejecutar es el siguiente:

In [12]: Estadístico = Prueba_F.statistic[0].item(0)


pvalue = Prueba_F.[Link](0)
Nombres = ["Estadístico", "p-value"]
[Link]([Estadístico, round(pvalue, 3)], index = Nombres)

73
Out[12]: Estadístico 55.4003
p-value 0.0000
dtype: float64

Asumiendo un nivel de significancia de 5 %, como p − value < α entonces se rechaza la hipótesis nula y se
concluye que los parámetros asociados a los instrumentos son estadísticamente distintos de cero (trabajando
con un α de 0.05). Se invita al lector a contrastar estos resultados con los presentados en Wooldridge (2010),
de modo que pueda comprobar que son exactamente iguales y que el proceso desarrollado es correcto.
Hasta este punto ya se ha llevado a cabo la regresión de la primera etapa y se ha verificado que los instru-
mentos empleados cumplen la condición de relevancia, por lo que es posible continuar con la segunda etapa
del método MC2E, la cual se aborda en la siguiente sección.

Segunda etapa
Durante la segunda etapa se regresa la variable dependiente de la ecuación original contra las variables ex-
plicativas exógenas y la variable estimada en la primera etapa (la cual reemplaza la variable explicativa endógena
que inicialmente causaba los inconvenientes) (Nótese que en esta etapa no se hace uso de los instrumentos).
Durante la primera etapa estimamos la variable explicativa endógena en función de las variables explicativas
exógenas y los instrumentos; sin embargo, aún desconocemos los valores de dicha variable, los cuales son los
que se emplearán en la segunda etapa. Para obtener tales valores, empleamos el método .predict() aplicado
a la instancia de resultados del modelo de la primera etapa (a la que hemos dado el conveniente nombre de
ResultadosPrimeraEtapa).
Los resultados generados por la aplicación del método .predict() serán asignados a una nueva variable
(que llamaremos predicted_educ) del DataFram data, de modo que podamos emplearlos con facilidad
posteriormente. El código a ejecutar es el siguiente:

In [13]: data["predicted_educ"] = [Link]()

Verificamos que la nueva variable haya sido efectivamente creada, recurriendo al método .head():

In [14]: [Link]()

Out[14]: inlf hours kidslt6 kidsge6 age educ wage repwage hushrs husage \
0 1 1610 1 0 32 12 3.3540 2.65 2708 34
1 1 1656 0 2 30 12 1.3889 2.65 2310 30
2 1 1980 1 3 35 12 4.5455 4.04 3072 40
3 1 456 0 3 34 12 1.0965 3.25 1920 53
4 1 1568 1 2 31 14 4.5918 3.60 2000 32

… mtr motheduc fatheduc unem city exper nwifeinc \


0 … 0.7215 12 7 5.0 0 14 10.910060
1 … 0.6615 7 7 11.0 1 5 19.499981
2 … 0.6915 12 7 5.0 0 15 12.039910
3 … 0.7815 7 7 5.0 0 6 6.799996
4 … 0.6215 12 14 9.5 1 7 20.100060

lwage expersq predicted_educ


0 1.210154 196 12.756017
1 0.328512 25 11.733558

74
2 1.514138 225 12.771979
3 0.092123 36 11.767683
4 1.524272 49 13.914615

[5 rows x 23 columns]

Podemos observar que la última columna corresponde a la variable predicted_educ, la cual es la que
hemos creado previamente; los datos que contiene corresponden a los valores estimados durante la primera
etapa.
Ya contamos con todas las variables a emplear en la regresión de la segunda etapa. Ahora, al igual que
durante la primera etapa, agruparemos las regresoras correspondientes en un único objeto. Nuevamente, crea-
remos una lista Python “[...]” cuyo contenido corresponderá a las regresoras de la segunda etapa, es decir, las
variables explicativas exógenas del modelo original y la variable endógena estimada en la primera etapa; esta
lista se empleará como argumento de la función concat(...) de pandas (recuerde la asignación del valor de
1 al parámetro axis). Así:

In [15]: FramesSegundaEtapa = [X, data["predicted_educ"]]


RegresorasSegundaEtapa = [Link](FramesSegundaEtapa, axis = 1)

Verificamos que el proceso se haya llevado a cabo exitosamente, recurriendo al método .head():

In [16]: [Link]()

Out[16]: exper expersq predicted_educ


0 14 196 12.756017
1 5 25 11.733558
2 15 225 12.771979
3 6 36 11.767683
4 7 49 13.914615

Ahora, tal y como hemos señalado repetidamente, construimos el modelo tomando como variable depen-
diente la variable dependiente de la ecuación original (y1 , siguiendo la notación empleada a inicios del capítulo)
y como variables explicativas las variables explicativas exógenas de la ecuación original (las x, siguiendo la no-
tación inicial) y la variable estimada en la primera etapa (¡ŷ2 y no y2 !).
Dado que hemos agrupado las variables explicativas de la regresión de la segunda etapa en un único objeto
(es lo que hemos hecho en la última celda de código), basta con emplear dicho objeto como argumento en la
construcción del modelo (la cual se lleva a cabo con la ya conocida función OLS(...) de Statsmodels).
El código (que ya debe resultar familiar) para la construcción y estimación del modelo de la segunda etapa
es el siguiente:

In [17]: SegundaEtapa = [Link](Y1, sm.add_constant(RegresorasSegundaEtapa))


ResultadosSegundaEtapa = [Link]()
print([Link]())

OLS Regression Results


==============================================================================
Dep. Variable: lwage R-squared: 0.050
Model: OLS Adj. R-squared: 0.043
Method: Least Squares F-statistic: 7.405

75
Date: xxx, xx xxx xxxx Prob (F-statistic): 7.62e-05
Time: xx:xx:xx Log-Likelihood: -457.17
No. Observations: 428 AIC: 922.3
Df Residuals: 424 BIC: 938.6
Df Model: 3
Covariance Type: nonrobust
==================================================================================
coef std err t P>|t| [0.025 0.975]
----------------------------------------------------------------------------------
const 0.0481 0.420 0.115 0.909 -0.777 0.873
exper 0.0442 0.014 3.136 0.002 0.016 0.072
expersq -0.0009 0.000 -2.134 0.033 -0.002 -7.11e-05
predicted_educ 0.0614 0.033 1.863 0.063 -0.003 0.126
==============================================================================
Omnibus: 53.587 Durbin-Watson: 1.959
Prob(Omnibus): 0.000 Jarque-Bera (JB): 168.354
Skew: -0.551 Prob(JB): 2.77e-37
Kurtosis: 5.868 Cond. No. 4.41e+03
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
specified.
[2] The condition number is large, 4.41e+03. This might indicate that there are
strong multicollinearity or other numerical problems.

Así concluye la aplicación del método MC2E en Python de forma manual. El lector debe tener en cuenta
que, aunque los parámetros estimados “manualmente” son correctos, los errores estándar no lo son (véase la
página 522 de Wooldridge (2010)), por lo que no es recomendable ejecutar las regresiones de primera y segun-
da etapa por cuenta propia. Para evitar este inconveniente, la mayoría de paquetes y programas estadísticos
ofrecen la posibilidad de ejecutar el método MC2E de forma automática, generando resultados correctos. El
lenguaje de programación Python no es la excepción, pues su librería linearmodels ofrece una función diseñada
específicamente para tal tarea, como veremos a continuación.

Proceso automático
Para evitar tener que hacer las regresiones de las dos etapas del método MC2E por nuestra propia cuenta,
la librería linearmodels de Python nos da la posibilidad de hacer uso de sus funciones para realizar “automática-
mente” dichas regresiones. La función específica empleada para tal propósito es IV2SLS(...) (acrónimo para
Instrumental Variables 2 Stages Least Squares), cuyos argumentos incluyen la variable dependiente (dependent), las
regresoras exógenas (exog), las regresoras endógenas (endog) y los instrumentos (instruments).
Para la estimación, como resulta familiar, se usa el método .fit(), y para el resumen de resultados el
atributo .summary.
El código correspondiente a nuestro ejemplo es:

In [18]: Modelo2Etapas = IV2SLS(dependent = Y1, exog = sm.add_constant(X),


endog = Y2, instruments = Z)

76
ResultadosModelo2Etapas = [Link]()
print([Link])

IV-2SLS Estimation Summary


==============================================================================
Dep. Variable: lwage R-squared: 0.1357
Estimator: IV-2SLS Adj. R-squared: 0.1296
No. Observations: 428 F-statistic: 18.611
Date: xxx, xxx xx xxxx P-value (F-stat) 0.0003
Time: xx:xx:xx Distribution: chi2(3)
Cov. Estimator: robust

Parameter Estimates
==============================================================================
Parameter Std. Err. T-stat P-value Lower CI Upper CI
------------------------------------------------------------------------------
const 0.0481 0.4278 0.1124 0.9105 -0.7903 0.8865
exper 0.0442 0.0155 2.8546 0.0043 0.0138 0.0745
expersq -0.0009 0.0004 -2.1001 0.0357 -0.0017 -5.997e-05
educ 0.0614 0.0332 1.8503 0.0643 -0.0036 0.1264
==============================================================================

Endogenous: educ
Instruments: motheduc, fatheduc
Robust Covariance (Heteroskedastic)
Debiased: False

El lector puede observar cómo la aplicación del método MC2E resulta tremendamente sencilla con el uso de
la función IV2SLS(...) de [Link] y que todas las tareas llevadas a cabo en el proceso manual han sido
ejecutadas perfectamente en tan solo ¡3 líneas de código!, sin tener que crear variables adicionales o emplear
múltiples grupos de las mismas, obteniendo, además, resultados completamente correctos.

Mínimos Cuadrados Ordinarios vs. Mínimos Cuadrados en 2 Etapas


Por último, se hará una regresión común y corriente por el método de Mínimos Cuadrados Ordinarios, sin
tener en cuenta que la regresora endógena es endógena sino considerando todas las regresoras como exógenas.
Esto se hará únicamente con propósitos ilustrativos, para examinar cuáles habrían sido los resultados.
Nuevamente, emplearemos la función concat(...) de pandas para agrupar las variables explicativas
(exógenas y endógena) en un único DataFrame. Asimismo, para no extendernos innecesariamente, llevaremos
a cabo la creación y estimación del modelo en la misma celda de código:

In [19]: FramesMCO = [X, Y2]


RegresorasMCO = [Link](FramesMCO, axis = 1)
ModeloMCO = [Link](Y1, sm.add_constant(RegresorasMCO))
ResultadosMCO = [Link](cov_type = "HC0")
print([Link]())

77
OLS Regression Results
==============================================================================
Dep. Variable: lwage R-squared: 0.157
Model: OLS Adj. R-squared: 0.151
Method: Least Squares F-statistic: 27.56
Date: xxx, xx xxx xxxx Prob (F-statistic): 2.68e-16
Time: xx:xx:xx Log-Likelihood: -431.60
No. Observations: 428 AIC: 871.2
Df Residuals: 424 BIC: 887.4
Df Model: 3
Covariance Type: HC0
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
const -0.5220 0.201 -2.601 0.009 -0.915 -0.129
exper 0.0416 0.015 2.734 0.006 0.012 0.071
expersq -0.0008 0.000 -1.940 0.052 -0.002 8.28e-06
educ 0.1075 0.013 8.170 0.000 0.082 0.133
==============================================================================
Omnibus: 77.792 Durbin-Watson: 1.961
Prob(Omnibus): 0.000 Jarque-Bera (JB): 300.917
Skew: -0.753 Prob(JB): 4.54e-66
Kurtosis: 6.822 Cond. No. 2.21e+03
==============================================================================

Warnings:
[1] Standard Errors are heteroscedasticity robust (HC0)
[2] The condition number is large, 2.21e+03. This might indicate that there are
strong multicollinearity or other numerical problems.

El lector puede observar que los resultados obtenidos por el método MCO difieren en cierta medida de los
correspondientes al método MC2E y que los errores estándar estimados por MC2E manualmente son diferentes
a los obtenidos automáticamente. Esto puede verificarlo al comparar las tablas de resumen de cada uno de los
modelos elaborados; sin embargo, tal ejercicio puede resultar tedioso, en cuanto cada una de estas tablas está
situada en una posición distinta dentro del notebook y comparar información entre estas puede resultar agota-
dor. Afortunadamente, la librería linearmodels brinda una herramienta para facilitar esta labor, como veremos
enseguida.

Comparación de modelos
Resulta muy práctico poder comparar los modelos estimados visualizando sus correspondientes resúme-
nes de instancias de resultados de forma simultánea; tal posibilidad puede materializarse gracias a la función
compare(...) que brinda la librería linearmodels en su módulo iv. Lo primero que debemos hacer para utilizar
la función compare(...) es contar con acceso a ésta, por lo que procedemos a importarla:

In [20]: from [Link] import compare

78
Habiendo ejecutado la celda de código anterior, habremos importado la función compare(...), por lo
que podremos hacer uso efectivo de la misma. Como argumento utilizaremos un diccionario Python “{...}”, en
el que emplearemos como claves los nombres que asignaremos a cada modelo y como valores las instancias de
resultados de los modelos correspondientes.
El usuario debe tener en cuenta que para que la función compare(...) de [Link] no genere un
error, todos los modelos a comparar deben corresponder a instancias de resultados del tipo generado por la
función IV2SLS(...). En este caso, como solo se utilizó la función IV2SLS(...) para el último modelo, no
es posible hacer una comparación con los demás. ¿Cómo puede solucionarse tal inconveniente? La respuesta es
muy simple en realidad: todos los modelos que hemos creado pueden construirse haciendo uso de la función
IV2SLS(...), tan solo hay que ser cuidadosos e incluir los argumentos correctos.
La función IV2SLS(...) tiene parámetros específicos para la variable dependiente, la(s) variable(s) exóge-
na(s), la(s) variable(s) endógena(s) y el(los) instrumento(s). Así, para una estimación común y corriente por el
método de MCO, basta con asignar None al parámetro endog y al parámetro instruments; para una estimación
por MC2E manual basta con emplear las regresoras de la segunda etapa en el parámetro exog y asignar None
a los parámetros endog e instruments; para una estimación por MC2E automática tan solo debe emplearse los
mismos argumentos que en el modelo elaborado previamente en la sección “Proceso automático”. El código
(se sugiere al lector examinarlo con detenimiento) para estimar los modelos correspondientes, empleando para
todos la función IV2SLS(...), es el siguiente:

In [21]: ResultadosMCO = IV2SLS(dependent = Y1, exog = sm.add_constant(RegresorasMCO),


endog = None, instruments = None).fit()
ResultadosManual = IV2SLS(dependent = Y1,
exog = sm.add_constant(RegresorasSegundaEtapa),
endog = None, instruments = None).fit()
ResultadosAutomatico = IV2SLS(dependent = Y1, exog = sm.add_constant(X),
endog = Y2, instruments = Z).fit()

Ahora, como todas las instancias de resultados han sido generadas por un modelo de la misma clase, po-
demos emplear la función compare(...) para comparar los resultados obtenidos y contrastar los modelos,
así:

In [22]: print(compare({"MCO": ResultadosMCO,


"2 Etapas Manual": ResultadosManual,
"2 Etapas Automático": ResultadosAutomatico}))

Model Comparison
======================================================================
2 Etapas Automático 2 Etapas Manual MCO
----------------------------------------------------------------------
Dep. Variable lwage lwage lwage
Estimator IV-2SLS OLS OLS
No. Observations 428 428 428
Cov. Est. robust robust robust
R-squared 0.1357 0.0498 0.1568
Adj. R-squared 0.1296 0.0431 0.1509
F-statistic 18.611 17.111 82.671
P-value (F-stat) 0.0003 0.0007 0.0000
================== =========== =========== ===========
const 0.0481 0.0481 -0.5220

79
(0.1124) (0.1071) (-2.6010)
exper 0.0442 0.0442 0.0416
(2.8546) (2.7045) (2.7344)
expersq -0.0009 -0.0009 -0.0008
(-2.1001) (-1.9627) (-1.9402)
educ 0.0614 0.1075
(1.8503) (8.1697)
predicted_educ 0.0614
(1.7553)
==================== ============= ============= =============
Instruments motheduc
fatheduc
----------------------------------------------------------------------

T-stats reported in parentheses

La salida generada por la ejecución del código de la celda anterior nos permite visualizar de forma simultá-
nea los resultados de los diferentes modelos construidos, facilitando la comparación de los mismos. El lector
puede observar que en la parte inferior de la salida se indica T-stats reported in parentheses, lo que
señala que el número reportado entre paréntesis, debajo de cada uno de los coeficientes, corresponde al valor
t; sin embargo, en múltiples ocasiones lo que se reporta con correspondencia a un coeficiente no es el valor t
sino el error estándar. ¿Es posible reportar el error estándar en lugar del valor t? La respuesta es sí: la función
compare(...) posee un parámetro precision, con valor por defecto tstats, que permite especificar el estimador
de precisión (dentro de los disponibles) a incluir en la salida.
Para reportar errores estándar en lugar de valores t, basta con asignar “std_errors” al parámetro precision
de la función compare(...), como se evidencia a continuación:

In [23]: print(compare({"MCO": ResultadosMCO,


"2 Etapas Manual": ResultadosManual,
"2 Etapas Automático": ResultadosAutomatico},
precision = "std_errors"))

Model Comparison
=====================================================================
2 Etapas Automático 2 Etapas Manual MCO
---------------------------------------------------------------------
Dep. Variable lwage lwage lwage
Estimator IV-2SLS OLS OLS
No. Observations 428 428 428
Cov. Est. robust robust robust
R-squared 0.1357 0.0498 0.1568
Adj. R-squared 0.1296 0.0431 0.1509
F-statistic 18.611 17.111 82.671
P-value (F-stat) 0.0003 0.0007 0.0000
================== ========== ========== ==========
const 0.0481 0.0481 -0.5220
(0.4278) (0.4492) (0.2007)

80
exper 0.0442 0.0442 0.0416
(0.0155) (0.0163) (0.0152)
expersq -0.0009 -0.0009 -0.0008
(0.0004) (0.0005) (0.0004)
educ 0.0614 0.1075
(0.0332) (0.0132)
predicted_educ 0.0614
(0.0350)
==================== ============ ============ ============
Instruments motheduc
fatheduc
---------------------------------------------------------------------

Std. Errors reported in parentheses

Nótese que los valores reportados entre paréntesis han cambiado y que en la sección inferior de la salida
aparece Std. Errors reported in parentheses en lugar del T-stats reported in parentheses
del caso previo. De este modo, lo que ahora se reporta en correspondencia a cada coeficiente es su error estándar
y no su valor t.
En este punto finaliza el tema de variables instrumentales y el método de Mínimos Cuadrados en 2 Etapas
en Python. El lector podrá observar que, hasta el momento, solo se ha trabajado con variables dependientes
continuas y que las regresiones llevadas a cabo en los distintos ejemplos son lineales; esto cambiará levemente
en el próximo capítulo, en donde se dará paso a una muy breve exposición de otro tipo de modelos.

81
Capítulo 6

Variable dependiente discreta - Caso binario

En los capítulos previos se ha trabajado con modelos de regresión lineal empleando una variable continua
como variable dependiente; ahora, nos alejaremos un poco de los modelos con tales características, explorando
regresiones no lineales con variables dependientes discretas.
En los primeros modelos que abordaremos, la variable dependiente es una variable categórica, con solo dos
categorías, codificada con los valores 0 y 1. Dicha variable solo toma estos dos valores, los cuales no tienen un
significado por sí mismos (a diferencia del caso de variables continuas), en cuanto su función es de codificadores:
toma el valor de 1 cuando la observación pertenece a un grupo específico y de 0 cuando no pertenece a tal grupo.
En palabras un tanto más simples: la variable toma el valor de 1 cuando se cumple una condición (pertenencia
a una clasificación específica) y de 0 cuando no se cumple dicha condición.
Es posible que el lector no tenga completa claridad sobre las características de las variables que emplea-
remos y que la descripción anterior no le haya resultado del todo satisfactoria; en tal caso, puede que algunos
ejemplos sean de ayuda:

Si contamos con un grupo formado por personas de ambos sexos, en dicho grupo habrá hombres y ha-
brá mujeres; si empleamos una variable ’femenino’ para realizar una clasificación por sexo, esta variable
tomará el valor de 1 cuando la información corresponda a una mujer y de 0 cuando corresponda a un
hombre. ’femenino’ es una variable binaria, pues toma un valor cuando una condición se cumple (1 cuan-
do la condición de sexo femenino se cumple) y toma otro valor cuando la condición no se cumple (0
cuando la condición de sexo femenino no se cumple).

Si tenemos un grupo formado por hombres, es posible que algunos de estos hombres estén casados
mientras que otros no, así, podemos emplear una variable binaria ’casado’ para clasificarlos en el grupo
correspondiente. La variable ’casado’ toma el valor de 1 cuando corresponde a un hombre casado y toma
el valor de 0 cuando corresponde a un hombre no casado.

En un grupo de estudiantes universitarios algunos de estos habrán perdido asignaturas mientras que
otros no. De este modo, podemos emplear una variable binaria que tome el valor de 1 cuando corres-
ponda a un estudiante que ha perdido asignaturas y de 0 cuando corresponda a un estudiante que no ha
perdido asignaturas.

El lector puede observar que la variable binaria toma el valor de 1 cuando se cumple la condición específica
y de 0 en caso contrario; tal asignación de valores no tiene sentido numérico y no afecta la regresión. Tranquila-
mente se puede asignar el valor de 0 cuando la condición se cumple y de 1 en caso contrario; sin embargo, por
convención, y mayor facilidad en la interpretación, se emplea 1 para el cumplimiento de la condición y 0 para
el no cumplimiento de la misma.

82
Hasta este punto el lector tiene una idea general de lo que es una variable binaria, sin embargo, aún no se le
ha informado cómo se empleará dicha variable en la regresión. Es muy importante señalar que en los modelos
que abordaremos a continuación, lo que se estima en realidad es la probabilidad de que la variable dependiente
tome el valor de 1 (al lector interesado en conocer el porqué, se recomienda consultar el Capítulo 15, Modelos
de regresión de respuesta cualitativa, de Gujarati y Porter (2010)); dicha probabilidad está dada por los parámetros
(β) y las variables explicativas (x).
En términos un tanto más técnicos:

Prob[yi = 1| xi ] = Pi = F ( xiT β)
en donde xiT corresponde al vector transpuesto de variables explicativas y β al vector de parámetros. Así,
los coeficientes que se obtienen durante la estimación se asocian al impacto que tiene determinada variable
explicativa sobre la probabilidad de que la variable dependiente tome el valor de 1; en otras palabras, los coefi-
cientes se relacionan (no necesariamente cuantifican de forma directa, debido a la forma funcional adoptada,
como veremos más adelante) a la variación en la probabilidad de que la variable dependiente tome el valor de
1.
Algunos de los modelos empleados para trabajar con variables de respuesta binaria son: 1) Modelo Lineal
de Probabilidad, 2) Modelo Logit y 3) Modelo Probit. Es posible construir y estimar fácilmente cada uno de
estos modelos en Python con la ayuda de librerías especializadas. Tales labores son las que llevaremos a cabo a
continuación, no sin antes realizar una muy mínima exposición de las ideas detrás de cada uno de los modelos
tratados.
El dataset que utilizaremos para desarrollar los distintos modelos será el empleado en el Ejemplo 15.7,
Fumar o no fumar, de Gujarati y Porter (2010). Este conjunto de información se ha obtenido del sitio web eco-
[Link] de SHAZAM Analytics Ltd.

Modelo lineal de probabilidad


El Modelo Lineal de Probabilidad (o MLP) es, recurriendo a una definición no-técnica, básicamente, un mo-
delo de regresión en el que la variable dependiente es una variable binaria y la estimación se lleva a cabo por el
Método de Mínimos Cuadrados Ordinarios.
Siguiendo la notación que hemos empleado a lo largo de la obra, en el MLP se tiene:

Prob[yi = 1] = β 0 + β 1 x1i + ... + β k xki + ui


en donde los estimadores de los β se obtienen por el método de Mínimos Cuadrados Ordinarios.
Dado que se trata de una regresión común y corriente por MCO, la estimación de un MLP en Python no
debe resultar extraña al lector, pues basta con utilizar las mismas funciones empleadas para el modelo de re-
gresión lineal múltiple tratado en el Capítulo 3. De hecho, los modelos de variable de respuesta binaria que se
abordan en este capítulo no deberían representar mayor inconveniente al usuario, en cuanto su construcción
es relativamente parecida a la del modelo de regresión lineal y no se requiere recurrir a librerías distintas a las
que hemos empleado en capítulos previos.

Preparación del entorno


La primera celda en la que el usuario escribirá y ejecutará código tendrá el siguiente contenido:

In [1]: import numpy as np


import pandas as pd

83
import [Link] as sm
import [Link] as sms
import [Link] as plt
El anterior bloque de código permite cargar las herramientas necesarias para llevar a cabo las acciones
requeridas en el análisis econométrico planteado; en caso de que el lector no tenga claridad al respecto, se le
pide consultar la sección “Preparación del entorno” del Capítulo 2, en donde se brinda una explicación sobre
tal cuestión.

Importación de los datos


La importación de los datos se lleva a cabo con funciones de la librería pandas. Dependiendo del tipo de
archivo en el cual se encuentre almacenada la información del dataset, deberemos recurrir a una función parti-
cular y un conjunto de parámetros específico.
El dataset que importaremos proviene de un sitio web, no tiene encabezado y está separado por espacios,
por lo cual debemos especificar cuidadosamente cada uno de los argumentos requeridos. Concretamente, el
siguiente código es el que debe ejecutarse para importar el dataset:
In [2]: data = pd.read_csv("[Link]
header = None,
delim_whitespace = True,
names = ["Fumador", "Edad", "Escolaridad", "Ingreso",
"Pcigs79"])
Al lector que no esté familiarizado con el proceso de importación de los datos, se sugiere dirigirse al Capítulo
2, en donde encontrará una explicación que seguramente aclarará sus dudas. Al lector que sí esté familiarizado
con dicho proceso, se hace notar que, a diferencia de otras ocasiones, se usan argumentos adicionales, requeri-
dos dadas las características del dataset original: el parámetro header permite especificar si se incluye o no un
encabezado; el parámetro delim_whitespace permite indicar si el delimitador es espacio en blanco (es un pará-
metro booleano); el parámetro names permite asignar los nombres a las columnas del DataFrame por medio
de una lista Python “[...]”.
Una vez ejecutada la línea de código de esta celda, el dataset debería haber sido importado correctamente.
En caso contrario, si no puede importarse el dataset directamente desde el sitio web, es posible cargarlo desde
el computador (como hemos hecho en todos los capítulos previos).
El código a emplear, en caso de que el proceso de importación desde el sitio web falle, es el siguiente:
In [3]: data = pd.read_csv("[Link]",
index_col = 0)
Habiendo ejecutado cualquiera de las celdas de código anteriores, la información del dataset a emplear de-
bería haberse importado correctamente. Para comprobarlo, recurrimos al método .head(), aplicado al objeto
que contiene el dataset (en este caso le hemos asignado el nombre data):
In [3]: [Link]()
Out[3]: Fumador Edad Escolaridad Ingreso Pcigs79
0 0 21 12.0 8500 60.6
1 1 28 15.0 12500 60.6
2 0 67 10.0 12500 60.6
3 0 20 12.0 12500 60.6
4 1 32 12.0 20000 60.6

84
Podemos observar que el proceso de importación ha sido exitoso y la información del dataset ha sido alma-
cenada correctamente en un DataFrame de pandas. Ahora, podemos continuar tranquilamente con el desarrollo
de nuestro análisis, en cuanto hemos concluido satisfactoriamente el primer paso.

Preparación de los datos


Antes de asignar las variables a los objetos correspondientes, se examinará la estadística descriptiva, para
lo que basta con emplear el método .describe():
In [4]: [Link]()
Out[4]: Fumador Edad Escolaridad Ingreso Pcigs79
count 1196.000000 1196.000000 1196.000000 1196.000000 1196.000000
mean 0.380435 41.806856 12.221154 19304.765886 60.984950
std 0.485697 17.056941 3.275847 9083.511331 4.848666
min 0.000000 17.000000 0.000000 500.000000 46.300000
25 % 0.000000 27.000000 10.000000 12500.000000 59.225000
50 % 0.000000 39.000000 12.000000 20000.000000 62.100000
75 % 1.000000 56.000000 13.500000 30000.000000 63.800000
max 1.000000 88.000000 18.000000 30000.000000 69.800000
Centre su atención en la columna correspondiente a la variable dependiente (’Fumador’). Esta variable es
binaria y solo toma el valor de 0 o el valor de 1; aunque su promedio es 0.38, no existe ninguna observación
para la cual el valor de ’Fumador’ sea 0.38, de hecho, no existe ninguna observación para la que el valor de esta
variable sea distinto de 0 o de 1. A pesar de esto, el promedio de una variable binaria ofrece información valiosa:
señala cierta idea de la manera en que están distribuidas las categorías.
Como solo existen ceros y unos, el promedio de este tipo de variable es igual a la suma de los unos dividida
por el número total de observaciones (n); por lo tanto, el promedio de una variable binaria corresponde al
porcentaje de observaciones que cumplen con la condición específica. Para este ejemplo en concreto, que el
promedio de ’Fumador’ sea 0.38 significa que el 38 % de los individuos examinados son fumadores y, por ende,
el 62 % restante no lo son. Para tener mayor claridad sobre esta distribución de clases puede que un breve
examen visual resulte apropiado.
Dado que la dependiente es una variable discreta con solo dos categorías, todos los datos de dicha variable
estarán agrupados en únicamente dos puntos (0 y 1) y, de acuerdo a la estadística descriptiva, la cantidad de
datos que corresponde a 1 será un poco más de la mitad de la que corresponde a 0, por lo que la barra de 1 será
algo más alta que 1/2 de la barra correspondiente a 0. Examinemos esto por medio de un histograma, usando
la función subplots(...) de [Link] y el método .hist():
In [5]: fig, ax = [Link]()
[Link]("Distribución de 'Fumador'", size = 17,
fontweight = "bold")
[Link](data["Fumador"], color = "darkblue")
[Link](.9,-.02,
"Elaboración:",
fontsize = 12, fontweight = "bold",
ha = "right")
[Link](.9,-.1,
"Triana, F.\n(2019)",
fontsize = 11, ha = "right")
[Link]()

85
El lector que haya seguido esta obra hasta este punto habrá notado que en los capítulos previos hemos
almacenado diversas variables en objetos específicos. Esto lo hemos hecho para agrupar dichas variables de
acuerdo a su rol particular en la regresión, tratando de simplificar el código a utilizar posteriormente. Ahora,
para los distintos modelos que abordaremos en este capítulo se utilizarán las mismas variables, agrupadas del
mismo modo para todos. Procederemos a realizar la agrupación por medio de los corchetes “[ ]” y las listas
Python “[...]”, tal y como hemos hecho en capítulos previos:

In [6]: Y = data["Fumador"]
X = data[["Edad", "Escolaridad", "Ingreso", "Pcigs79"]]

El primer modelo a examinar es el Modelo Lineal de Probabilidad, el cual es, básicamente, un modelo de
regresión lineal con variable dependiente binaria y estimación por el método de Mínimos Cuadrados Ordinarios.
La regresión lineal y el método MCO se trataron en el Capítulo 3 de esta obra, por lo que a quien no tenga claridad
al respecto se le sugiere consultar dicho capítulo.
Para la construcción del modelo y estimación de los coeficientes se emplean (como lo hemos hecho en oca-
siones previas) la función OLS(...) de Statsmodels y el método .fit(); para la visualización de los resultados,
la función print(...) y el método .summary(). El código a ejecutar es el siguiente:

In [7]: MLP = [Link](Y, sm.add_constant(X))


ResultadosMLP = [Link]()
print([Link]())

86
OLS Regression Results
==============================================================================
Dep. Variable: Fumador R-squared: 0.039
Model: OLS Adj. R-squared: 0.036
Method: Least Squares F-statistic: 12.01
Date: xxx, xx xxx xxxx Prob (F-statistic): 1.43e-09
Time: xx:xx:xx Log-Likelihood: -809.19
No. Observations: 1196 AIC: 1628.
Df Residuals: 1191 BIC: 1654.
Df Model: 4
Covariance Type: nonrobust
===============================================================================
coef std err t P>|t| [0.025 0.975]
-------------------------------------------------------------------------------
const 1.1231 0.188 5.963 0.000 0.754 1.493
Edad -0.0047 0.001 -5.701 0.000 -0.006 -0.003
Escolaridad -0.0206 0.005 -4.465 0.000 -0.030 -0.012
Ingreso 1.026e-06 1.63e-06 0.629 0.530 -2.18e-06 4.23e-06
Pcigs79 -0.0051 0.003 -1.799 0.072 -0.011 0.000
==============================================================================
Omnibus: 37.223 Durbin-Watson: 1.944
Prob(Omnibus): 0.000 Jarque-Bera (JB): 173.523
Skew: 0.449 Prob(JB): 2.09e-38
Kurtosis: 1.364 Cond. No. 2.91e+05
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
specified.
[2] The condition number is large, 2.91e+05. This might indicate that there are
strong multicollinearity or other numerical problems.

Uno de los aspectos más importantes a considerar en los modelos de variable dependiente binaria es el
efecto marginal de cada una de las variables explicativas; tal efecto corresponde a la variación en la probabili-
dad (de que la variable dependiente tome el valor de 1) respecto a la variación en una variable explicativa. En
∂F ( x T β)
términos un poco más técnicos, el efecto marginal de una variable x j corresponde al valor de ∂xiji , en donde
x j es una variable que hace parte del vector x.
Así, para el Modelo Lineal de Probabilidad, los efectos marginales no son más que los coeficientes, pues:

F ( xiT β) = β 0 + β 1 x1i + ... + β k xki

∂F ( xiT β)
= βj
∂x ji
Por lo tanto, para conocer los efectos marginales del Modelo Lineal de Probabilidad en Python, basta con
conocer los coeficientes asociados a las variables. Estos coeficientes corresponden a uno de los atributos (que
pueden consultarse con la función dir(...)) de la instancia de resultados, y se identifica como .params.

87
Podemos asignar la aplicación de este atributo a un objeto específico y visualizar su contenido utilizando la
función print(...). Así:

In [8]: MLPMargEff = [Link]


print("Efectos Marginales MLP\n", MLPMargEff)

Efectos Marginales MLP


const 1.123089
Edad -0.004726
Escolaridad -0.020613
Ingreso 0.000001
Pcigs79 -0.005132
dtype: float64

Así, observamos, por ejemplo, que un incremento de un año en la edad está asociado, en promedio, a una
disminución de 0.47 puntos porcentuales en la probabilidad de ser fumador, y que un aumento de 1 año en la
escolaridad está asociado, en promedio, a una disminución de 2 puntos porcentuales en la probabilidad de ser
fumador.
En realidad, para nuestros propósitos, no hay más aspectos a tratar con detenimiento en el Modelo Lineal
de Probabilidad; basta con conocer su proceso de construcción y estimación y la obtención de los efectos mar-
ginales. Ahora, tal y como se señaló a inicios del capítulo, nos alejaremos de los modelos de regresión lineal y
exploraremos otras posibilidades: la primera, el Modelo Logit.

Modelo Logit
El Modelo Lineal de Probabilidad, aunque muy simple, no tiende a ser ampliamente usado en la práctica;
la razón para tal situación se encuentra, principalmente, en el hecho de que este modelo puede llevar a la
obtención de probabilidades carentes de sentido (por ejemplo, mayores a 1 o que violan el axioma de que la
probabilidad no puede ser negativa).
Aunque no se mencionó en la sección anterior, el MLP puede resultar muy problemático, en cuanto puede
generar probabilidades menores a 0 o mayores a 1, lo que le resta atractivo y conveniencia. Para superar las
deficiencias del Modelo Lineal de Probabilidad se recurre a modelos de regresión no lineales, de los cuales los
más populares son el Logit y el Probit.
En el Modelo Logit, la probabilidad de que la variable dependiente tome el valor de 1 se expresa como una
función de la siguiente forma:

1
Pi = F xiT β =

1 + e−( β0 + β1 x1i +...+ β k xki )
Alternativamente, tomando Zi = β 0 + β 1 x1i + ... + β k xki , la función puede expresarse de la siguiente
manera:

1
Pi = F ( Zi ) =
1 + e−Zi
e Zi 1
Otro modo de escribir la expresión anterior es Pi = 1+e Zi
, por lo que 1 − Pi = 1+e Zi
. Así, se tiene que:

Pi
= e Zi
1 − Pi

88
Aplicando el logaritmo a la anterior expresión se obtiene:
 
Pi
L = ln = Zi = β 0 + β 1 x1i + ... + β k xki
1 − Pi
L se denomina logit y corresponde al logaritmo de la razón de las probabilidades. Así, “mientras el MLP
supone que Pi está linealmente relacionado con xi , el modelo logit supone que el logaritmo de la razón de
probabilidades está relacionado linealmente con xi ” (Gujarati y Porter, 2010, p. 555).
La estimación de los parámetros (β) en el Modelo Logit es llevada a cabo por medio del método de Máxima
Verosimilitud (Maximum Likelihood) o MV. Al lector interesado en conocer con mayor detalle el Modelo Logit
y su proceso de estimación, se sugiere consultar el Capítulo 15, Modelos de regresión de respuesta cualitativa, de
Gujarati y Porter (2010).
Uno de los puntos a considerar en el Modelo Logit es que la interpretación de los coeficientes no resulta de
mayor utilidad, en cuanto estos cuantifican el impacto que tiene el cambio en una variable explicativa sobre el
logaritmo de la razón de probabilidades, algo que, a primera vista, no tiene gran valor ilustrativo. Debido a esto, se
recurre a los efectos marginales para cuantificar el impacto de las variables explicativas.
Como resulta evidente, el efecto marginal de una variable explicativa en un Modelo Logit, a diferencia del
caso del Modelo Lineal de Probabilidad, no corresponde al coeficiente asociado a dicha variable, sino que está
dado por:

∂F xiT β

e Zi
= βj 2
∂x ji (1 + e Zi )
Así, se observa que el efecto marginal de una variable explicativa depende del conjunto de variables expli-
cativas y no está dado directamente por el coeficiente asociado a ésta. Asimismo, es importante notar que en la
expresión anterior se ha hallado el efecto marginal de la variable x j para la observación i, pero debe tenerse en
consideración que el dataset contiene información de múltiples observaciones y los valores de las variables son
diferentes entre dichas observaciones, por lo que no existe un único valor para el efecto marginal de la variable
x j sino n (número de observaciones) valores para éste.
Dado que no resulta muy práctico conocer la magnitud del efecto marginal de una variable para cada una de
las observaciones, es necesario contar con una medida “general” del efecto marginal de determinada variable,
pero ¿cómo se obtiene dicha medida “general”? Una respuesta posible a este interrogante es: existen varios
“caminos”. A continuación, se presentan los más populares.
El primer camino (conocido como efectos marginales en la media) para obtener una medida “general” del
efecto marginal de una variable es utilizar la observación promedio para calcularlo; es decir, emplear el promedio
de cada variable como valor de la variable correspondiente dentro de Z.
Definiendo el vector que contiene los valores promedio de las variables explicativas como x̄, y teniendo en
cuenta que, consecuentemente, x̄ T β = Z̄, el efecto marginal de la variable x j está dado por:

∂P e Z̄
= βj 2
∂x j 1 + e Z̄
Así, el efecto marginal “general” de determinada variable corresponde al efecto marginal de dicha variable
calculado con los valores correspondientes a la observación promedio.
El otro camino (conocido como efectos marginales promedio) para obtener una medida “general” del efecto
marginal de una variable es obtener el promedio de los efectos marginales individuales correspondientes a dicha
variable; es decir, calcular el efecto marginal de la variable x j para cada una de las observaciones i (en total se
tienen n observaciones), y hallar el promedio de dichos efectos marginales. Concretamente, el efecto marginal
de la variable x j viene dado por:

89
∂F xiT β e Zi
∑in=1


∂P ∑in=1 ∂x ji 1+e Zi
2
= = βj
∂x j n n
Así, el efecto marginal “general” de determinada variable corresponde al promedio de los efectos marginales
individuales para dicha variable.
Hasta este punto se ha dado una descripción, muy breve, de la idea general sobre la que trata el Modelo
Logit; ahora, es momento de aplicar a la práctica los conocimientos adquiridos, llevando a cabo la construcción
y estimación de un modelo logit en Python.

Modelo Logit en Python


El dataset que se empleará para la construcción y estimación del Modelo Logit ha sido importado previa-
mente, en la sección de este capítulo que aborda el Modelo Lineal de Probabilidad; asimismo, la preparación
del entorno y de los datos se ha llevado a cabo exitosamente con anterioridad. Dado que la construcción y esti-
mación de modelos logit se consigue con funciones de Statsmodels y no requieren de instrumentos provenientes
de librerías distintas a las ya importadas, se cuenta con todas las herramientas para poder trabajar adecuada-
mente. Al usuario que no haya seguido el ejercicio correspondiente a la sección del MLP, se invita a ejecutar las
celdas de código #1, #2 y #6, las cuales son indispensables para la labor que ahora se pretende desarrollar.
Para construir un modelo logit en Python se emplea la función Logit(...) de Statsmodels; sus argumentos
incluyen la variable dependiente, las variables explicativas y el parámetro missing (con valor por defecto None)
que permite indicar la acción a seguir en caso de que existan valores faltantes.
Para la estimación se usa, al igual que en los modelos que hemos tratado en capítulos previos, el método
.fit(); para la visualización del resumen de resultados el método .summary() (aplicado sobre la instancia
de resultados) y la función print(...) (tomando como argumento la aplicación del método .summary()).
El código correspondiente a nuestro caso es:

In [9]: ModeloLogit = [Link](Y, sm.add_constant(X))


ResultadosLogit = [Link]()
print([Link]())

Optimization terminated successfully.


Current function value: 0.644516
Iterations 5
Logit Regression Results
==============================================================================
Dep. Variable: Fumador No. Observations: 1196
Model: Logit Df Residuals: 1191
Method: MLE Df Model: 4
Date: xxx, xx xxx xxxx Pseudo R-squ.: 0.02975
Time: xx:xx:xx Log-Likelihood: -770.84
converged: True LL-Null: -794.47
LLR p-value: 1.341e-09
===============================================================================
coef std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------
const 2.7451 0.829 3.311 0.001 1.120 4.370
Edad -0.0209 0.004 -5.577 0.000 -0.028 -0.014

90
Escolaridad -0.0910 0.021 -4.402 0.000 -0.131 -0.050
Ingreso 4.72e-06 7.17e-06 0.658 0.510 -9.33e-06 1.88e-05
Pcigs79 -0.0223 0.012 -1.789 0.074 -0.047 0.002
===============================================================================

El lector debe recordar que la interpretación directa de los coeficientes en el Modelo Logit no resulta par-
ticularmente útil a primera vista, en cuanto estos coeficientes cuantifican el impacto de determinada variable
sobre el logaritmo de la razón de probabilidades y no directamente sobre la probabilidad de que la variable depen-
diente tome el valor de 1.
Para conocer el impacto de una variable específica en un modelo logit, tal y como se señaló a ini-
cio de este capítulo, se recurre a los efectos marginales. Estos efectos se obtienen por medio del método
.get_margeff(), aplicado a la instancia de resultados del modelo. El lector debe tener en cuenta que pa-
ra obtener una medida “general” del efecto marginal de una variable específica se señaló la existencia de dos
caminos (que no son los únicos): 1) obtener el efecto marginal usando la observación promedio y 2) obtener el
promedio de los efectos marginales individuales.
Para obtener los efectos marginales utilizando el primer “camino”, basta con asignar el valor ’mean’ al
parámetro at del método .get_margeff(); para obtenerlos por el segundo “camino”, basta con asignarle el
valor de ’overall’. El parámetro at del método .get_margeff() corresponde a ’overall’ por defecto, por lo
que los efectos marginales promedio son los que se obtienen en caso de que no se especifique el valor de at.
Los efectos marginales promedio (segundo camino) son:
In [10]: LogitMargEff = ResultadosLogit.get_margeff()
print([Link]())
Logit Marginal Effects
=====================================
Dep. Variable: Fumador
Method: dydx
At: overall
===============================================================================
dy/dx std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------
Edad -0.0047 0.001 -5.864 0.000 -0.006 -0.003
Escolaridad -0.0206 0.005 -4.539 0.000 -0.030 -0.012
Ingreso 1.069e-06 1.62e-06 0.659 0.510 -2.11e-06 4.25e-06
Pcigs79 -0.0051 0.003 -1.798 0.072 -0.011 0.000
===============================================================================

Esta tabla, además del valor específico de los efectos marginales, también presenta los intervalos de con-
fianza correspondientes e indica qué método es empleado para calcularlos (At:). Así, observamos, por ejemplo,
que un incremento de un año en la edad está asociado, en promedio, a una disminución de 0.47 puntos por-
centuales en la probabilidad de ser fumador y que un aumento de 1 año en la escolaridad está asociado, en
promedio, a una disminución de 2 puntos porcentuales en la probabilidad de ser fumador.
Para los efectos marginales en la media (primer camino), tan solo se necesita asignar ’mean’ al parámetro
at. El código es el siguiente:
In [11]: LogitMargEff = ResultadosLogit.get_margeff(at = "mean")
print([Link]())

91
Logit Marginal Effects
=====================================
Dep. Variable: Fumador
Method: dydx
At: mean
===============================================================================
dy/dx std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------
Edad -0.0049 0.001 -5.598 0.000 -0.007 -0.003
Escolaridad -0.0213 0.005 -4.411 0.000 -0.031 -0.012
Ingreso 1.107e-06 1.68e-06 0.658 0.510 -2.19e-06 4.4e-06
Pcigs79 -0.0052 0.003 -1.790 0.073 -0.011 0.000
===============================================================================

Así, observamos, por ejemplo, que un incremento de un año en la edad está asociado, en promedio, a una
disminución de 0.49 puntos porcentuales en la probabilidad de ser fumador y que un aumento de 1 año en la
escolaridad está asociado, en promedio, a una disminución de 2.13 puntos porcentuales en la probabilidad de
ser fumador.

Modelo Probit
Al igual que el Modelo Logit, una alternativa popular al MLP es el Modelo Probit. En este modelo, la pro-
babilidad de que la variable dependiente tome el valor de 1 viene dada por lo siguiente:
Z xT
i
xiT β =Φ xiT β
 
Pi = F = φ(z)dz
−∞

Así, F xiT β corresponde a la Función de Distribución Acumulada de la Distribución Normal Estándar. Al




igual que con el Modelo Logit, las probabilidades obtenidas no son inferiores a 0 o superiores a 1 y el método de
estimación de los parámetros es el de Máxima Verosimilitud (MV). Al lector interesado en conocer con mayor
detalle el Modelo Probit y su proceso de estimación, se le recomienda consultar el Capítulo 15, Modelos de
regresión de respuesta cualitativa, de Gujarati y Porter (2010).
Como en el caso del Modelo Logit, la interpretación directa de los coeficientes asociados a las variables
explicativas no resulta de gran utilidad y los efectos marginales correspondientes difieren de estos. Para el
Modelo Probit, el efecto marginal de la variable x j viene dado por:

∂F xiT β

= φ xiT β β j

x ji
en donde φ(.) es la Función de Densidad de Probabilidad de la Distribución Normal Estándar. Como puede
observarse, los efectos marginales en el Modelo Probit, al igual que en el Modelo Logit, no están dados directa-
mente por el coeficiente asociado a la variable, sino que dependen de los valores de las variables explicativas.
Así, en un dataset con n observaciones se obtiene n valores para el efecto marginal de la misma variable; para
obtener una medida “general”, pueden seguirse los dos caminos previamente señalados en el caso del Modelo
Logit.
En el primer camino se emplean los valores promedio de las variables con el propósito de hallar un único
efecto marginal para determinada variable. Definiendo, nuevamente, el vector que contiene los valores prome-
dio de las variables explicativas como x̄, el efecto marginal de la variable x j está dado por:

92
∂P
= φ x̄iT β

∂x j
Así, el efecto marginal ’general’ de determinada variable corresponde al efecto marginal de dicha variable
calculado con los valores correspondientes a la observación promedio.
En el segundo camino, se calculan los n efectos marginales correspondientes a la misma variable y simple-
mente se obtiene su promedio. Concretamente:

∑in=1 φ xiT β

∂P
= βj
∂x j n
Así, el efecto marginal “general” de determinada variable corresponde al promedio de los efectos marginales
individuales para dicha variable.
Hasta este punto se ha dado una descripción muy breve de la idea general sobre la que trata el Modelo
Probit; ahora, es momento de aplicar a la práctica los conocimientos adquiridos, llevando a cabo la construcción
y estimación de un modelo probit en Python.

Modelo Probit en Python


El dataset que se empleará para la construcción y estimación del Modelo Probit es el mismo utilizado para
el MLP y el Modelo Logit. Dado que la construcción y estimación de modelos probit se consigue con funciones
de Statsmodels y no requieren de instrumentos provenientes de librerías distintas a las ya importadas, se cuenta
con todas las herramientas para poder trabajar adecuadamente. Al usuario que no haya seguido el ejercicio
correspondiente a la sección del MLP o a la sección del Modelo Logit, se le invita a ejecutar las celdas de código
#1, #2 y #6, las cuales son indispensables para la labor que ahora se pretende desarrollar.
Para construir un modelo probit en Python se emplea la función Probit(...) de Statsmodels; sus argu-
mentos incluyen la variable dependiente, las variables explicativas y el parámetro missing (con valor por defecto
’none’) que permite indicar la acción a seguir en caso de que existan valores faltantes.
Para la estimación se usa, al igual que en los modelos que hemos tratado en capítulos previos, el método
.fit(); para la visualización de resultados, el método .summary() (aplicado sobre la instancia de resulta-
dos) y la función print(...) (tomando como argumento la aplicación del método .summary()). El código
correspondiente a nuestro caso es:

In [12]: ModeloProbit = [Link](Y, sm.add_constant(X))


ResultadosProbit = [Link]()
print([Link]())

Optimization terminated successfully.


Current function value: 0.644304
Iterations 5
Probit Regression Results
==============================================================================
Dep. Variable: Fumador No. Observations: 1196
Model: Probit Df Residuals: 1191
Method: MLE Df Model: 4
Date: xxx, xx xxx xxxx Pseudo R-squ.: 0.03007
Time: xx:xx:xx Log-Likelihood: -770.59
converged: True LL-Null: -794.47
LLR p-value: 1.052e-09

93
===============================================================================
coef std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------
const 1.7019 0.511 3.333 0.001 0.701 2.703
Edad -0.0130 0.002 -5.655 0.000 -0.017 -0.008
Escolaridad -0.0562 0.013 -4.450 0.000 -0.081 -0.031
Ingreso 2.72e-06 4.4e-06 0.619 0.536 -5.9e-06 1.13e-05
Pcigs79 -0.0138 0.008 -1.792 0.073 -0.029 0.001
===============================================================================

El lector debe recordar que la interpretación directa de los coeficientes en el Modelo Probit no resulta
particularmente útil a primera vista y que para conocer el impacto de una variable específica en un modelo
probit, tal y como se señaló a inicio de este capítulo, se recurre a los efectos marginales.
Los efectos marginales se obtienen por medio del método .get_margeff(), aplicado a la instancia de re-
sultados del modelo. El lector debe tener en cuenta que para obtener una medida “general” del efecto marginal
de una variable específica se señaló la existencia de dos caminos (que no son los únicos): 1) obtener el efecto
marginal usando la observación promedio y 2) obtener el promedio de los efectos marginales individuales.
Para obtener los efectos marginales utilizando el primer “camino” basta con asignar el valor ’mean’ al pará-
metro at del método .get_margeff(); para obtenerlos por el segundo “camino”, basta con asignarle el valor
de ’overall’. El parámetro at del método .get_margeff() corresponde a ’overall’ por defecto, por lo que los
efectos marginales promedio son los que se obtienen en caso de que no se especifique el valor de at.
Los efectos marginales promedio (segundo camino) son:

In [13]: ProbitMargEff = ResultadosProbit.get_margeff()


print([Link]())

Probit Marginal Effects


=====================================
Dep. Variable: Fumador
Method: dydx
At: overall
===============================================================================
dy/dx std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------
Edad -0.0048 0.001 -5.899 0.000 -0.006 -0.003
Escolaridad -0.0207 0.005 -4.565 0.000 -0.030 -0.012
Ingreso 1.002e-06 1.62e-06 0.619 0.536 -2.17e-06 4.18e-06
Pcigs79 -0.0051 0.003 -1.800 0.072 -0.011 0.000
===============================================================================

La información que se presenta en esta tabla es la misma que se obtiene en el caso del modelo Logit. Así, ob-
servamos, por ejemplo, que un incremento de un año en la edad está asociado, en promedio, a una disminución
de 0.48 puntos porcentuales en la probabilidad de ser fumador y que un aumento de 1 año en la escolaridad
está asociado, en promedio, a una disminución de 2 puntos porcentuales en la probabilidad de ser fumador.
Para los efectos marginales en la media (primer camino) solo es necesario modificar el valor del parámetro
at; el código es el siguiente:

94
In [14]: ProbitMargEff = ResultadosProbit.get_margeff(at = "mean")
print([Link]())

Probit Marginal Effects


=====================================
Dep. Variable: Fumador
Method: dydx
At: mean
===============================================================================
dy/dx std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------
Edad -0.0049 0.001 -5.666 0.000 -0.007 -0.003
Escolaridad -0.0213 0.005 -4.455 0.000 -0.031 -0.012
Ingreso 1.032e-06 1.67e-06 0.619 0.536 -2.24e-06 4.3e-06
Pcigs79 -0.0052 0.003 -1.792 0.073 -0.011 0.000
===============================================================================

Empleando este método, observamos, por ejemplo, que un incremento de un año en la edad está asociado,
en promedio, a una disminución de 0.49 puntos porcentuales en la probabilidad de ser fumador y que un au-
mento de 1 año en la escolaridad está asociado, en promedio, a una disminución de 2.13 puntos porcentuales
en la probabilidad de ser fumador.

Comparación de modelos
Resulta útil poder comparar los resultados de los distintos modelos tratados teniendo a disposición su
información en un único espacio; es decir, contando con una tabla de resumen en la que se presente simultá-
neamente la información básica de la instancia de resultados correspondiente a cada modelo.
Es posible comparar resultados de modelos creados con funciones de Statsmodels empleando la función
summary_col(...) del módulo iolib.summary2 de Statsmodels. Esta función permite resumir múltiples
instancias de resultados presentándolas una al lado de la otra; algunos de sus argumentos son la lista de ins-
tancias de resultados a comparar (results), la lista de nombres a asignar al resumen de cada instancia (model_na-
mes) y el parámetro stars (de tipo booleano), que indica si se quiere o no visualizar la significancia por medio de
asteriscos (*).
El primer paso a seguir para poder comparar las instancias de resultados de los modelos es importar la fun-
ción que permite llevar a cabo tal tarea. La línea de importación de la función summary_col(...) de Statsmo-
[Link].summary2 es la siguiente:

In [15]: from [Link].summary2 import summary_col

Ahora, teniendo a disposición la función requerida, basta con especificar los argumentos correspondientes
y ejecutar el código para obtener la tabla de resumen (que denominaremos MiTabla):

In [16]: MiTabla = summary_col(results = [ResultadosMLP, ResultadosLogit,


ResultadosProbit],
stars = True,
model_names = ["Modelo MLP", "Modelo Logit",
"Modelo Probit"])
print(MiTabla)

95
=================================================
Modelo MLP Modelo Logit Modelo Probit
-------------------------------------------------
const 1.1231*** 2.7451*** 1.7019***
(0.1884) (0.8292) (0.5106)
Edad -0.0047*** -0.0209*** -0.0130***
(0.0008) (0.0037) (0.0023)
Escolaridad -0.0206*** -0.0910*** -0.0562***
(0.0046) (0.0207) (0.0126)
Ingreso 0.0000 0.0000 0.0000
(0.0000) (0.0000) (0.0000)
Pcigs79 -0.0051* -0.0223* -0.0138*
(0.0029) (0.0125) (0.0077)
=================================================
Standard errors in parentheses.
* p<.1, ** p<.05, ***p<.01

El lector debe recordar que la interpretación directa de los coeficientes de los modelos Logit y Probit no
resulta particularmente útil y que no es posible comparar su magnitud con la de los correspondientes al Modelo
Lineal de Probabilidad, en tanto cuantifican relaciones distintas. Debido a esto, se ha recurrido a los efectos
marginales para poder obtener una medida comparable, entre modelos, del efecto de una variable explicativa
sobre la probabilidad de que la variable dependiente tome el valor de 1. La tabla de resumen que hemos apenas
creado presenta los coeficientes de cada modelo y no los efectos marginales, ¿esto quiere decir que no es posible
comparar la información presentada?
Aunque no es posible comparar los coeficientes de los modelos MLP, Logit y Probit directamente y, por
tanto, utilizamos los efectos marginales, el lector debe tener en cuenta que existe una relación aproximada
entre las magnitudes de dichos coeficientes.
Siguiendo a Katchova (2013), los coeficientes de los modelos MLP, Logit y Probit se caracterizan por las
siguientes relaciones:

β Logit ' 4β MLP

β Probit ' 2,5β MLP

β Logit ' 1,6β Probit


Así, basta con aplicar las transformaciones correspondientes a los coeficientes de los modelos Logit y Probit
para obtener medidas directamente comparables con los coeficientes del Modelo Lineal de Probabilidad; sin
embargo, debe resaltarse que dichas transformaciones se basan en relaciones aproximadas. Por tanto, si se quiere
comparar los modelos, lo más indicado es basar tal comparación en los efectos marginales.
Statsmodels no ofrece una función o método (a la fecha de publicación de esta obra y al conocimiento del
autor) que permita comparar los efectos marginales de varios modelos; sin embargo, esta situación no debe des-
alentar al usuario, en cuanto aún es posible obtener una tabla de resumen que presente los efectos marginales.
Ahora, algo muy importante que debe tener en cuenta es que la labor asociada a la consecución de este objetivo
puede resultar un tanto tediosa (o incluso complicada para el novato) y debe desarrollarse cuidadosamente.

96
El objetivo que pretendemos es obtener una tabla de resumen en la que se presenten los efectos marginales
correspondientes al Modelo Lineal de Probabilidad, al Modelo Logit y al Modelo Probit, por lo que los datos
indispensables que necesitamos son los valores de tales efectos marginales. Lo primero que haremos es crear
DataFrames de pandas para almacenar los datos correspondientes.
La información de los efectos marginales del Modelo Logit y del Modelo Probit se ha almacenado con an-
terioridad en objetos específicos; ahora, extraeremos únicamente las magnitudes de dichos efectos marginales
(con el atributo .margeff, aplicado al objeto que contiene la información de los efectos marginales respecti-
vos) y las emplearemos como valores del parámetro data en la función DataFrame(...) de pandas. Asimismo,
para el parámetro index haremos uso de una lista Python “[...]” que contenga los nombres de las variables co-
rrespondientes y asignaremos al parámetro columns el nombre del respectivo modelo. El procedimiento es el
mismo para los modelos Logit y Probit, como se evidencia a continuación:

In [17]: Coeficientes = ["Edad", "Escolaridad", "Ingreso", "Pcigs79"]


EfectosLogit = [Link]([Link], index = Coeficientes,
columns = ["Logit"])
EfectosProbit = [Link]([Link], index = Coeficientes,
columns = ["Probit"])

Ya contamos con los datos de los efectos marginales de los modelos Logit y Probit, pero, ¿qué hay de los
efectos marginales del MLP? El lector debe recordar que los efectos marginales del Modelo Lineal de Probabi-
lidad corresponden directamente a los coeficientes, por lo que, a diferencia de los modelos Logit y Probit, su
obtención se logra con la aplicación del atributo .params. El resultado de la aplicación de este atributo difiere
del que se obtiene con la aplicación del método .get_margeff() de los modelos Logit y Probit, por lo que
el procedimiento para extraer las magnitudes de los efectos marginales (coeficientes) será un tanto distinto al
que hemos desarrollado previamente.
Lo primero que el usuario debe notar es que el atributo .params genera un resultado en el que se incluye
el coeficiente correspondiente al término del intercepto, sin embargo, para los modelos Logit y Probit, como re-
sulta lógico, no se reporta un efecto marginal correspondiente a éste. Por lo tanto, con el propósito de comparar
los efectos marginales de las mismas variables, se ignorará el término del intercepto (β 0 ).
Para realizar la “extracción” de las magnitudes de todos los coeficientes exceptuando el término del inter-
cepto se hace uso de los corchetes “[ ]”, aplicados al objeto que contiene los parámetros del MLP (previamente
lo almacenamos como una Series de pandas). Dentro de los corchetes se empleará la secuencia 1:5; esto,
con el propósito de ignorar el término del intercepto. Adicionalmente, el nombre del modelo se asignará al
parámetro columns.

Nota: Al lector que lo desconozca, se le informa que una Series de pandas indiza (por defecto) sus ob-
servaciones con números enteros desde 0 y que la selección por corchetes “[ ]” con enteros es incluyente
en el primer valor y excluyente en el último, por lo que el empleo de una secuencia 1:5 dentro de cor-
chetes “[ ]” de selección significa “ignorar la primera observación (que está identificada con 0) e incluir
todas las demás hasta la indizada con el número 4 (no incluye la que tiene 5 como índice)”.

El código a ejecutar para el caso del MLP es el siguiente:

In [18]: EfectosMLP = [Link](MLPMargEff[1:5], columns = ["MLP"])

Ya se cuenta con los DataFrame que contienen las magnitudes de los efectos marginales de los modelos
Logit, Probit y MLP. Ahora, es momento de agrupar estos DataFrames en uno solo; para lograrlo, se emplea
la función concat(...) de pandas; en sus argumentos se incluye una lista Python “[...]” que contiene los
DataFrame a concatenar y el parámetro axis con valor de 1, de modo que la concatenación se logre ubicando
un DataFrame al lado de otro:

97
In [19]: [Link]([EfectosMLP, EfectosLogit, EfectosProbit], axis = 1)

Out[19]: MLP Logit Probit


Edad -0.004726 -0.004890 -0.004920
Escolaridad -0.020613 -0.021334 -0.021340
Ingreso 0.000001 0.000001 0.000001
Pcigs79 -0.005132 -0.005234 -0.005235

Ahora se cuenta con una única tabla de resumen en la que se presentan los efectos marginales de los distin-
tos modelos: magnitudes que sí son directamente comparables. Podemos observar que los efectos estimados
no difieren significativamente: por ejemplo, el efecto marginal de la variable Escolaridad es de -2.06 p.p en el
MLP, de -2.13 p.p en el modelo Logit y de -2.13 p.p en el modelo Probit; para la variable Edad el efecto marginal
va desde -0.4726 p.p (MLP) hasta -0.492 p.p (Probit).
En este punto cierra este capítulo, en el cual hemos tratado los modelos con variable dependiente binaria;
ahora, extenderemos un tanto el alcance de los modelos de variable dependiente discreta al incluir regresadas
que no toman únicamente dos valores, es decir, variables no binarias.

98
Capítulo 7

Variable dependiente discreta - Caso no binario

En el capítulo previo se trabajó con modelos en los que la variable dependiente era discreta y, además, solo
podía tomar dos valores; en este capítulo, se dará continuidad al tema de la variable dependiente discreta, pero
se eliminará la restricción de que ésta solo puede tomar dos valores, es decir, abordaremos el tema de variables
discretas no binarias.
Las variables dependientes con las que trabajaremos serán categóricas, pero no estarán limitadas a solamen-
te dos categorías, por lo que no tomarán únicamente dos valores (0 y 1) sino tantos valores como alternativas
tenga dicha variable.
La breve exposición de los fundamentos teóricos del modelo a desarrollar en este capítulo está basada, en
su totalidad, en Katchova (2013, 2), por lo que, en caso de ser necesario, se invita al lector a consultar dicha
fuente para tener mayor claridad al respecto.
Las variables dependientes a emplear son discretas no binarias, por lo que toman valores de un conjunto
finito compuesto por más de dos opciones. Es posible que algunos ejemplos tengan cierto valor para ilustrar la
naturaleza de las mencionadas variables:

Cuando una persona desea consumir una cerveza, tiene a su disposición un conjunto finito y bien defi-
nido de marcas de cerveza que puede adquirir. Estas marcas pueden codificarse por medio de números,
asignando un número distinto a cada una, por lo que la variable resultante corresponde a una variable dis-
creta no binaria (asumiendo que existen más de dos marcas de cerveza a disposición), en cuanto existen
múltiples, pero limitadas, posibilidades.

Cuando alguien asiste a una sala de cine, tiene a su disposición distintos “combos” de comida. Existe un
conjunto bien definido de “combos” y su cantidad es limitada; de este modo, se puede asignar un número
distinto a cada combo, con el propósito de identificarlo. Así, la variable resultante de la codificación de
los “combos” es una variable discreta no binaria (asumiendo que existen más de dos combos).

A una variable discreta con m categorías (o alternativas) se aplica un proceso de codificación empleando
m números; estos números no tienen significado por sí mismos, en cuanto su magnitud no es interpretable y
su función es exclusivamente de codificadores. Asimismo, debe notarse que el orden en el que se asignan los
números es libre, en cuanto estos carecen de significado propio y no tienen naturaleza ordinal, por lo que no
establecen un orden definido para las alternativas de las variables.
La variable tiene múltiples categorías, pero es importante señalar que a determinado individuo (entidad)
solo corresponde una única alternativa; es decir, cada individuo o entidad, de las múltiples alternativas a dis-
posición, puede elegir únicamente una. Retomando los ejemplos anteriores, para el caso de las cervezas, el
individuo solo puede elegir una marca para la cerveza que consumirá en este momento y, para el caso de los
combos de comida, la persona solo debe elegir un único combo.

99
Para este tipo de modelos, la información se presenta en dos formatos específicos: wide y long. En el formato
wide, la información para cada individuo (entidad) se presenta en una única fila, por lo tanto y = j ( j =
1, 2, 3, ..., m); en el formato long, la información para cada individuo (entidad) se presenta en m filas, cada una
de las cuales corresponde a una de las alternativas de la variable dependiente, la cual toma el valor de 1 una
única vez y de 0 m − 1 veces para cada individuo (entidad).
Recurriendo a una descripción un tanto más ilustrativa, a continuación, se presenta la misma información,
en formato wide y en formato long. Retomando el ejemplo de las cervezas, tomando en consideración solo 5
marcas (codificadas de la siguiente manera: Aguila : 1, Poker : 2, [Link] : 3, Corona : 4, Heineken :
5), la siguiente información se presenta en formato wide:

Esta misma información, en formato long, corresponde a la siguiente tabla:

Así, el lector puede apreciar que para el formato wide y = j, mientras que para el formato long:

100
(
1, si y = j
yj =
0, si y 6= j
Algo importante que debe mencionarse es que en los modelos de variable dependiente discreta no binaria,
al igual que en los modelos de variable dependiente discreta binaria, lo que interesa no es el valor de la variable
dependiente como tal sino la probabilidad de que esta tome un valor en específico. Así, en el Modelo Logit,
puesto que la variable dependiente era binaria, solo existían dos posibilidades, por lo que se estimaba la proba-
bilidad de que la variable dependiente tomará el valor de 1, y la probabilidad de que esta tomará el valor de 0
simplemente correspondía a uno menos la probabilidad de que tomará el valor de 1. Sin embargo, en el modelo
Logit Multinomial (que es el que trabajaremos en adelante) no existen únicamente dos alternativas, entonces,
¿qué probabilidad es la que se estima?
El lector debe saber que en el modelo Logit Multinomial lo que se busca es estimar la probabilidad de que se
elija la alternativa j dentro de las m alternativas que tiene la variable dependiente. Para los modelos de variable
dependiente discreta no binaria, la probabilidad de que el individuo i elija la alternativa j está dada por:

Pji = Prob (yi = j) = Fj ( xi γ)


Dependiendo de la forma funcional que adopte Fj , se obtendrá un modelo distinto: Logit Multinomial,
Probit Multinomial, Logit Condicional, Logit Ordenado, etc. En esta obra, únicamente se abordará el Modelo
Logit Multinomial; esto, debido a la disponibilidad de herramientas efectivas y de fácil manejo en las librerías
Python especializadas.
Antes de proceder a la labor práctica en Python, es importante que el lector tenga en consideración los tipos
de variables explicativas que se emplean en los modelos de variable dependiente discreta no binaria, en cuanto
su clasificación resulta relevante; las variables explicativas pueden ser alternative-invariant regressors (también
llamados case-specific regressors) o alternative-variant regressors (también llamados alternative-specific regressors).
Las variables explicativas de tipo alternative-invariant regressors no tienen correspondencia directa con de-
terminada alternativa; los valores de estas variables varían entre individuos (entidades) pero no varían entre
alternativas para el mismo individuo (entidad). Retomando el ejemplo de la cerveza que hemos tratado con an-
terioridad, la variable Ingreso es una variable alternative-invariant en cuanto tiene el mismo valor para el mismo
individuo y no cambia dependiendo de la alternativa elegida; para el Individuo 1, el ingreso sigue siendo COP
2,300,000 sin importar cuál sea la marca de cerveza que elija; para el Individuo 2, el ingreso sigue siendo COP
3,000,000 sin importar cuál sea la marca de cerveza que elija, etc.
Las variables explicativas de tipo alternative-variant regressors tienen correspondencia directa con determi-
nada alternativa, por lo que su valor depende de ésta; los valores de estas variables varían entre alternativas y
también pueden variar entre individuos. Una vez más, recurriendo al ejemplo del consumo de cerveza, el pre-
cio es una variable alternative-variant, en cuanto tiene distintos valores para distintas alternativas; asimismo, se
presenta cierta variación entre individuos. Siendo un tanto más explícitos, el lector puede observar que el pre-
cio de ’Águila’ no es el mismo que el de ’Corona’ o el de ’Póker’; a esto se hace referencia con alternative-variant,
pues, aunque se trata de la misma característica (precio), su valor depende de la alternativa correspondiente.
Un aspecto adicional a considerar es que las variables explicativas tipo alternative-invariant regressors se em-
plean en el Modelo Logit Multinomial y las variables explicativas tipo alternative-variant regressors se utilizan en
el Modelo Logit Condicional y Logit Mixto. En la labor práctica que adelantaremos con Python, tal y como se ha
señalado de forma previa, abordaremos únicamente el Modelo Logit Multinomial, por lo que solo se emplearán
variables explicativas tipo alternative-invariant durante el proceso de construcción y estimación.
Retornando a la descripción del Modelo Logit Multinomial, la forma funcional adoptada es la siguiente:
T
e wi γ j
Pji = Prob (yi = j) = T
∑m
h =1 e
wi γh

101
en donde Pji es la probabilidad de que el individuo (entidad) i elija la alternativa j, wi es el vector de
variables alternative-invariant para el individuo (entidad) i y γ j es el conjunto de coeficientes correspondientes
a la alternativa j. Para lograr la estimación de la probabilidad, uno de los conjuntos de coeficientes se normaliza
a 0, de modo que la interpretación de los demás se realiza con referencia a la categoría base (a la que corresponde
el conjunto de coeficientes 0).
Un punto a destacar es que la magnitud de los coeficientes del Modelo Logit Multinomial, al igual que de
los Logit y Probit, no debe interpretarse directamente; estos coeficientes tan solo indican si aumenta (cuando
el coeficiente es positivo) o disminuye (cuando el coeficiente es negativo) la probabilidad de que se elija la
alternativa j, en comparación con la categoría base, pero no cuantifican la variación.
Para contar con una medida que cuantifique el impacto de determinada variable sobre la probabilidad de
elegir la alternativa j, se recurre a los efectos marginales, los cuales, para el Modelo Logit Multinomial, están
dados por lo siguiente:
T
e wi γ j
Pji = T T T
ewi γalt1 + ewi γalt2 + ... + ewi γaltm
en donde altj hace referencia a la alternativa j (alt1 se refiere a la alternativa 1, alt2 se refiere a la alternativa
2 y así sucesivamente). Ahora, definiendo wiT γalth como zhi , se tiene:

ez ji
Pji =
ez1i + ez2i + ... + ezmi
Por lo tanto, como zhi = wiT γalth = γh0 + γh1 w1i + ... + γhr wri [el primer subíndice (en los coeficien-
tes) hace referencia a la alternativa a la que corresponden los coeficientes (m alternativas), el segundo subín-
dice de los coeficientes (primer subíndice para las variables) hace referencia a la variable alternative-invariant
(r variables alternative-invariant) y el segundo subíndice de las variables hace referencia al individuo (entidad)],
entonces se tiene que:

γ j f ez ji (∑m zhi z ji
∑m zhi

∂Pji h =1 e ) − e h =1 γh f e
=
(∑m zhi 2
h =1 e )
∂w f
γ j f ez ji (∑m zhi ez ji ∑m zhi

∂Pji h =1 e ) h =1 γh f e
= −
(∑m zhi 2 (∑m zhi 2
h =1 e ) h =1 e )
∂w f

ez ji ∑m h =1 γh f e
zhi 

∂Pji
= m z γj f −
∂w f ∑h=1 e hi ∑m h =1 e
zhi
!
∂Pji m
= Pji γ j f − ∑ Phi γh f
∂w f h =1
Debe notarse que los efectos marginales no necesariamente poseen el mismo signo que los coeficientes; por
lo tanto, los coeficientes indican (no miden) si la probabilidad de elegir la alternativa j aumenta o disminuye en
comparación con la categoría base, y los efectos marginales cuantifican la variación en la probabilidad de elegir
determinada alternativa ante la variación en una variable explicativa (alternative-invariant) específica.
Al igual que con los modelos Logit y Probit estudiados en el capítulo anterior, en el Modelo Logit Multino-
mial, para una misma variable se obtienen n efectos marginales distintos (hay n individuos (entidades)). Por lo
tanto, para obtener una medida general del efecto marginal de una variable particular, pueden emplearse los
dos “caminos” expuestos en el Capítulo 6 de esta obra (al lector que no tenga claridad al respecto, se sugiere
consultar dicho capítulo, en cuanto la lógica aplicada al actual es la misma).
Es importante señalar que los efectos marginales de la misma variable deben sumar cero entre las alterna-
tivas, es decir:

102
m ∂Pj
∑ ∂w f =0
j =1

∂P
en donde ∂wjf es el efecto marginal de la variable w f sobre la probabilidad de elegir la alternativa j y exis-
ten m alternativas. Asimismo, es importante traer a colación que sin importar la alternativa que se elija como
categoría base, aunque los coeficientes cambien, los efectos marginales serán los mismos.
Hasta este punto se ha dado una muy breve exposición de los fundamentos teóricos sobre los que se es-
tructura el modelo que ahora pretendemos abordar de manera práctica en Python. Es posible que la estructura
matemática sobre la que se construye el Modelo Logit Multinomial no sea totalmente comprensible para el
lector; sin embargo, puede que la labor práctica le sea de ayuda para tener mayor claridad sobre determinados
aspectos.

Modelo Logit Multinomial en Python


El dataset que se utilizará para la labor práctica en Python es el empleado en Katchova (2013, 3) (los data-
sets usados por la profesora Katchova provienen, en su versión original, de Herriges y King (1999) y Cameron
y Trivedi (2005)). El dataset se ha obtenido del sitio web de Econometrics Academy [Link]
te/econometricsacademy/. Lo que se pretende estudiar es cómo afecta el ingreso (’income’), que es una variable
de tipo alternative-invariant, a la elección del tipo de pesca (’mode’) de los individuos.
Las funciones para trabajar con modelos Logit Multinomial pertenecen a la librería Statsmodels, la cual vie-
ne integrada en la distribución Anaconda; asimismo, las demás librerías que brindan soporte para el análisis
econométrico que se adelantará (pandas y NumPy) también vienen integradas en esta distribución, por lo cual
no se requiere de la instalación de librerías adicionales por parte del usuario.

Preparación del entorno


La primera celda en la que el usuario escribirá y ejecutará código tendrá el siguiente contenido:

In [1]: import numpy as np


import pandas as pd
import [Link] as sm

El anterior bloque de código permite cargar las herramientas necesarias para llevar a cabo las acciones
requeridas en el análisis econométrico planteado; en caso de que el lector no tenga claridad al respecto, se le
pide consultar la sección “Preparación del entorno” del Capítulo 2, en donde se brinda una explicación sobre
tal cuestión.

Importación de los datos


La importación de los datos se lleva a cabo con funciones de la librería pandas. Dependiendo del tipo de
archivo en el cual se encuentre almacenada la información del dataset, deberemos recurrir a una función parti-
cular y un conjunto de parámetros específicos.
Para nuestro caso concreto, el siguiente código es el que importa el dataset:

In [2]: data = pd.read_csv("multinomial_fishing1.csv")

103
Una vez ejecutada la línea de código de esta celda, el dataset debería haber sido importado correctamente.
Para comprobarlo, recurrimos al método .head(), aplicado al objeto que contiene el dataset (en este caso le
hemos asignado el nombre data):

In [3]: [Link]()

Out[3]: mode price catchrate [Link] [Link] [Link] [Link] \


0 charter 182.930 0.5391 0 0 0 1
1 charter 34.534 0.4671 0 0 0 1
2 private 24.334 0.2413 0 0 1 0
3 pier 15.134 0.0789 0 1 0 0
4 private 41.514 0.1082 0 0 1 0

[Link] [Link] [Link] [Link] [Link] \


0 157.930 157.930 157.930 182.930 0.0678
1 15.114 15.114 10.534 34.534 0.1049
2 161.874 161.874 24.334 59.334 0.5333
3 15.134 15.134 55.930 84.930 0.0678
4 106.930 106.930 41.514 71.014 0.0678

[Link] [Link] [Link] income mode1


0 0.0503 0.2601 0.5391 7.083332 4
1 0.0451 0.1574 0.4671 1.250000 4
2 0.4522 0.2413 1.0266 3.750000 3
3 0.0789 0.1643 0.5391 2.083333 2
4 0.0503 0.1082 0.3240 4.583332 3

Podemos observar que el proceso de importación ha sido exitoso y la información del dataset ha sido alma-
cenada correctamente en un DataFrame de pandas. Ahora, podemos continuar tranquilamente con el desarrollo
de nuestro análisis, en cuanto hemos concluido satisfactoriamente el primer paso. Procederemos a construir y
estimar un modelo logit multinomial, el cual toma como variable dependiente una variable discreta no binaria;
por lo tanto, resulta de utilidad conocer las alternativas que contiene dicha variable.
Para conocer las alternativas de la variable dependiente basta con seleccionarla (empleando los corchetes
’[ ]’) y aplicarle el método .unique(), el cual permite conocer los valores únicos que tiene un objeto. El código
a ejecutar es:

In [4]: data["mode"].unique()

Out[4]: array(['charter', 'private', 'pier', 'beach'], dtype=object)

El resultado que obtenemos es un ndarray de NumPy, en el cual se nos informa que los valores únicos
(alternativas) que toma la variable dependiente son ’charter’, ’private’, ’pier’ y ’beach’; por lo tanto, contamos
con cuatro alternativas y, consecuentemente, obtendremos tres sets de coeficientes (el lector ya debería saber
el porqué).
Para nuestro caso es muy sencillo saber el número de alternativas con las que se cuenta, pues basta con
enumerarlas; sin embargo, si la variable tuviera muchas alternativas resultaría tedioso contarlas una a una.
pandas ofrece una solución efectiva a dicho inconveniente: en vez de aplicar el método .unique() se utiliza el
método .nunique(), el cual cuenta el número de valores únicos. Para comprobar que efectivamente se tienen
4 alternativas, se ejecuta el siguiente código:

104
In [5]: data["mode"].nunique()

Out[5]: 4

Preparación de los datos


El lector que haya seguido esta obra hasta este punto habrá notado que en los capítulos previos hemos
almacenado diversas variables en objetos específicos. Esto lo hemos hecho para agrupar dichas variables de
acuerdo a su rol particular en la regresión y tratando de simplificar el código a utilizar posteriormente. Ahora,
en este ejercicio, aunque tan solo se emplearán dos variables, se asignarán las variables a objetos específicos
para simplificar un tanto la sintaxis y facilitar la comprensión del código. Procederemos a realizar la asignación
extrayendo las variables por medio de los corchetes “[ ]”, tal y como hemos hecho en capítulos previos:

In [6]: Y = data["mode"]
X = data["income"]

Construcción y estimación
Para la construcción y estimación del modelo se emplean la función MNLogit(...) de Statsmodels y el
método .fit(); para la visualización de los resultados, la función print(...) y el método .summary(). El
código a ejecutar es el siguiente:

In [7]: ModeloLogitMN = [Link](Y, sm.add_constant(X))


ResultadosLogitMN = [Link]()
print([Link]())

Optimization terminated successfully.


Current function value: 1.249704
Iterations 5
MNLogit Regression Results
==============================================================================
Dep. Variable: mode No. Observations: 1182
Model: MNLogit Df Residuals: 1176
Method: MLE Df Model: 3
Date: xxx, xx xxx xxxx Pseudo R-squ.: 0.01374
Time: xx:xx:xx Log-Likelihood: -1477.2
converged: True LL-Null: -1497.7
LLR p-value: 6.093e-09
================================================================================
mode=charter coef std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
const 1.3413 0.195 6.896 0.000 0.960 1.723
income -0.0316 0.042 -0.756 0.450 -0.114 0.050
--------------------------------------------------------------------------------
mode=pier coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
const 0.8142 0.229 3.561 0.000 0.366 1.262
income -0.1434 0.053 -2.691 0.007 -0.248 -0.039
------------------------------------------------------------------------------

105
mode=private coef std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
const 0.7389 0.197 3.756 0.000 0.353 1.125
income 0.0919 0.041 2.260 0.024 0.012 0.172
================================================================================

Nota: La información con la que trabaja la función MNLogit(...) de Statsmodels debe estar contenida
en un dataset de formato wide. En caso de contar con un formato long, el dataset debe reorganizarse de
modo que, antes de emplear su información en la función MNLogit(...) de Statsmodels, su formato sea
wide.

El lector debe recordar que se mencionó que, para la estimación, el set de coeficientes de una alternativa
se normaliza a cero y que la interpretación de los coeficientes obtenidos se realiza con referencia a la categoría
base, sin embargo, ¿cuál es la categoría base?
Para este ejemplo concreto, la categoría base es la alternativa ’beach’, pero ¿por qué? La respuesta se en-
cuentra en la lógica que aplica la función MNLogit(...) de Statsmodels: cuando se pasa como variable depen-
diente una variable no codificada por números (el lector debe observar que la variable mode no está codificada),
la función ejecuta la codificación automáticamente de forma alfabética y toma como categoría base la alterna-
tiva que ocupa la primera posición en dicho orden. Así, como las alternativas de la variable mode son ’charter’,
’private’, ’beach’ y ’pier’, la categoría base es ’beach’, pues es la que ocupa la primera posición si se organizan
alfabéticamente tales alternativas.
Ahora, la interpretación de los coeficientes se debe realizar con respecto a la alternativa ’beach’. Así, se tiene
que en comparación con pesca en la playa (’beach’), un ingreso más alto está asociado a una menor probabilidad
de pesca en bote alquilado (’charter’) o en el muelle (’pier’) y a una mayor probabilidad de pesca en bote privado
(’private’).
Hemos dicho que un mayor ingreso está asociado, en comparación con pesca en la playa, a una probabilidad
mayor o menor de determinadas alternativas, pero ¿de cuánto es esta mayor o menor probabilidad? Recuerde
que los coeficientes no cuantifican la relación, tan solo indican su sentido; para obtener una medida del im-
pacto de determinada variable sobre la probabilidad de elegir una alternativa específica se emplean los efectos
marginales.
Para obtener los efectos marginales de un modelo logit multinomial basta con aplicar el método
.get_margeff() a la instancia de resultados de dicho modelo; para visualizar estos efectos marginales, tan
solo se requiere el empleo del método .summary() y de la función print(...), así:

In [8]: LogitMNMargEff = ResultadosLogitMN.get_margeff()


print([Link]())

MNLogit Marginal Effects


=====================================
Dep. Variable: mode
Method: dydx
At: overall
================================================================================
mode=beach dy/dx std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
income 0.0002 0.004 0.044 0.965 -0.007 0.008
--------------------------------------------------------------------------------
mode=charter dy/dx std err z P>|z| [0.025 0.975]

106
--------------------------------------------------------------------------------
income -0.0112 0.006 -1.876 0.061 -0.023 0.000
--------------------------------------------------------------------------------
mode=pier dy/dx std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
income -0.0208 0.005 -4.040 0.000 -0.031 -0.011
------------------------------------------------------------------------------
mode=private dy/dx std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
income 0.0318 0.005 6.039 0.000 0.021 0.042
================================================================================

De este modo, se tiene que un incremento de una unidad en el ingreso está asociado a que la pesca en la
playa (’beach’) sea 0.02 puntos porcentuales más probable; a que la pesca en bote alquilado (’charter’) sea 1.12
puntos porcentuales menos probable; a que la pesca en el muelle (’pier’) sea 2.08 puntos porcentuales menos
probable; y a que la pesca en bote privado (’private’) sea 3.18 puntos porcentuales más probable. (El lector
puede comprobar que la suma de estos efectos marginales es cero)
El lector debe tener en cuenta que existen varias maneras de obtener una medida general del efec-
to marginal de determinada variable; los efectos marginales que son calculados por defecto por el método
.get_margeff() son los efectos marginales promedio. Si lo que se quiere conocer son los efectos marginales
en la media, se debe asignar el valor de ’mean’ al parámetro at del método .get_margeff(), así:

In [9]: LogitMNMargEff = ResultadosLogitMN.get_margeff(at = "mean")


print([Link]())

MNLogit Marginal Effects


=====================================
Dep. Variable: mode
Method: dydx
At: mean
================================================================================
mode=beach dy/dx std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
income 7.496e-05 0.004 0.019 0.985 -0.008 0.008
--------------------------------------------------------------------------------
mode=charter dy/dx std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
income -0.0120 0.006 -1.977 0.048 -0.024 -0.000
--------------------------------------------------------------------------------
mode=pier dy/dx std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
income -0.0207 0.005 -4.239 0.000 -0.030 -0.011
------------------------------------------------------------------------------
mode=private dy/dx std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
income 0.0326 0.006 5.727 0.000 0.021 0.044
================================================================================

107
Ahora, se tiene que un incremento de una unidad en el ingreso está asociado a que la pesca en la playa
(’beach’) sea 0.008 p.p más probable; a que la pesca en bote alquilado (’charter’) sea 1.2 p.p menos probable;
a que la pesca en el muelle (’pier’) sea 2.07 p.p menos probable; y a que la pesca en bote privado (’private’) sea
3.26 p.p más probable. (Nuevamente, el lector puede comprobar que la suma de estos efectos marginales es
cero).
Si el lector contrasta los resultados obtenidos con los de Katchova (2013, 3) notará que los que ella obtiene
son diferentes; esto se debe a que en el ejercicio desarrollado por la profesora Katchova no se toma como
categoría base ’beach’ sino ’charter’, ¿es posible que nosotros hagamos lo mismo? La respuesta es sí.
La función MNLogit(...) de Statsmodels toma como categoría base la alternativa que ocupa la primera
posición en orden alfabético o numérico y no es posible modificar tal lógica en su funcionamiento. Sin embargo,
si se recodifican las alternativas de modo que la que se desee tener como categoría base ocupe la primera
posición, entonces la función MNLogit(...) de Statsmodels sí asumirá dicha alternativa como la categoría
base.
A modo de ejemplo, recurriendo a herramientas de pandas, se puede codificar una variable categórica usando
el método .map(...) y empleando un diccionario Python “{...}” como argumento; dentro de dicho diccionario
las claves corresponderán a las alternativas y los valores a los códigos asignados a cada una.
Por ejemplo, a continuación se crea una variable ’mode2’ que corresponde a la variable ’mode’ codificada
de modo que la alternativa ’charter’ se encuentre en la primera posición en orden numérico y, por ende, sea
tomada como categoría base por la función MNLogit(...) de Statsmodels:

In [10]: data["mode2"] = data["mode"].map({"charter":"1_charter",


"beach":"2_beach",
"pier":"3_pier",
"private":"4_private"})

Empleando la misma estructura de código que hemos utilizado con anterioridad, se obtiene el siguiente
modelo:

In [11]: Y = data["mode2"]
ModeloLogitMN = [Link](Y, sm.add_constant(X))
ResultadosLogitMN = [Link]()
print([Link]())

Optimization terminated successfully.


Current function value: 1.249704
Iterations 5
MNLogit Regression Results
==============================================================================
Dep. Variable: mode2 No. Observations: 1182
Model: MNLogit Df Residuals: 1176
Method: MLE Df Model: 3
Date: xxx, xx xxx xxxx Pseudo R-squ.: 0.01374
Time: xx:xx:xx Log-Likelihood: -1477.2
converged: True LL-Null: -1497.7
LLR p-value: 6.093e-09
===================================================================================
mode2=2_beach coef std err z P>|z| [0.025 0.975]
-----------------------------------------------------------------------------------
const -1.3413 0.195 -6.896 0.000 -1.723 -0.960

108
income 0.0316 0.042 0.756 0.450 -0.050 0.114
-----------------------------------------------------------------------------------
mode2=3_pier coef std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
const -0.5271 0.178 -2.965 0.003 -0.876 -0.179
income -0.1118 0.044 -2.541 0.011 -0.198 -0.026
--------------------------------------------------------------------------------
mode2=4_private coef std err z P>|z| [0.025 0.975]
-----------------------------------------------------------------------------------
const -0.6024 0.136 -4.426 0.000 -0.869 -0.336
income 0.1235 0.028 4.426 0.000 0.069 0.178
===================================================================================

Como se puede apreciar, las magnitudes de los coeficientes han cambiado y, además, ahora su interpretación
no se debe hacer con referencia a ’beach’ sino a ’charter’. Así, se tiene que, en comparación con pesca en bote
alquilado (’charter’), un ingreso más alto está asociado a una mayor probabilidad de pesca en la playa (’beach’)
y en bote privado (’private’) y a una menor probabilidad de pesca en el muelle (’pier’). ¿Qué hay de los efectos
marginales?
El lector debe recordar que, sin importar cuál sea la categoría base, el efecto marginal de una variable
específica, a diferencia del coeficiente, siempre será el mismo. Para comprobarlo, basta con aplicar el método
.get_margeff() a la instancia de resultados del modelo:

In [12]: LogitMNMargEff = ResultadosLogitMN.get_margeff()


print([Link]())

MNLogit Marginal Effects


=====================================
Dep. Variable: mode2
Method: dydx
At: overall
===================================================================================
mode2=1_charter dy/dx std err z P>|z| [0.025 0.975]
-----------------------------------------------------------------------------------
income -0.0112 0.006 -1.876 0.061 -0.023 0.000
-----------------------------------------------------------------------------------
mode2=2_beach dy/dx std err z P>|z| [0.025 0.975]
---------------------------------------------------------------------------------
income 0.0002 0.004 0.044 0.965 -0.007 0.008
---------------------------------------------------------------------------------
mode2=3_pier dy/dx std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
income -0.0208 0.005 -4.040 0.000 -0.031 -0.011
--------------------------------------------------------------------------------
mode2=4_private dy/dx std err z P>|z| [0.025 0.975]
-----------------------------------------------------------------------------------
income 0.0318 0.005 6.039 0.000 0.021 0.042
===================================================================================

109
Se puede observar que los efectos marginales obtenidos del modelo con categoría base ’charter’ son exac-
tamente iguales a los obtenidos del modelo con categoría base ’beach’ (puede examinarlos uno por uno si así
lo desea). Recordando que el método .get_margeff() calcula por defecto efectos marginales promedio, si lo
que se desea conocer son los efectos marginales en la media, tan solo se requiere asignar el valor de ’mean’ al
parámetro at:

In [13]: LogitMNMargEff = ResultadosLogitMN.get_margeff(at = "mean")


print([Link]())

MNLogit Marginal Effects


=====================================
Dep. Variable: mode2
Method: dydx
At: mean
===================================================================================
mode2=1_charter dy/dx std err z P>|z| [0.025 0.975]
-----------------------------------------------------------------------------------
income -0.0120 0.006 -1.977 0.048 -0.024 -0.000
-----------------------------------------------------------------------------------
mode2=2_beach dy/dx std err z P>|z| [0.025 0.975]
---------------------------------------------------------------------------------
income 7.496e-05 0.004 0.019 0.985 -0.008 0.008
---------------------------------------------------------------------------------
mode2=3_pier dy/dx std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
income -0.0207 0.005 -4.239 0.000 -0.030 -0.011
--------------------------------------------------------------------------------
mode2=4_private dy/dx std err z P>|z| [0.025 0.975]
-----------------------------------------------------------------------------------
income 0.0326 0.006 5.727 0.000 0.021 0.044
===================================================================================

Nuevamente, los efectos marginales en la media son exactamente iguales para el modelo con categoría base
’beach’ y para el modelo con categoría base ’charter’, igualdad que se mantiene para cualquier otra alternativa
de la variable dependiente (empleando, obviamente, los mismos datos).
En este punto se cierra este capítulo, con el cual se concluye esta breve obra, la cual ha buscado ofrecer
ejemplos ilustrativos de análisis econométrico de nivel básico con el lenguaje de programación Python y en la
que se han presentado algunas de las herramientas más relevantes para dicho ejercicio, tratando de brindar al
lector breves, pero útiles, descripciones del funcionamiento de cada una de estas.

110
Referencias

Chatterjee, S., y Simonoff, J. (2013). Handbook of Regression Analysis. Estados Unidos: John Wiley & Sons,
Inc.

Dormann, C.F., Elith, J., Bacher, S., Buchmann, C., Gudrun, C., Carré, G., García, J.R., Gruber, B., Lafourcade,
B., Leitão, P.J., Münkemüller, T., McClean, C., Osborne, P.E., Reineking, B., Schröder, B., Skidmore, A.K.,
Zurell, D., y Lautenbach, S. (2013). Collinearity: a review of methods to deal with it and a simulation
study evaluating their performance. Ecograpghy, 36, 27-46. doi: 10.1111/j.1600-0587.2012.07348.x

Fahrmeier, L., Kneib, T., y Lang, Stefan. (2007). Regression. Modelle, Methoden und Anwendungen. Springer.

Greene, W.H. (2003). Econometric Analysis. New Jersey, Estados Unidos: Pearson Education, Inc.

Gujarati, D.N. y Porter, D.C. (2010). Econometría. México: McGraw-Hill/Interamericana Editores, S.A. de
C.V.

James, G., Witten, D., Hastie, T., y Tibshirani, R. (2013). An Introduction to Statistical Learning with Applica-
tions in R. doi: 10.1007/978-1-4614-7138-7

Katchova, A. (2013). Econometrics – Probit and Logit Models. Recuperado de: [Link]
le/d/0BwogTI8d6EEidjg2OGl4b3dxVmc/edit

Katchova, A. (2013,2). Econometrics – Multinomial Probit and Logit Models. Recuperado de:
[Link]

Katchova, A. (2013, 3). Econometrics – Multinomial Probit and Logit Models, Conditional Lo-
git Model, Mixed Logit Model Examples. Recuperado de: [Link]
TI8d6EEiLVZ0N1h3LTBLYlk/edit

Kleiber, C., y Zeileis, A. (2008). Applied Econometrics with R. Estados Unidos: Springer Science+Business
Media, LLC.

Murray, L., Nguyen, H., Lee, Y-F., Remmenga, M., y Smith, D.W. (2012). Variance Inflation Factors in Re-
gression Models with dummy variables. Conference on Applied Statistics in Agriculture. doi: 10.4148/2475-
7772.1034

Pandas. (s.f). Python Data Analysis Library. Recuperado de: [Link]

Perktold, J., Seabold, Skipper., y Taylor, J. (2018). Welcome to Statsmodels’s Documentation. Recuperado
de: [Link]

Project Jupyter. (2019). The Jupyter Notebook. Recuperado de: [Link]

111
Python Software Foundation. (s.f.). What is Python? Executive Summary. Recuperado de: [Link]
[Link]/doc/essays/blurb/

Razali, N., y Wah, Y. (2011). Power comparisons of Shapiro-Wilk, Kolmogorov-Smirnov, Lilliefors


and Anderson-Darling tests. Journal of Statistical Modeling and Analytics, 2(1), 21-33. Recuperado de:
[Link]

The Matplotlib development team. (2018). Matplotlib. Recuperado de: [Link]

The SciPy community. (2019). What is NumPy? Recuperado de: [Link]


docs/user/[Link]

Vaart, A.W. van der. (1998). Asymptotic Statistics. Reino Unido: Cambridge University Press.

Verbeek, M. (2004). A guide to modern econometrics. Inglaterra: John Wiley & Sons Ltd.

Waskom, M. (2018). An introduction to seaborn. Recuperado de: [Link]


[Link]

Wooldridge, J.M. (2010). Introducción a la econometría. Un enfoque moderno. México: Cengage Learning
Editores, S.A. de C.V.

Würtz, D., y Katzgraber, H.G. (2009). Precise finite-sample quantiles of the Jarque-Bera adjusted Lagran-
ge multiplier test. ETH Econohysics Working and White Papers Series. Recuperado de: [Link]
[Link]/sites/default/files/[Link]

112

También podría gustarte