Simulacion

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 252

Simulación Estadística

Rubén Fernández Casal ([email protected]), Ricardo Cao ([email protected])

2020-04-21
2
Índice general

Prólogo 5

1 Introducción a la simulación 7
1.1 Conceptos básicos . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2 Generación de números (pseudo)aleatorios . . . . . . . . . . . . . 9
1.3 Números aleatorios puros . . . . . . . . . . . . . . . . . . . . . . 10
1.4 Números pseudoaleatorios . . . . . . . . . . . . . . . . . . . . . . 12

2 Números aleatorios en R 15
2.1 Opciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2 Paquetes de R . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.3 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.4 Tiempo de CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

3 Generación de números pseudoaleatorios con distribución uni-


forme 27
3.1 Generadores congruenciales (lineales) . . . . . . . . . . . . . . . . 27
3.2 Análisis de la calidad de un generador . . . . . . . . . . . . . . . 34
3.3 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

4 Análisis de resultados de simulación 47


4.1 Convergencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.2 Estimación de la precisión . . . . . . . . . . . . . . . . . . . . . . 50
4.3 Teorema central del límite . . . . . . . . . . . . . . . . . . . . . . 51
4.4 Determinación del número de generaciones . . . . . . . . . . . . . 52
4.5 El problema de la dependencia . . . . . . . . . . . . . . . . . . . 53
4.6 Observaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

5 Simulación de variables continuas 65


5.1 Método de inversión . . . . . . . . . . . . . . . . . . . . . . . . . 65
5.2 Método de aceptación rechazo . . . . . . . . . . . . . . . . . . . . 70
5.3 Modificaciones del método de aceptación rechazo . . . . . . . . . 79
5.4 Método de composición . . . . . . . . . . . . . . . . . . . . . . . 82

3
4 ÍNDICE GENERAL

5.5 Métodos específicos para la generación de algunas distribuciones


notables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

6 Simulación de variables discretas 85


6.1 Método de la transformación cuantil . . . . . . . . . . . . . . . . 86
6.2 Método de la tabla guía . . . . . . . . . . . . . . . . . . . . . . . 91
6.3 Método de Alias . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.4 Simulación de una variable discreta con dominio infinito . . . . . 96
6.5 Cálculo directo de la función cuantil . . . . . . . . . . . . . . . . 97
6.6 Otros métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
6.7 Métodos específicos para generación de distribuciones notables . 101

7 Simulación de distribuciones multivariantes 103


7.1 Simulación de componentes independientes . . . . . . . . . . . . 103
7.2 El método de aceptación/rechazo . . . . . . . . . . . . . . . . . . 105
7.3 Factorización de la matriz de covarianzas . . . . . . . . . . . . . 107
7.4 Método de las distribuciones condicionadas . . . . . . . . . . . . 111
7.5 Simulación condicional e incondicional . . . . . . . . . . . . . . . 114
7.6 Simulación basada en cópulas . . . . . . . . . . . . . . . . . . . . 121
7.7 Simulación de distribuciones multivariantes discretas . . . . . . . 127
7.8 Ejercicios propuestos . . . . . . . . . . . . . . . . . . . . . . . . . 133

8 Aplicaciones de la simulación en Inferencia Estadística 135


8.1 Distribución en el muestreo . . . . . . . . . . . . . . . . . . . . . 136
8.2 Intervalos de confianza . . . . . . . . . . . . . . . . . . . . . . . . 139
8.3 Contrastes de hipótesis . . . . . . . . . . . . . . . . . . . . . . . . 148
8.4 Comparación de estimadores . . . . . . . . . . . . . . . . . . . . 158
8.5 Remuestreo Bootstrap . . . . . . . . . . . . . . . . . . . . . . . . 161

9 Integración y Optimización Montecarlo 171


9.1 Integración Monte Carlo (clásica) . . . . . . . . . . . . . . . . . . 171
9.2 Muestreo por importancia . . . . . . . . . . . . . . . . . . . . . . 176
9.3 Optimización Monte Carlo . . . . . . . . . . . . . . . . . . . . . . 182
9.4 Temple simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
9.5 Algoritmos genéticos . . . . . . . . . . . . . . . . . . . . . . . . . 190

10 Técnicas de reducción de la varianza 193


10.1 Reducción de la varianza . . . . . . . . . . . . . . . . . . . . . . . 193
10.2 Variables antitéticas . . . . . . . . . . . . . . . . . . . . . . . . . 194
10.3 Estratificación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
10.4 Variables de control . . . . . . . . . . . . . . . . . . . . . . . . . 202
10.5 Números aleatorios comunes . . . . . . . . . . . . . . . . . . . . . 204
10.6 Ejercicios fin de práctica . . . . . . . . . . . . . . . . . . . . . . . 205

Referencias 207
Bibliografía básica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Bibliografía complementaria . . . . . . . . . . . . . . . . . . . . . . . . 207
ÍNDICE GENERAL 5

A Enlaces 211

B Bondad de Ajuste y Aleatoriedad 215


B.1 Métodos de bondad de ajuste . . . . . . . . . . . . . . . . . . . . 216
B.2 Diagnosis de la independencia . . . . . . . . . . . . . . . . . . . . 227
B.3 Contrastes específicos para generadores aleatorios . . . . . . . . . 237

C Integración numérica 243


C.1 Integración numérica unidimensional . . . . . . . . . . . . . . . . 243
C.2 Integración numérica bidimensional . . . . . . . . . . . . . . . . . 247
6 ÍNDICE GENERAL
Prólogo

Este libro contiene los apuntes de la asignatura de Simulación Estadística del


Máster en Técnicas Estadísticas.
Este libro ha sido escrito en R-Markdown empleando el paquete bookdown y
está disponible en el repositorio Github: rubenfcasal/simbook. Se puede acceder
a la versión en línea a través del siguiente enlace:
https://rubenfcasal.github.io/simbook/index.html.
donde puede descargarse en formato pdf.
Para instalar los paquetes necesarios para poder ejecutar los ejemplos mostrados
en el libro se puede emplear el siguiente comando:
pkgs <- c('boot', 'randtoolbox', 'MASS', 'DEoptim', 'nortest', 'geoR', 'copula', 'sm',
'car', 'tseries', 'forecast', 'plot3D', 'rgl')
install.packages(setdiff(pkgs, installed.packages()[,"Package"]),
dependencies = TRUE)

# Si aparecen errores debidos a incompatibilidades entre las versiones de los paquetes,


# probar a ejecutar en lugar de lo anterior:
# install.packages(pkgs, dependencies = TRUE) # Instala todos...

Para generar el libro (compilar) serán necesarios paquetes adicionales, para lo


que se recomendaría consultar el libro de “Escritura de libros con bookdown”
en castellano.

Este obra está bajo una licencia de Creative Commons Reconocimiento-


NoComercial-SinObraDerivada 4.0 Internacional (esperamos poder liberarlo
bajo una licencia menos restrictiva más adelante…).

7
8 ÍNDICE GENERAL
Capítulo 1

Introducción a la simulación

Podríamos definir la Simulación como una técnica que consiste en realizar expe-
rimentos de muestreo sobre el modelo de un sistema, con el objetivo de recopilar
información bajo determinadas condiciones.

1.1 Conceptos básicos

La experimentación directa sobre la realidad puede tener muchos inconvenientes:


• Coste elevado.
– En ocasiones las pruebas son destructivas.
– Lentitud.
• Puede no ser ética.
– Experimentación sobre seres humanos.
• Puede resultar imposible.
– Acontecimientos futuros, alternativas en el pasado, …
• …
Además la realidad puede ser demasiado compleja como para ser estudiada di-
rectamente y resultar preferible trabajar con un modelo del sistema real. Un
modelo no es más que un conjunto de variables junto con ecuaciones matemá-
ticas que las relacionan y restricciones sobre dichas variables. Habría dos tipos
de modelos:
• Modelos deterministas.

9
10 CAPÍTULO 1. INTRODUCCIÓN A LA SIMULACIÓN

• Modelos estocásticos (con componente aleatoria): tienen en cuenta la in-


certidumbre debida a que no se disponga de toda la información sobre las
variables que influyen en el fenómeno en estudio (puede ser simplemente
que haya errores de medida).

“Nothing in Nature is random… a thing appears random only th-


rough the incompleteness of our knowledge.”

— Spinoza, Baruch (Ethics, 1677)

La modelización es una etapa presente en la mayor parte de los trabajos de


investigación (especialmente en las ciencias experimentales). El modelo debería
considerar las variables más relevantes para explicar el fenómeno en estudio
y las principales relaciones entre ellas. La inferencia estadística proporciona
herramientas para estimar los parámetros y contrastar la validez de un modelo
estocástico a partir de los datos observados.

La idea es emplear el modelo (suponiendo que es válido) para resolver el pro-


blema de interés. Si se puede obtener la solución de forma análitica, esta suele
ser exacta (aunque en ocasiones solo se dispone de soluciones aproximadas, ba-
sadas en resultados asintóticos, o que dependen de supociones que pueden ser
cuestionables) y a menudo la resolución también es rápida. Cuando la solución
no se puede obtener de modo analítico (o si la aproximación disponible no es
adecuada) se puede recurrir a la simulación.

Nos centraremos en el caso de la Simulación Estocástica: las conclusiones se


obtienen generando repetidamente simulaciones del modelo aleatorio.

1.1.1 Ventajas e inconvenientes de la simulación

Ventajas (Shannon, 1975):

• Cuando la resolución analítica no puede llevarse a cabo.

• Cuando existen medios de resolver analíticamente el problema pero di-


cha resolución es complicada y costosa (o solo proporciona una solución
aproximada).

• Si se desea experimentar antes de que exista el sistema (pruebas para la


construcción de un sistema).

• Cuando es imposible experimentar sobre el sistema real por ser dicha ex-
perimentación destructiva.

• En ocasiones en las que la experimentación sobre el sistema es posible pero


no ética.

• En sistemas que evolucionan muy lentamente en el tiempo.


1.2. GENERACIÓN DE NÚMEROS (PSEUDO)ALEATORIOS 11

El principal incoveniente puede ser el tiempo de computación necesario, aunque


gracias a la gran potencia de cálculo de los computadores actuales, se puede ob-
tener rápidamente una solución aproximada en la mayor parte de los problemas
susceptibles de ser modelizados. Además siempre están presentes los posibles
problemas debidos a emplear un modelo:
• La construcción de un buen modelo puede ser una tarea muy costosa
(compleja, laboriosa y requerir mucho tiempo; e.g. modelos climáticos).
• Frecuentemente el modelo omite variables o relaciones importantes entre
ellas (los resultados pueden no ser válidos para el sistema real).
• Resulta difícil conocer la precisión del modelo formulado.
Otro problema de la simulación es que se obtienen resultados para unos valo-
res concretos de los parámetros del modelo, por lo que en principio resultaría
complicado extrapolar las conclusiones a otras situaciones.

1.2 Generación de números (pseudo)aleatorios

Aplicaciones:
• Estadística:
– Muestreo, remuestreo, …
– Aproximación de distribuciones (de estadísticos, estimadores, …)
– Realización de contrastes, intervalos de confianza, …
– Comparación de estimadores, contrastes, …
– Validación teoría (distribución asintótica,…)
– Inferencia Bayesiana
• Optimización: Algoritmos genéticos, …
• Análisis numérico: Aproximación de integrales, resolución de ecuaciones,

• Computación: Diseño, verificación y validación de algoritmos,…
• Criptografía: Protocolos de comunicación segura, …
• Física: Simulación de fenómenos naturales, …
• …
En los capítulos 8 y 9 nos centraremos en algunas de las aplicaciones de utilidad
en Estadística.
12 CAPÍTULO 1. INTRODUCCIÓN A LA SIMULACIÓN

1.3 Números aleatorios puros

Una sucesión de números aleatorios puros (true random), se caracteriza por que
no existe ninguna regla o plan que nos permita conocer sus valores.

Normalmente son obtenidos por procesos físicos (loterías, ruletas, ruidos,…) y se


almacenaban en tablas de dígitos aleatorios. Por ejemplo, en 1955 la Corporación
RAND publicó el libro A Million Random Digits with 100,000 Normal Devia-
tes que contenía números aleatorios generados mediante una ruleta electrónica
conectada a una computadora (ver Figura 1.1).

Figura 1.1: Líneas 10580-10594, columnas 21-40, del libro *A Million Random
Digits with 100,000 Normal Deviates*.

El procediento para generar de forma manual números aleatorios en un rango


de 1 a m era el siguiente:

• Se selecciona al azar un pto de inicio en la tabla y una dirección.

• Se agrupan los dígitos de forma que “cubran” el valor de m.

• Se seleccionan los valores menores o iguales que m (se descarta el resto).

Hoy en día están disponibles generadores de números aleatorios “online”:

• http://www.random.org/integers (ver paquete random en R).

• http://www.fourmilab.ch/hotbits

Aunque para un uso profesional puede ser recomendable emplear generadores


implementados mediante hardware:

• http://software.intel.com.

• http://spectrum.ieee.org

Sus principales aplicaciones hoy en día son en criptografía (impredictibilidad).


1.3. NÚMEROS ALEATORIOS PUROS 13

1.3.1 Inconvenientes:

Para su aplicación en el campo de la Estadística:


• Es necesario conocer su distribución.
• Los datos generados deberían ser i.i.d.
(sería también muy recomendable para otros casos) y en general:
• Reproductivilidad.
• Pueden requerir del almacenamiento en tablas.
Además siempre está la posible presencia de sesgos, principalmente debidos a
fallos del sistema o interferencias. Por ejemplo en el caso de la máquina RAND,
fallos mecánicos en el sistema de grabación de los datos causaron problemas de
aleatoriedad (Hacking, 1965, p. 129).

1.3.2 Alternativas:

A partir de la década de 1960, al disponer de computadoras de mayor velocidad,


empezó a resultar más eficiente generar valores mediante software en lugar de
leerlos de las tablas. Se distingue entre dos tipos de secuencias:
• números pseudo-aleatorios: simulan realizaciones de una variable aleatoria
(uniforme).
• números cuasi-aleatorios: secuencias determinísticas con una distribución
más uniforme en el rango considerado (se podría pensar que son una única
generación de una variable aleatoria).
Algunos problemas, como la integración numérica (en el Capítulo 9 se tratarán
métodos de integración Monte Carlo), no dependen realmente de la aleatorie-
dad de la secuencia. Para evitar generaciones poco probables, se puede recurrir
a secuencias cuasi-aleatorias, también denominadas sucesiones de baja discre-
pancia (hablaríamos entonces de métodos cuasi-Monte Carlo). La idea sería que
la proporción de valores en una región cualquiera sea siempre aproximadamente
proporcional a la medida de la región (como sucedería en media con la distribu-
ción uniforme, aunque no necesariamente para una realización concreta).
Por ejemplo, el paquete randtoolbox implementa métodos para la generación
de secuencias cuasi-aleatorias (ver Figura 1.2).
library(randtoolbox)
n <- 2000
par.old <- par( mfrow=c(1,3))
plot(halton(n, dim = 2), xlab = 'x1', ylab = 'x2')
plot(sobol(n, dim = 2), xlab = 'x1', ylab = 'x2')
plot(torus(n, dim = 2), xlab = 'x1', ylab = 'x2')
14 CAPÍTULO 1. INTRODUCCIÓN A LA SIMULACIÓN

1.0

1.0

1.0
0.8

0.8

0.8
0.6

0.6

0.6
x2

x2

x2
0.4

0.4

0.4
0.2

0.2

0.2
0.0

0.0

0.0
0.0 0.2 0.4 0.6 0.8 1.0 0.0 0.2 0.4 0.6 0.8 1.0 0.0 0.2 0.4 0.6 0.8 1.0

x1 x1 x1

Figura 1.2: Secuencias cuasi-aleatorias bidimensionales obtenidas con los méto-


dos de Halton (izquierda), Sobol (centro) y Torus (derecha).

par(par.old)

En este libro sólo consideraremos los números pseudoaleatorios y por comodidad


se eliminará el prefijo “pseudo” en algunos casos.

1.4 Números pseudoaleatorios


La mayoría de los métodos de simulación se basan en la posibilidad de generar
números pseudoaleatorios que imiten las propiedades de generaciones indepen-
dientes de una distribución U(0, 1).
El procedimiento habitual para obtiener estas secuencias es emplear un algorit-
mo recursivo denominado generador:

xi = f (xi−1 , xi−2 , · · · , xi−k )

donde:
• k es el orden del generador.
• (x0 , x1 , · · · , xk−1 ) es la semilla (estado inicial).
El periodo o longitud del ciclo es la longitud de la secuencia antes de que vuelva
a repetirse. Lo denotaremos por p.
Los números de la sucesión serán predecibles, conociendo el algoritmo y la semi-
lla.
• Sin embargo, si no se conociesen, no se debería poder distinguir una serie
de números pseudoaleatorios de una sucesión de números verdaderamente
aleatoria (utilizando recursos computacionales razonables).
• En caso contrario esta predecibilidad puede dar lugar a serios problemas
(e.g. http://eprint.iacr.org/2007/419).
1.4. NÚMEROS PSEUDOALEATORIOS 15

Como regla general, por lo menos mientras se está desarrollando un programa,


interesa fijar la semilla de aleatorización.
• Permite la reproductibilidad de los resultados.
• Facilita la depuración del código.
Todo generador de números pseudoaleatorios mínimamente aceptable debe com-
portarse como si se tratase de una muestra genuina de datos independientes de
una U(0, 1). Otras propiedades de interés serían:
• Reproducibilidad a partir de la semilla.
• Periodo suficientemente largo.
• Eficiencia (rapidez y requerimientos de memoria).
• Portabilidad.
• Generación de sub-secuencias (computación en paralelo).
• Parsimonia, …
“… random numbers should not be generated with a method chosen
at random.”
— Knuth, D.E. (TAOCP, 2002)
Gran cantidad de algoritmos:
• Cuadrados medios, Lehmer,…
• Congruenciales
• Registros desfasados
• Combinaciones
• …
Código fuente disponible en múltiples librerias:
• GNU Scientific Library (GSL): http://www.gnu.org/software/gsl/manual
• StatLib: http://lib.stat.cmu.edu
• Numerical recipes: http://www.nrbook.com/nr3
• http://random.mat.sbg.ac.at/software
• KISS (Keep It Simple Stupid / Small and Simple): http://www.fortran.
com/kiss.f90
• UNU.RAN (paquete Runuran): http://statmath.wu.ac.at/unuran
• …
Nos centraremos en los generadores congruenciales, descritos en la Sección 3.1.
16 CAPÍTULO 1. INTRODUCCIÓN A LA SIMULACIÓN
Capítulo 2

Números aleatorios en R

La generación de números pseudoaleatorios en R es una de las mejores dispo-


nibles en paquetes estadísticos. Entre las herramientas en el paquete base de R
estarían:
• set.seed(entero): permite establecer la semilla (y el generador).
• RNGkind(): selecciona el generador.
• rdistribución(n,...): genera valores aleatorios de la correspondiente
distribución. Por ejemplo: runif(n, min = 0, max = 1), generaría n va-
lores de una uniforme.
• sample(): genera muestras aleatorias de variables discretas y permutacio-
nes (se tratará en el Capítulo 6).
• simulate(): genera realizaciones de la respuesta de un modelo ajustado.
Además están disponibles otros paquetes, como el paquete distr, que imple-
mentan distribuciones adicionales.
La semilla se almacena (en globalenv) en .Random.seed; es un vector de enteros
cuya dimensión depende del tipo de generador:
• No debe ser modificado manualmente; se guarda con el entorno de trabajo.
• Si no se especifica con set.seed (o no existe) se genera a partir del reloj
del sistema.
Nota: . Puede ser recomendable (para depurar) almacenarla antes de generar
simulaciones, e.g. semilla <- .Random.seed.
En la mayoría de los ejemplos de este libro se generan todos los valores de
una vez, se guardan y se procesan vectorialmente (normalmente empleando la
función apply). En problemas mas complejos, en los que no es necesario almace-
nar todas las simulaciones, puede ser preferible emplear un bucle para generar

17
18 CAPÍTULO 2. NÚMEROS ALEATORIOS EN R

y procesar cada simulación iterativamente. Por ejemplo podríamos emplear el


siguiente esquema:
# Fijar semilla
set.seed(1)
for (isim in 1:nsim) {
seed <- .Random.seed
# Si se produce un error, podremos depurarlo ejecutando:
# .Random.seed <- seed
...
# Generar valores pseudoaleatorios
...
}

o alternativamente fijar la semilla en cada iteración, por ejemplo:


for (isim in 1:nsim) {
set.seed(isim)
...
# Generar valores pseudoaleatorios
...
}

Hay que tener en cuenta que .Random.seed se almacena en el entorno de tra-


bajo…

2.1 Opciones

La función RNGkind(kind = NULL, normal.kind = NULL) permite seleccionar


el tipo de generador (en negrita los valores por defecto):

• kindespecifica el generador aleatorio:

– “Wichmann-Hill”: Ciclo 6.9536 × 1012

– “Marsaglia-Multicarry”: Ciclo mayor de 260

– “Super-Duper”: Ciclo aprox. 4.6 × 1018 (S-PLUS)

– “Mersenne-Twister”: Ciclo 219937 − 1 y equidistribution en 623


dimensiones.

– “Knuth-TAOCP-2002”: Ciclo aprox. 2129 .

– “Knuth-TAOCP”

– “user-supplied”
2.2. PAQUETES DE R 19

• normal.kindselecciona el método de generación de normales (se trata-


rá más adelante). “Kinderman-Ramage”, “Buggy Kinderman-Ramage”,
“Ahrens-Dieter”, “Box-Muller”, “Inversion” , o “user-supplied”

2.2 Paquetes de R
Otros paquetes de R que pueden ser de interés:
• setRNG contiene herramientas que facilitan operar con la semilla (dentro
de funciones,…).
• random permite la descarga de numeros “true random” desde RAN-
DOM.ORG.
• randtoolbox implementa generadores más recientes (rngWELL) y genera-
ción de secuencias cuasi-aleatorias.
• RDieHarder implementa diversos contrastes para el análisis de la calidad
de un generador y varios generadores.
• Runuran interfaz para la librería UNU.RAN para la generación (automá-
tica) de variables aleatorias no uniformes (ver Hörmann et al., 2004).
• rsprng, rstream y rlecuyer implementan la generación de múltiples se-
cuencias (para programación paralela).
• gls, rngwell19937, randaes, SuppDists, lhs, mc2d, fOptions, …

2.3 Ejercicios
Ejercicio 2.1.

Sea (X, Y ) es un vector aleatorio con distribución uniforme en el cuadrado


[−1, 1] × [−1, 1] de área 4.
a) Aproximar mediante simulación P (X + Y ≤ 0) y compararla con la pro-
babilidad teórica (obtenida aplicando la regla de Laplace área favorable
área posible ).

Probabilidad teórica 1/2 (area favorable / área total)


set.seed(1)
n <- 10000
x <- runif(n, -1, 1)
y <- runif(n, -1, 1)
indice <- (x+y < 0)
# Aproximación por simulación
sum(indice)/n
20 CAPÍTULO 2. NÚMEROS ALEATORIOS EN R

## [1] 0.4996
mean(indice) # Alternativa

## [1] 0.4996
Nota: . R maneja internamente los valores lógicos como 1 (TRUE) y 0
(FALSE).

( )
b) Aproximar el valor de π mediante simulación a partir de P X 2 + Y 2 ≤ 1 .
set.seed(1)
n <- 10000
x <- runif(n, -1, 1)
y <- runif(n, -1, 1)
indice <- (x^2+y^2 < 1)
mean(indice)

## [1] 0.7806
pi/4

## [1] 0.7853982
pi_aprox <- 4*mean(indice)
pi_aprox

## [1] 3.1224

Gráfico
# Colores y símbolos depediendo de si el índice correspondiente es verdadero:
color <- ifelse(indice, "black", "red")
simbolo <- ifelse(indice, 1, 4)
plot(x, y, pch = simbolo, col = color,
xlim = c(-1, 1), ylim = c(-1, 1), xlab="X", ylab="Y", asp = 1)
# asp = 1 para dibujar circulo
symbols(0, 0, circles = 1, inches = FALSE, add = TRUE)
symbols(0, 0, squares = 2, inches = FALSE, add = TRUE)
2.3. EJERCICIOS 21

1.0
0.5
0.0
Y

−0.5
−1.0

−1.0 −0.5 0.0 0.5 1.0

Ejercicio 2.2.

Consideramos el experimento de Bernoulli consistente en el lanzamiento de una


moneda.

a) Empleando la función sample, obtener 1000 simulaciones del lanzamiento


de una moneda (0 = cruz, 1 = cara), suponiendo que no está trucada.
Aproximar la probabilidad de cara a partir de las simulaciones.
set.seed(1)
nsim <- 10000
x <- sample(c(cara = 1, cruz = 0), nsim, replace = TRUE, prob = c(0.5,0.5))
mean(x)

## [1] 0.4953
barplot(100*table(x)/nsim) # Representar porcentajes
22 CAPÍTULO 2. NÚMEROS ALEATORIOS EN R

50
40
30
20
10
0

0 1

b) En R pueden generarse valores de la distribución de Bernoulli mediante


la función rbinom(nsim, size=1, prob). Generar un gráfico de lineas
considerando en el eje X el número de lanzamientos (de 1 a 10000) y en
el eje Y la frecuencia relativa del suceso cara (puede ser recomendable
emplear la función cumsum).
set.seed(1)
nsim <- 1000
p <- 0.4
x <- rbinom(nsim, size = 1, prob = p) # Simulamos una Bernouilli
n <- 1:nsim
# Alternativa programación: x <- runif(nsim) < p
mean(x)

## [1] 0.394
plot(n, cumsum(x)/n, type="l", ylab="Proporción de caras",
xlab="Número de lanzamientos", ylim=c(0,1))
abline(h=p, lty=2, col="red")
2.3. EJERCICIOS 23

1.0
0.8
Proporción de caras

0.6
0.4
0.2
0.0

0 200 400 600 800 1000

Número de lanzamientos

Ejercicio 2.3.

Simular el paso de corriente a través del siguiente circuito, donde figuran las
probabilidades de que pase corriente por cada uno de los interruptores:

Considerar que cada interruptor es una v.a. de Bernoulli independiente para


simular 1000 valores de cada una de ellas.
Nota: . R maneja internamente los valores lógicos como 1 (TRUE) y 0 (FALSE).
Recíprocamente, cualquier nº puede ser tratado como lógico (al estilo de C). El
entero 0 es equivalente a FALSE y cualquier entero distinto de 0 a TRUE.
set.seed(1)
nsim <- 10000
x1 <- rbinom(nsim, size=1, prob=0.9)
x2 <- rbinom(nsim, size=1, prob=0.8)
z1 <- x1 | x2 # Operador lógico "O"
x3 <- rbinom(nsim, size=1, prob=0.6)
x4 <- rbinom(nsim, size=1, prob=0.5)
24 CAPÍTULO 2. NÚMEROS ALEATORIOS EN R

z2 <- x3 | x4
z3 <- z1 | z2
x5 <- rbinom(nsim, size=1, prob=0.7)
fin <- z3 & x5 # Operador lógico "Y"
mean(fin)

## [1] 0.6918

Ejercicio 2.4.

En 1651, el Caballero de Méré le planteó a Pascal una pregunta relacionada


con las apuestas y los juegos de azar: ¿es ventajoso apostar a que en cuatro
lanzamientos de un dado se obtiene al menos un seis? Este problema generó
una fructífera correspondencia entre Pascal y Fermat que se considera, simbóli-
camente, como el nacimiento del Cálculo de Probabilidades.
a) Escribir una función que simule el lanzamiento de n dados. El parámetro de
entrada es el número de lanzamientos n, que toma el valor 4 por defecto,
y la salida debe ser TRUE si se obtiene al menos un 6 y FALSE en caso
contrario.
deMere <- function(n = 4){
lanz <- sample(1:6, replace=TRUE, size=n)
return(6 %in% lanz)
}

n <- 4
lanz <- sample(1:6, replace=TRUE, size=n)
lanz

## [1] 3 5 1 6
6 %in% lanz

## [1] TRUE
b) Utilizar la función anterior para simular nsim = 10000 jugadas de este
juego y calcular la proporción de veces que se gana la apuesta (obtener al
menos un 6 en n lanzamientos), usando n = 4. Comparar el resultado con
la probabilidad teórica 1 − (5/6)n .
set.seed(1)
n <- 4
nsim <- 10000
mean(replicate(nsim, deMere(n)))

## [1] 0.5148
1-(5/6)^n
2.4. TIEMPO DE CPU 25

## [1] 0.5177469

2.4 Tiempo de CPU

La velocidad del generador suele ser una característica importante. Para evaluar
el rendimiento están disponibles en R distintas herramientas:

• proc.time(): permite obtener tiempo de computación real y de CPU.

tini <- proc.time()


# Código a evaluar
tiempo <- proc.time() - tini

• system.time(expresión): muestra el tiempo de computación (real y de


CPU) de expresión.

• Rprof(fichero): permite evaluar el rendimiento muestreando la pila en


intervalos para determinar en que funciones se emplea el tiempo de compu-
tación (de utilidad para optimizar la velocidad).Rprof(NULL): desactiva
el muestreo.summaryRprof(fichero): muestra los resultados.

2.4.1 Utilidades tiempo de CPU

Por ejemplo, podríamos emplear las siguientes funciones para ir midiendo los
tiempos de CPU durante una simulación:
CPUtimeini <- function() {
.tiempo.ini <<- proc.time()
.tiempo.last <<- .tiempo.ini
}

CPUtimeprint <- function() {


tmp <- proc.time()
cat("\nTiempo última operación:\n")
print(tmp-.tiempo.last)
cat("Tiempo total operación:\n")
print(tmp-.tiempo.ini)
.tiempo.last <<- tmp
}

Llamando a CPUtimeini() donde se quiere empezar a contar, y a


CPUtimeprint() para imprimir el tiempo total y el tiempo desde la últi-
ma llamada a una de estas funciones. Ejemplo:
26 CAPÍTULO 2. NÚMEROS ALEATORIOS EN R

funtest <- function(n) median(runif(n)) # Función de prueba...


CPUtimeini()
funtest(1000000)

## [1] 0.5003313
CPUtimeprint()

##
## Tiempo última operación:
## user system elapsed
## 0.06 0.00 0.06
## Tiempo total operación:
## user system elapsed
## 0.06 0.00 0.06
funtest(1000)

## [1] 0.5050682
CPUtimeprint()

##
## Tiempo última operación:
## user system elapsed
## 0.02 0.00 0.02
## Tiempo total operación:
## user system elapsed
## 0.08 0.00 0.08

2.4.2 Paquetes de R

Por ejemplo, se puede emplear el paquete npsp (fichero cpu.time.R):


• Call cpu.time(restart = TRUE) where you want to start counting.
• Call cpu.time() to print/get total and/or partial (since the last call to
this function) real and CPU times.
# CPU time utilities
# ------------------

#' @rdname npsp-internals


#' @keywords internal
#' @export
.cpu.time.ini <- function() {
time.ini <- structure(rep(0, 5), .Names = c("user.self", "sys.self", "elapsed",
"user.child", "sys.child"), class = "proc_time")# proc.time()
2.4. TIEMPO DE CPU 27

time.last <- time.ini


function(..., reset = FALSE, total = TRUE, last = TRUE, flush = FALSE) {
res <- list(time = proc.time())
if (reset) {
time.ini <<- res$time
time.last <<- time.ini
res$last <- res$total <- 0
if (total | last) cat("CPU time has been initialized.\n")
} else {
res$last <- res$time - time.last
res$total <- res$time - time.ini
if (last) {
cat("Time of last operation:", ..., "\n")
print(res$last)
}
if (total) {
cat("Total time:\n")
print(res$total)
}
if (flush) flush.console()
time.last <<- res$time
}
return(invisible(res))
}
}

#' Total and partial CPU time used


#'
#' Returns and (optionally) prints the total and/or partial
#' (since the last call to this function)
#' real and CPU times.
#' @param ... objects (describing the last operation) to be printed
#' (using \code{\link{cat}}),
#' if \code{last == TRUE}.
#' @param reset logical; if \code{TRUE}, time counters are initialized.
#' @param total logical; if \code{TRUE}, the total time used is printed.
#' @param last logical; if \code{TRUE}, the partial time used is printed.
#' @param flush logical; if \code{TRUE}, \code{\link{flush.console}} is called.
#' @return Invisibly returns a list with the following 3 components
#' (objects of class \code{"proc_time"}):
#' \item{time}{user, system, and total elapsed times for the currently running R process
#' (result of a call to \code{\link{proc.time}}). }
#' \item{last, total}{differences between the corresponding \code{\link{proc.time}} calls.}
#' @seealso
#' \code{\link{proc.time}}, \code{\link{system.time}}, \code{\link{flush.console}}.
28 CAPÍTULO 2. NÚMEROS ALEATORIOS EN R

#' @export
cpu.time <- .cpu.time.ini()

Ejemplo:
cpu.time(reset = TRUE)

## CPU time has been initialized.


res <- funtest(1000000)
cpu.time('\nSample median of', 1000000, 'values =', res, total = FALSE)

## Time of last operation:


## Sample median of 1e+06 values = 0.4993323
## user system elapsed
## 0.07 0.00 0.07
res <- funtest(1000)
cpu.time('\nSample median of', 1000, 'values =', res)

## Time of last operation:


## Sample median of 1000 values = 0.5126436
## user system elapsed
## 0 0 0
## Total time:
## user system elapsed
## 0.07 0.00 0.07
Otro paquete que puede ser de utilidad es microbenchmark (si se quieren estu-
diar con más detalle los tiempos de computación; aunque en este libro no será
el caso…). Hay que tener en cuenta que, por construcción, aunque se realicen en
la mismas condiciones (en el mismo equipo), los tiempos de CPU en R pueden
variar “ligeramente” entre ejecuciones.
Capítulo 3

Generación de números
pseudoaleatorios con
distribución uniforme

Como ya se comentó, los distintos métodos de simulación requieren disponer de


secuencias de números pseudoaleatorios que imiten las propiedades de generacio-
nes independientes de una distribución U(0, 1). En primer lugar nos centraremos
en el caso de los generadores congruenciales. A pesar de su simplicidad, podrían
ser adecuados en muchos casos y constituyen la base de los generadores avan-
zados habitualmente considerados. Posteriormente se dará una visión de las
diferentes herramientas para estudiar la calidad de un generador de números
pseudoaleatorios.

3.1 Generadores congruenciales (lineales)


Se basan en la idea de considerar una combinación lineal de los últimos k enteros
generados y calcular su resto al dividir por un entero fijo m. En el método
congruencial simple (de orden k = 1), partiendo de una semilla inicial x0 , el
algoritmo secuencial es el siguiente:

xi = (axi−1 + c) mod m
xi
ui =
m
i = 1, 2, . . .

donde a (multiplicador), c (incremento) y m (módulo) son parámetros enteros


del generador fijados de antemano. Si c = 0 el generador se denomina con-

29
30CAPÍTULO 3. GENERACIÓN DE NÚMEROS PSEUDOALEATORIOS CON DISTRIBUCIÓN UNI

gruencial multiplicativo (Lehmer, 1951) y en caso contrario se dice que es mixto


(Rotenburg, 1960).

Este método está implementado en el fichero RANDC.R, tratando de imitar el


funcionamiento de un generador de R (aunque de un forma no muy eficiente):
# Generador congruencial de números pseudoaleatorios
# ==================================================

# --------------------------------------------------
# initRANDC(semilla,a,c,m)
# Selecciona el generador congruencial
# Por defecto RANDU de IBM con semilla del reloj
# OJO: No se hace ninguna verificación de los parámetros
initRANDC <- function(semilla=as.numeric(Sys.time()), a=2^16+3, c=0, m=2^31) {
.semilla <<- as.double(semilla) %% m #Cálculos en doble precisión
.a <<- a
.c <<- c
.m <<- m
return(invisible(list(semilla=.semilla,a=.a,c=.c,m=.m))) #print(initRANDC())
}

# --------------------------------------------------
# RANDC()
# Genera un valor pseudoaleatorio con el generador congruencial
# Actualiza la semilla (si no existe llama a initRANDC)
RANDC <- function() {
if (!exists(".semilla", envir=globalenv())) initRANDC()
.semilla <<- (.a * .semilla + .c) %% .m
return(.semilla/.m)
}

# --------------------------------------------------
# RANDCN(n)
# Genera un vector de valores pseudoaleatorios con el generador congruencial
# (por defecto de dimensión 1000)
# Actualiza la semilla (si no existe llama a initRANDC)
RANDCN <- function(n=1000) {
x <- numeric(n)
for(i in 1:n) x[i]<-RANDC()
return(x)
# return(replicate(n,RANDC())) # Alternativa más rápida
}

initRANDC(543210) # Fijar semilla 543210 para reproductibilidad


Nota: . Para evitar problemas computacionales, se recomienda emplear el mé-
3.1. GENERADORES CONGRUENCIALES (LINEALES) 31

todo de Schrage (ver Bratley et al., 1987; L’Ecuyer, 1988)


Ejemplos:
• c = 0, a = 216 + 3 = 65539 y m = 231 , generador RANDU de IBM (no
recomendable).
• c = 0, a = 75 = 16807 y m = 231 − 1 (primo de Mersenne), Park y Miller
(1988) “minimal standar”, empleado por las librerías IMSL y NAG.
• c = 0, a = 48271 y m = 231 − 1 actualización del “minimal standar”
propuesta por Park, Miller y Stockmeyer (1993).
Los parámetros y la semilla determinan los valores generados:
( )
i ai − 1
xi = a x0 + c mod m
a−1

A pesar de su simplicidad, una adecuada elección de los parámetros permite ob-


tener de manera eficiente secuencias de números “aparentemente” i.i.d. U(0, 1).
El procedimiento habitual solía ser escoger m de forma que la operación del
módulo se pudiese realizar de forma muy eficiente, para posteriormente seleccio-
nar c y a de forma que el período fuese lo más largo posible (o suficientemente
largo).
Teorema 3.1
(Hull y Dobell, 1962)
Un generador congruencial tiene período máximo (p = m) si y solo si:
1. c y m son primos relativos (i.e. m.c.d.(c, m) = 1).
2. a − 1 es múltiplo de todos los factores primos de m (i.e. a ≡ 1 mod q, para
todo q factor primo de m).
3. Si m es múltiplo de 4, entonces a − 1 también lo ha de ser (i.e. m ≡
0 mod 4 ⇒ a ≡ 1 mod 4).
.
Algunas consecuencias:
• Si m primo, p = m si y solo si a = 1.
• Un generador multiplicativo no cumple la condición 1 (m.c.d.(0, m) = m).
Teorema 3.2
Un generador multiplicativo tiene período máximo (p = m − 1) si:
1. m es primo.
2. a es una raiz primitiva de m (i.e. el menor entero q tal que aq = 1 mod m
es q = m − 1).
.
32CAPÍTULO 3. GENERACIÓN DE NÚMEROS PSEUDOALEATORIOS CON DISTRIBUCIÓN UNI

Además de preocuparse de la longitud del ciclo, las secuencias generadas deben


aparentar muestras i.i.d. U(0, 1).
Uno de los principales problemas es que los valores generados pueden mostrar
una clara estructura reticular. Este es el caso por ejemplo del generador RANDU
de IBM muy empleado en la década de los 70 (ver Figura 3.1). Por ejemplo, el
conjunto de datos randu contiene 400 tripletas de números sucesivos obtenidos
con la implementación en VAX/VMS 1.5 (1977).
system.time(u <- RANDCN(9999)) # Generar

## user system elapsed


## 0.03 0.00 0.03
xyz <- matrix(u, ncol = 3, byrow = TRUE)

library(plot3D)
points3D(xyz[,1], xyz[,2], xyz[,3], colvar = NULL, phi = 60, theta = -50, pch = 21, cex
z

Figura 3.1: Grafico de dispersión de tripletas del generador RANDU de IBM


(contenidas en 15 planos)

# Alternativamente se podría utilizar la función `plot3d` del paquete `rgl`,


# y pulsando con el ratón se podría rotar la figura para ver los hiperplanos:
# library(rgl)
# plot3d(xyz)

En general todos los generadores de este tipo van a presentar estructuras reti-
culares. Marsaglia (1968) demostró que las k-uplas de un generadores multipli-
3.1. GENERADORES CONGRUENCIALES (LINEALES) 33

1/k
cativo están contenidas en a lo sumo (k!m) hiperplanos paralelos (para más
detalles sobre la estructura reticular, ver por ejemplo Ripley, 1987, sección 2.7).
Por tanto habría que seleccionar adecuadamente m y c (a solo influiría en la
pendiente) de forma que la estructura reticular sea impreceptible teniendo en
cuenta el número de datos que se pretende generar (por ejemplo de forma que
la distancia mínima entre los puntos sea próxima a la esperada en teoría).
Se han propuesto diversas pruebas (ver Sección 3.2) para determinar si un gene-
rador tiene problemas de este tipo y se han realizado numerosos estudios para
determinadas familias (e.g. Park y Miller, 1988, estudiaron que parámetros son
adecuados para m = 231 − 1).
• En cualquier caso, se recomienda considerar un “periodo de seguridad”

≈ p para evitar este tipo de problemas.
• Aunque estos generadores tiene limitaciones en su capacidad para producir
secuencias muy largas de números i.i.d. U(0, 1), es un elemento básico en
generadores más avanzados.

3.1.1 Otros generadores

Se han considerado diversas extensiones del generador congruencial lineal sim-


ple:
• Lineal múltiple: xi = a0 + a1 xi−1 + a2 xi−2 + · · · + ak xi−k mod m, con
periodo p ≤ mk − 1.
• No lineal: xi = f (xi−1 , xi−2 , · · · , xi−k ) mod m. Por ejemplo xi = a0 +
a1 xi−1 + a2 x2i−1 mod m.
• Matricial: xi = A0 + A1 xi−1 + A2 xi−2 + · · · + Ak xi−k mod m.
Un ejemplo de generador congruencia lineal múltiple es el denominado generador
de Fibonacci retardado (Fibonacci-lagged generator; Knuth, 1969):

xn = (xn−37 + xn−100 ) mod 230 ,

con un período aproximado de 2129 y que puede ser empleado en R (lo cual no
sería en principio recomendable; ver Knuth Recent News 2002) estableciendo
kind a "Knuth-TAOCP-2002" o "Knuth-TAOCP" en la llamada a set.seed() o
RNGkind().
El generador Mersenne-Twister (Matsumoto y Nishimura, 1998), empleado por
defecto en R, de periodo 219937 − 1 y equidistribution en 623 dimensiones, se
puede expresar como un generador congruencial matricial lineal.
Un caso particular del generador lineal múltiple son los denominados genera-
dores de registros desfasados (más relacionados con la Criptografía). Se gene-
ran bits de forma secuencial considerando m = 2 y ai ∈ {0, 1} y se van com-
binando l bits para obtener valores en el intervalo (0, 1), por ejemplo ui =
34CAPÍTULO 3. GENERACIÓN DE NÚMEROS PSEUDOALEATORIOS CON DISTRIBUCIÓN UNI

0.xit+1 xit+2 . . . xit+l , siendo t un parámetro denominado aniquilación (Taus-


worthe, 1965). Los cálculos se pueden realizar rápidamente mediante operaciones
lógicas (los sumandos de la combinación lineal se traducen en un “o” exclusivo
XOR), empleando directamente los registros del procesador (ver por ejemplo,
Ripley, 1987, Algoritmo 2.1).
Otras alternativas consisten en la combinanción de varios generadores, las más
empleadas son:
∑L (l) (l)
• Combinar las salidas: por ejemplo ui = l=1 ui mod 1, donde ui es el
i-ésimo valor obtenido con el generador l.
• Barajar las salidas: por ejemplo se crea una tabla empleando un generador
y se utiliza otro para seleccionar el índice del valor que se va a devolver y
posteriormente actualizar.
El generador L’Ecuyer-CMRG (L’Ecuyer, 1999), empleado como base para la
generación de múltiples secuencias en el paquete parallel, combina dos gene-
radores concruenciales lineales múltiples de orden k = 3 (el periodo aproximado
es 2191 ).

Ejercicio 3.1.

Considera el generador congruencial definido por:

xn+1 = (5xn + 1) mod 512,


xn+1
un+1 = , n = 0, 1, . . .
512
(de ciclo máximo).
NOTA: El algoritmo está implementado en el fichero RANDC.R y se muestra
en la Sección 3.1.
a) Generar 500 valores de este generador, obtener el tiempo de CPU, repre-
sentar su distribución mediante un histograma (en escala de densidades)
y compararla con la densidad teórica.
initRANDC(321, 5, 1, 512) # Fijar semilla para reproductibilidad
nsim <- 500
system.time(u <- RANDCN(nsim)) # Generar

## user system elapsed


## 0 0 0
hist(u, freq = FALSE)
abline(h = 1) # Equivalente a curve(dunif(x, 0, 1), add = TRUE)

En este caso concreto la distribución de los valores generados es aparente-


mente más uniforme de lo que cabría esperar, lo que induciría a sospechar
de la calidad de este gerenador.
3.1. GENERADORES CONGRUENCIALES (LINEALES) 35

Histogram of u

1.0
0.8
0.6
Density

0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Figura 3.2: Histograma de los valores generados

b) Calcular la media de las simulaciones (mean) y compararla con la teórica.

La aproximación por simulación de la media teórica es:


mean(u)

## [1] 0.4999609

La media teórica es 0.5. Error absoluto 3.90625 × 10−5 .

c) Aproximar (mediante simulación) la probabilidad del intervalo (0.4; 0.8) y


compararla con la teórica.

La probabilidad teórica es 0.8 - 0.4 = 0.4

La aproximación mediante simulación:


sum((0.4 < u) & (u < 0.8))/nsim

## [1] 0.402
mean((0.4 < u) & (u < 0.8)) # Alternativa

## [1] 0.402
36CAPÍTULO 3. GENERACIÓN DE NÚMEROS PSEUDOALEATORIOS CON DISTRIBUCIÓN UNI

3.2 Análisis de la calidad de un generador


Para verificar si un generador tiene las propiedades estadísticas deseadas hay dis-
ponibles una gran cantidad de test de hipótesis y métodos gráficos, incluyendo
métodos genéricos (de bondad de ajuste y aleatoriedad) y contrastes específicos
para generadores aleatorios. Se trata principalmente de contrastar si las mues-
tras generadas son i.i.d. U (0, 1) (análisis univariante). Aunque los métodos más
avanzados tratan normalmente de contrastar si las d-uplas:

(Ut+1 , Ut+2 , . . . , Ut+d ); t = (i − 1)d, i = 1, . . . , m


d
son i.i.d. U (0, 1) (uniformes independientes en el hipercubo; análisis multiva-
riante). En el Apéndice B se describen algunos de estos métodos.
En esta sección emplearemos únicamente métodos genéricos, ya que también
pueden ser de utilidad para evaluar generadores de variables no uniformes y pa-
ra la construcción de modelos del sistema real (e.g. para modelar variables que se
tratarán como entradas del modelo general). Sin embargo, los métodos clásicos
pueden no ser muy adecuados para evaluar generadores de números pseudoalea-
torios (e.g. L’Ecuyer y Simard, 2007). La recomendación sería emplear baterías
de contrastes recientes, como las descritas en la Subsección 3.2.2.
Hay que destacar algunas diferencias entre el uso de este tipo de métodos en
inferencia y en simulación. Por ejemplo, si empleamos un constrate de hipótesis
del modo habitual, desconfiamos del generador si la muestra (secuencia) no se
ajusta a la distribución teórica (p-valor ≤ α). En este caso además, también se
sospecha si se ajusta demasiado bien a la distribución teórica (p-valor ≥ 1 − α),
lo que indicaría que no reproduce adecuadamente la variabilidad.
Uno de los contrastes más conocidos es el test ji-cuadrado de bondad de ajuste
(chisq.test para el caso discreto). Aunque si la variable de interés es continua,
habría que discretizarla (con la correspondiente perdida de información). Por
ejemplo, se podría emplear la siguiente función (que imita a las incluídas en R):
#-------------------------------------------------------------------------------
# chisq.test.cont(x, distribution, nclasses, output, nestpar,...)
#-------------------------------------------------------------------------------
# Realiza el test ji-cuadrado de bondad de ajuste para una distribución continua
# discretizando en intervalos equiprobables.
# Parámetros:
# distribution = "norm","unif",etc
# nclasses = floor(length(x)/5)
# output = TRUE
# nestpar = 0= nº de parámetros estimados
# ... = parámetros distribución
# Ejemplo:
# chisq.test.cont(x, distribution="norm", nestpar=2, mean=mean(x), sd=sqrt((nx-1)/nx)
3.2. ANÁLISIS DE LA CALIDAD DE UN GENERADOR 37

#-------------------------------------------------------------------------------
chisq.test.cont <- function(x, distribution = "norm", nclasses = floor(length(x)/5),
output = TRUE, nestpar = 0, ...) {
# Funciones distribución
q.distrib <- eval(parse(text = paste("q", distribution, sep = "")))
d.distrib <- eval(parse(text = paste("d", distribution, sep = "")))
# Puntos de corte
q <- q.distrib((1:(nclasses - 1))/nclasses, ...)
tol <- sqrt(.Machine$double.eps)
xbreaks <- c(min(x) - tol, q, max(x) + tol)
# Gráficos y frecuencias
if (output) {
xhist <- hist(x, breaks = xbreaks, freq = FALSE, lty = 2, border = "grey50")
curve(d.distrib(x, ...), add = TRUE)
} else {
xhist <- hist(x, breaks = xbreaks, plot = FALSE)
}
# Cálculo estadístico y p-valor
O <- xhist$counts # Equivalente a table(cut(x, xbreaks)) pero más eficiente
E <- length(x)/nclasses
DNAME <- deparse(substitute(x))
METHOD <- "Pearson's Chi-squared test"
STATISTIC <- sum((O - E)^2/E)
names(STATISTIC) <- "X-squared"
PARAMETER <- nclasses - nestpar - 1
names(PARAMETER) <- "df"
PVAL <- pchisq(STATISTIC, PARAMETER, lower.tail = FALSE)
# Preparar resultados
classes <- format(xbreaks)
classes <- paste("(", classes[-(nclasses + 1)], ",", classes[-1], "]",
sep = "")
RESULTS <- list(classes = classes, observed = O, expected = E, residuals = (O -
E)/sqrt(E))
if (output) {
cat("\nPearson's Chi-squared test table\n")
print(as.data.frame(RESULTS))
}
if (any(E < 5))
warning("Chi-squared approximation may be incorrect")
structure(c(list(statistic = STATISTIC, parameter = PARAMETER, p.value = PVAL,
method = METHOD, data.name = DNAME), RESULTS), class = "htest")
}

Continuando con el generador congruencial anterior, obtendríamos:


38CAPÍTULO 3. GENERACIÓN DE NÚMEROS PSEUDOALEATORIOS CON DISTRIBUCIÓN UNI

chisq.test.cont(u, distribution = "unif",


nclasses = 10, nestpar = 0, min = 0, max = 1)

Histogram of x

1.0
0.8
0.6
Density

0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Figura 3.3: Gráfico resultante de aplicar la función ‘chisq.test.cont()‘ comparan-


do el histograma de los valores generados con la densidad uniforme.

##
## Pearson's Chi-squared test table
## classes observed expected residuals
## 1 (-1.490116e-08, 1.000000e-01] 51 50 0.1414214
## 2 ( 1.000000e-01, 2.000000e-01] 49 50 -0.1414214
## 3 ( 2.000000e-01, 3.000000e-01] 49 50 -0.1414214
## 4 ( 3.000000e-01, 4.000000e-01] 50 50 0.0000000
## 5 ( 4.000000e-01, 5.000000e-01] 51 50 0.1414214
## 6 ( 5.000000e-01, 6.000000e-01] 51 50 0.1414214
## 7 ( 6.000000e-01, 7.000000e-01] 49 50 -0.1414214
## 8 ( 7.000000e-01, 8.000000e-01] 50 50 0.0000000
## 9 ( 8.000000e-01, 9.000000e-01] 50 50 0.0000000
## 10 ( 9.000000e-01, 9.980469e-01] 50 50 0.0000000
##
## Pearson's Chi-squared test
##
## data: u
## X-squared = 0.12, df = 9, p-value = 1
Como se muestra en la Figura 3.3 el histograma de la secuencia generada es muy
plano (comparado con lo que cabría esperar de una muestra de tamaño 500 de
3.2. ANÁLISIS DE LA CALIDAD DE UN GENERADOR 39

una uniforme), y consecuentemente el p-valor del contraste ji-cuadrado es prác-


ticamente 1, lo que indicaría que este generador no reproduce adecuadamente
la variabilidad de una distribución uniforme.
Otro contraste de bondad de ajuste muy conocido es el test de Kolmogorov-
Smirnov, implementado en ks.test. En la Sección B.1 se describen con más
detalle estos contrastes.

Ejercicio 3.2.

Continuando con el generador congruencial anterior:


initRANDC(321, 5, 1, 512) # Fijar semilla para reproductibilidad
nsim <- 500
system.time(u <- RANDCN(nsim)) # Generar

a) Realizar el contraste de Kolmogorov-Smirnov para estudiar el ajuste a una


U(0, 1).
Este contraste de hipótesis compara la función de distribución bajo la
hipótesis nula con la función de distribución empírica (ver Sección B.1.2):
# Distribución empírica
curve(ecdf(u)(x), type = "s", lwd = 2)
curve(punif(x, 0, 1), add = TRUE)
1.0
0.8
0.6
ecdf(u)(x)

0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Figura 3.4: Comparación de la distribución empírica de la secuencia generada


con la función de distribución uniforme.

Podemos realizar el contraste con el siguiente código:


40CAPÍTULO 3. GENERACIÓN DE NÚMEROS PSEUDOALEATORIOS CON DISTRIBUCIÓN UNI

# Test de Kolmogorov-Smirnov
ks.test(u, "punif", 0, 1)

##
## One-sample Kolmogorov-Smirnov test
##
## data: u
## D = 0.0033281, p-value = 1
## alternative hypothesis: two-sided

b) Obtener el gráfico secuencial y el de dispersión retardado, ¿se observa


algún problema?

Gráfico secuencial:
plot(as.ts(u))
1.0
0.8
0.6
as.ts(u)

0.4
0.2
0.0

0 100 200 300 400 500

Time

Gráfico de dispersión retardado:


plot(u[-nsim],u[-1])
3.2. ANÁLISIS DE LA CALIDAD DE UN GENERADOR 41

1.0
0.8
0.6
u[−1]

0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

u[−nsim]

c) Estudiar las correlaciones del vector (ui , ui+k ), con k = 1, . . . , 10. Contras-
tar si son nulas.
Correlaciones:
acf(u)

Series u
1.0
0.8
0.6
ACF

0.4
0.2
0.0

0 5 10 15 20 25

Lag

Test de Ljung-Box:
Box.test(u, lag = 10, type = "Ljung")

##
## Box-Ljung test
##
42CAPÍTULO 3. GENERACIÓN DE NÚMEROS PSEUDOALEATORIOS CON DISTRIBUCIÓN UNI

## data: u
## X-squared = 22.533, df = 10, p-value = 0.01261

3.2.1 Repetición de contrastes

Los contrastes se plantean habitualmente desde el punto de vista de la inferencia


estadística en la práctica: se realiza una prueba sobre la única muestra disponible.
Si se realiza una única prueba, en las condiciones de H0 hay una probabilidad α
de rechazarla. En simulación tiene mucho más sentido realizar un gran número
de pruebas:
• La proporción de rechazos debería aproximarse al valor de α(se puede
comprobar para distintos valores de α).
• La distribución del estadístico debería ajustarse a la teórica bajo H0 (se
podría realizar un nuevo contraste de bondad de ajuste).
• Los p-valores obtenidos deberían ajustarse a una U (0, 1) (se podría realizar
también un contraste de bondad de ajuste).
Este procedimiento es también el habitual para validar un método de contraste
de hipótesis por simulación.

Ejemplo 3.1.

Consideramos el generador congruencial RANDU:


# Valores iniciales
initRANDC(543210) # Fijar semilla para reproductibilidad
# set.seed(543210)
n <- 500
nsim <- 1000
estadistico <- numeric(nsim)
pvalor <- numeric(nsim)

# Realizar contrastes
for(isim in 1:nsim) {
u <- RANDCN(n) # Generar
# u <- runif(n)
tmp <- chisq.test.cont(u, distribution="unif",
nclasses=100, output=FALSE, nestpar=0, min=0, max=1)
estadistico[isim] <- tmp$statistic
pvalor[isim] <- tmp$p.value
}

Proporción de rechazos:
3.2. ANÁLISIS DE LA CALIDAD DE UN GENERADOR 43

# cat("\nProporción de rechazos al 1% =", sum(pvalor < 0.01)/nsim, "\n")


cat("\nProporción de rechazos al 1% =", mean(pvalor < 0.01), "\n")

##
## Proporción de rechazos al 1% = 0.014
# cat("Proporción de rechazos al 5% =", sum(pvalor < 0.05)/nsim, "\n")
cat("Proporción de rechazos al 5% =", mean(pvalor < 0.05), "\n")

## Proporción de rechazos al 5% = 0.051


# cat("Proporción de rechazos al 10% =", sum(pvalor < 0.1)/nsim, "\n")
cat("Proporción de rechazos al 10% =", mean(pvalor < 0.1), "\n")

## Proporción de rechazos al 10% = 0.112


Análisis del estadístico contraste:
# Histograma
hist(estadistico, breaks = "FD", freq=FALSE)
curve(dchisq(x,99), add=TRUE)

Histogram of estadistico
0.030
0.025
0.020
Density

0.015
0.010
0.005
0.000

60 80 100 120 140

estadistico

# Test ji-cuadrado
# chisq.test.cont(estadistico, distribution="chisq", nclasses=20, nestpar=0, df=99)
# Test de Kolmogorov-Smirnov
ks.test(estadistico, "pchisq", df=99)

##
## One-sample Kolmogorov-Smirnov test
##
44CAPÍTULO 3. GENERACIÓN DE NÚMEROS PSEUDOALEATORIOS CON DISTRIBUCIÓN UNI

## data: estadistico
## D = 0.023499, p-value = 0.6388
## alternative hypothesis: two-sided
Análisis de los p-valores:
# Histograma
hist(pvalor, freq=FALSE)
abline(h=1) # curve(dunif(x,0,1), add=TRUE)

Histogram of pvalor
1.2
1.0
0.8
Density

0.6
0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

pvalor

# Test ji-cuadrado
# chisq.test.cont(pvalor, distribution="unif", nclasses=20, nestpar=0, min=0, max=1)
# Test de Kolmogorov-Smirnov
ks.test(pvalor, "punif", min=0, max=1)

##
## One-sample Kolmogorov-Smirnov test
##
## data: pvalor
## D = 0.023499, p-value = 0.6388
## alternative hypothesis: two-sided
Adicionalmente, si queremos estudiar la proporción de rechazos (el tamaño del
contraste) para los posibles valores de α, podemos emplear la distribución em-
pírica del p-valor (proporción de veces que resultó menor que un determinado
valor):
# Distribución empírica
curve(ecdf(pvalor)(x), type = "s", lwd = 2,
xlab = 'Nivel de significación', ylab = 'Proporción de rechazos')
3.2. ANÁLISIS DE LA CALIDAD DE UN GENERADOR 45

abline(a=0, b=1, lty=2) # curve(punif(x, 0, 1), add = TRUE)

1.0
0.8
Proporción de rechazos

0.6
0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Nivel de significación

3.2.2 Baterías de contrastes

Hay numerosos ejemplos de generadores que pasaron diferentes test de unifor-


midad y aleatoriedad pero que fallaron estrepitosamente al considerar nuevos
contrastes diseñados específicamente para generadores aleatorios (ver Marsaglia
et al., 1990). Por este motivo, el procedimiento habitual en la práctica es aplicar
un número más o menos elevado de contrastes (de distinto tipo y difíciles de
pasar, e.g. Marsaglia y Tsang, 2002), de forma que si el generador los pasa tendre-
mos mayor confianza en que sus propiedades son las adecuadas. Este conjunto
de pruebas es lo que se denomina batería de contrastes. Una de las primeras se
introdujo en Knuth (1969) y de las más recientes podríamos destacar:
• Diehard tests (The Marsaglia Random Number CDROM, 1995):
http://www.stat.fsu.edu/pub/diehard (versión archivada el 2016-01-25).
• Dieharder (Brown y Bauer, 2003): Dieharder Page, paquete RDieHarder.
• TestU01 (L’Ecuyer y Simard, 2007): http://simul.iro.umontreal.ca/
testu01/tu01.html.
• NIST test suite (National Institute of Standards and Technology, USA,
2010): http://csrc.nist.gov/groups/ST/toolkit/rng.
Para más detalles, ver por ejemplo1 :
1 También puede ser de interés el enlace Randomness Tests: A Literature Survey y la entidad

certificadora (gratuita) en línea CAcert.


46CAPÍTULO 3. GENERACIÓN DE NÚMEROS PSEUDOALEATORIOS CON DISTRIBUCIÓN UNI

• Marsaglia, G. y Tsang, W.W. (2002). Some difficult-to-pass tests of ran-


domness. Journal of Statistical Software, 7(3), 1-9.
• Demirhan, H. y Bitirim, N. (2016). CryptRndTest: an R package for tes-
ting the cryptographic randomness. The R Journal, 8(1), 233-247.

3.3 Ejercicios
Ejercicio 3.3.

Uno de los primeros generadores fue el denominado método de los cuadrados


medios propuesto por Von Neumann (1946). Con este procedimiento se generan
números pseudoaleatorios de 4 dígitos de la siguiente forma:
i. Se escoge un número de cuatro dígitos x0 (semilla).
ii. Se eleva al cuadrado (x20 ) y se toman los cuatro dígitos centrales (x1 ).
iii. Se genera el número pseudo-aleatorio como
x1
u1 = .
104

iv. Volver al paso ii y repetir el proceso.


Para obtener los k (número par) dígitos centrales de x2i se puede utilizar que:
⌊( ⌊ ⌋ ) ⌋
x2i (2k− k k
xi+1 = x2i − k 10 2) /10 2

10(2k− 2 )

El algoritmo está implementado en el fichero RANDVN.R:


# Generador Von Neumann de números pseudoaleatorios
# =================================================

# -------------------------------------------------
# initRANDVN(semilla,n)
# Inicia el generador
# n número de digitos centrales, 4 por defecto (debe ser un nº par)
# Por defecto semilla del reloj
# OJO: No se hace ninguna verificación de los parámetros
initRANDVN <- function(semilla = as.numeric(Sys.time()), n = 4) {
.semilla <<- as.double(semilla) %% 10^n # Cálculos en doble precisión
.n <<- n
.aux <<- 10^(2*n-n/2)
.aux2 <<- 10^(n/2)
return(invisible(list(semilla=.semilla,n=.n)))
3.3. EJERCICIOS 47

# -------------------------------------------------
# RANDVN()
# Genera un valor pseudoaleatorio con el generador de Von Neumann
# Actualiza la semilla (si no existe llama a initRANDVN)
RANDVN <- function() {
if (!exists(".semilla", envir=globalenv())) initRANDVN()
z <- .semilla^2
.semilla <<- trunc((z-trunc(z/.aux)*.aux)/.aux2)
return(.semilla/10^.n)
}

# -------------------------------------------------
# RANDVNN(n)
# Genera un vector de valores pseudoaleatorios con el generador congruencial
# (por defecto de dimensión 1000)
# Actualiza la semilla (si no existe llama a initRANDVN)
RANDVNN <- function(n = 1000) {
x <- numeric(n)
for(i in 1:n) x[i] <- RANDVN()
return(x)
# return(replicate(n,RANDVN())) # Alternativa más rápida
}

Estudiar las características del generador de cuadrados medios a partir de una


secuencia de 500 valores. Emplear únicamente métodos gráficos.

Ejercicio 3.4.

Considerando el generador congruencial multiplicativo de parámetros a = 75 =


16807, c = 0 y m = 231 − 1. ¿Se observan los mismos problemas que con el
algoritmo RANDU al considerar las tripletas (xk , xk+1 , xk+2 )?
48CAPÍTULO 3. GENERACIÓN DE NÚMEROS PSEUDOALEATORIOS CON DISTRIBUCIÓN UNI
Capítulo 4

Análisis de resultados de
simulación

Work in progress…
En este capítulo nos centraremos en la aproximación mediante simulación de la
media teórica de un estadístico a partir de la media muestral de una secuencia
de simulaciones de dicho estadístico. La aproximación de una probabilidad sería
un caso particular considerando una variable de Bernouilli.
En primer lugar se tratará el análisis de la convergencia y la precisión de la
aproximación por simulación. Al final del capítulo se incluye una breve intro-
ducción a los problemas de estabilización y dependencia (con los que nos solemos
encontrar en simulación dinámica y MCMC).

4.1 Convergencia

Supongamos que estamos interesados en aproximar la media teórica µ = E (X)


a partir de una secuencia i.i.d. X1 , X2 , · · ·, Xn mediante la media muestral X̄n .
Una justificación teórica de la validez de la aproximación obtenida mediante
simulación es la ley (débil) de los grandes números:
• Si X1 , X2 , · · · es una secuencia de v.a.’s independientes con:

E (Xi ) = µ y V ar (Xi ) = σ 2 < ∞,

entonces X n = (X1 + · · · + Xn ) /n converge en probabilidad a µ. i.e. para


cualquier ε > 0:
( )
lim P X n − µ < ε = 1.
n→∞

49
50 CAPÍTULO 4. ANÁLISIS DE RESULTADOS DE SIMULACIÓN

• La ley fuerte establece la convergencia casi segura.

Ejemplo 4.1. Aproximación de una probabilidad

Simulamos una distribución de Bernoulli de parámetro p = 0.5:


p <- 0.5
set.seed(1)
nsim <- 10000
# nsim <- 100
rx <- runif(nsim) <= p

La aproximación por simulación de p será:


mean(rx)

## [1] 0.5047
Podemos generar un gráfico con la evolución de la aproximación con el siguiente
código:
plot(cumsum(rx)/1:nsim, type="l", lwd=2, xlab="Número de generaciones",
ylab="Proporción muestral", ylim=c(0,1))
abline(h = mean(rx), lty = 2)
# valor teórico
abline(h = p)
1.0
0.8
Proporción muestral

0.6
0.4
0.2
0.0

0 2000 4000 6000 8000 10000

Número de generaciones

Figura 4.1: Aproximación de la proporción en función del número de generacio-


nes.
4.1. CONVERGENCIA 51

4.1.1 Detección de problemas de convergencia

Una suposición crucial es que las variables Xi deben tener varianza finita (real-
mente esta suposición puede relajarse: E (|Xi |) < ∞). En caso contrario la
media muestral puede no converger a una constante. Un ejemplo conocido es la
distribución de Cauchy:
set.seed(1)
nsim <- 10000
rx <- rcauchy(nsim)
plot(cumsum(rx)/1:nsim, type="l", lwd=2,
xlab="Número de generaciones", ylab="Media muestral")
8
6
Media muestral

4
2
0
−2

0 2000 4000 6000 8000 10000

Número de generaciones

Figura 4.2: Evolución de la media muestral de una distribución de Cauchy en


función del número de generaciones.

Para detectar problemas de convergencia es recomendable representar la evo-


lución de la aproximación de la característica de interés (sobre el número de
generaciones), además de realizar otros análisis descriptivos de las simulaciones.
Por ejemplo, en este caso podemos observar los valores que producen estos saltos
mediante un gráfico de cajas:
boxplot(rx)
52 CAPÍTULO 4. ANÁLISIS DE RESULTADOS DE SIMULACIÓN

15000
10000
5000
0
−5000

Figura 4.3: Gráfico de cajas de 10000 generaciones de una distribución de


Cauchy.

4.2 Estimación de la precisión


( )
En el caso de la media muestral X n , un estimador insesgado de V ar X n =
σ 2 /n es la cuasi-varianza muestral:

( ) Sb2
Vd
ar X n =
n
con:
1 ∑( )2
n
Sbn2 = Xi − X .
n − 1 i=1

En el caso de una proporción p̂n :

p̂n (1 − p̂n )
Vd
ar (p̂n ) = ,
n−1

aunque se suele emplear la varianza muestral.

Los valores obtenidos servirían como medidas básicas de la precisión de la apro-


ximación, aunque su principal aplicación es la construcción de intervalos de
confianza.
4.3. TEOREMA CENTRAL DEL LÍMITE 53

4.3 Teorema central del límite

Si X1 , X2 , · · · es una secuencia de v.a.’s independientes con E (Xi ) = µ y


V ar (Xi ) = σ 2 < ∞, entonces:

Xn − µ d
Zn = → N (0, 1)
√σ
n

i.e. lim FZn (z) = Φ(z). Por tanto, un intervalo de confianza asintótico para µ
n→∞
es: ( )
Sbn Sbn
IC1−α (µ) = X n − z1−α/2 √ , X n + z1−α/2 √ .
n n

Sbn
Podemos considerar que z1−α/2 √ es la precisión obtenida (con nivel de con-
n
fianza 1 − α).
La convergencia de la aproximación, además de ser aleatoria, se podría conside-
rar lenta. La idea es que para doblar la precisión (disminuir el error a la mitad),
necesitaríamos un número de generaciones cuatro veces mayor. Pero una ventaja,
es que este error no depende del número de dimensiones (en el caso multidimen-
sional puede ser mucho más rápida que otras alternativas numéricas).

Ejemplo 4.2. Aproximación de la media de una distribución normal

xsd <- 1
xmed <- 0
set.seed(1)
nsim <- 1000
rx <- rnorm(nsim, xmed, xsd)

La aproximación por simulación de la media será:


mean(rx)

## [1] -0.01164814
Como medida de la precisión de la aproximación podemos considerar (se suele
denominar error de la aproximación):
2*sd(rx)/sqrt(nsim)

## [1] 0.06545382
(es habitual emplear 2 en lugar de 1.96, lo que se correspondería con 1 − α =
0.9545 en el caso de normalidad). Podemos añadir también los correspondientes
intervalos de confianza al gráfico de convergencia:
54 CAPÍTULO 4. ANÁLISIS DE RESULTADOS DE SIMULACIÓN

n <- 1:nsim
est <- cumsum(rx)/n
# Errores estándar calculados con la varianza muestral por comodidad:
esterr <- sqrt(cumsum((rx-est)^2))/n
plot(est, type = "l", lwd = 2, xlab = "Número de generaciones",
ylab = "Media y rango de error", ylim = c(-1, 1))
abline(h = est[nsim], lty=2)
lines(est + 2*esterr, lty=3)
lines(est - 2*esterr, lty=3)
abline(h = xmed)
1.0
0.5
Media y rango de error

0.0
−0.5
−1.0

0 200 400 600 800 1000

Número de generaciones

Figura 4.4: Gráfico de convergencia incluyendo el error de la aproximación.

4.4 Determinación del número de generaciones

Normalmente el valor de n se toma del orden de varias centenas o millares. En los


casos en los que la simulación se utiliza para aproximar una característica central
de la distribución (como una media) puede bastar un número de simulaciones
del orden de n = 100, 200, 500. Sin embargo, en otros casos pueden ser necesarios
valores del tipo B = 1000, 2000, 5000, 10000.

En muchas ocasiones puede interesar obtener una aproximación con un nivel


de precisión fijado. Para una precisión absoluta ε, se trata de determinar n de
4.5. EL PROBLEMA DE LA DEPENDENCIA 55

forma que:
Sbn
z1−α/2 √ < ε
n

Un algoritmo podría ser el siguiente:


1. Hacer j = 0 y fijar un tamaño inicial n0 (e.g. 30 ó 60).
y calcular X n0 y Sbn0 .
n0
2. Generar {Xi }i=1
/

3. Mientras z1−α/2 Sbnj nj > ε hacer:

3.1. j = j + 1.
⌈( / )2 ⌉
3.2. nj = z1−α/2 Sbnj−1 ε .

y calcular X nj y Sbnj .
nj
3.3. Generar {Xi }i=nj−1 +1
/

4. Devolver X nj y z1−α/2 Sbnj nj .

Para una precisión relativa ε |µ| se procede análogamente de forma que:

Sbn
z1−α/2 √ < ε X n .
n

4.5 El problema de la dependencia


En el caso de dependencia, la estimación de la precisión se complica:
 
( ) 1 ∑ ∑
n
V ar X = 2 V ar (Xi ) + 2 Cov (Xi , Xj ) .
n i=1 i<j

Ejemplo 4.3. Aproximación de una proporción bajo dependencia (cadena de


Markov)

Supongamos que en A Coruña llueve de media 1/3 días al año, y que la probabi-
lidad de que un día llueva solo depende de lo que ocurrió el día anterior, siendo
0.94 si el día anterior llovió y 0.03 si no. Podemos generar valores de la variable
indicadora de día lluvioso con el siguiente código:
# Variable dicotómica 0/1 (FALSE/TRUE)
set.seed(1)
nsim <- 10000
alpha <- 0.03 # prob de cambio si seco
beta <- 0.06 # prob de cambio si lluvia
rx <- logical(nsim) # x == "llueve"
56 CAPÍTULO 4. ANÁLISIS DE RESULTADOS DE SIMULACIÓN

rx[1] <- FALSE # El primer día no llueve


for (i in 2:nsim)
rx[i] <- if (rx[i-1]) runif(1) > beta else runif(1) < alpha

Se podría pensar en emplear las expresiones anteriores:


n <- 1:nsim
est <- cumsum(rx)/n
esterr <- sqrt(est*(1-est)/(n-1)) # OJO! Supone independencia
plot(est, type="l", lwd=2, ylab="Probabilidad",
xlab="Número de simulaciones", ylim=c(0,0.6))
abline(h = est[nsim], lty=2)
lines(est + 2*esterr, lty=2)
lines(est - 2*esterr, lty=2)
abline(h = 1/3, col="darkgray") # Prob. teor. cadenas Markov
0.6
0.5
0.4
Probabilidad

0.3
0.2
0.1
0.0

0 2000 4000 6000 8000 10000

Número de simulaciones

Figura 4.5: Gráfico de convergencia incluyendo el error de la aproximación (cal-


culado asumiendo independencia).

La aproximación de la proporción sería correcta (es consistente):


est[nsim]

## [1] 0.3038

Sin embargo, al ser datos dependientes esta aproximación del error estandar no
es adecuada:
4.5. EL PROBLEMA DE LA DEPENDENCIA 57

esterr[nsim]

## [1] 0.004599203
En este caso al haber dependencia positiva se produce una subestimación del
verdadero error estandar.
acf(as.numeric(rx))

Series as.numeric(rx)
1.0
0.8
0.6
ACF

0.4
0.2
0.0

0 10 20 30 40

Lag

Figura 4.6: Correlograma de la secuencia indicadora de días de lluvia.

El gráfico de autocorrelaciones sugiere que si tomamos 1 de cada 25 podemos


suponer independencia.
lag <- 24
xlag <- c(rep(FALSE, lag), TRUE)
rxi <- rx[xlag]
acf(as.numeric(rxi))
nrxi <- length(rxi)
n <- 1:nrxi
est <- cumsum(rxi)/n
esterr <- sqrt(est*(1-est)/(n-1))
plot(est, type="l", lwd=2, ylab="Probabilidad",
xlab=paste("Número de simulaciones /", lag + 1), ylim=c(0,0.6))
abline(h = est[length(rxi)], lty=2)
lines(est + 2*esterr, lty=2) # Supone independencia
lines(est - 2*esterr, lty=2)
abline(h = 1/3, col="darkgray") # Prob. teor. cadenas Markov
58 CAPÍTULO 4. ANÁLISIS DE RESULTADOS DE SIMULACIÓN

Series as.numeric(rxi)

1.0
0.8
0.6
ACF

0.4
0.2
0.0

0 5 10 15 20 25

Lag

Figura 4.7: Correlograma de la subsecuencia de días de lluvia obtenida seleccio-


nando uno de cada 25 valores.
0.6
0.5
0.4
Probabilidad

0.3
0.2
0.1
0.0

0 100 200 300 400

Número de simulaciones / 25

Figura 4.8: Gráfico de convergencia de la aproximación de la probabilidad a


partir de la subsecuencia de días de lluvia (calculando el error de aproximación
asumiendo independencia).

Esta forma de proceder podría ser adecuada para tratar de aproximar la preci-
4.5. EL PROBLEMA DE LA DEPENDENCIA 59

sión:
esterr[nrxi]

## [1] 0.02277402
pero no sería eficiente para aproximar la media. Siempre será preferible emplear
todas las observaciones.
Por ejemplo, se podría pensar en considerar las medias de grupos de 24 valores
consecutivos y suponer que hay independencia entre ellas:
rxm <- rowMeans(matrix(rx, ncol = lag, byrow = TRUE))
nrxm <- length(rxm)
n <- 1:nrxm
est <- cumsum(rxm)/n
esterr <- sqrt(cumsum((rxm-est)^2))/n # Error estándar
plot(est, type="l", lwd=2, ylab="Probabilidad",
xlab=paste("Número de simulaciones /", lag + 1), ylim=c(0,0.6))
abline(h = est[length(rxm)], lty=2)
lines(est + 2*esterr, lty=2) # OJO! Supone independencia
lines(est - 2*esterr, lty=2)
abline(h = 1/3, col="darkgray") # Prob. teor. cadenas Markov
0.6
0.5
0.4
Probabilidad

0.3
0.2
0.1
0.0

0 100 200 300 400

Número de simulaciones / 25

Figura 4.9: Gráfico de convergencia de las medias por lotes.

Esta es la idea del método de medias por lotes (batch means; macro-micro
replicaciones) para la estimación de la varianza. En el ejemplo anterior se calcula
el error estándar de la aproximación por simulación de la proporción:
60 CAPÍTULO 4. ANÁLISIS DE RESULTADOS DE SIMULACIÓN

esterr[nrxm]

## [1] 0.01569017
pero si el objetivo es la aproximación de la varianza (de la variable y no de
las medias por lotes), habrá que reescalarlo adecuadamente. Supongamos que
la correlación entre Xi y Xi+k es aproximadamente nula, y consideramos las
subsecuencias (lotes) (Xt+1 , Xt+2 , . . . , Xt+k ) con t = (j − 1)k, j = 1, . . . , m y
n = mk. Entonces:

( )   
( ) 1∑ ∑ ∑
n m ik
1 1
V ar X̄ = V ar Xi = V ar   Xt 
n i=1 m j=1 k
t=(i−1)k+1
 
1 ∑ ∑ ( )
m ik
1 1
≈ 2 V ar  Xt  ≈ V ar X̄k
m j=1 k m
t=(i−1)k+1

donde X̄k es la media de una subsecuencia de longitud k.


var.aprox <- nsim * esterr[length(rxm)]^2
var.aprox

## [1] 2.461814
Obtenida asumiendo independencia entre las medias por lotes, y que será una
mejor aproximación que asumir independencia entre las generaciones de la va-
riable:
var(rx)

## [1] 0.2115267
Alternativamente se podría recurrir a la generación de múltiples secuencias in-
dependientes entre sí:
# Variable dicotómica 0/1 (FALSE/TRUE)
set.seed(1)
nsim <- 1000
nsec <- 10
alpha <- 0.03 # prob de cambio si seco
beta <- 0.06 # prob de cambio si lluvia
rxm <- matrix(FALSE, nrow = nsec, ncol= nsim)
for (i in 1:nsec) {
# rxm[i, 1] <- FALSE # El primer día no llueve
# rxm[i, 1] <- runif(1) < 1/2 # El primer día llueve con probabilidad 1/2
rxm[i, 1] <- runif(1) < 1/3 # El primer día llueve con probabilidad 1/3 (ideal)
for (j in 2:nsim)
rxm[i, j] <- if (rxm[i, j-1]) runif(1) > beta else runif(1) < alpha
}
4.5. EL PROBLEMA DE LA DEPENDENCIA 61

La idea sería considerar las medias de las series como una muestra independiente
de una nueva variable y estimar su varianza de la forma habitual:
# Media de cada secuencia
n <- 1:nsim
est <- apply(rxm, 1, function(x) cumsum(x)/n)
matplot(n, est, type = 'l', lty = 3, col = "lightgray",
ylab="Probabilidad", xlab="Número de simulaciones")
# Aproximación
mest <- apply(est, 1, mean)
lines(mest, lwd = 2)
abline(h = mest[nsim], lty = 2)
# Precisión
mesterr <- apply(est, 1, sd)/sqrt(nsec)
lines(mest + 2*mesterr, lty = 2)
lines(mest - 2*mesterr, lty = 2)
# Prob. teor. cadenas Markov
abline(h = 1/3, col="darkgray")
1.0
0.8
0.6
Probabilidad

0.4
0.2
0.0

0 200 400 600 800 1000

Número de simulaciones

Figura 4.10: Gráfico de convergencia de la media de 10 secuencias generadas de


forma independiente.

# Aproximación final
mest[nsim] # mean(rxm)

## [1] 0.3089
62 CAPÍTULO 4. ANÁLISIS DE RESULTADOS DE SIMULACIÓN

# Error estándar
mesterr[nsim]

## [1] 0.02403491

Trataremos este tipo de problemas en la diagnosis de algoritmos de simulación


Monte Carlo de Cadenas de Markov (MCMC). Aparecen también en la simula-
ción dinámica (por eventos o cuantos).

4.5.1 Periodo de calentamiento

En el caso de simulación de datos dependientes (simulación dinámica) pueden


aparecer problemas de estabilización. Puede ocurrir que el sistema evolucione
lentamente en el tiempo hasta alcanzar su distribución estacionaria, siendo muy
sensible a las condiciones iniciales con las que se comienzó la simulación. En tal
caso resulta conveniente ignorar los resultados obtenidos durante un cierto pe-
ríodo inicial de tiempo (denominado período de calentamiento o estabilización),
cuyo único objeto es conseguir que se estabilice la distribución de probabilidad.

Como ejemplo comparamos la simulación del Ejemplo 4.3 con la obtenida con-
siderando como punto de partida un día lluvioso (con una semilla distinta para
evitar dependencia).
set.seed(2)
nsim <- 10000
rx2 <- logical(nsim)
rx2[1] <- TRUE # El primer día llueve
for (i in 2:nsim)
rx2[i] <- if (rx2[i-1]) runif(1) > beta else runif(1) < alpha
n <- 1:nsim
est <- cumsum(rx)/n
est2 <- cumsum(rx2)/n
plot(est, type="l", ylab="Probabilidad",
xlab="Número de simulaciones", ylim=c(0,0.6))
lines(est2, lty = 2)
# Ejemplo periodo calentamiento nburn = 2000
abline(v = 2000, lty = 3)
# Prob. teor. cadenas Markov
abline(h = 1/3, col="darkgray")
4.5. EL PROBLEMA DE LA DEPENDENCIA 63

0.6
0.5
0.4
Probabilidad

0.3
0.2
0.1
0.0

0 2000 4000 6000 8000 10000

Número de simulaciones

En estos casos puede ser recomendable ignorar los primeros valores generados
(por ejemplo los primeros 2000) y recalcular los estadísticos deseados.
También trataremos este tipo de problemas en la diagnosis de algoritmos
MCMC.

Ejemplo 4.4. Simulación de un proceso autorregresivo (serie de tiempo)

Xt = µ + ρ ∗ (Xt−1 − µ) + εt
Podemos tener en cuenta que en este caso la varianza es:

σε2
var(Xt ) = E(Xt2 ) − µ2 = .
1 − ρ2

Establecemos los parámetros:


nsim <- 200 # Numero de simulaciones
xmed <- 0 # Media
rho <- 0.5 # Coeficiente AR
nburn <- 10 # Periodo de calentamiento (burn-in)

Se podría fijar la varianza del error:


evar <- 1
# Varianza de la respuesta
xvar <- evar / (1 - rho^2)

pero la recomendación sería fijar la varianza de la respuesta:


64 CAPÍTULO 4. ANÁLISIS DE RESULTADOS DE SIMULACIÓN

xvar <- 1
# Varianza del error
evar <- xvar*(1 - rho^2)

Para simular la serie, al ser un AR(1), normalmente simularíamos el primer


valor
rx[1] <- rnorm(1, mean = xmed, sd = sqrt(xvar))

o lo fijamos a la media (en este caso nos alejamos un poco de la distribución


estacionaria, para que el “periodo de calentamiento” sea mayor). Después gene-
ramos los siguientes valores de forma recursiva:
set.seed(1)
x <- numeric(nsim + nburn)
# Establecer el primer valor
x[1] <- -10
# Simular el resto de la secuencia
for (i in 2:length(x))
x[i] <- xmed + rho*(x[i-1] - xmed) + rnorm(1, sd=sqrt(evar))
x <- as.ts(x)
plot(x)
abline(v = nburn, lty = 2)
2
0
−2
−4
x

−6
−8
−10

0 50 100 150 200

Time

Figura 4.11: Ejemplo de una simulación de una serie de tiempo autorregresiva.

y eliminamos el periodo de calentamiento:


4.6. OBSERVACIONES 65

rx <- x[-seq_len(nburn)]

Para simular una serie de tiempo en R se puede emplear la función arima.sim


del paquete base stats. En este caso el periodo de calentamiento se establece
mediante el parámetro n.start (que se fija automáticamente a un valor ade-
cuado).
Por ejemplo, podemos generar este serie autoregressiva con:
rx2 <- arima.sim(list(order = c(1,0,0), ar = rho), n = nsim, n.start = nburn, sd = sqrt(evar))

La recomendación es fijar la varianza de las series simuladas si se quieren com-


parar resultados considerando distintos parámetros de dependencia.

4.6 Observaciones
• En el caso de que la característica de interés de la distribución de X no
sea la media, los resultados anteriores no serían en principio aplicables.
• Incluso en el caso de la media, las “bandas de confianza” obtenidas con el
TCL son puntuales (si generamos nuevas secuencias de simulación es muy
probable que no estén contenidas).
• En muchos casos (p.e. la generación de múltiples secuencias de simulación
puede suponer un coste computacional importante), puede ser preferible
emplear un método de remuestreo.
66 CAPÍTULO 4. ANÁLISIS DE RESULTADOS DE SIMULACIÓN
Capítulo 5

Simulación de variables
continuas

En este capítulo se expondrán métodos generales para simular distribuciones


continuas: el método de inversión y los basados en aceptación/rechazo. En to-
dos los casos como punto de partida es necesario disponer de un método de
generación de números pseudoaleatorios uniformes en (0, 1).

5.1 Método de inversión


Se trataría del método preferible para la simulación de una variable continua
(siempre que se disponga de la función cuantil). Está basado en los siguientes
resultados:
Si X es una variable aleatoria con función de distribución F continua y estric-
tamente monótona (invertible), entonces:

U = F (X) ∼ U (0, 1) ,

ya que:
( ) ( )
G (u) = P (Y ≤ u) = P (F (X) ≤ u) = P X ≤ F −1 (u) = F F −1 (u) = u.

El recíproco también es cierto, si U ∼ U (0, 1) entonces:

F −1 (U ) ∼ X

Algoritmo (método de inversión):


1. Generar U ∼ U (0, 1).

67
68 CAPÍTULO 5. SIMULACIÓN DE VARIABLES CONTINUAS

2. Devolver X = F −1 (U ).

Ejemplo 5.1 (Simulación de una distribución exponencial).

La distribución exponencial exp (λ) de parámetro λ > 0 tiene como función de


densidad f (x) = λe−λx , si x ≥ 0, y como función de distribución:
{
1 − e−λx si x ≥ 0
F (x) =
0 si x < 0

Teniendo en cuenta que:

ln (1 − u)
1 − e−λx = u ⇔ x = − ,
λ
el algoritmo para simular esta variable mediante el método de inversión es:
1. Generar U ∼ U (0, 1).
ln (1 − U )
2. Devolver X = − .
λ
En el último paso podemos emplear directamente U en lugar de 1 − U , ya que
1 − U ∼ U (0, 1). Esta última expresión para acelerar los cálculos es la que
denominaremos forma simplificada.
El código para implementar este algoritmo en R podría ser el siguiente:
# Simular vector exp(lambda)
tini <- proc.time()

lambda <- 2
nsim <- 10^5
set.seed(1)
U <- runif(nsim)
X <- -log(U)/lambda # -log(1-U)/lambda

tiempo <- proc.time() - tini


tiempo

## user system elapsed


## 0.02 0.00 0.02
hist(X, breaks = "FD", freq = FALSE,
main = "", xlim = c(0, 5), ylim = c(0, 2.5))
curve(dexp(x, lambda), lwd = 2, add = TRUE)

Como se observa en la Figura 5.1 se trata de un método exacto (si está bien
implementado) y la distribución de los valores generados se aproxima a la dis-
tribución teórica como cabría esperar con una muestra de ese tamaño.
5.1. MÉTODO DE INVERSIÓN 69

2.5
2.0
1.5
Density

1.0
0.5
0.0

0 1 2 3 4 5

Figura 5.1: Distribución de los valores generados de una exponencial mediante


el método de inversión.

5.1.1 Algunas distribuciones que pueden simularse por el


método de inversión

A continuación se incluyen algunas distribuciones que se pueden simular fácil-


mente mediante el método de inversión. Se adjunta una forma simplificada del
método que tiene por objeto evitar cálculos innecesarios (tal y como se hizo en
el ejemplo de la exponencial).

Forma
Nombre Densidad F (x) F −1 (U ) simplificada
ln (1 − U ) ln U
exp (λ) λe−λx , si 1 − e−λx − −
λ λ
(λ > 0) x≥0 ( ( ))
1 1 arctan x 1
Cauchy + tan π U − tan πU
π (1 + x2 ) 2( π ) 2
2( x) 2 x2 ( √ ) ( √ )
Triangular 1− , x− a 1− 1−U a 1− U
a a a 2a
en (0, a) si 0 ≤ x ≤ a ( )a
aba b b b
Pareto , si 1−
xa+1 x (1 − U )
1/a U 1/a
(a, b > 0) x≥b
1/α 1/α
(− ln (1 − U )) (− ln U )
αλα xα−1 e−(λx) 1, − e−(λx)
α α
Weibull
λ λ
(λ, α > 0) si x ≥ 0 strut

Ejercicio 5.1.

La distribución doble exponencial (o distribución de Laplace) de parámetro λ


tiene función de densidad:
λ
f (x) = e−λ|x| , x ∈ R
2
y función de distribución:
∫ x { 1 λx
2e si x < 0
F (x) = f (t) dt =
1 − 1 e−λx si x ≥ 0
70 CAPÍTULO 5. SIMULACIÓN DE VARIABLES CONTINUAS

rdexp <- function(lambda = 1){


# Simulación por inversión
# Doble exponencial
U <- runif(1)
if (U<0.5) {
return(log(2*U)/lambda)
} else {
return(-log(2*(1-U))/lambda)
}
}

rdexpn <- function(n = 1000, lambda = 1) {


# Simulación n valores de doble exponencial
x <- numeric(n)
for(i in 1:n) x[i]<-rdexp(lambda)
return(x)
}

b) Generar 104 valores de la distribución doble exponencial de parámetro


λ = 2 y obtener el tiempo de CPU que tarda en generar la secuencia.
set.seed(54321)
system.time(x <- rdexpn(10^4, 2))

## user system elapsed


## 0.03 0.00 0.03
c) Representar el histograma y compararlo con la densidad teórica.
hist(x, breaks = "FD", freq = FALSE, main="")
# lines(density(x), col = 'blue')
curve(ddexp(x, 2), add = TRUE)

5.1.2 Ventajas e inconvenientes

Ventajas:
• Aplicable, en principio, a cualquier distribución continua.
Inconvenientes:
• Puede no ser posible encontrar una expresión explícita para F −1 (u) .
• Aún disponiendo de una expresión explícita para F −1 (u), su evaluación
directa puede requerir mucho tiempo de computación.
Alternativas:
5.1. MÉTODO DE INVERSIÓN 71

1.0
0.8
0.6
Density

0.4
0.2
0.0

−4 −2 0 2 4

Figura 5.2: Distribución de los valores generados de una doble exponencial me-
diante el método de inversión.

• Emplear métodos numéricos para resolver F (x) − u = 0 (requeriría resol-


ver numéricamente esta ecuación para cada valor aleatorio que se desee
generar).

• Utilizar una aproximación a F −1 (u) (inversión aproximada).

5.1.3 Inversión aproximada

En muchos casos en los que no se puede emplear la expresión exacta de la


función cuantil F −1 (u), se dispone de una aproximación suficientemente buena
que se puede emplear en el algoritmo anterior (se obtendrían simulaciones con
una distribución aproximada a la deseada).

Por ejemplo, para aproximar la función cuantil de la normal estándar, Odeh y


Evans consideraron la siguiente función auxiliar :
(√ )
√ A −2 ln v
g (v) = −2 ln v (√ ),
B −2 ln v

∑4 ∑4
siendo A (x) = i=0 ai xi y B (x) = i=0 bi x
i
con:
72 CAPÍTULO 5. SIMULACIÓN DE VARIABLES CONTINUAS

a0 = −0.322232431088 b0 = 0.0993484626060
a1 = −1 b1 = 0.588581570495
a2 = −0.342242088547 b2 = 0.531103462366
a3 = −0.0204231210245 b3 = 0.103537752850
a4 = −0.0000453642210148 b4 = 0.0038560700634

La aproximación consiste en utilizar g (1 − u) en lugar de F −1 (u) para los valo-


res de u ∈ [10−20 , 12 ] y −g (u) si u ∈ [ 12 , 1 − 10−20 ]. Para u ∈
/ [10−20 , 1 − 10−20 ]
−20
(que sólo ocurre con una probabilidad de 2 · 10 ) la aproximación no es reco-
mendable.

Algoritmo de Odeh y Evans

1. Generar U ∼ U (0, 1).

2. Si U < 10−20 ó U > 1 − 10−20 entonces volver a 1.

3. Si U < 0.5 entonces hacer X = g (1 − U ) en caso contrario hacer X =


−g (U ).

4. Devolver X.

En manuales de funciones matemáticas, como Abramowitz y Stegun (1964), se


tienen aproximaciones de la función cuantil de las principales distribuciones (por
ejemplo en la página 993 las correspondientes a la normal estándar).

5.2 Método de aceptación rechazo

Se trata de un método universal alternativo al de inversión para el caso de


que no se pueda emplear la función cuantil, pero se dispone de una expresión
(preferiblemente sencilla) para la función de densidad f (x).

Si f es la densidad objetivo, la idea es simular una variable aleatoria bidimen-


sional (X, Y ) con distribución uniforme en el hipografo de f (el conjunto de
puntos del plano comprendidos entre el eje OX y f ):
{ }
Af = (x, y) ∈ R2 : 0 ≤ y ≤ f (x) .

De esta forma la primera componente tendrá la distribución deseada:


5.2. MÉTODO DE ACEPTACIÓN RECHAZO 73

{ } ∫ b
Area de (x, y) ∈ R2 : a < x < b; 0 ≤ y ≤ f (x)
P (a < X < b) = = f (x) dx
Area de Af a

El resultado anterior es también válido para una cuasi-densidad f ∗ (no depende


de la constante normalizadora). El resultado general sería en siguiente:

• Si X es una variable aleatoria con función de densidad f y U ∼ U (0, 1)


entonces
(X, c · U · f (X)) ∼ U (Acf )
{ }
siendo Acf = (x, y) ∈ R2 : 0 ≤ y ≤ cf (x) .

• Recíprocamente si (X, Y ) ∼ U (Acf ) ⇒ X ∼ f .

Para generar valores de una variable aleatoria bidimensional con distribución


uniforme en Af (o en Af ∗ ), se emplea el resultado anterior para generar valores
en Acg ⊃ Af , siendo g una densidad auxiliar (preferiblemente fácil de simular y
similar a f ). Teniendo en cuenta además que:

• Si (X, Y ) ∼ U (A) y B ⊂ A ⇒ (X, Y )|B ∼ U (B)

Por tanto, si (T, Y ) sigue una distribución uniforme en Acg , aceptando los va-
lores de (T, Y ) que pertenezcan a Af (o a Af ∗ ) se obtendrán generaciones con
distribución uniforme sobre Af (o Af ∗ ) y la densidad de la primera componente
T será f .
74 CAPÍTULO 5. SIMULACIÓN DE VARIABLES CONTINUAS

5.2.1 Algoritmo

Supongamos que f es la densidad objetivo y g es una densidad auxiliar (fácil de


simular y similar a f ), de forma que existe una constante c > 0 tal que:

f (x) ≤ c · g (x) , ∀x ∈ R.

Algoritmo (Von Neuman, 1951):


1. Generar U ∼ U (0, 1).
2. Generar T ∼ g.
3. Si c · U · g (T ) ≤ f (T ) devolver X = T ,
en caso contrario volver al paso 1.

5.2.2 Densidades acotadas en un intervalo cerrado

Sea f una función de densidad cualquiera con soporte en un intervalo cerrado


[a, b] (es decir, {x/f (x) ̸= 0} = [a, b]) de tal forma que existe una constante
M > 0 tal que f (x) ≤ M ∀x (es decir, f es acotada superiormente). En este
caso puede tomarse como densidad auxiliar g, la de una U(a, b). En efecto,
tomando c = M (b − a) y teniendo en cuenta que
{ 1
b−a si x ∈ [a, b]
g (x) =
0 en caso contrario

se tiene que f (x) ≤ M = c


b−a = c · g (x), ∀x ∈ [a, b]. Así pues, el algoritmo
quedaría como sigue:
1. Generar U, V ∼ U (0, 1).
2. Hacer T = a + (b − a) V .
3. Si M · U ≤ f (T ) devolver X = T ,
en caso contrario volver al paso 1.
Nota: no confundir M con c = M (b − a).

Ejercicio 5.2.

Desarrollar el código necesario para generar, por el método de aceptación-


rechazo, una muestra de n observaciones de una distribución normal estándar:

1 x2
f (x) = √ e− 2 , x ∈ R,

5.2. MÉTODO DE ACEPTACIÓN RECHAZO 75

empleando como distribución auxiliar una doble exponencial con λ = 1 (más


adelante veremos que esta es la elección óptima para el parámetro de la densidad
auxiliar) y que la cota optima es:


2e
copt = ≃ 1. 3155.
π

Para establecer la condición de aceptación o rechazo se puede tener en cuenta


que:
√ √ ( ) ( 2 )
g (T ) 2e π T2 T 1
c·U · = U exp − |T | = U · exp − |T | + ,
f (T ) π 2 2 2 2

aunque en general puede ser recomendable emplear c · U · g (T ) ≤ f (T ).


# densidad objetivo: dnorm
# densidad auxiliar: ddexp

# EJECUTAR CÓDIGO DEL APARTADO A DEL EJERCICIO 1


c.opt <- sqrt(2*exp(1)/pi)
lambda.opt <- 1
ngen <- 0

rnormAR <- function() {


# Simulación por aceptación-rechazo
# Normal estandar a partir de doble exponencial
while (TRUE) {
U <- runif(1)
X <- rdexp(1) # lambda = 1
ngen <<- ngen+1 # Comentar esta línea para uso normal
# if (U*exp((X^2+1)*0.5-abs(X)) <= 1) return(X)
if (c.opt * U * ddexp(X, lambda.opt) <= dnorm(X)) return(X)
}
}

rnormARn <- function(n=1000) {


# Simulación n valores N(0,1)
x <- numeric(n)
for(i in 1:n) x[i]<-rnormAR()
return(x)
}

# Grafico
curve(c.opt * ddexp(x), xlim = c(-4, 4), lty = 2)
curve(dnorm(x), add = TRUE)
76 CAPÍTULO 5. SIMULACIÓN DE VARIABLES CONTINUAS

0.6
0.5
c.opt * ddexp(x)

0.4
0.3
0.2
0.1
0.0

−4 −2 0 2 4

a) Generar una muestra de 104 observaciones empleando este algoritmo. Ob-


tener el tiempo de CPU y calcular el número medio de generaciones de la
distribución auxiliar.
set.seed(54321)
nsim <- 10^4

ngen <- 0
system.time(x <- rnormARn(nsim))

## user system elapsed


## 0.09 0.00 0.09
# Nº generaciones
{
cat("\nNº de generaciones = ", ngen)
cat("\nNº medio de generaciones = ", ngen/nsim)
cat("\nProporción de rechazos = ", 1-nsim/ngen, "\n")
}

##
## Nº de generaciones = 13163
## Nº medio de generaciones = 1.3163
## Proporción de rechazos = 0.2402948

b) Representar el histograma y compararlo con la densidad teórica.


hist(x, breaks="FD", freq=FALSE)
curve(dnorm(x), add=TRUE)
5.2. MÉTODO DE ACEPTACIÓN RECHAZO 77

Histogram of x

0.4
0.3
Density

0.2
0.1
0.0

−2 0 2 4

5.2.3 Eficiencia del algoritmo

Como medida de la eficiencia del algoritmo de aceptación-rechazo podríamos


considerar el número de iteraciones del algoritmo, es decir, el número de ge-
neraciones de la densidad auxiliar y de comparaciones para aceptar un valor
de la densidad objetivo. Este número N es aleatorio y sigue una distribución
geométrica (número de pruebas necesarias hasta obtener el primer éxito) con
parámetro p (probabilidad de éxito) la probabilidad de aceptación en el paso 3:

area (Af ) 1
p= = .
area (Acg ) c

Por tanto:
1
E (N ) = =c
p
es el número medio de iteraciones del algoritmo (el número medio de pares de
variables (T, U ) que se necesitan generar, y de comparaciones, para obtener una
simulación de la densidad objetivo).
Es obvio, por tanto, que cuanto más cercano a 1 sea el valor de c más eficiente
será el algoritmo (el caso de c = 1 se correspondería con g = f y no tendría senti-
do emplear este algoritmo). El principal problema con este método es encontrar
una densidad auxiliar g de forma que:

f (x)
copt = max .
{x:g(x)>0} g (x)

sea próximo a 1. Una solución intermedia consiste en seleccionar una familia


paramétrica de densidades {gθ : θ ∈ Θ} entre las que haya alguna que se parezca
78 CAPÍTULO 5. SIMULACIÓN DE VARIABLES CONTINUAS

bastante a f , encontrar el valor de c óptimo para cada densidad de esa familia:


f (x)
cθ = max
x gθ (x)
y, finalmente, elegir el mejor valor θ0 del parámetro, en el sentido de ofrecer el
menor posible cθ :
f (x)
cθ0 = min max .
θ∈Θ x gθ (x)

Ejercicio 5.3.

Continuando con el ejercicio anterior del método de aceptación-rechazo para


generar observaciones de una distribución normal estándar, empleando como
distribución auxiliar una doble exponencial:
c) Aproximar la cota óptima numéricamente.
# Obtención de un valor c óptimo aproximado
optimize(f=function(x){dnorm(x)/ddexp(x)}, maximum=TRUE, interval=c(-1,1))

## $maximum
## [1] -0.999959
##
## $objective
## [1] 1.315489
# NOTA: Cuidado con los límites
# optimize(f=function(x){dnorm(x)/ddexp(x)}, maximum=TRUE, interval=c(0,2))

# Valor óptimo real


# sqrt(2*exp(1)/pi)
c.opt

## [1] 1.315489
d) Aproximar el parámetro óptimo de la densidad auxiliar numéricamente
(normalmente comenzaríamos por este paso).
# Obtención de valores c y lambda óptimos aproximados
fopt <- function(lambda) {
# Obtiene c fijado lambda
optimize(f = function(x){dnorm(x)/ddexp(x,lambda)},
maximum=TRUE, interval=c(0,2))$objective
}

# Encontar lambda que minimiza


res <- optimize(f=function(x){fopt(x)}, interval=c(0.5,2))
lambda.opt2 <- res$minimum
c.opt2 <- res$objective
5.2. MÉTODO DE ACEPTACIÓN RECHAZO 79

5.2.4 Ejemplo: Inferencia Bayesiana

El algoritmo de Aceptación-Rechazo se emplea habitualmente en Inferencia Ba-


yesiana:
• f (x|θ) densidad muestral.
• π(θ) densidad a priori.
• x = (x1 , ..., xn )′ muestra observada.
• La distribución a posteriori de θ es:
L(x|θ)π(θ)
π(θ|x) = ∫
L(x|θ)π(θ)dθ

n
siendo L(x|θ) la función de verosimilitud (L(x|θ) = f (xi |θ) suponiendo
i=1
i.i.d.). Es decir:
π(θ|x) ∝ L(x|θ)π(θ).

Para simular valores de la densidad a posteriori π(θ|x) a partir de la densidad


a priori π(θ)
• π(θ|x)/π(θ) ∝ L(x|θ)
• L(x|θ) ≤ c′ = L(x|θ̂) siendo θ̂ el estimador MV de θ.
Algoritmo:
1. Generar U ∼ U (0, 1).
2. Generar θ̃ ∼ π(θ).
3. Si L(x|θ̂) · U ≤ L(x|θ̃) devolver θ̃,
en caso contrario volver al paso 1.
Ejercicio 5.4.

Para la estimación Bayes de la media de una normal se suele utilizar como


distribución a priori una Cauchy.
a) Generar una muestra i.i.d. Xi ∼ N (θ0 , 1) de tamaño n = 10 con θ0 = 1.
Utilizar una Cauchy(0, 1) (rcauchy) como distribución a priori y como
densidad auxiliar para simular por aceptación-rechazo una muestra de
la densidad a posteriori (emplear dnorm para construir la verosimilitud).
Obtener el intervalo de probabilidad al 95%.
mu0 <- 1
n <- 10
nsim <- 10^3
set.seed(54321)
80 CAPÍTULO 5. SIMULACIÓN DE VARIABLES CONTINUAS

x <- rnorm(n, mean = mu0)

# Función de verosimilitud
lik <- function(mu){prod(dnorm(x, mean = mu))}

# Cota óptima
# Estimación por máxima verosimilitud
emv <- optimize(f = lik, int = range(x), maximum = TRUE)
emv

## $maximum
## [1] 0.7353805
##
## $objective
## [1] 3.303574e-08
c <- emv$objective

En este caso concreto, ya sabríamos que el estimador máximo verosímil es


la media muestral:
mean(x)

## [1] 0.7353958
y por tanto:
c <- lik(mean(x))
c

## [1] 3.303574e-08
Finalmente podríamos emplear el siguiente código para generar simulacio-
nes de la distribución a posteriori mediante aceptación-rechazo a partir de
la distribución de Cauchy:
ngen <- nsim
Y <- rcauchy(nsim)
ind <- (c*runif(nsim) > sapply(Y, lik)) # TRUE si no verifica condición
# Volver a generar si no verifica condición
while (sum(ind)>0){
le <- sum(ind)
ngen <- ngen + le
Y[ind] <- rcauchy(le)
ind[ind] <- (c*runif(le) > sapply(Y[ind], lik)) # TRUE si no verifica condición
}

{ # Número generaciones
cat("Número de generaciones = ", ngen)
5.3. MODIFICACIONES DEL MÉTODO DE ACEPTACIÓN RECHAZO 81

cat("\nNúmero medio de generaciones = ", ngen/nsim)


cat("\nProporción de rechazos = ", 1-nsim/ngen,"\n")
}

## Número de generaciones = 5898


## Número medio de generaciones = 5.898
## Proporción de rechazos = 0.830451
# Intervalo de probabilidad al 95% (IC Bayes)
q <- quantile(Y, c(0.025, 0.975))

# Representar estimador e IC Bayes


hist(Y, freq=FALSE, main="Distribución a posteriori")
# abline(v = mean(x), lty = 3) # Estimación frecuentista
abline(v = mean(Y), lty = 2, lwd = 2) # Estimación Bayesiana
abline(v = q, lty = 2)

Distribución a posteriori
1.2
1.0
0.8
Density

0.6
0.4
0.2
0.0

−0.5 0.0 0.5 1.0 1.5

b) Repetir el apartado anterior con n = 100.

5.3 Modificaciones del método de aceptación re-


chazo
En el tiempo de computación influye:
• La proporción de aceptación (debería ser grande).
• La dificultad de simular con la densidad auxiliar.
• El tiempo necesario para hacer la comparación en el paso 4.
82 CAPÍTULO 5. SIMULACIÓN DE VARIABLES CONTINUAS

En ciertos casos el tiempo de computación necesario para evaluar f (x) puede


ser alto.
Para evitar evaluaciones de la densidad se puede emplear una función “squeeze”
(Marsaglia, 1977) que aproxime la densidad por abajo:

s(x) ≤ f (x).

Algoritmo:
1. Generar U ∼ U (0, 1) y T ∼ g.
2. Si c · U · g (T ) ≤ s (T ) devolver X = T ,
en caso contrario
2.a. si c · U · g (T ) ≤ f (T ) devolver X = T ,
2.b. en caso contrario volver al paso 1.

Cuanto mayor sea el área bajo s (x) (más próxima a 1) más efectivo será el
algoritmo.
Se han desarrollado métodos generales para la construcción de las funciones g y
s de forma automática (cada vez que se evalúa la densidad se mejoran las apro-
ximaciones). Estos métodos se basan principalmente en que una transformación
de la densidad objetivo es cóncava o convexa.

5.3.1 Muestreo por rechazo adaptativo (ARS)


∂2
Supongamos que f es una cuasi-densidad log-cóncava (i.e. ∂x2 log f (x) < 0, ∀x).
Sea Sn = {xi : i = 0, · · · , n + 1} con f (xi ) conocidos.
Denotamos por Li,i+1 (x) la recta pasando por (xi , log f (xi )) y (xi+1 , log f (xi+1 ))
• Li,i+1 (x) ≤ log f (x) en el intervalo Ii = (xi , xi+1 ]
• Li,i+1 (x) ≥ log f (x) fuera de Ii
5.3. MODIFICACIONES DEL MÉTODO DE ACEPTACIÓN RECHAZO 83

En el intervalo Ii se definen las envolventes de log f (x):


• ϕn (x) = Li,i+1 (x)

• ϕn (x) = min {Li−1,i (x), Li+1,i+2 (x)}


Las envolventes de f (x) en Ii serán:
( )
• sn (x) = exp ϕn (x)
( )
• Gn (x) = exp ϕn (x)
Tenemos entonces que:

sn (x) ≤ f (x) ≤ Gn (x) = c · gn (x)

donde gn (x) es una mixtura discreta de distribuciones tipo exponencial trunca-


das (las tasas pueden ser negativas), que se puede simular fácilmente mediante
el método de inversión.
Algoritmo (Gilks, 1992):
1. Inizializar n y sn .
2. Generar U ∼ U (0, 1) y T ∼ gn .
3. Si U · Gn (T ) ≤ sn (T ) devolver X = T ,
en caso contrario,
3.a Si U · Gn (T ) ≤ f (T ) devolver X = T .
3.b Hacer n = n + 1, añadir T a Sn y actualizar sn y Gn .
4. Volver al paso 2.
Gilks y Wild (1992) propusieron un método similar pero empleando tangentes
para construir la cota superior.
La mayoría de las densidades de la familia de distribuciones exponencial son
log-cóncavas. Hörmann (1995) extendió esta aproximación al caso de densidades
Tc -cóncavas:
Tc (x) = signo(c)xc T0 (x) = log(x).

Aparte de la transformación logarítmica, la transformación T−1/2 (x) = −1/ x
es habitualmente la más empleada.

5.3.2 Método del cociente de uniformes

Se puede ver como una modificación del método de aceptación rechazo, de espe-
cial interés cuando el soporte no es acotado.
84 CAPÍTULO 5. SIMULACIÓN DE VARIABLES CONTINUAS

Si (U, V ) se distribuye uniformemente sobre:


{ √ }
Ch = (u, v) ∈ R2 : 0 < u ≤ h(v/u) ,

siendo h una función no negativa integrable (cuasi-densidad), entonces X =


V /U tiene función de densidad proporcional a h (Kinderman y Monahan, 1977).
Además Ch tiene área finita.
De modo análogo al método de aceptación-rechazo, hay modificaciones para
acelerar los cálculos y automatizar el proceso, construyendo regiones mediante
polígonos:
Ci ⊂ Ch ⊂ Cs .

5.4 Método de composición


En ocasiones la densidad de interés se puede expresar como una mixtura discreta
de densidades:
∑k
f (x) = pj fj (x)
j=1
∑k
con j=1 pj = 1, pj ≥ 0 y fj densidades (sería también válido para funciones
de distribución o variables aleatorias, incluyendo el caso discreto).
Algoritmo:
1. Generar J con distribución P (J = j) = pj .
2. Generar X ∼ fJ .

Ejemplo 5.2 (Distribución doble exponencial).

A partir de la densidad de la distribución doble exponencial:

λ −λ|x|
f (x) = e , ∀x ∈ R,
2
se deduce que:
1 1
f (x) = f1 (x) + f2 (x)
2 2
siendo:
{ {
λe−λx si x ≥ 0 λeλx si x < 0
f1 (x) = f2 (x) =
0 six < 0 0 six ≥ 0

Algoritmo:
1. Generar U, V ∼ U (0, 1).
5.5. MÉTODOS ESPECÍFICOS PARA LA GENERACIÓN DE ALGUNAS DISTRIBUCIONES NOTABLES85

2. Si U < 0.5 devolver X = − ln (1 − V ).

3. En caso contrario devolver X = ln V .

Observaciones:

• En ocasiones se hace un reciclado de los números aleatorios (solo se genera


una uniforme, e.g. V = 2(U − 0.5) si U ∈ (0.5, 1)).

• En ciertas ocasiones por comodidad, para simular una muestra de tamaño


n, se simulan muestras de tamaño npi con densidad fi y se combinan
aleatoriamente.

Otro ejemplo de una mixtura discreta es el estimador tipo núcleo de la densidad


(ver e.g. la ayuda de la función density() de R o la Sección 4.3 del libro Técnicas
de Remuestreo). Simular a partir de una estimación de este tipo es lo que se
conoce como bootstrap suavizado.

En el caso de una mixtura continua tendríamos:



f (x) = g(x|y)h(y)dy

Algoritmo:

1. Generar Y ∼ h.

2. Generar X ∼ g(·|Y ).

Este algoritmo es muy empleado en Inferencia Bayesiana y en la simulación de


algunas variables discretas (como la Binomial Negativa, denominada también
distribución Gamma–Poisson, o la distribución Beta-Binomial), ya que el resul-
tado sería válido cambiando las funciones de densidad f y g por funciones de
masa de probabilidad.

5.5 Métodos específicos para la generación de


algunas distribuciones notables

Ver ejemplos en el libro de Ricardo (cuidado con la notación de la distribución


Gamma, no es la empleada en R y en el presente libro) y ayuda de las funciones
implementadas en R:

• Método de Box-Müller (para la generación de normales independientes)

• Algoritmos de Jöhnk y Cheng para la generación de la distribución Beta.


86 CAPÍTULO 5. SIMULACIÓN DE VARIABLES CONTINUAS

5.5.1 Método de Box-Müller

Se basa en la siguiente propiedad. Dadas dos variables


√ aleatorias
√ independientes
E ∼ exp (1) y U ∼ U(0, 1), las variables 2E cos 2πU y 2E sen 2πU son
N (0, 1) independientes.
Algoritmo de Box-Müller
1. Generar U, V ∼ U (0, 1).

2. Hacer W1 = −2 ln U y W2 = 2πV .
3. Devolver X1 = W1 cos W2 , X2 = W1 sen W2 .
Podemos hacer que la función rnorm() de R emplee este algoritmo estable-
ciendo el parámetro normal.kind a "Box-Muller" en una llamada previa a
set.seed() o RNGkind().
Este método está relacionado con el de la FFT (transformada de Fourier; e.g. Da-
vies y Harte, 1987) o el Circular embedding (Dietrich and Newsam, 1997), para
la generación de una normal multidimensional con dependencia. Considerando
módulos exponenciales y fases aleatorias generamos normales independientes:
• Cambiando la varianza de W1 se induce dependencia.
• Cambiando la distribución de W2 se generan distribuciones distintas de la
normal.
Capítulo 6

Simulación de variables
discretas

Se trata de simular una variable aleatoria discreta X con función de masa de


probabilidad (f.m.p.):

xi x1 x2 ··· xn ···
P (X = xi ) p1 p2 ··· pn ···

Considerando como partida una U (0, 1), la idea general consiste en asociar a
cada posible valor xi de X un subintervalo de (0, 1) de logitud igual a la corres-
pondiente probabilidad. Por ejemplo, como ya se mostró en capítulos anteriores,
es habitual emplear código de la forma:
x <- runif(nsim) < p

para simular una distribución Bernoulli(p).

Para generar variables discretas con dominio finito en R, si no se dispone de un


algoritmo específico más eficiente, es recomendable emplear:
sample(valores, nsim, replace = TRUE, prob)

Esta función del paquete base implementa eficientemente el método “alias” que
describiremos más adelante en la Sección 6.3.

87
88 CAPÍTULO 6. SIMULACIÓN DE VARIABLES DISCRETAS

6.1 Método de la transformación cuantil


Este método es una adaptación del método de inversión (válido para el caso
continuo) a distribuciones discretas. En este caso, la función de distribución es:

F (x) = pj ,
xj ≤x

y la distribución de la variable aleatoria F (X) no es uniforme (es una variable


aleatoria discreta que toma los valores F (xi ) con probabilidad pi , i = 1, 2, . . .).
Sin embargo, se puede generalizar el método de inversión a situaciones en las
que F no es invertible considerando la función cuantil.
Se define la función cuantil o inversa generalizada de una función de distribución
F como:
Q (u) = inf {x ∈ R : F (x) ≥ u} , ∀u ∈ (0, 1) .
Si F es invertible Q = F −1 .
Teorema 6.1 (de inversión generalizada)
Si U ∼ U (0, 1), la variable aleatoria Q (U ) tiene función de distribución F .

Prueba. Bastaría ver que:

Q (u) ≤ x ⇐⇒ u ≤ F (x).

Como F es monótona y por la definición de Q:

Q (u) ≤ x ⇒ u ≤ F (Q (u)) ≤ F (x).

Por otro lado como Q también es monótona:

u ≤ F (x) ⇒ Q (u) ≤ Q(F (x)) ≤ x

A partir de este resultado se deduce el siguiente algoritmo general para simular


una distribución de probabilidad discreta.
Algoritmo 6.1 (de transformación cuantil) 1. Generar U ∼ U (0, 1).
2. Devolver X = Q (U ).
El principal problema es el cáculo de Q (U ). En este caso, suponiendo por co-
modidad que los valores que toma la variable están ordenados (x1 < x2 < · · ·),
la función cuantil será:
{ ∑j }
Q (U ) = inf xj : i=1 pi ≥ U
∑k−1 ∑k
= xk , tal que i=1 pi < U ≤ i=1 pi

Para encontrar este valor se puede emplear el siguiente algoritmo:


6.1. MÉTODO DE LA TRANSFORMACIÓN CUANTIL 89

Algoritmo 6.2 (de transformación cuantil con búsqueda secuencial) 1.


Generar U ∼ U (0, 1).
2. Hacer I = 1 y S = p1 .
3. Mientras U > S hacer I = I + 1 y S = S + pI
4. Devolver X = xI .
Este algoritmo no es muy eficiente, especialmente si el número de posibles valores
de la variable es grande.
Nota: . El algoritmo anterior es válido independientemente de que los valores
que tome la variable estén ordenados.
Si la variable toma un número finito de valores, se podría implementar en R de
la siguiente forma:
rfmp0 <- function(x, prob = 1/length(x), nsim = 1000) {
# Simulación nsim v.a. discreta a partir de fmp
# por inversión generalizada (transformación cuantil)
X <- numeric(nsim)
U <- runif(nsim)
for(j in 1:nsim) {
i <- 1
Fx <- prob[1]
while (Fx < U[j]) {
i <- i + 1
Fx <- Fx + prob[i]
}
X[j] <- x[i]
}
return(X)
}

Adicionalmente, para disminuir el tiempo de computación, se puede almacenar


las probabilidades acumuladas en una tabla. Si también se quiere obtener el
número de comparaciones se puede considerar una variable global ncomp:
ncomp <- 0

rfmp <- function(x, prob = 1/length(x), nsim = 1000) {


# Simulación nsim v.a. discreta a partir de fmp
# por inversión generalizada (transformación cuantil)
# Inicializar FD
Fx <- cumsum(prob)
# Simular
X <- numeric(nsim)
U <- runif(nsim)
for(j in 1:nsim) {
90 CAPÍTULO 6. SIMULACIÓN DE VARIABLES DISCRETAS

i <- 1
while (Fx[i] < U[j]) i <- i + 1
X[j] <- x[i]
ncomp <<- ncomp + i
}
return(X)
}

Ejercicio 6.1.

Generar, por el método de la transformación cuantil usando búsqueda secuencial,


una muestra de nsim = 105 observaciones de una variable B(10, 0.5). Obtener
el tiempo de CPU empleado. Aproximar por simulación la función de masa de
probabilidad, representarla gráficamente y compararla con la teórica. Calcular
también la media muestral (compararla con la teórica np) y el número medio
de comparaciones para generar cada observación.
Empleamos la rutina anterior para generar las simulaciones:
set.seed(54321)
n <- 10
p <- 0.5
nsim <- 10^5

x <- 0:n
fmp <- dbinom(x, n, p)

ncomp <- 0
system.time( rx <- rfmp(x, fmp, nsim) )

## user system elapsed


## 0.07 0.00 0.08
Aproximación de la media:
mean(rx)

## [1] 5.00322
El valor teórico es n*p = 5.
Número medio de comparaciones:
ncomp/nsim

## [1] 6.00322
# Se verá más adelante que el valor teórico es sum((1:length(x))*fmp)

Análisis de los resultados:


6.1. MÉTODO DE LA TRANSFORMACIÓN CUANTIL 91

res <- table(rx)/nsim


plot(res, ylab = "frecuencia relativa", xlab = "valores")
points(x, fmp, pch = 4) # Comparación teórica
0.25
0.20
frecuencia relativa

0.15
0.10
0.05
0.00

0 1 2 3 4 5 6 7 8 9 10

valores

Figura 6.1: Comparación de las frecuencias relativas de los valores generados


con las probabilidades teóricas.

res <- as.data.frame(res)


names(res) <- c("x", "psim")
res$pteor <- fmp
res

## x psim pteor
## 1 0 0.00107 0.0009765625
## 2 1 0.00990 0.0097656250
## 3 2 0.04432 0.0439453125
## 4 3 0.11778 0.1171875000
## 5 4 0.20425 0.2050781250
## 6 5 0.24375 0.2460937500
## 7 6 0.20454 0.2050781250
## 8 7 0.11898 0.1171875000
## 9 8 0.04419 0.0439453125
## 10 9 0.01023 0.0097656250
## 11 10 0.00099 0.0009765625
# Errores
max(abs(res$psim - res$pteor))
92 CAPÍTULO 6. SIMULACIÓN DE VARIABLES DISCRETAS

## [1] 0.00234375
max(abs(res$psim - res$pteor) / res$pteor)

## [1] 0.09568
Nota: . Puede ocurrir que no todos los valores sean generados en la simulación.
En el código anterior si length(x) > length(psim), la sentencia res$pteor
<- fmp gererará un error. Alternativamente se podría emplear por ejemplo:
res <- data.frame(x = x, pteor = fmp, psim = 0)
res.sim <- table(rx)/nsim
index <- match(names(res.sim), x)
res$psim[index] <- res.sim

6.1.1 Eficiencia del algoritmo

Si consideramos la variable aleatoria I correspondiente a las etiquetas, su fun-


ción de masa de probabilidad sería:

i 1 2 ··· n ···
P (I = i) p1 p2 ··· pn ···

y el número de comparaciones en el paso 3 sería un valor aleatorio de esta


variable. Una medida de la eficiencia del algoritmo de la transformación cuantil
es el número medio de comparaciones:

E (I) = ipi .
i

Realmente, cuando la variable toma un número finito∑


de valores: x1 , x2 , . . ., xn ,
n
no sería necesario hacer la última comprobación U > i=1 pi = 1 y se generaría
directamente xn , por lo que el número medio de comparaciones sería:


n−1
ipi + (n − 1) pn .
i=1

Para disminuir el número esperado de comparaciones podemos reordenar los


valores xi de forma que las probabilidades correspondientes sean decrecientes.
Esto equivale a considerar un etiquetado l de forma que:

pl(1) ≥ pl(2) ≥ · · · ≥ pl(n) ≥ · · ·

Ejercicio 6.2.

Repetir el ejercicio anterior ordenando previamente las probabilidades en orden


decreciente y también empleando la función sample de R.
6.2. MÉTODO DE LA TABLA GUÍA 93

tini <- proc.time()

ncomp <- 0
ind <- order(fmp, decreasing=TRUE)
rx <- rfmp(x[ind], fmp[ind], nsim)

tiempo <- proc.time() - tini


tiempo

## user system elapsed


## 0.05 0.01 0.07
# Número de comparaciones
ncomp/nsim

## [1] 3.08969
sum((1:length(x))*fmp[ind]) # Valor teórico

## [1] 3.083984
Como ya se comentó, en R se recomienda emplear la función sample (implementa
eficientemente el método de Alias):
system.time( rx <- sample(x, nsim, replace = TRUE, prob = fmp) )

## user system elapsed


## 0 0 0

6.2 Método de la tabla guía


La idea consiste en construir m subintervalos equiespaciados contenidos en [0, 1]
de la forma
[ )
j−1 j
Ij = [uj , uj+1 ) = , para j = 1, 2, . . . , m
m m

y utilizarlos como punto de partida para la búsqueda. En una tabla guía se


almancenan los indices de los cuantiles correspondientes a los extremos inferiores
de los intervalos:
{ }
j−1
gj = QI (uj ) = inf i : Fi ≥ uj =
m

El punto de partida para un valor U será gj0 siendo:

j0 = ⌊mU ⌋ + 1
94 CAPÍTULO 6. SIMULACIÓN DE VARIABLES DISCRETAS

En este caso, puede verse que una cota del número medio de comparaciones es:
n
E (N ) ≤ 1 +
m
Algoritmo 6.3 (de simulación mediante una tabla guía)
Inicialización:
1. Hacer F1 = p1 .
2. Desde i = 2 hasta n hacer Fi = Fi−1 + pi .
Cálculo de la tabla guía:
1. Hacer g1 = 1 e i = 1.
2. Desde j = 2 hasta m hacer
2.a Mientras (j − 1)/m > Fi hacer i = i + 1.
2.b gj = i
Simulación mediante tabla guía:
1. Generar U ∼ U (0, 1).
2. Hacer j = ⌊mU ⌋ + 1.
3. Hacer i = gj .
4. Mientras U > Fi hacer i = i + 1.
5. Devolver X = xi .

Ejercicio 6.3.

Diseñar una rutina que permita generar nsim valores de una distribución dis-
creta usando una tabla guía. Repetir el ejercicio anterior empleando esta rutina
con m = n − 1.
rfmp.tabla <- function(x, prob = 1/length(x), m, nsim = 1000) {
# Simulación v.a. discreta a partir de función de masa de probabilidad
# por tabla guia de tamaño m
6.3. MÉTODO DE ALIAS 95

# Inicializar tabla y FD
Fx <- cumsum(prob)
g <- rep(1,m)
i <- 1
for(j in 2:m) {
while (Fx[i] < (j-1)/m) i <- i+1
g[j] <- i
}
# Generar valores
X <- numeric(nsim)
U <- runif(nsim)
for(j in 1:nsim) {
i <- g[floor(U[j]*m)+1]
while (Fx[i] < U[j]) i <- i + 1
X[j] <- x[i]
}
return(X)
}

system.time( rx <- rfmp.tabla(x, fmp, n-1, nsim) )

## user system elapsed


## 0.04 0.00 0.05
Análisis de los resultados:
res <- table(rx)/nsim
plot(res, ylab = "frecuencia relativa", xlab = "valores")
points(x, fmp, pch = 4) # Comparación teórica

6.3 Método de Alias


Se basa en representar la distribución de X como una mixtura (uniforme) de
variables dicotómicas (Walker, 1977):
{
xi con prob. qi
Q(i) =
xai con prob. 1 − qi

La idea para construir estas variables es “tomar prestada” parte de la probabi-


lidad de los valores más probables (ricos) para asignársela a los valores menos
probables (pobres), almacenando en ai el índice del valor de donde procede.
El algoritmo “Robin Hood” de inicialización (Kronmal y Peterson, 1979) es el
siguiente:
1. Desde i = 1 hasta n hacer qi = npi .
96 CAPÍTULO 6. SIMULACIÓN DE VARIABLES DISCRETAS

0.25
0.20
frecuencia relativa

0.15
0.10
0.05
0.00

0 1 2 3 4 5 6 7 8 9 10

valores

Figura 6.2: Comparación de las frecuencias relativas de los valores generados


con las probabilidades teóricas.

2. Establecer L = {l : ql < 1} y H = {h : qh ≥ 1}.


3. Si L ó H vacios terminar.
4. Seleccionar l ∈ L y h ∈ H.
5. Hacer al = h.
6. Eliminar l de L.
7. Hacer qh = qh − (1 − ql ).
8. Si qh < 1 mover h de H a L.
9. Ir al paso 3.
El algoritmo para generar las simulaciones es el estándar del método de compo-
sición:
1. Generar U, V ∼ U (0, 1).
2. Hacer i = ⌊nU ⌋ + 1.
3. Si V < qi devolver X = xi .
4. En caso contrario devolver X = xai .
Este algoritmo es muy eficiente y es el implementado en la función sample de
R.
Ejercicio 6.4.
6.3. MÉTODO DE ALIAS 97

Figura 6.3: Pasos del algoritmo de inicialización del método Alias.

Diseñar una rutina que permita generar nsim valores de una distribución dis-
creta usando el método de Alias. Repetir el ejercicio anterior empleando esta
rutina.
rfmp.alias <- function(x, prob = 1/length(x), nsim = 1000) {
# Inicializar tablas
a <- numeric(length(x))
q <- prob*length(x)
low <- q < 1
high <- which(!low)
low <- which(low)
while (length(high) && length(low)) {
l <- low[1]
h <- high[1]
a[l] <- h
q[h] <- q[h] - (1 - q[l])
if (q[h] < 1) {
high <- high[-1]
low[1] <- h
} else low <- low[-1]
} # while
# Generar valores
V <- runif(nsim)
i <- floor(runif(nsim)*length(x)) + 1
return( x[ ifelse( V < q[i], i, a[i]) ] )
}

system.time( rx <- rfmp.alias(x,fmp,nsim) )

## user system elapsed


## 0.03 0.00 0.04
98 CAPÍTULO 6. SIMULACIÓN DE VARIABLES DISCRETAS

Análisis de los resultados:


res <- table(rx)/nsim
plot(res, ylab = "frecuencia relativa", xlab = "valores")
points(x, fmp, pch = 4) # Comparación teórica

0.25
0.20
frecuencia relativa

0.15
0.10
0.05
0.00

0 1 2 3 4 5 6 7 8 9 10

valores

Figura 6.4: Comparación de las frecuencias relativas de los valores generados


con las probabilidades teóricas.

6.4 Simulación de una variable discreta con do-


minio infinito

Los métodos anteriores están pensados para variables que toman un número
finito de valores. Si la variable discreta tiene dominio infinito no se podrían
almacenar las probabilidades acumuladas, aunque en algunos casos podrían cal-
cularse de forma recursiva.

Ejemplo 6.1 (distribución de Poisson).

Una variable X con distribución de Poisson de parámetro λ, toma los valores


x1 = 0, x2 = 1, . . . con probabilidades:

e−λ λj−1
pj = P (X = xj ) = P (X = j − 1) = , j = 1, 2, . . .
(j − 1)!
6.5. CÁLCULO DIRECTO DE LA FUNCIÓN CUANTIL 99

En este caso, como:

e−λ λj λ e−λ λj−1 λ


P (X = j) = = = P (X = j − 1) ,
j! j (j − 1)! j
el algoritmo de inversión con búsqueda secuencial sería:
1. Generar U ∼ U (0, 1).
2. Hacer I = 0, p = e−λ y S = p.
3. Mientras U > S hacer I = I + 1, p = λI p y S = S + p.
4. Devolver X = I.
Hay modificaciones de los algoritmos anteriores, e.g. incluyendo una búsqueda
secuencial en la cola de la distribución, para estos casos.
Como alternativa, siempre se puede pensar en truncar la distribución, eliminan-
do los valores muy poco probables (teniendo en cuenta el número de generaciones
que se pretenden realizar), de esta forma la distribución de las simulaciones sería
aproximada.

6.5 Cálculo directo de la función cuantil


En ocasiones el método de la transformación cuantil puede acelerarse compu-
tacionalmente porque, mediante cálculos directos, es posible encontrar el valor
de la función cuantil en cualquier U , evitando el bucle de búsqueda. Normal-
mente se realiza mediante truncamiento de una distribución continua.
Ejemplo 6.2 (distribución uniforme discreta).

La función de masa de probabilidad de una distribución uniforme discreta en


{1, 2, . . . , n} viene dada por
1
pj = , para j = 1, 2, . . . n.
n

Pueden generarse valores de esta distribución de forma muy eficiente truncando


la distribución uniforme:
1. Generar U ∼ U (0, 1).
2. Devolver X = ⌊nU ⌋ + 1.
Ejemplo 6.3 (distribución geométrica).

La función de masa de probabilidad de una distribución geométrica es:


j
P (X = j) = P (I = j + 1) = p (1 − p) , j = 0, 1, . . .
100 CAPÍTULO 6. SIMULACIÓN DE VARIABLES DISCRETAS

Si se considera como variable aleatoria continua auxiliar una exponencial, con


función de distribución G (x) = 1 − e−λx si x ≥ 0, se tiene que:
( )
G (i) − G (i − 1) = 1 − e−λi − 1 − e−λ(i−1) = e−λ(i−1) − e−λi
( ) ( )( )i−1
= e−λ(i−1) 1 − e−λ = 1 − e−λ e−λ
i−1
= p (1 − p) ,

tomando p = 1 − e−λ . De donde se obtendría el algoritmo:


0. Hacer λ = − ln (1 − p).
1. Generar U ∼ U (0, 1).
2. Hacer T = − lnλU .
3. Devolver X = ⌊T ⌋.

6.6 Otros métodos


• Aceptación-Rechazo: Este método también sería directamente aplicable
al caso discreto. En principio habría que considerar una variable auxiliar
discreta con el mismo soporte, pero también hay modificaciones para va-
riables auxiliares continuas.
• Método de composición: este es otro método directamente aplicable y que
se emplea en el método de Alias
• Hay otros métodos que tratan de reducir el número medio de compara-
ciones de la búsqueda secuencial, por ejemplo los árboles (binarios) de
Huffman (e.g. Cao, 2002, Sección 4.2).
Ejercicio 6.5.

Considera la variable aleatoria con función de distribución dada por:




 0 si x < 0
 x
2 + 1
10 si x ∈ [0, 15 )
F (x) =

 x + 10 si x ∈ [ 15 , 10
1 9
]

1 en otro caso

Función de distribución:
fdistr <- function(x) {
ifelse(x < 0, 0,
ifelse(x < 1/5, x/2 + 1/10,
ifelse(x <= 9/10, x + 1/10, 1) ) )
}
6.6. OTROS MÉTODOS 101

# Empleando ifelse se complica un poco más pero el resultado es una función vectorial.
curve(fdistr(x), from = -0.1, to = 1.1, type = 's',
main = 'Función de distribución')
# Discontinuidades en 0 y 1/5
abline(h = c(1/10, 2/10, 3/10), lty = 2)

Función de distribución
1.0
0.8
0.6
fdistr(x)

0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Nota: Esta variable toma los valores 0 y 1/5 con probabilidad 1/10.
a) Diseña un algoritmo basándote en el método de inversión generalizado
para generar observaciones de esta variable.
El algoritmo general es siempre el mismo. Empleando la función cuantil:

Q (u) = inf {x ∈ R : F (x) ≥ u} ,

el algoritmo sería:
1. Generar U ∼ U (0, 1)
2. Devolver X = Q (U )
En este caso concreto:
1. Generar U ∼ U (0, 1)
1
2. Si U < 10 devolver X = 0
3. Si U < 2
10 devolver X = 2(U − 1
10 )
3 2
4. Si U < 10 devolver X = 10

5. En caso contrario devolver X = U − 1


10
102 CAPÍTULO 6. SIMULACIÓN DE VARIABLES DISCRETAS

b) Implementa el algoritmo en una función que permita generar nsim valores


de la variable.
# Función cuantil:
fquant <- function(u) {
ifelse(u < 1/10, 0,
ifelse(u < 2/10, 2*(u - 1/10),
ifelse(u < 3/10, 1/5, u - 1/10) ) )
}
# Función para generar nsim valores:
rx <- function(nsim) fquant(runif(nsim))

Ejemplo:
set.seed(1)
nsim <- 10^4
system.time(simx <- rx(nsim))

## user system elapsed


## 0 0 0
hist(simx, breaks = "FD", freq = FALSE)

Histogram of simx
2.5
2.0
1.5
Density

1.0
0.5
0.0

0.0 0.2 0.4 0.6 0.8

simx

En este caso como no es una variable absolutamente continua mejor em-


plear la función de distribución para compararla con la teórica:
curve(ecdf(simx)(x), from= -0.1, to = 1.1, type = "s")
curve(fdistr(x), type = "s", lty = 2, add = TRUE)
6.7. MÉTODOS ESPECÍFICOS PARA GENERACIÓN DE DISTRIBUCIONES NOTABLES103

1.0
0.8
0.6
ecdf(simx)(x)

0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Ejercicio 6.6. (propuesto)

Se pretende simular nsim = 104 observaciones de una variable hipergeométrica


(dhyper(x, m, n, k)) de parámetros m = el nº de grupo multiplicado por 10,
n = 100 − m y k = 20.
a) Comprobar que el rango de posibles valores de esta variable es max(0,
k-n):min(m, k). Generar los valores empleando el método de la transfor-
mación cuantil usando búsqueda secuencial. Obtener el tiempo de CPU
empleado. Aproximar por simulación la función de masa de probabilidad,
representarla gráficamente y compararla con la teórica. Calcular también
la media muestral (compararla con la teórica km/(m + n)) y el número
medio de comparaciones para generar cada observación.
b) Repetir el apartado anterior ordenando previamente las probabilidades en
orden decreciente y también: empleando la función sample de R, mediante
una tabla guía (con k − 1 subintervalos) y usando el método de Alias.

6.7 Métodos específicos para generación de dis-


tribuciones notables
Ver ejemplos del libro de Ricardo y de las funciones implementadas en R.
104 CAPÍTULO 6. SIMULACIÓN DE VARIABLES DISCRETAS
Capítulo 7

Simulación de
distribuciones
multivariantes

La simulación de vectores aleatorios X = (X1 , X2 , . . . , Xd ) que sigan cierta dis-


tribución dada no es tarea siempre sencilla. En general, no resulta una extensión
inmediata del caso unidimensional, aúnque muchos de los algoritmos descritos
en los temas anteriores (como el de aceptación-rechazo o el de composición) son
válidos para distribuciones multivariantes. En este caso sin embargo, puede ser
mucho más dificil cumplir los requerimientos (e.g. encontrar una densidad auxi-
liar adecuada) y los algoritmos obtenidos pueden ser computacionalmente poco
eficientes (especialmente si el número de dimensiones es grande).
En las primeras secciones de este capítulo supondremos que se pretende simular
una variable aleatoria multidimensional continua X con función de densidad
conjunta f (x1 , x2 , . . . , xd ) (aunque muchos resultados serán válidos para varia-
bles discretas multidimensionales, simplemente cambiando funciones de densi-
dad por las correspondientes de masa de probabilidad). En la Sección 7.7 se
tratará brevemente la simulación de vectores aleatorios discretos y de tablas de
contingencia, centrándose en el caso bidimensional.

7.1 Simulación de componentes independientes


Si las componentes son independientes y fi son las correspondientes densidades
marginales, bastará con generar Xi ∼ fi . Las dificultades aparecerán cuando se
quiera simular componentes con una determinada estructura de dependencia.
Ejemplo 7.1 (simulación de normales independientes).

105
106CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

t
Si µ = (µ1 , µ2 , . . . , µd ) es un vector (de medias) y Σ es una matriz d×d definida
positiva (de varianzas-covarianzas), el vector aleatorio X sigue una distribución
normal multivariante con esos parámetros, X ∼ Nd (µ, Σ), si su función de
densidad es de la forma:
( )
1 1 t −1
f (x) = exp − (x − µ) Σ (x − µ) ,
(2π)n/2 |Σ|1/2 2

donde |Σ| es el determinante de Σ.


( )
Si la matriz de covarianzas ( es
) diagonal Σ = diag σ12 , σ22 , . . . , σd2 , entonces las
componentes Xi ∼ N µi , σi2 son independientes y podemos simular el vector
aleatorio de forma trivial, por ejemplo mediante el siguiente algoritmo:
Algoritmo 7.1 (de simulación de normales independientes) 1. Simular
Z1 , Z2 , . . . , Zd ∼ N (0, 1) independientes.
2. Para i = 1, 2, . . . , d hacer Xi = µi + σi Zi .
Las funciones implementadas en el paquete base de R permiten simular fácil-
mente en el caso independiente ya que admiten vectores como (parámetros.
) Por
ejemplo en el caso bidimensional con X1 ∼ N (0, 1) y X2 ∼ N −1, 0.52 :
f1 <- function(x) dnorm(x)
f2 <- function(x) dnorm(x, -1, 0.5)
curve(f1, -3, 3, ylim = c(0, f2(-1)), ylab = "fdp")
curve(f2, add = TRUE, lty = 2)
0.8
0.6
0.4
fdp

0.2
0.0

−3 −2 −1 0 1 2 3

Figura 7.1: Densidades marginales de las componentes del Ejemplo 7.1.

Para simular una generación bastaría con:


7.2. EL MÉTODO DE ACEPTACIÓN/RECHAZO 107

set.seed(1)
rnorm(2, c(0, -1), c(1, 0.5))

## [1] -0.6264538 -0.9081783


y para simular nsim:
set.seed(1)
nsim <- 5
rx <- matrix(rnorm(2*nsim, c(0, -1), c(1, 0.5)), nrow = nsim, byrow = TRUE)
colnames(rx) <- paste0("X", 1:ncol(rx))
rx

## X1 X2
## [1,] -0.6264538 -0.9081783
## [2,] -0.8356286 -0.2023596
## [3,] 0.3295078 -1.4102342
## [4,] 0.4874291 -0.6308376
## [5,] 0.5757814 -1.1526942

7.2 El método de aceptación/rechazo


El algoritmo de aceptación-rechazo es el mismo que el del caso univariante
descrito en la Sección 5.2, la única diferencia es que las densidades son multi-
dimensionales. Supongamos que la densidad objetivo f y la densidad auxiliar g
verifican:
f (x1 , x2 , . . . , xd ) ≤ c · g (x1 , x2 , . . . , xd ) , ∀x = (x1 , x2 , . . . , xd ) ∈ Rd .
para una constante c > 0. El algoritmo sería:
1. Generar U ∼ U (0, 1).
2. Generar T = (T1 , T2 , . . . , Td ) ∼ g.
3. Si c · U · g (T1 , T2 , . . . , Td ) ≤ f (T1 , T2 , . . . , Td ) devolver X = T.
En caso contrario volver al paso 1.
Por ejemplo, de forma análoga al caso unidimensional, en el caso de una den-
sidad acotada en un hipercubo (intervalo cerrado multidimensional) siempre
podríamos considerar una uniforme como densidad auxiliar.
Ejemplo 7.2 (distribución bidimensional acotada).

Supongamos que estamos interesados en generar valores de una variable aleato-


ria bidimensional (X, Y ) con función de densidad:
{ 3 ( ( 2 ))
16 2 − x + y si x ∈ [−1, 1] e y ∈ [−1, 1]
2
f (x, y) =
0 en otro caso
108CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

Podríamos considerar como densidad auxilar la uniforme en [−1, 1] × [−1, 1]:


{ 1
4 si x, y ∈ [−1, 1]
g (x, y) =
0 en otro caso

Como f (x, y) ≤ M = f (0, 0) = 38 , tomando c = M


g(x,y) = 3
2 tendríamos que
f (x, y) ≤ cg(x, y) = M y el algoritmo sería:
1. Generar U ∼ U (0, 1).
2. Generar T1 , T2 ∼ U (−1, 1).
3. Si M · U ≤ f (T1 , T2 ) devolver X = (T1 , T2 ).
4. En caso contrario volver al paso 1.
En (este caso,) la condición de aceptación del paso 3 simplificada sería: U ≤
1 − T12 + T22 /2.

Ejemplo 7.3 (distribución uniforme en la esfera).

Supongamos que el objetivo es simular puntos uniformemente distribuídos sobre


la “esfera” unitaria d-dimensional:
{ }
Cd = (x1 , x2 , . . . , xd ) : x21 + x22 + · · · + x2d ≤ 1 .

Denotando por Vd (1), el “volumen” (la medida) de la esfera d-dimensional de


radio 1 (en general, la de radio r verifica Vd (r) = rd Vd (1)), se tiene:
{ 1
Vd (1) si (x1 , x2 , . . . , xd ) ∈ Cd
f (x1 , x2 , . . . , xd ) =
0 si (x1 , x2 , . . . , xd ) ∈
/ Cd

Para simular valores


( en Rd , con densidad f , podemos
) (utilizar como
) distribución
d d
auxiliar una U [−1, 1] × [−1, 1] × · · · × [−1, 1] = U [−1, 1] , dada por:
{ 1
2d
si xi ∈ [−1, 1] , para todo i = 1, 2, . . . , d
g (x1 , x2 , . . . , xd ) =
0 en otro caso

La constante c óptima para la utilización del método de aceptación/rechazo es:


1
f (x) Vd (1) 2d
c= max = 1 =
{x:g(x)>0} g (x) Vd (1)
2d

y la condición de aceptación cU g (T) ≤ f (T) se convierte en:

2d 1 1
U 1 d (T) ≤ 1C (T) ,
Vd (1) 2d [−1,1] Vd (1) d
7.3. FACTORIZACIÓN DE LA MATRIZ DE COVARIANZAS 109

o, lo que es lo mismo, U 1[−1,1]d (T) ≤ 1Cd (T). Dado que el número aleatorio
U está en el intervalo (0, 1) y que las funciones indicadoras valen 0 ó 1, esta
condición equivale a que 1[−1,1]d (T) = 1Cd (T), es decir, a que T ∈ Cd , es decir,
que se verifique:
T12 + T22 + · · · + Td2 ≤ 1.
( )
d
Por otra parte, la simulación de T ∼ U [−1, 1] puede hacerse trivialmente
mediante Ti ∼ U (−1, 1) para cada i = 1, 2, . . . , d, ya que las componentes son
independientes. Como el valor de U es superfluo en este caso, el algoritmo queda:
1. Simular V1 , V2 , . . . , Vd ∼ U (0, 1) independientes.
2. Para i = 1, 2, . . . , d hacer Ti = 2Vi − 1.
3. Si T12 + T22 + · · · + Td2 > 1 entonces volver al paso 1.
t
4. Devolver X = (T1 , T2 , . . . , Td ) .
Ver el Ejercicio 2.1 para el caso de d = 2.
Usando las fórmulas del “volumen” de una “esfera” d-dimensional:
 d/2 d

 π r
 si d es par
Vd (r) = (d/2)!

 ⌊ 2 ⌋+1 π ⌊ 2 ⌋ rd
d d

 2 si d es impar
1 · 3 · 5···d
puede verse que el número medio de iteraciones del algoritmo, dado por la
d
constante c = Vd2(1) , puede llegar a ser enórmemente grande. Así, si d = 2
se tiene c = 1.27, si d = 3 se tiene c = 1.91, si d = 4 entonces c = 3.24 y
para d = 10 resulta c = 401.5 que es un valor que hace que el algoritmo sea
tremendamente lento en dimensión 10. Esto está relacionado con la maldición de
la dimensionalidad (curse of dimensionality), a medida que aumenta el número
de dimensiones el volumen de la “frontera” crece exponencialmente.

7.3 Factorización de la matriz de covarianzas


Teniendo en cuenta que si Cov(X) = I, entonces:

Cov(AX) = AAt .

La idea de este tipo de métodos es simular datos independientes y transformarlos


linealmente de modo que el resultado tenga la covarianza deseada Σ = AAt .
Este método se emplea principalmente para la simulación de una normal mul-
tivariante, aunque también es válido para muchas otras distribuciónes como la
t-multivariante.
En el caso de normalidad, el resultado general es el siguiente.
110CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

Proposición 7.1
Si X ∼ Nd (µ, Σ) y A es una matriz p × d, de rango máximo, con p ≤ d,
entonces:
( )
AX ∼ Np Aµ, AΣAt .

Partiendo de Z ∼ Nd (0, Id ), se podrían considerar distrintas factorizaciones de


la matriz de covarianzas:
• Factorización espectral: Σ = HΛH t = HΛ1/2 (HΛ1/2 )t , donde H es una
matriz ortogonal (i.e. H −1 = H t ), cuyas columnas son los autovectores de
la matriz Σ, y Λ es una matriz diagonal, cuya diagonal esta formada por
los correspondientes autovalores (positivos). De donde se deduce que:

Y = µ + HΛ1/2 Z ∼ Nd (µ, Σ) .

• Factorización de Cholesky: Σ = LLt , donde L es una matriz triangular


inferior (fácilmente invertible), por lo que:

Y = µ + LZ ∼ Nd (µ, Σ) .

Desde el punto de vista de la eficiencia computacional la factorización de Cho-


lesky sería la preferible. Pero en ocasiones, para evitar problemas numéricos
(por ejemplo, en el caso de matrices no definidas positivas) puede ser más ade-
cuado emplear la factorización espectral. En el primer caso el algoritmo sería el
siguiente:
Algoritmo 7.2 (de simulación de una normal multivariante) 1. Obtener la
factorización de Cholesky Σ = LLt .
2. Simular Z = (Z1 , Z2 , . . . , Zd ) i.i.d. N (0, 1).
3. Hacer X = µ + LZ.
4. Repetir los pasos 2 y 3 las veces necesarias.
Nota: Hay que tener en cuenta el resultado del algoritmo empleado para la
factorización de Cholesky. Por ejemplo si se obtiene Σ = U t U , hará que emplear
L = U t.

Ejemplo 7.4 (simulación de datos funcionales o temporales).

Supongamos que el objetivo es generar una muestra de tamaño nsim de la


variable funcional:
Y (x) = sin (2πx) + ε (x)
con 0 ≤ x ≤ 1 y Cov(ε (x) , ε (y)) = e−∥x−y∥ , considerando 100 puntos de
discretización (se puede pensar también que es un proceso temporal).
7.3. FACTORIZACIÓN DE LA MATRIZ DE COVARIANZAS 111

nsim <- 20
n <- 100
x <- seq(0, 1, length = n)
# Media
mu <- sin(2*pi*x)
# Covarianzas
x.dist <- as.matrix(dist(x))
x.cov <- exp(-x.dist)

Para la factorización de la matriz de covarianzas emplearemos la función chol


del paquete base de R (si las dimensiones fueran muy grandes podría ser prefe-
rible emplear otros paquetes, e.g. spam::chol.spam), pero al devolver la matriz
triangular superior habrá que transponer el resultado:
U <- chol(x.cov)
L <- t(U)

Si queremos simular una realización:


set.seed(1)
head(mu + L %*% rnorm(n))

## [,1]
## 1 -0.6264538
## 2 -0.5307633
## 3 -0.5797968
## 4 -0.2844357
## 5 -0.1711797
## 6 -0.2220796
y para simular nsim:
z <- matrix(rnorm(nsim * n), nrow = n)
y <- mu + L %*% z

matplot(x, y, type = "l", ylim = c(-3.5, 3.5))


lines(x, mu, lwd = 2)

Alternativamente se podría emplear, por ejemplo, la funcion mvrnorm del paque-


te MASS que emplea la factorización espectral (eigen):
library(MASS)
mvrnorm

## function (n = 1, mu, Sigma, tol = 1e-06, empirical = FALSE, EISPACK = FALSE)


## {
## p <- length(mu)
## if (!all(dim(Sigma) == c(p, p)))
## stop("incompatible arguments")
112CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

3
2
1
0
y

−1
−2
−3

0.0 0.2 0.4 0.6 0.8 1.0

Figura 7.2: Realizaciones del proceso funcional del Ejemplo 7.4, obtenidas a
partir de la factorización de Cholesky.

## if (EISPACK)
## stop("'EISPACK' is no longer supported by R", domain = NA)
## eS <- eigen(Sigma, symmetric = TRUE)
## ev <- eS$values
## if (!all(ev >= -tol * abs(ev[1L])))
## stop("'Sigma' is not positive definite")
## X <- matrix(rnorm(p * n), n)
## if (empirical) {
## X <- scale(X, TRUE, FALSE)
## X <- X %*% svd(X, nu = 0)$v
## X <- scale(X, FALSE, TRUE)
## }
## X <- drop(mu) + eS$vectors %*% diag(sqrt(pmax(ev, 0)), p) %*%
## t(X)
## nm <- names(mu)
## if (is.null(nm) && !is.null(dn <- dimnames(Sigma)))
## nm <- dn[[1L]]
## dimnames(X) <- list(nm, NULL)
## if (n == 1)
## drop(X)
## else t(X)
## }
## <bytecode: 0x000000002e2a61e8>
## <environment: namespace:MASS>
7.4. MÉTODO DE LAS DISTRIBUCIONES CONDICIONADAS 113

y <- mvrnorm(nsim, mu, x.cov)

matplot(x, t(y), type = "l")


lines(x, mu, lwd = 2) 3
2
1
0
t(y)

−1
−2
−3
−4

0.0 0.2 0.4 0.6 0.8 1.0

Figura 7.3: Realizaciones del proceso funcional del Ejemplo 7.4, obtenidas em-
pleando la función MASS::mvrnorm.

Otros métodos para variables continuas relacionados con la factorización de


la matriz de covarianzas son el método FFT (transformada rápida de Fourier;
e.g. Davies y Harte, 1987) o el Circular embedding (Dietrich and Newsam, 1997),
que realmente son el mismo.

7.4 Método de las distribuciones condicionadas


Teniendo en cuenta que:

f (x1 , x2 , . . . , xd ) = f1 (x1 ) · f2 (x2 |x1 ) · · · fd (xd |x1 , x2 , . . . , xd−1 ) ,

donde las densidades condicionales pueden obtenerse a partir de las correspon-


dientes marginales:
f1,...,i (x1 , x2 , . . . , xi )
fi (xi |x1 , x2 , . . . , xi−1 ) = ,
f1,...,i−1 (x1 , x2 , . . . , xi−1 )

se obtiene el siguiente algoritmo general:


114CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

Algoritmo 7.3 (de simulación mediante distribuciones condicionadas) 1.


Generar X1 ∼ f1 .
2. Desde i = 2 hasta d generar Xi ∼ fi (·|X1 , X2 , . . . , Xi−1 ).
3. Devolver X = (X1 , X2 , . . . , Xd ).
Nota: En las simulaciones unidimensionales se puede tener en cuenta que
fi (xi |x1 , x2 , . . . , xi−1 ) ∝ f1,...,i (x1 , x2 , . . . , xi ).

Ejemplo 7.5 (distribución uniforme en el círculo unitario).

Se trata de la distribución bidimensional continua con densidad constante en el


círculo: { }
C = (x1 , x2 ) ∈ R : x21 + x22 ≤ 1 .

Su función de densidad viene dada por:


{ 1
π si (x1 , x2 ) ∈ C
f (x1 , x2 ) =
0 si (x1 , x2 ) ∈
/C

La densidad marginal de la primera variable resulta:


∫ +√1−x21 √
1 2 1 − x21
f1 (x1 ) = √ dx2 = si x1 ∈ [−1, 1] ,
− 1−x21 π π

es decir: { √
2
π 1 − x21 si x1 ∈ [−1, 1]
f1 (x1 ) =
0 si x1 ∈
/ [−1, 1]

Además:
1 [ √ √ ]
f (x1 , x2 ) 1
f2 (x2 |x1 ) = = √ 2 = √
π
, si x2 ∈ − 1 − x1 , 1 − x1
2 2
f1 (x1 ) 2 1−x1 2 1 − x21
π

valiendo cero en otro caso. Se tiene entonces que:


( √ √ )
X2 |X1 ∼ U − 1 − X12 , 1 − X12 ,

siempre que X1 ∈ [−1, 1].


Finalmente, el algoritmo resulta:

1. Simular X1 con densidad f1 (x1 ) = π2 1 − x21 1{|x1 |≤1} .
( √ √ )
2. Simular X2 con densidad U − 1 − X12 , 1 − X12 .
t
3. Devolver X = (X1 , X2 ) .
7.4. MÉTODO DE LAS DISTRIBUCIONES CONDICIONADAS 115

Para el paso 1 puede utilizarse, por ejemplo, el método de aceptación/rechazo,


pues se trata de una densidad acotada definida en un intervalo acotado.
Ejemplo 7.6 (distribución normal bidimensional).

En el caso de una distribución normal bidimensional:


( ) (( ) ( 2 ))
X1 µ1 σ1 σ1 σ2 ρ
X= ∼N ,
X2 µ2 σ1 σ2 ρ σ22

tendríamos que:

1
f (x1 , x2 ) = √
2πσ1 σ2 1 − ρ2
( ( ))
1 (x1 − µ1 )2 (x2 − µ2 )2 2ρ(x1 − µ1 )(x2 − µ2 )
exp − + −
2(1 − ρ2 ) σ12 σ22 σ1 σ2

de donde se deduce (ver e.g. Cao, 2002, p.88; o ecuaciones (7.1) y (7.2) en la
Sección 7.5.1) que:
( )
1 (x1 − µ1 )2
f1 (x1 ) = √ exp −
σ1 2π 2σ12

f (x1 , x2 )
f2 (x2 |x1 ) =
f1 (x1 )
 ( )2 
 x2 − µ 2 − − µ1 )  σ2
1 σ1 ρ(x1
= √ exp − 
σ2 2π(1 − ρ2 ) 2σ2 (1 − ρ )
2 2

( ) ( )
Por tanto X1 ∼ N µ1 , σ12 y X2 |X1 ∼ N µ2 + σσ21 ρ(X1 − µ1 ), σ22 (1 − ρ2 ) , y
el algoritmo sería el siguiente:
Algoritmo 7.4 (de simulación de una normal bidimensional) 1. Simular
Z1 , Z2 ∼ N (0, 1) independientes.
2. Hacer X1 = µ1 + σ1 Z1 .

3. Hacer X2 = µ2 + σ2 ρZ1 + σ2 1 − ρ2 Z 2 .
Este algoritmo es el mismo que obtendríamos con la factorización de la matrix
de covarianzas ya que Σ = LLt con:
( 2 )
σ1 √ 0
L=
σ2 ρ σ2 1 − ρ2

Además, esta aproximación puede generalizarse al caso multidimensional, ver


Sección 7.5.1.
116CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

7.5 Simulación condicional e incondicional


En ocasiones en inferencia estadística interesa la simulación condicional de nue-
vos valores de forma que se preserven los datos observados, para lo que se suele
emplear el algoritmo anterior partiendo de la muestra observada:
Algoritmo 7.5 (de simulación condicional) 1. Obtener la distribución condi-
cional (correspondiente al punto que se quiere simular) dada la muestra y
los valores simulados anteriormente.
2. Simular un valor de la distribución condicional.
3. Agregar este valor al conjunto de datos y volver al paso 1.
En el caso de normalidad, en lugar de simular punto a punto, podemos obte-
ner fácilmente la distribución condicionada para simular los valores de forma
conjunta.

7.5.1 Simulación condicional de una normal multivariante

Si X ∼ Nd (µ, Σ) es tal que X, µ y Σ se particionan de la forma:


( ) ( ) ( )
X1 µ1 Σ11 Σ12
X= ,µ = ,Σ = ,
X2 µ2 Σ21 Σ22

suponiendo que X1 se corresponde con los valores observados y X2 con los que
se pretende simular, entonces puede verse (e.g. Ripley, 1987) que la distribución
de X2 |X1 es normal con:

E (X2 |X1 ) = µ2 + Σ21 Σ−1


11 (X1 − µ1 ) , (7.1)

Cov (X2 |X1 ) = Σ22 − Σ21 Σ−1


11 Σ12 . (7.2)

Nota: La ecuación (7.1) coincide con la expresión de la predicción lineal óptima


de X2 a partir de X1 con media y varianzas conocidas (denominado predictor
del kriging simple en estadística espacial, y la diagonal de (7.2) son las corres-
pondientes varianzas kriging).
Ejemplo 7.7 (simulación condicional de datos funcionales o temporales).

Continuando con el Ejemplo 7.4 anterior, consideramos los primeros valores de


una simulación incondicional como los datos:
idata <- x < 0.5
# idata <- x < 0.2 | x > 0.8
mu1 <- mu[idata]
n1 <- length(mu1)
7.5. SIMULACIÓN CONDICIONAL E INCONDICIONAL 117

cov.data <- x.cov[idata, idata]


U <- chol(cov.data)
# Simulación (incondicional):
set.seed(1)
data <- drop(mu1 + t(U) %*% rnorm(n1))

Para obtener la simulación condicional en los puntos de predicción, calculamos


la correspondiente media y matriz de covarianzas condicionadas:
mu2 <- mu[!idata]
n2 <- length(mu2)
cov.pred <- x.cov[!idata, !idata]
cov.preddat <- x.cov[!idata, idata]
# Cálculo de los pesos kriging:
cov.data.inv <- chol2inv(U)
lambda <- cov.preddat %*% cov.data.inv
# La media serán las predicciones del kriging simple:
kpred <- mu2 + drop(lambda %*% (data - mu1))
# Varianza de la distribución condicional
kcov <- cov.pred - lambda %*% t(cov.preddat)
# (La diagonal serán las varianzas kriging).

Las simulaciones condicionales se obtendrán de forma análoga (Figura 7.4):


z <- matrix(rnorm(nsim * n2), nrow = n2)
y <- kpred + t(chol(kcov)) %*% z
# Representación gráfica:
plot(x, mu, type = "l", lwd = 2, ylab = "y", ylim = c(-3.5, 3.5)) # media teórica
lines(x[idata], data) # datos
# y <- rep(NA, n)
# y[idata] <- data
# lines(x, y)
matplot(x[!idata], y, type = "l", add = TRUE) # simulaciones condicionales
lines(x[!idata], kpred, lwd = 2, lty = 2) # media condicional (predicción kriging)

Ejemplo 7.8 (simulación condicional de datos espaciales).

Consideramos un proceso espacial bidimensional normal Z(s) ≡ Z(x, y) de me-


dia 0 y covariograma exponencial:
Cov(Z(s1 ), Z(s2 )) = C(∥s1 − s2 ∥) = e−∥s1 −s2 ∥ .

En primer lugar, obtendremos una simulación del proceso en las posiciones


{(0, 0), (0, 1), (1, 0), (1, 1)} que será considerada posteriormente como los datos
observados.
Empleando las herramientas del paquete geoR, resulta muy fácil obtener una si-
mulación incondicional en una rejilla en el cuadrado unidad mediante la función
118CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

3
2
1
0
y

−1
−2
−3

0.0 0.2 0.4 0.6 0.8 1.0

Figura 7.4: Realizaciones condicionales del proceso funcional del Ejemplo 7.7.

grf:
library(geoR)
n <- 4
set.seed(1)
z <- grf(n, grid="reg", cov.pars=c(1,1))

## grf: generating grid 2 * 2 with 4 points


## grf: process with 1 covariance structure(s)
## grf: nugget effect is: tausq= 0
## grf: covariance model 1 is: exponential(sigmasq=1, phi=1)
## grf: decomposition algorithm used is: cholesky
## grf: End of simulation procedure. Number of realizations: 1
# names(z)
z$coords

## x y
## [1,] 0 0
## [2,] 1 0
## [3,] 0 1
## [4,] 1 1
z$data

## [1] -0.62645381 -0.05969442 -0.98014198 1.09215113


La grf función emplea por defecto el método de la factorización de la matriz de
7.5. SIMULACIÓN CONDICIONAL E INCONDICIONAL 119

covarianzas, sin embargo, si se desean obtener múltiples realizaciones, en lugar


de llamar repetidamente a esta función, puede ser preferible emplear la siguiente
aproximación:
# Posiciones datos
nx <- c(2, 2)
data.s <- expand.grid(x = seq(0, 1, len = nx[1]), y = seq(0, 1, len = nx[2]))
plot(data.s, type = "p", pch = 20, asp = 1) # Representar posiciones
1.0
0.8
0.6
y

0.4
0.2
0.0

−0.2 0.0 0.2 0.4 0.6 0.8 1.0 1.2

# Matriz de varianzas covarianzas


cov.matrix <- varcov.spatial(coords=data.s, cov.pars=c(1,1))$varcov
cov.matrix

## [,1] [,2] [,3] [,4]


## [1,] 1.0000000 0.3678794 0.3678794 0.2431167
## [2,] 0.3678794 1.0000000 0.2431167 0.3678794
## [3,] 0.3678794 0.2431167 1.0000000 0.3678794
## [4,] 0.2431167 0.3678794 0.3678794 1.0000000
# Simular valores
set.seed(1)
L <- t(chol(cov.matrix))

# Bucle simulación
nsim <- 1 # 1000
for (i in 1:nsim) {
y <- L %*% rnorm(n)
# calcular estadísticos, errores,...
}
120CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

## [,1]
## [1,] -0.62645381
## [2,] -0.05969442
## [3,] -0.98014198
## [4,] 1.09215113

Para generar simulaciones condicionales podemos emplear la función


krige.conv. Por ejemplo, para generar 4 simulaciones en la rejilla regu-
lar 10 × 10 en el cuadrado unidad [0, 1] × [0, 1] condicionadas a los valores
generados en el apartado anterior podríamos emplear el siguiente código:
# Posiciones simulación condicional
nnx <- c(20, 20)
nn <- prod(nnx)
ndata.s <- expand.grid(x = seq(0, 1, len = nnx[1]), y = seq(0, 1, len = nnx[2]))
plot(data.s, type = "p", pch = 20, asp = 1)
points(ndata.s)
1.0
0.8
0.6
y

0.4
0.2
0.0

−0.2 0.0 0.2 0.4 0.6 0.8 1.0 1.2

# Simulación condicional
set.seed(1)
s.out <- output.control(n.predictive = 4)
kc <- krige.conv(z, loc = ndata.s, output = s.out,
krige = krige.control(type.krige="SK", beta = 0, cov.pars = c(1, 1)))

## krige.conv: results will be returned only for prediction locations inside the border
## krige.conv: model with constant mean
## krige.conv: sampling from the predictive distribution (conditional simulations)
7.5. SIMULACIÓN CONDICIONAL E INCONDICIONAL 121

## krige.conv: Kriging performed using global neighbourhood


# Generar gráficos
par.old <- par(mfrow = c(2, 2), mar = c(3.5, 3.5, 1, 0), mgp = c(1.5, .5, 0))
zlim <- range(kc$simul) # Escala común
image(kc, val=kc$simul[,1], main="simul. cond. 1", zlim=zlim)

simul. cond. 1
1.0
0.8
0.6
Y Coord
0.4 0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0


X Coord

image(kc, val=kc$simul[,2], main="simul. cond. 2", zlim=zlim)

simul. cond. 2
1.0
0.8
0.6
Y Coord
0.4 0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0


X Coord

image(kc, val=kc$simul[,3], main="simul. cond. 3", zlim=zlim)


122CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

simul. cond. 3

1.0
0.8
0.6
Y Coord
0.4 0.2
0.0
0.0 0.2 0.4 0.6 0.8 1.0
X Coord

image(kc, val=kc$simul[,4], main="simul. cond. 3", zlim=zlim)

simul. cond. 3
1.0
0.8
0.6
Y Coord
0.4 0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0


X Coord

par(par.old)

Los valores en las posiciones {(0, 0), (0, 1), (1, 0), (1, 1)} coinciden con los gene-
rados anteriormente.
7.6. SIMULACIÓN BASADA EN CÓPULAS 123

7.5.2 Simulación condicional a partir de un modelo ajus-


tado

En la práctica normalmente se ajusta un modelo a los datos observados y pos-


teriormente se obtienen las simulaciones condicionadas empleando el modelo
ajustado.
Por ejemplo, en el caso de series de tiempo, se puede emplear la función
simulate del paquete forecast:
library(forecast)
data <- window(co2, 1990) # datos de co2 desde 1990
plot(data, ylab = expression("Atmospheric concentration of CO"[2]),
xlim=c(1990,2000), ylim=c(350, 375))
fit <- ets(data)
# plot(forecast(fit, h=24))
set.seed(1)
lines(simulate(fit, 24), col="red")
375
370
Atmospheric concentration of CO2

365
360
355
350

1990 1992 1994 1996 1998 2000

Time

7.6 Simulación basada en cópulas

Una cópula es una función de distribución multidimensional con distribuciones


marginales uniformes (e.g. Nelsen, 2006). Se emplean principalmente para la
construcción de distribuciones multivariantes a partir de distribuciones margi-
nales (también en análisis de dependencia y medidas de asociación).
Nelsen, R.B. (2006). An introduction to copulas, 2ª ed., Springer
124CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

Hofert, M. (2018). Elements of Copula Modeling with R, Springer.


Por simplicidad nos centraremos en el caso bidimensional. El teorema central
en la teoría de cópulas es el teorema de Sklar (1959), que en este caso es:
Teorema 7.1 (de Sklar, caso bidimensional)
Si (X, Y ) es una variable aleatoria bidimensional con función de distribución
conjunta F (·, ·) y distribuciones marginales F1 (·) y F2 (·) respectivamente, en-
tonces existe una cópula C(·, ·) tal que:

F (x, y) = C (F1 (x), F2 (y)) , ∀x, y ∈ R.

Además, si F1 (·) y F2 (·) son continuas C(·, ·) es única.


El recíproco también es cierto.

7.6.1 Cópulas Arquimedianas

Además de las cópulas Gausianas, es una de las familias de cópulas más utiliza-
das. Son de la forma:
( d )

−1
C(x1 , x2 , . . . , xd ) = Ψ Ψ (Fi (xi )) ,
i=1

siendo Ψ su función generadora.


Una condición suficiente para que sea una cópula multidimensional válida es
que Ψ(1) = 0, lim Ψ(x) = ∞, Ψ′ (x) < 0 y Ψ′′ (x) > 0.
x →0

Ejemplos:
• Cópula producto o independiente: Ψ(x) = − ln(x),

F (x, y) = F1 (x)F2 (y).

• Cópula de Clayton: Ψ(x) = 1


α (x−α − 1) ; α > 0,

F (x, y) = (F1 (x)−α + F2 (y)−α − 1)−1/α .

α
• Cópula de Gumbel: Ψ(x) = (− ln(x)) ; α ≥ 1

7.6.2 Simulación

Las cópulas pueden facilitar notablemente la simulación de la distribución con-


junta. Si (U, V ) ∼ C(·, ·) (marginales uniformes):
( −1 )
F1 (U ), F2−1 (V ) ∼ F (·, ·)
7.6. SIMULACIÓN BASADA EN CÓPULAS 125

En la mayoría de los casos se dispone de expresiones explicitas de Cu (v) ≡


C2 ( v| u) y de su inversa Cu−1 (w), por lo que se puede generar (U, V ) fácilmente
mediante el método secuencial de distribuciones condicionadas descrito en la
Sección 7.4.
Algoritmo 7.6 (de simulación bidimensional mediante cópulas) 1. Generar
U, W ∼ U (0, 1)

2. Obtener V = CU−1 (W )

( )
3. Devolver F1−1 (U ), F2−1 (V )

Ejercicio 7.1.

Consideramos una v.a. bidimensional con distribuciónes marginales uniformes


y distribución bidimensional determinada por la cópula de Clayton.

a) Teniendo en cuenta que en este caso:

( ( ) )− α1
Cu−1 (w) ≡ u−α w− α+1 − 1 + 1
α
,

diseñar una rutina que permita generar una muestra de tamaño n de esta
distribución.
rcclayton <- function(alpha, n) {
val <- cbind(runif(n), runif(n))
val[, 2] <- (val[, 1]^(-alpha) *
(val[, 2]^(-alpha/(alpha + 1)) - 1) + 1)^(-1/alpha)
return(val)
}

b) Utilizando la rutina anterior generar una muestra de tamaño 10000 y repre-


sentar gráficamente los valores obtenidos y sus distribuciones marginales.
set.seed(54321)
rcunif <- rcclayton(2,10000)
plot(rcunif, xlab = "u", ylab = "v")
126CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

1.0
0.8
0.6
v

0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Representar la densidad conjunta (con sm::sm.density()) y las margina-


les:
# Densidad conjunta
# if(!require(sm)) stop('Required pakage `sm` not installed.')
sm::sm.density(rcunif)

## Warning: weights overwritten by binning

2.0
Density fu

1.5

1.0
nction

0.5

0.0

0.8
0.6
rcu

0.8
nif

0.4
0.6
[2]

0.2 0.4 nif[1]


0.2 rcu

# Distribuciones marginales
par.old <- par(mfrow = c(1, 2))
hist(rcunif[,1], freq = FALSE)
abline(h = 1)
7.6. SIMULACIÓN BASADA EN CÓPULAS 127

hist(rcunif[,2], freq = FALSE)


abline(h = 1)

Histogram of rcunif[, 1] Histogram of rcunif[, 2]

1.0
1.0

0.8
0.8

0.6
0.6
Density

Density

0.4
0.4

0.2
0.2
0.0

0.0

0.0 0.2 0.4 0.6 0.8 1.0 0.0 0.2 0.4 0.6 0.8 1.0

rcunif[, 1] rcunif[, 2]

par(par.old)

Empleando el paquete copula:


if(!require(copula)) stop('Required pakage `copula` not installed.')
clayton.cop <- claytonCopula(2, dim = 2) # caso bidimensional
y <- rCopula(10000, clayton.cop)
plot(y)
1.0
0.8
0.6
y[,2]

0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

y[,1]
128CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

clayton.cop <- claytonCopula(2, dim = 3) # caso tridimensional


y <- rCopula(10000, clayton.cop)
scatterplot3d::scatterplot3d(y)

1.0
0.8
0.6
y[,3]

y[,2]
1.0
0.4

0.8
0.6
0.2

0.4
0.2
0.0

0.0
0.0 0.2 0.4 0.6 0.8 1.0

y[,1]

# plot3D:::points3D(y[,1], y[,2], y[, 3], colvar = NULL)

c) A partir de la muestra anterior generar una muestra de una v.a. bidimen-


sional con distribuciones marginales exponenciales de parámetros 1 y 2
respectivamente (y distribución bidimensional determinada por la cópula
de Clayton).
rcexp <- cbind(qexp(rcunif[,1], 1), qexp(rcunif[,2], 2))
plot(rcexp, xlab = "exp1", ylab = "exp2")
4
3
exp2

2
1
0

0 2 4 6 8

exp1
7.7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES DISCRETAS129

# Distribuciones marginales
par.old <- par(mfrow = c(1, 2))
hist(rcexp[,1], freq = FALSE)
curve(dexp(x,1), add = TRUE)
hist(rcexp[,2], freq = FALSE)
curve(dexp(x,2), add = TRUE)

Histogram of rcexp[, 1] Histogram of rcexp[, 2]


0.8

1.2
1.0
0.6

0.8
Density

Density
0.4

0.6
0.4
0.2

0.2
0.0

0.0

0 2 4 6 8 0 1 2 3 4 5

rcexp[, 1] rcexp[, 2]

par(par.old)

7.7 Simulación de distribuciones multivariantes


discretas

7.7.1 Métodos de codificación o etiquetado para variables


discretas

En el caso de una distribución d-dimensional discreta el procedimiento habitual


es simular una variable aleatoria discreta unidimensional equivalente. Este tipo
de procedimientos son conocidos como métodos de etiquetado o codificación y
la idea básica consistiría en construir un indice unidimensional equivalente al
indice multidimensional.

Si la variable discreta multidimensional tiene soporte finito, este tipo de reco-


dificación se puede hacer de forma automática en R cambiando simplemente el
indexado (internamente un objeto matrix o array ya está almacenado como un
vector y R permite un indexado multidimensional a partir del atributo dim).
130CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

z <- 1:10
xy <- matrix(z, ncol = 2)
xy

## [,1] [,2]
## [1,] 1 6
## [2,] 2 7
## [3,] 3 8
## [4,] 4 9
## [5,] 5 10
as.vector(xy)

## [1] 1 2 3 4 5 6 7 8 9 10
Si la variable discreta multidimensional no tiene soporte finito (no se podría
guardar la f.m.p. en una tabla), se podrían emplear métodos de codificación
más avanzados (ver sección 6.3 del libro de R. Cao).

7.7.2 Simulación de una variable discreta bidimensional

Consideramos datos recogidos en un estudio de mejora de calidad en una fá-


brica de semiconductores. Se obtuvo una muestra de obleas que se clasificaron
dependiendo de si se encontraron partículas en la matriz que producía la oblea
y de si la calidad de oblea era buena (para más detalles Hall, 1994. Analysis
of defectivity of semiconductor wafers by contigency table. Proceedings of the
Institute of Environmental Sciences 1, 177-183).
n <- c(320, 14, 80, 36)
particulas <- gl(2, 1, 4, labels = c("no","si"))
calidad <- gl(2, 2, labels = c("buena", "mala"))
df <- data.frame(n, particulas, calidad)
df

## n particulas calidad
## 1 320 no buena
## 2 14 si buena
## 3 80 no mala
## 4 36 si mala
En lugar de estar en el formato de un conjunto de datos (data.frame) puede
que los datos estén en formato de tabla (table, matrix):
tabla <- xtabs(n ~ calidad + particulas)
tabla

## particulas
## calidad no si
7.7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES DISCRETAS131

## buena 320 14
## mala 80 36

Lo podemos convertir directamente a data.frame:


as.data.frame(tabla)

## calidad particulas Freq


## 1 buena no 320
## 2 mala no 80
## 3 buena si 14
## 4 mala si 36

En este caso definimos las probabilidades a partir de las frecuencias:


df$p <- df$n/sum(df$n)
df

## n particulas calidad p
## 1 320 no buena 0.71111111
## 2 14 si buena 0.03111111
## 3 80 no mala 0.17777778
## 4 36 si mala 0.08000000

En formato tabla:
pij <- tabla/sum(tabla)
pij

## particulas
## calidad no si
## buena 0.71111111 0.03111111
## mala 0.17777778 0.08000000

Para simular la variable bidimensional consideramos una variable unidimensio-


nal de índices:
z <- 1:nrow(df)
z

## [1] 1 2 3 4

Con probabilidades:
pz <- df$p
pz

## [1] 0.71111111 0.03111111 0.17777778 0.08000000

Si las probabilidades estuviesen en una matriz, las convertiríamos a un vector


con:
132CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

as.vector(pij)

## [1] 0.71111111 0.17777778 0.03111111 0.08000000


Si simulamos la variable unidimensional:
set.seed(1)
nsim <- 20
rz <- sample(z, nsim, replace = TRUE, prob = pz)

Podríamos obtener simulaciones bidimensionales, por ejemplo:


etiquetas <- as.matrix(df[c('particulas', 'calidad')])
rxy <- etiquetas[rz, ]
head(rxy)

## particulas calidad
## [1,] "no" "buena"
## [2,] "no" "buena"
## [3,] "no" "buena"
## [4,] "si" "mala"
## [5,] "no" "buena"
## [6,] "si" "mala"
Alternativamente, si queremos trabajar con data.frames:
etiquetas <- df[c('particulas', 'calidad')]
rxy <- etiquetas[rz, ]
head(rxy)

## particulas calidad
## 1 no buena
## 1.1 no buena
## 1.2 no buena
## 4 si mala
## 1.3 no buena
## 4.1 si mala
# Si se quieren eliminar las etiquetas de las filas:
row.names(rxy) <- NULL
head(rxy)

## particulas calidad
## 1 no buena
## 2 no buena
## 3 no buena
## 4 si mala
## 5 no buena
## 6 si mala
7.7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES DISCRETAS133

7.7.3 Simulación de tablas de contingencia

El código anterior puede ser empleado para simular tablas de contingencia. Aun-
que en estos casos se suele fijar el total de la tabla (o incluso las frecuencias
marginales). En este caso, sólo habría que fijar el nº de simulaciones al total de
la tabla:
nsim <- sum(n)
set.seed(1)
rz <- sample(z, nsim, replace = TRUE, prob = pz)
rtable <- table(rz) # Tabla de frecuencias unidimensional
matrix(rtable, ncol = 2) # Tabla de frecuencias bidimensional

## [,1] [,2]
## [1,] 321 78
## [2,] 15 36
Aunque puede ser preferible emplear directamente rmultinom si se van a generar
muchas:
ntsim <- 1000
rtablas <- rmultinom(ntsim, sum(n), pz)
rtablas[ , 1:5] # Las cinco primeras simulaciones

## [,1] [,2] [,3] [,4] [,5]


## [1,] 298 329 323 323 307
## [2,] 15 21 5 15 15
## [3,] 92 68 91 77 92
## [4,] 45 32 31 35 36
Por ejemplo, si se quiere simular bajo independencia, estimando las probabili-
dades a partir de la tabla:
tabla

## particulas
## calidad no si
## buena 320 14
## mala 80 36
Consideraríamos como probabilidades:
pind <- (rowSums(tabla) %o% colSums(tabla))/(sum(tabla)^2)
matrix(pind, nrow = nrow(tabla))

## [,1] [,2]
## [1,] 0.6597531 0.08246914
## [2,] 0.2291358 0.02864198
rtablas <- rmultinom(ntsim, sum(n), pind)
rtablas[ , 1:5] # Las cinco primeras simulaciones
134CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES

## [,1] [,2] [,3] [,4] [,5]


## [1,] 292 285 309 303 290
## [2,] 96 105 97 84 113
## [3,] 48 48 36 49 39
## [4,] 14 12 8 14 8

Para realizar el contraste de independencia:


res <- chisq.test(tabla)
res

##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: tabla
## X-squared = 60.124, df = 1, p-value = 8.907e-15

Ejercicio 7.2 (Distribución del estadístico chi-cuadrado de independencia).

Aproximar por simulación la distribución (exacta) del estadístico chi-cuadrado


bajo independencia.
simstat <- apply(rtablas, 2, function(x){chisq.test(matrix(x,nrow=nrow(tabla)))$statist
hist(simstat, freq = FALSE, breaks = 'FD')
# Distribución asintótica (aproximación chi-cuadrado)
curve(dchisq(x, res$parameter), add = TRUE)

Histogram of simstat
2.0
1.5
Density

1.0
0.5
0.0

0 2 4 6 8 10 12

simstat
7.8. EJERCICIOS PROPUESTOS 135

7.8 Ejercicios propuestos


Ejercicio 7.3.

Dar un algoritmo basado en el método de aceptación-rechazo (considerando


como densidad auxiliar una uniforme) que permita generar observaciones de
una variable aleatoria bidimensional (X, Y ) con función de densidad:
{ 3 ( ( 2 ))
16 2 − x + y si x ∈ [−1, 1] e y ∈ [−1, 1]
2
f (x, y) =
0 en otro caso

Ejercicio 7.4.

Considerando la variable aleatoria bidimensional del ejercicio anterior y teniendo


en cuenta que la densidad marginal de la variable X es:
{ 1( )
8 5 − 3x2 si x ∈ [−1, 1]
fX (x) =
0 en otro caso

Describir brevemente un algoritmo para la simulación del vector aleatorio basado


en el método de las distribuciones condicionadas (asumir que se dispone de un
algoritmo para generar observaciones de las distribuciones unidimensionales de
interés).
136CAPÍTULO 7. SIMULACIÓN DE DISTRIBUCIONES MULTIVARIANTES
Capítulo 8

Aplicaciones de la
simulación en Inferencia
Estadística

Como ya se comentó en la introducción muchas de las aplicaciones de la simu-


lación serían de utilidad en Estadística:
• Muestreo, aproximación de distribuciones, remuestreo,…
• Optimización: Algoritmos genéticos, …
• Análisis numérico: Evaluación de expresiones, …
• …
En este capítulo nos centraremos en algunas de las aplicaciones en inferencia
estadística:
• Distribución de estimadores puntuales/estadísticos:
– Aproximación de la distribución.
∗ Aproximación de características de la distribución.
∗ Valided de la distribución asintótica.
– Comparación de estimadores.
• Estimación por intervalo de confianza:
– Obtención de intervalos/bandas de confianza (probabilidad).
– Análisis de un estimador por intervalo de confianza.
• Contrastes de hipótesis:

137
138CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

– Aproximación del p-valor.


– Análisis de un contraste de hipótesis.
– Validación teoría.
• Métodos de remuestro bootstrap.
• Inferencia Bayesiana
• …
En el siguiente capítulo trararemos la Integración y Optimización Monte Carlo.
Observación: En este capítulo se obtendrán simulaciones de estadísticos a partir
de muestras (podemos pensar que se parte de generaciones de una variable
multivariante). En la mayoría de los ejemplos se generan todas las muestras de
una vez, se guardan y se procesan vectorialmente (normalmente empleando la
función apply). Como ya se comentó en la el Capítulo 2, en problemas mas
complejos, en los que no es necesario almacenar todas las muestras, puede ser
preferible emplear un bucle para generar y procesar las muestras iterativamente.

8.1 Distribución en el muestreo


Ejercicio 8.1.

Si X1 , . . . , Xn es una muestra aleatoria simple de una variable aleatoria X ∼


N (µ, σ), la distribución en el muestreo de:

1∑
n
µ̂ = X = Xi
n i=1

es: ( )
σ
X∼N µ, √
n
Confirmar este resultado mediante simulación, para ello:
a) Crear un conjunto de datos muestras con 500 muestras de tamaño n = 10
de una N (1, 2). Añadir al conjunto de datos las estimaciones de la media
y desviación típica obtenidas con cada una de las muestras.
Valores iniciales:
set.seed(54321) # Fijar semilla para reproductibilidad
nsim <- 500
nx <- 10

Valores teóricos:
8.1. DISTRIBUCIÓN EN EL MUESTREO 139

mux <- 1
sdx <- 2

Simulación de las muestras (al estilo Rcmdr):


muestras <- as.data.frame(matrix(rnorm(nsim*nx, mean=mux, sd=sdx), ncol=nx))
rownames(muestras) <- paste("muestra", 1:nsim, sep="")
colnames(muestras) <- paste("obs", 1:nx, sep="")
str(muestras)

## 'data.frame': 500 obs. of 10 variables:


## $ obs1 : num 0.642 -0.856 -0.568 -2.301 0.184 ...
## $ obs2 : num 3.483 2.216 1.1 4.305 0.677 ...
## $ obs3 : num 1.24 -1.51 -3.98 2.29 2.46 ...
## $ obs4 : num 3.286 0.947 0.953 -1.663 2.623 ...
## $ obs5 : num 3.77 -1.34 1.61 -2.46 1.11 ...
## $ obs6 : num -2.044 0.32 3.046 0.136 3.555 ...
## $ obs7 : num 0.6186 -1.8614 4.3386 0.0996 0.8334 ...
## $ obs8 : num -0.829 2.202 -1.688 1.534 -0.114 ...
## $ obs9 : num 0.4904 -0.6713 0.5451 -0.6517 0.0168 ...
## $ obs10: num 2.79 2.84 1.27 3.93 2.17 ...
Estimaciones:
muestras$mean <- rowMeans(muestras[,1:nx])
muestras$sd <- apply(muestras[,1:nx], 1, sd)
Nota: . La fila muestras[i,] contiene las observaciones de la i-ésima
muestra y la correspondiente media y desviación típica.
muestras[1,]

## obs1 obs2 obs3 obs4 obs5 obs6 obs7


## muestra1 0.6421985 3.482661 1.242483 3.28559 3.766896 -2.04443 0.6186323
## obs8 obs9 obs10 mean sd
## muestra1 -0.8293636 0.4903819 2.790091 1.344514 1.951292
Normalmente emplearemos sin embargo una ordenación por columnas (cada fila
se corresponderá con una generación).

b) Generar el histograma (en escala de densidades) de las medias muestrales


y compararlo con la densidad teórica.
Distribución de la media muestral:
hist(muestras$mean, freq = FALSE, breaks = "FD",
xlab = "Medias", ylab = "Densidad")
# Densidad observada (estimación)
lines(density(muestras$mean))
# Densidad teórica (bajo normalidad)
140CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

curve(dnorm(x, mux, sdx/sqrt(nx)), lwd = 2, col = "blue", add = TRUE)


# Aproximación del valor esperado de la media muestral mediante simulación
abline(v = mean(muestras$mean), lty = 2)
# Valor esperado de la media muestral (teórico)
abline(v = mux, col = "blue")

Histogram of muestras$mean
0.6
0.5
0.4
Densidad

0.3
0.2
0.1
0.0

−1 0 1 2

Medias

Figura 8.1: Distribución de la media muestral de una distribución normal.

Ejercicio 8.2.

Si X1 , . . . , Xn es una m.a.s. de una variable aleatoria X (cualquiera) con


E (X) = µ y V ar (X) = σ 2 , por el Teorema Central del Límite, la distribución
en el muestreo de µ̂ = X se aproxima a la normalidad:
( )
σ
X −→ N µ, √
n→∞ n

Típicamente se suele considerar que esta aproximación es buena para tamaños


muestrales n > 30, aunque dependerá de las características de la distribución
de X.

a) Repetir el ejercicio anterior considerando muestras de una Exp(1) (tener


en cuenta que X ∼ Exp(λ) ⇒ µX = σX = 1/λ). ¿Qué ocurre con la
distribución de la media muestral?
8.2. INTERVALOS DE CONFIANZA 141

set.seed(54321) # Fijar semilla para reproductibilidad


nsim <- 500
nx <- 10
# nx <- 50

Valores teóricos:
lambda <- 1
muexp <- 1/lambda
sdexp <- muexp

Simulación de las muestras:


muestras2 <- as.data.frame(matrix(rexp(nsim*nx, rate=lambda), ncol=nx))
rownames(muestras2) <- paste("muestra", 1:nsim, sep="")
colnames(muestras2) <- paste("obs", 1:nx, sep="")

Estimaciones:
muestras2$mean <- rowMeans(muestras2[,1:nx])
muestras2$sd <- apply(muestras2[,1:nx], 1, sd)

Distribución de la media muestral:


hist(muestras2$mean, xlim = c(-0.1, 2.5), freq = FALSE, breaks = "FD",
xlab = "Medias", ylab = "Densidad")
# Densidad observada (estimación)
lines(density(muestras2$mean))
# Distribución asintótica (TCL)
curve(dnorm(x,muexp,sdexp/sqrt(nx)), lwd=2, col="blue", add=TRUE)
# Aproximación del valor esperado de la media muestral mediante simulación
abline(v=mean(muestras2$mean),lty=2)
# Valor esperado de la media muestral (teórico)
abline(v=muexp, col="blue")

b) Aumentar el tamaño muestral a 50. ¿Se aproxima más la distribución de


las medias muestrales a la teórica bajo normalidad?
Ejecutar el código del apartado anterior fijando nx <- 50.

8.2 Intervalos de confianza


Ejercicio 8.3.

Siguiendo el enunciado del ejercicio 1, se deduce que el intervalo de confianza


(de nivel 1 − α) para la media µ de una población normal con varianza conocida
142CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

Histogram of muestras2$mean

1.4
1.2
1.0
0.8
Densidad

0.6
0.4
0.2
0.0

0.0 0.5 1.0 1.5 2.0 2.5

Medias

Figura 8.2: Distribución de la media muestral de una distribución exponencial


y distribución asintótica.

es: ( )
σ σ
IC1−α (µ) = X − z1−α/2 √ , X + z1−α/2 √ .
n n

La idea es que el 100(1 − α)% de los intervalos así construidos contentrán el


verdadero valor del parámetro.

a) Utilizando el conjunto de datos muestras del ejercicio 1 (500 muestras


de tamaño n = 10 de una N (1, 2)), añadir en dos nuevas variables los
extremos del intervalo de confianza para la media con varianza conocida
al conjunto de datos. Analizar la cobertura de estas estimaciones por IC.

IC para la media con varianza conocida (bajo normalidad):


alfa <- 0.05
z <- qnorm(1 - alfa/2)
muestras$ici <- muestras$mean - z*sdx/sqrt(nx)
muestras$ics <- muestras$mean + z*sdx/sqrt(nx)

Cobertura de las estimaciones por IC:


muestras$cob <- (muestras$ici < mux) & (mux < muestras$ics)
ncob <- sum(muestras$cob) # Nº de intervalos que contienen la verdadera media
ncob

## [1] 480
8.2. INTERVALOS DE CONFIANZA 143

100*ncob/nsim # Proporción de intervalos

## [1] 96
100*(1 - alfa) # Proporción teórica bajo normalidad

## [1] 95

Como ejemplo ilustrativo, generamos el gráfico de los primeros 50 interva-


los:
m <- 50
tmp <- muestras[1:m,]
attach(tmp)
color <- ifelse(cob,"blue","red")
plot(1:m, mean, col = color, ylim = c(min(ici),max(ics)),
xlab = "Muestra", ylab = "IC")
arrows(1:m, ici, 1:m, ics, angle = 90, length = 0.05, code = 3, col = color)
abline(h = mux, lty = 3)
4
3
2
IC

1
0
−1

0 10 20 30 40 50

Muestra

Figura 8.3: Cobertura de las estimaciones por IC.

detach(tmp)

b) Repetir el apartado anterior considerando muestras de una Exp(1). ¿Qué


ocurre con la cobertura del intervalo de confianza obtenido bajo normali-
dad?
144CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

Ejecutar el código del apartado a) del ejercicio 2.


IC para la media con varianza conocida (bajo normalidad)
alfa <- 0.05
z <- qnorm(1 - alfa/2)
muestras2$ici <- muestras2$mean - z*sdexp/sqrt(nx)
muestras2$ics <- muestras2$mean + z*sdexp/sqrt(nx)

Cobertura de las estimaciones por IC:


muestras2$cob <- (muestras2$ici < muexp) & (muexp < muestras2$ics)
ncob <- sum(muestras2$cob) # Nº de intervalos que contienen la verdadera media
ncob

## [1] 469
100*ncob/nsim # Proporción de intervalos

## [1] 93.8
100*(1 - alfa) # Proporción teórica bajo normalidad

## [1] 95
Como ejemplo ilustrativo, generamos el gráfico de los primeros 100 inter-
valos:
m <- 100
tmp <- muestras2[1:m,]
attach(tmp)
color <- ifelse(cob,"blue","red")
plot(1:m, mean, col = color, ylim = c(min(ici),max(ics)),
xlab = "Muestra", ylab = "IC")
arrows(1:m, ici, 1:m, ics, angle = 90, length = 0.05, code = 3, col = color)
abline(h = muexp, lty = 3)

detach(tmp)

c) ¿Qué ocurre si aumentamos el tamaño muestral a 50?


Ejecutar el código del ejercicio anterior fijando nx <- 50 y el del apartado
anterior.
En los apartados b) y c) podíamos considerar bootstrap descrito al final de este
capítulo.
Podemos aproximar por simulación los intervalos de probabilidad de la media
muestral (tendríamos una idea del valor esperado de lo que obtendríamos con
el bootstrap percentil; en este caso el estimador es insesgado…):
# Distribución de la media muestral
hist(muestras2$mean, freq=FALSE, breaks="FD",
8.2. INTERVALOS DE CONFIANZA 145

2.5
2.0
1.5
IC

1.0
0.5
0.0

0 20 40 60 80 100

Muestra

Figura 8.4: Cobertura de las estimaciones por IC (bajo normalidad).

main="Distribución de la media muestral", xlab="Medias", ylab="Densidad")


# Densidad observada (estimación)
lines(density(muestras2$mean), lwd=2, col='red')
# Densidad teórica (bajo normalidad)
curve(dnorm(x,muexp,sdexp/sqrt(nx)), col="blue", add=TRUE)
# Aproximación por simulación del valor esperado de la media muestral
abline(v=mean(muestras2$mean), lty=2)
# Valor esperado de la media muestral (teórico)
abline(v=muexp, col="blue")
# IP bajo normalidad
ic.aprox <- apply(muestras2[ ,c('ici','ics')], 2, mean)
## ic.aprox
## ici ics
## 0.3865199 1.6261099
# Intervalo de probabilidad para la media muestral aproximado bajo normalidad
abline(v = ic.aprox, col='blue')

# Intervalo de probabilidad para la media muestral (aproximado por simulación)


ic.sim <- quantile(muestras2$mean, c(alfa/2, 1 - alfa/2))
## ic.sim
## 2.5% 97.5%
## 0.4714233 1.8059094
# IP (aprox.)
abline(v=ic.sim, lty=2, col='red')
146CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

Distribución de la media muestral

1.4
1.2
1.0
0.8
Densidad

0.6
0.4
0.2
0.0

0.5 1.0 1.5 2.0

Medias

Nota: . Estimaciones puntuales, por intervalo de confianza y contrastes de hipó-


tesis para la media con varianza desconocida bajo normalidad se pueden obtener
con la función t.test.

Ejercicio 8.4.

El Intervalo de confianza para una proporción construido usando la aproxima-


ción normal tiene un mal comportamiento cuando el tamaño de la muestra es
pequeño. Una simple y efectiva mejora consiste en añadir a la muestra 2a ele-
mentos, a exitos y a fracasos. Así el intervalo de confianza al (1 − α) 100% para
una proporción mejorado es:
( √ √ )
p̃(1 − p̃) p̃(1 − p̃)
IC1−α (p) = p̃ − z1−α/2
a
, p̃ + z1−α/2 ,
ñ ñ
np + a
siendo ñ = n + 2a, p̃ = .

En el caso de a = 2 se denomina IC Agresti-Coull.
a) Teniendo en cuenta que la v.a. X = np̂ ∼ B(n, p), obtener y represen-
tar gráficamente la cobertura teórica del intervalo de confianza estándar
(a = 0) de una proporción para una muestra de tamaño n = 30, α =
0.05 y distintos valores de p (p.teor <- seq(1/n, 1 - 1/n, length =
1000)).
Parámetros:
n <- 30
alpha <- 0.05
adj <- 0 # (adj <- 2 para Agresti-Coull)
8.2. INTERVALOS DE CONFIANZA 147

Probabilidades teóricas:
m <- 1000
p.teor <- seq(1/n, 1 - 1/n, length = m)

Posibles resultados:
x <- 0:n
p.est <- (x + adj)/(n + 2 * adj)
ic.err <- qnorm(1 - alpha/2) * sqrt(p.est * (1 - p.est)/(n + 2 * adj))
lcl <- p.est - ic.err
ucl <- p.est + ic.err

Recorrer prob. teóricas:


p.cov <- numeric(m)
for (i in 1:m) {
# cobertura de los posibles intervalos
cover <- (p.teor[i] >= lcl) & (p.teor[i] <= ucl)
# prob. de los posibles intervalos
p.rel <- dbinom(x[cover], n, p.teor[i])
# prob. total de cobertura
p.cov[i] <- sum(p.rel)
}

Gráfico coberturas:
plot(p.teor, p.cov, type = "l", ylim = c(1 - 4 * alpha, 1))
abline(h = 1 - alpha, lty = 2)
1.00
0.95
p.cov

0.90
0.85
0.80

0.0 0.2 0.4 0.6 0.8 1.0

p.teor

Fuente Suess y Trumbo (2010).


148CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

b) Repetir el apartado anterior considerando intervalos de confianza Agresti-


Coull (a = 2).

Parámetros:
n <- 30
alpha <- 0.05
adj <- 2 # Agresti-Coull

# Probabilidades teóricas:
m <- 1000
p.teor <- seq(1/n, 1 - 1/n, length = m)
# Posibles resultados:
x <- 0:n
p.est <- (x + adj)/(n + 2 * adj)
ic.err <- qnorm(1 - alpha/2) * sqrt(p.est * (1 - p.est)/(n + 2 * adj))
lcl <- p.est - ic.err
ucl <- p.est + ic.err
# Recorrer prob. teóricas:
p.cov <- numeric(m)
for (i in 1:m) {
# cobertura de los posibles intervalos
cover <- (p.teor[i] >= lcl) & (p.teor[i] <= ucl)
# prob. de los posibles intervalos
p.rel <- dbinom(x[cover], n, p.teor[i])
# prob. total de cobertura
p.cov[i] <- sum(p.rel)
}
# Gráfico coberturas:
plot(p.teor, p.cov, type = "l", ylim = c(1 - 4 * alpha, 1))
abline(h = 1 - alpha, lty = 2)
8.2. INTERVALOS DE CONFIANZA 149

1.00
0.95
p.cov

0.90
0.85
0.80

0.0 0.2 0.4 0.6 0.8 1.0

p.teor

c) Repetir el apartado anterior empleando simulación para aproximar la co-


bertura.

Parámetros:
n <- 30
alpha <- 0.05
adj <- 2 #' (2 para Agresti-Coull)

set.seed(54321)
nsim <- 500
# Probabilidades teóricas:
m <- 1000
p.teor <- seq(1/n, 1 - 1/n, length = m)

Recorrer prob. teóricas:


# m <- length(p.teor)
p.cov <- numeric(m)
for (i in 1:m) {
# Equivalente a simular nsim muestras de tamaño n
# ry <- matrix(rbinom(n*nsim, 1, p.teor[i]), ncol=n)
# rx <- apply(ry, 1, sum)
rx <- rbinom(nsim, n, p.teor[i])
p.est <- (rx + adj)/(n + 2 * adj)
ic.err <- qnorm(1 - alpha/2) * sqrt(p.est * (1 - p.est)/(n + 2 * adj))
p.cov[i] <- mean( abs(p.est - p.teor[i]) < ic.err )
}

Representar:
150CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

plot(p.teor, p.cov, type = "l", ylim = c(1 - 4 * alpha, 1))


abline(h = 1 - alpha, lty = 2)

1.00
0.95
p.cov

0.90
0.85
0.80

0.0 0.2 0.4 0.6 0.8 1.0

p.teor

Como ya se comentó, el caso de ajustar un modelo a los datos y realizar si-


mulaciones a partir de ese modelo ajustado para aproximar las características
de interés de un estadístico, se denomina también bootstrap paramétrico (Ver
Sección 4.1 de Cao y Fernández-Casal, 2019).
En este libro en las secciones 5.6 y E.3.2, se incluyen ejemplos adicionales de
estudios de simulación.

8.3 Contrastes de hipótesis


Ver Apéncide A de Cao y Fernández-Casal (2019).
Ejercicio 8.5.

En el tema 2 se propuso el análisis de la bondad de ajuste de un generador de


números pseudo-aleatorios mediante el test de Kolmogorov-Smirnov. Sin embar-
go, si H0 es compuesta (los parámetros desconocidos se estiman por máxima
verosimilitud y se trabaja con F̂0 ) los cuantiles de la distribución (asintótica) de
Dn pueden ser demasiado conservativos y sería preferible utilizar la distribución
exacta.
a) Analizar el comportamiento del contraste de Kolmogorov-Smirnov para
contrastar normalidad empleando repetidamente este test, considerando
1000 pruebas con muestras de tamaño 30 de una N (0, 1). Comparar gráfi-
camente el ajuste de la distribución del p-valor a la de referencia (estudiar
el tamaño del contraste).
8.3. CONTRASTES DE HIPÓTESIS 151

Valores iniciales:
set.seed(54321)
nx <- 30
mx <- 0
sx <- 1
nsim <- 1000
estadistico <- numeric(nsim)
pvalor <- numeric(nsim)

Realizar contrastes
for(isim in 1:nsim) {
rx <- rnorm(nx, mx, sx)
tmp <- ks.test(rx, "pnorm", mean(rx), sd(rx))
estadistico[isim] <- tmp$statistic
pvalor[isim] <- tmp$p.value
}

Proporción de rechazos:
{
cat("\nProporción de rechazos al 1% =", mean(pvalor < 0.01), "\n")
cat("Proporción de rechazos al 5% =", mean(pvalor < 0.05), "\n")
cat("Proporción de rechazos al 10% =", mean(pvalor < 0.1), "\n")
}

##
## Proporción de rechazos al 1% = 0
## Proporción de rechazos al 5% = 0
## Proporción de rechazos al 10% = 0.001

Análisis de los p-valores:


hist(pvalor, freq=FALSE)
abline(h=1, lty=2) # curve(dunif(x,0,1), add=TRUE)
152CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

Histogram of pvalor

4
3
Density

2
1
0

0.0 0.2 0.4 0.6 0.8 1.0

pvalor

# Distribución empírica
curve(ecdf(pvalor)(x), type = "s", lwd = 2,
main = 'Tamaño del contraste', ylab = 'Proporción de rechazos',
xlab = 'Nivel de significación')
abline(a=0, b=1, lty=2) # curve(punif(x, 0, 1), add = TRUE)

Tamaño del contraste


1.0
0.8
Proporción de rechazos

0.6
0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Nivel de significación

b) Repetir el apartado anterior considerando el test de Lilliefors (rutina


lillie.test del paquete nortest).
library(nortest, quietly = TRUE)

Valores iniciales:
8.3. CONTRASTES DE HIPÓTESIS 153

set.seed(54321)
nx <- 30
mx <- 0
sx <- 1
nsim <- 1000
estadistico <- numeric(nsim)
pvalor <- numeric(nsim)

Realizar contrastes
for(isim in 1:nsim) {
rx <- rnorm(nx, mx, sx)
# tmp <- ks.test(rx, "pnorm", mean(rx), sd(rx))
tmp <- lillie.test(rx)
estadistico[isim] <- tmp$statistic
pvalor[isim] <- tmp$p.value
}

Proporción de rechazos:
{
cat("\nProporción de rechazos al 1% =", mean(pvalor < 0.01), "\n")
cat("Proporción de rechazos al 5% =", mean(pvalor < 0.05), "\n")
cat("Proporción de rechazos al 10% =", mean(pvalor < 0.1), "\n")
}

##
## Proporción de rechazos al 1% = 0.01
## Proporción de rechazos al 5% = 0.044
## Proporción de rechazos al 10% = 0.089

Análisis de los p-valores:


hist(pvalor, freq=FALSE)
abline(h=1, lty=2) # curve(dunif(x,0,1), add=TRUE)
154CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

Histogram of pvalor

1.2
1.0
0.8
Density

0.6
0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

pvalor

# Distribución empírica
curve(ecdf(pvalor)(x), type = "s", lwd = 2, main = 'Tamaño del contraste',
ylab = 'Proporción de rechazos', xlab = 'Nivel de significación')
abline(a=0, b=1, lty=2) # curve(punif(x, 0, 1), add = TRUE)

Tamaño del contraste


1.0
0.8
Proporción de rechazos

0.6
0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Nivel de significación

c) Repetir el apartado a) contrastando una distribución exponencial y consi-


derando 500 pruebas con muestras de tamaño 30 de una Exp(1).

Valores iniciales:
set.seed(54321)
nx <- 30
ratex <- 1
8.3. CONTRASTES DE HIPÓTESIS 155

nsim <- 500


estadistico <- numeric(nsim)
pvalor <- numeric(nsim)

Realizar contrastes
for(isim in 1:nsim) {
rx <- rexp(nx, ratex)
tmp <- ks.test(rx, "pexp", 1/mean(rx))
estadistico[isim] <- tmp$statistic
pvalor[isim] <- tmp$p.value
}

Proporción de rechazos:
{
cat("\nProporción de rechazos al 1% =", mean(pvalor < 0.01), "\n")
cat("Proporción de rechazos al 5% =", mean(pvalor < 0.05), "\n")
cat("Proporción de rechazos al 10% =", mean(pvalor < 0.1), "\n")
}

##
## Proporción de rechazos al 1% = 0
## Proporción de rechazos al 5% = 0.004
## Proporción de rechazos al 10% = 0.008

Análisis de los p-valores:


hist(pvalor, freq=FALSE)
abline(h=1, lty=2) # curve(dunif(x,0,1), add=TRUE)

Histogram of pvalor
2.0
1.5
Density

1.0
0.5
0.0

0.0 0.2 0.4 0.6 0.8 1.0

pvalor
156CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

# Distribución empírica
curve(ecdf(pvalor)(x), type = "s", lwd = 2,
main = 'Tamaño del contraste', ylab = 'Proporción de rechazos',
xlab = 'Nivel de significación')
abline(a=0, b=1, lty=2) # curve(punif(x, 0, 1), add = TRUE)

Tamaño del contraste

1.0
0.8
Proporción de rechazos

0.6
0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Nivel de significación

d) Diseñar una rutina que permita realizar el contraste KS de bondad de


ajuste de una variable exponencial aproximando el p-valor por simulación
y repetir el apartado anterior empleando esta rutina.
ks.exp.sim <- function(x, nsim = 10^3) {
DNAME <- deparse(substitute(x))
METHOD <- "Kolmogorov-Smirnov Test of pexp by simulation"
n <- length(x)
RATE <- 1/mean(x)
ks.exp.stat <- function(x, rate=1/mean(x)) {
DMinus <- pexp(sort(x), rate=rate) - (0:(n - 1))/n
DPlus <- 1/n - DMinus
Dn = max(c(DMinus, DPlus))
}
STATISTIC <- ks.exp.stat(x, rate = RATE)
names(STATISTIC) <- "Dn"
# PVAL <- 0
# for(i in 1:nsim) {
# rx <- rexp(n, rate = RATE)
# if (STATISTIC <= ks.exp.stat(rx)) PVAL <- PVAL+1
# }
# PVAL <- PVAL/nsim
# PVAL <- PVAL/(nsim + 1)
8.3. CONTRASTES DE HIPÓTESIS 157

# PVAL <- (PVAL + 1)/(nsim + 2)


rx <- matrix(rexp(n*nsim, rate = RATE), ncol=n)
PVAL <- mean(STATISTIC <= apply(rx, 1, ks.exp.stat))
return(structure(list(statistic = STATISTIC, alternative = "two.sided",
p.value = PVAL, method = METHOD, data.name = DNAME),
class = "htest"))
}

Simulación:
set.seed(54321)
nx <- 30
ratex <- 1
nsim <- 500
estadistico <- numeric(nsim)
pvalor <- numeric(nsim)

Realizar contrastes
for(isim in 1:nsim) {
rx <- rexp(nx, ratex)
# tmp <- ks.test(rx, "pexp", 1/mean(rx))
tmp <- ks.exp.sim(rx, nsim = 200)
estadistico[isim] <- tmp$statistic
pvalor[isim] <- tmp$p.value
}

Proporción de rechazos:
{
cat("\nProporción de rechazos al 1% =", mean(pvalor < 0.01), "\n")
cat("Proporción de rechazos al 5% =", mean(pvalor < 0.05), "\n")
cat("Proporción de rechazos al 10% =", mean(pvalor < 0.1), "\n")
}

##
## Proporción de rechazos al 1% = 0.008
## Proporción de rechazos al 5% = 0.058
## Proporción de rechazos al 10% = 0.106

Análisis de los p-valores:


hist(pvalor, freq=FALSE)
abline(h=1, lty=2) # curve(dunif(x,0,1), add=TRUE)
158CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

Histogram of pvalor

1.2
1.0
0.8
Density

0.6
0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

pvalor

# Distribución empírica
curve(ecdf(pvalor)(x), type = "s", lwd = 2,
main = 'Tamaño del contraste', ylab = 'Proporción de rechazos',
xlab = 'Nivel de significación')
abline(a=0, b=1, lty=2) # curve(punif(x, 0, 1), add = TRUE)

Tamaño del contraste


1.0
0.8
Proporción de rechazos

0.6
0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Nivel de significación

e) Estudiar la potencia de los contrastes de los apartados c) y d), conside-


rando como alternativa una distribución Weibull.
La distribución exponencial es un caso particular de la Weibull: dexp(x,
ratex) == dweibull(x, 1, 1/ratex). Estudiamos lo que ocurre al des-
plazar dweibull(x, shape, 1/ratex) con 0 < shape < 2.
CUIDADO: las simulaciones pueden requerir de mucho tiempo de
8.3. CONTRASTES DE HIPÓTESIS 159

computación (consideramos valores pequeños de nx y nsim en datos y en


ks.exp.sim).
set.seed(54321)
nx <- 20
ratex <- 1 # Puede ser interesante representarlo variando rate
nsim <- 200
alfa <- 0.1 # Puede ser interesante representarlo variando alfa

shapex <- seq(0.25, 1.75, len=21)


preject <- numeric(length(shapex)) # Porporciones de rechazos con ks.test
ks.test.p <- function(x) ks.test(x, "pexp", 1/mean(x))$p.value
preject2 <- preject # Porporciones de rechazos con ks.exp.sim
ks.exp.sim.p <- function(x) ks.exp.sim(x, 200)$p.value

for (i in seq_along(shapex)) {
rx <- matrix(rweibull(nx*nsim, shape = shapex[i], scale = 1/ratex), ncol=nx)
preject[i] <- mean( apply(rx, 1, ks.test.p) <= alfa )
preject2[i] <- mean( apply(rx, 1, ks.exp.sim.p) <= alfa )
}

plot(shapex, preject, type="l", main = paste("Potencia del contraste ( alfa =", alfa, ")"),
xlab = "shape", ylab = "Proporción de rechazos")
lines(shapex, preject2, lty = 2)
abline(h = alfa, v = 1, lty = 3)

Potencia del contraste ( alfa = 0.1 )


1.0
0.8
Proporción de rechazos

0.6
0.4
0.2
0.0

0.5 1.0 1.5

shape

El estadístico de Kolmogorov-Smirnov Dn = max(c(DMinus, DPlus)) tiene ven-


tajas desde el punto de vista teórico, pero puede no ser muy potente para detec-
tar diferencias entre la distribución bajo la hipótesis nula y la distribución de los
160CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

datos. La ventaja de la aproximación por simulación es que no estamos atados a


resultados teóricos y podemos emplear el estadístico que se considere oportuno
(la principal desventaja es el tiempo de computación). Por ejemplo, podríamos
pensar en utilizar como estadístico la suma de los errores en valor absoluto del
correspondiente gráfico PP, y solo habría que cambiar el estadístico Dn en la
función ks.exp.sim por Dn = sum(abs( (1:n - 0.5)/n - pexp(sort(x),
rate=rate) )).

8.4 Comparación de estimadores


Ejercicio 8.6.

Supongamos que estamos interesados en estudiar el efecto de datos atípicos en


la estimación de la media teórica mediante la media y la mediana muestrales.
Consideramos una v.a. con distribución normal contaminada, en la que una
observación procede de una N (0, 1) con probabilidad 0.95 y de una N (3, 32 )
con probabilidad 0.05 (mixtura). Se puede generar una muestra de esta variable
mediante los comandos:
p.sim <- rbinom(n, 1, 0.05)
dat.sim <- rnorm(n, 3*p.sim, 1+2*p.sim)
Función de densidad:
curve(dnorm(x, 0, 1), -3, 12, ylab = 'densidad', lty = 3)
curve(0.95*dnorm(x, 0, 1) + 0.05*dnorm(x, 3, 3), add = TRUE)
0.4
0.3
densidad

0.2
0.1
0.0

0 5 10

Nota: . También es habitual simular este tipo de datos generando un porcentaje


alto de valores (en este caso un 95%) de la distribución base (N (0, 1)) y el resto
8.4. COMPARACIÓN DE ESTIMADORES 161

(5%) de la distibución “contaminadora” (N (3, 32 )), aunque se suele considerar


un porcentaje de contaminación del 1% o inferior. En el tema 7 se describirá el
método de composición para simular mixturas.

a) Aproximar mediante simulación (500 generaciones) el sesgo y error están-


dar de la media y la mediana en el caso de una muestra de tamaño n = 100
(suponiendo que se pretende estimar la media no contaminada 0).
# media y mediana
xsd <- 1
xmed <- 0
ndat <- 100
nsim <- 500

# for (isim in 1:nsim) # evitar matrix y apply


set.seed(1)
ntsim <- ndat*nsim
p.sim <- rbinom(ntsim, 1, 0.05)
dat.sim <- rnorm(ntsim, 3*p.sim, 1+2*p.sim)
dat.sim <- matrix(dat.sim, ncol=nsim)

Cada columna es una muestra


str(dat.sim[,1])

## num [1:100] 0.197 -0.42 1.163 -0.406 0.744 ...


hist(dat.sim[,1])

Histogram of dat.sim[, 1]
40
30
Frequency

20
10
0

−2 0 2 4 6

dat.sim[, 1]

Calculamos los estimadores:


162CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

mean.sim <- apply(dat.sim, 2, mean)


median.sim <- apply(dat.sim, 2, median)

Estimamos sus características:


mean(mean.sim) # Coincide con el sesgo (media teórica es 0)

## [1] 0.1459986
sd(mean.sim)

## [1] 0.1349537
mean(median.sim) # Coincide con el sesgo (media teórica es 0)

## [1] 0.04453509
sd(median.sim)

## [1] 0.1300611

Sesgo:
boxplot(mean.sim-xmed, median.sim-xmed,
names=c("Media","Mediana"), ylab="Sesgo")
abline(h = 0, lty = 2)
0.6
0.4
0.2
Sesgo

0.0
−0.2
−0.4

Media Mediana

Error cuadrático:
boxplot((mean.sim-xmed)^2, (median.sim-xmed)^2,
names=c("Media","Mediana"), ylab="Error cuadrático")
8.5. REMUESTREO BOOTSTRAP 163

0.3
Error cuadrático

0.2
0.1
0.0

Media Mediana

Estadísticos error cuadrático:


# SE media
summary((mean.sim-xmed)^2)

## Min. 1st Qu. Median Mean 3rd Qu. Max.


## 0.0000005 0.0045072 0.0206272 0.0394917 0.0591531 0.3619587
# SE mediana
summary((median.sim-xmed)^2)

## Min. 1st Qu. Median Mean 3rd Qu. Max.


## 0.0000001 0.0016481 0.0070625 0.0188654 0.0243903 0.2618368

8.5 Remuestreo Bootstrap

Ver Cao y Fernández-Casal (2019).

8.5.1 Idea:

Consideramos un conjunto de datos simulado:


set.seed(1)
data <- runif(50)

La idea es aproximar características poblacionales por las correspondientes de


la distribución empírica de los datos observados:
164CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

# Distribución bootstrap
curve(ecdf(data)(x), ylab = "FD", type = "s", lwd = 2)
# Distribución teórica
abline(a = 0, b = 1, lty = 2)

1.0
0.8
0.6
FD

0.4
0.2
0.0

0.0 0.2 0.4 0.6 0.8 1.0

Las características de la distribución empírica se pueden aproximar mediante


simulación. En el caso i.i.d. esto puede ser implementado mediante remuestreo,
realizando repetidamente muestreo aleatorio con reemplazamiento del conjunto
de datos original (manteniendo el tamaño muestral):
# Muestra bootstrap
xboot <- sample(data, replace=TRUE)

8.5.2 Métodos de remuestreo Bootstrap

Método de remuestreo (Efron, 1979) utilizado para aproximar características de


la distribución en el muestreo de un estadístico:
• Aproximación del sesgo y de la varianza.
• Construcción de intervalos de confianza
• Realizar contrastes de hipótesis.
De utilidad cuando no se dispone la distribución exacta, no es adecuado emplear
la distribución asintótica, etc. La idea es aproximar características poblacionales
por las correspondientes de la distribución empírica de los datos observados.
En el caso i.i.d. esto puede ser implementado mediante remuestreo, realizando
8.5. REMUESTREO BOOTSTRAP 165

repetidamente muestreo aleatorio con reemplazamiento del conjunto de


datos original (manteniendo el tamaño muestral).
t
Si x = (x1 , x2 , · · · , xn ) es una muestra i.i.d. Fθ y θ̂ = T (x) es un estimador de
θ. Para b = 1, . . . , B :
• xb∗ = (x∗b1 , x∗b2 , · · · , x∗bn ) muestra bootstrap obtenida mediante muestreo
t

con reemplazamiento de {x1 , x2 , · · · , xn }.

• θ̂b∗ = T (xb∗ ) valor del estadístico en la muestra bootstrap.

La idea original (bootstrap natural, Efron) es que la variabilidad de θ̂b∗ en torno


a θ̂ aproxima la variabilidad de θ̂ en torno a θ:
• La distribución de θ̂b∗ − θ̂ aproxima la distribución de θ̂ − θ.
En general podríamos decir que:
• la muestra es a la población lo que la muestra bootstrap es a la
muestra.

Para información adicional sobre bootstrap ver p.e.: Davison, A.C. and Hinkley,
D.V. (1997). Bootstrap Methods and Their Application. Cambridge University
Press

8.5.3 Ejemplo

Como ejemplo consideraremos una muestra de tamaño n = 100 de una normal


estándar para tratar de aproximar el sesgo y error estándar de la media y la
mediana mediante bootstrap.
# dat <- dat.sim[, 1]
set.seed(54321)
ndat <- 100
datmed <- 0
166CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

datsd <- 1
dat <- rnorm(ndat, mean=datmed, sd=datsd)

Consideramos 1000 réplicas bootstrap:


nboot <- 1000

Es habitual tomar nboot + 1 entero múltiplo de 100 e.g. nboot = 999 ó 1999
(facilita el cálculo de percentiles, orden (nboot + 1)*p)

El valor del estadístico en la muestra es:


stat.dat <- mean(dat)

Generamos las réplicas bootstrap:


set.seed(1)
stat.boot <- numeric(nboot)
for (i in 1:nboot) {
dat.boot <- sample(dat, replace=TRUE)
stat.boot[i] <- mean(dat.boot)
}
# Valor esperado bootstrap del estadístico
mean.boot <- mean(stat.boot)
mean.boot

## [1] -0.09274131

Bootstrap percentil:
hist(stat.boot, freq=FALSE, ylim = c(0,4))
abline(v=mean.boot, lwd=2)
# abline(v=stat.dat)

# Distribución poblacional
curve(dnorm(x, datmed, datsd/sqrt(ndat)), lty=2, add=TRUE)
abline(v=datmed, lwd=2, lty=2)
8.5. REMUESTREO BOOTSTRAP 167

Histogram of stat.boot

4
3
Density

2
1
0

−0.4 −0.2 0.0 0.2

stat.boot

Bootstrap natural/básico:
hist(stat.boot-stat.dat, freq=FALSE, ylim = c(0,4))
abline(v=mean.boot-stat.dat, lwd=2)

# Distribución poblacional
# Distribución teórica de stat.dat - stat.teor
curve(dnorm(x, 0, datsd/sqrt(ndat)), lty=2, add=TRUE)
abline(v=0, lwd=2, lty=2)

Histogram of stat.boot − stat.dat


4
3
Density

2
1
0

−0.2 0.0 0.2 0.4

stat.boot − stat.dat
168CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

Sesgo y error estándar bootstrap:


# sesgo (teor=0)
mean.boot - stat.dat

## [1] 0.0006390906
# error estándar
sd(stat.boot)

## [1] 0.1044501
# error estándar teórico
datsd/sqrt(ndat)

## [1] 0.1
Versión “optimizada” para R:
boot.strap <- function(dat, nboot=1000, statistic=mean)
{
ndat <- length(dat)
dat.boot <- sample(dat, ndat*nboot, replace=T)
dat.boot <- matrix(dat.boot, ncol=nboot, nrow=ndat)
stat.boot <- apply(dat.boot, 2, statistic)
}

fstatistic <- function(dat){


# mean(dat)
mean(dat, trim=0.2)
# median(dat)
# max(dat)
}

set.seed(1)
stat.dat <- fstatistic(dat)
stat.boot <- boot.strap(dat, nboot, fstatistic)

res.boot <- c(stat.dat, mean(stat.boot)-stat.dat, sd(stat.boot))


names(res.boot) <- c("Estadístico", "Sesgo", "Err.Std")
res.boot

## Estadístico Sesgo Err.Std


## -0.144801929 0.005968729 0.119231743

8.5.4 Paquetes R: bootstrap, boot

Ver Sección 1.4 de Cao y Fernández-Casal (2019).


8.5. REMUESTREO BOOTSTRAP 169

library(boot)
# ?boot

Función estadístico:
boot.f <- function(data, indices){
# data[indices] será la muestra bootstrap
mean(data[indices])
}

Generación de las muestras


set.seed(1)
stat.boot <- boot(dat, boot.f, nboot)
stat.boot

##
## ORDINARY NONPARAMETRIC BOOTSTRAP
##
##
## Call:
## boot(data = dat, statistic = boot.f, R = nboot)
##
##
## Bootstrap Statistics :
## original bias std. error
## t1* -0.0933804 0.0006390906 0.1049474
names(stat.boot)

## [1] "t0" "t" "R" "data" "seed" "statistic"


## [7] "sim" "call" "stype" "strata" "weights"

8.5.5 Gráficos

hist(stat.boot$t, freq=FALSE)
170CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

Histogram of stat.boot$t

4
3
Density

2
1
0

−0.4 −0.3 −0.2 −0.1 0.0 0.1 0.2

stat.boot$t

plot(stat.boot)

Histogram of t
0.2
4

0.1
3

0.0
Density

−0.1
t*
2

−0.2
1

−0.3
−0.4
0

−0.4 −0.2 0.0 0.2 −3 −2 −1 0 1 2 3

t* Quantiles of Standard Normal

jack.after.boot(stat.boot)
8.5. REMUESTREO BOOTSTRAP 171

0.2
* * * * * *
*** * **** *** * *** * *********** **** ** ******************** *** * * ** *
* ** * ** * * *
5, 10, 16, 50, 84, 90, 95 %−iles of (T*−t)

* * ** **** ** *
* * **** ********** *
********************* **** *
* ** ** * ** ** *
*
*** * **** *** * * *** * * ** ** * ** * * ** * * * * * * *
** * * ****** **** * *** * ****************** *********************** ** **
0.1
* ***** * ** * * * ** * *
0.0

** * * ** ** ***** * *** * ***************** ********************** *** ** ******** *** ** * ** ** * * *


*
−0.1

* ** * *
** * * ****** *** * *** ** ************** ** ** * * * * * ** ** * *
***** ******* **** ** *** * **** * *** ** *
*** * ****** *** ** ** **************** **
************** ***** *** * * ***** * ** ** * ** ** *
* *
* * * * * * * *
** * * ***** *** * *** ****************** *********************** ** * ****** * *** ** * ** ** *
−0.2

** *
95 58 96 80 91 29 55 34 84 83 3697 7022 100 38 50 42 75 21
33 30 63 89 47 27 31 11 46 88 37 1 28 51 16 9 45 67 73 62
15 66 99 4 69 65 68 78 24 92 821498 81 48 18 20 86 90 52
−0.3

94 25 60 79 40 5735 6 59 76 43 7787 44 74 26 23 13 17 72
7 85 3 53 41 10 2 32 39 5
64 61 54 93 71 49 12 56 19 8

−2 −1 0 1 2

standardized jackknife value

8.5.6 Intervalos de confianza bootstrap

boot.ci(stat.boot, type=c("norm", "basic", "perc", "bca"))

## BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS


## Based on 1000 bootstrap replicates
##
## CALL :
## boot.ci(boot.out = stat.boot, type = c("norm", "basic", "perc",
## "bca"))
##
## Intervals :
## Level Normal Basic
## 95% (-0.2997, 0.1117 ) (-0.3028, 0.1147 )
##
## Level Percentile BCa
## 95% (-0.3015, 0.1161 ) (-0.2993, 0.1180 )
## Calculations and Intervals on Original Scale
Ejercicio 8.7.

Repetir el ejemplo anterior considerando la media recortada al 10% (ejemplo


con parámetros adicionales).
boot.f <- function(data, indices, trim=0.1){
mean(data[indices], trim)
}
172CAPÍTULO 8. APLICACIONES DE LA SIMULACIÓN EN INFERENCIA ESTADÍSTICA

set.seed(1)
boot(dat, boot.f, nboot, trim=0.2)

##
## ORDINARY NONPARAMETRIC BOOTSTRAP
##
##
## Call:
## boot(data = dat, statistic = boot.f, R = nboot, trim = 0.2)
##
##
## Bootstrap Statistics :
## original bias std. error
## t1* -0.1448019 0.006095294 0.119021

Ejercicio 8.8. (propuesto)

En el tema 2 se propuso el análisis de la aleatoriedad de un generador de nú-


meros pseudo-aleatorios mediante el test de rachas, que se podría implementar
repetidamente. Sin embargo, la aproximación asintótica empleada por la rutina
runs.test de la librería tseries no es adecuada para tamaños muéstrales pe-
queños (n < 40) y sería preferible utilizar la distribución exacta (o por lo menos
utilizar una corrección por continuidad).
a) Analizar el comportamiento del contraste empleando repetidamente el test
de rachas, considerando 500 pruebas con muestras de tamaño 10 de una
Bernoulli(0.5). ¿Se observa algo extraño?
b) Realiza un programa que permita aproximar por simulación la función de
masa de probabilidad del estadístico número de rachas (a partir de valores
de una Bernoulli(0.5)). Representarla gráficamente y compararla con la
densidad normal. Obtener los puntos críticos para contrastar la hipóte-
sis nula de aleatoriedad para α = 0.01, 0.05 y 0.1. ¿Es esta dístribución
adecuada para el contraste de aleatoriedad de variables continuas?¿Cual
debería ser la probabilidad de obtener una única racha al aplicar el test a
una variable continua?
c) Diseñar una rutina que permita realizar el contraste de aleatoriedad de
una variable continua aproximando el p-valor por simulación. Asumir que
la distribución
{ (del estadístico
) puede
( ser asimétrica,
)} en cuyo caso el p-valor
p = 2 min P R ≤ R̂ | H0 , P R ≥ R̂ | H0 .

d) Diseñar una rutina que permita realizar el contraste de aleatoriedad de


una variable continua aproximando el p-valor mediante bootstrap.
Capítulo 9

Integración y Optimización
Montecarlo

Uno de los objetivos habituales de los estudios de simulación es la aproxima-


ción de una esperanza, es decir, se trataría de evaluar una integral, que en
ocasiones puede ser compleja y de alta dimensión. Esto puede ser de interés en
otros campos, aunque la integral no esté relacionada con procesos estocásticos.
Adicionalmente, en muchos campos, incluido la Estadística, hay que resolver
problemas de optimización. Para evitar problemas de mínimos locales se puede
recurrir a herramientas que emplean búsquedas aleatorias.

9.1 Integración Monte Carlo (clásica)


La integración Monte Carlo se emplea principalmente para aproximar integrales
multidimensionales:
∫ ∫
I = · · · h (x1 , . . . , xd ) dx1 · · · dxd

donde puede presentar ventajas respecto a los métodos tradicionales de integra-


ción numérica (ver Apéndice C), ya que la velocidad de convergencia no depende
del número de dimensiones.
Supongamos que nos interesa:
∫ 1
I= h (x) dx
0
Si x1 , x2 , . . . , xn i.i.d. U (0, 1) entonces:
1∑
n
I = E (h (U (0, 1))) ≈ h (xi )
n i=1

173
174 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO

Si el intervalo de integración es genérico:


∫ b ∫ b
1
I= h (x) dx = (b − a) h (x) dx = (b − a)E (h (U (a, b))) .
a a (b − a)

Si x1 , x2 , . . . , xn i.i.d. U (a, b):

1∑
n
I≈ h (xi ) (b − a)
n i=1

Ejercicio 9.1.

Crear una función que implemente la integración Monte Carlo clásica para apro-
ximar integrales del tipo:
∫ b
I= h(x)dx.
a

mc.integral0 <- function(fun, a, b, n) {


# Integración Monte Carlo de fun entre a y b utilizando una muestra de tamaño n
# fun es una función de una sola variable (y que no es vectorial)
# Se asume a < b y n entero positivo
# -------------------------------
x <- runif(n, a, b)
fx <- sapply(x, fun) # Si fun fuese vectorial bastaría con: fx <- fun(x)
return(mean(fx) * (b - a))
}

Emplearla para aproximar:


∫ 1
4
4x4 dx = ,
0 5
y representar gráficamente la aproximación en función de n.
Función a integrar:
fun <- function(x) ifelse((x > 0) & (x < 1), 4 * x^4, 0)
# return(4 * x^4)

curve(fun, 0, 1)
abline(h = 0, lty = 2)
abline(v = c(0, 1), lty = 2)

set.seed(1)
mc.integral0(fun, 0, 1, 20)

## [1] 0.977663
9.1. INTEGRACIÓN MONTE CARLO (CLÁSICA) 175

3
fun(x)

2
1
0

0.0 0.2 0.4 0.6 0.8 1.0

Figura 9.1: Ejemplo de integral en dominio acotado.

mc.integral0(fun, 0, 1, 100)

## [1] 0.7311169
mc.integral0(fun, 0, 1, 100)

## [1] 0.8304299
La función mc.integral0 no es adecuada para analizar la convergencia de la
aproximación por simulación. Una alternativa más eficiente para representar
gráficamente la convergencia:
mc.integral <- function(fun, a, b, n, plot = TRUE) {
fx <- sapply(runif(n, a, b), fun) * (b - a)
if (plot) {
estint <- cumsum(fx)/(1:n)
esterr <- sqrt(cumsum((fx - estint)^2))/(1:n)
plot(estint, ylab = "Media y rango de error", type = "l", lwd = 2, ylim = mean(fx) +
2 * c(-esterr[1], esterr[1]), xlab = "Iteraciones")
lines(estint + 2 * esterr, col = "darkgray", lwd = 2)
lines(estint - 2 * esterr, col = "darkgray", lwd = 2)
valor <- estint[n]
abline(h = valor)
return(list(valor = valor, error = 2 * esterr[n]))
} else return(list(valor = mean(fx), error = 2 * sd(fx)/sqrt(n)))
}
176 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO

set.seed(1)
mc.integral(fun, 0, 1, 5000)

## $valor
## [1] 0.8142206
##
## $error
## [1] 0.03087305
abline(h = 4/5, lty = 2)
1.1
1.0
Media y rango de error

0.9
0.8
0.7
0.6
0.5

0 1000 2000 3000 4000 5000

Iteraciones

Si sólo interesa la aproximación:


set.seed(1)
mc.integral(fun, 0, 1, 5000, plot = FALSE)

## $valor
## [1] 0.8142206
##
## $error
## [1] 0.0309005

Nota: Es importante tener en cuenta que la función mc.integral solo es válida


para dominio finito.
9.1. INTEGRACIÓN MONTE CARLO (CLÁSICA) 177

9.1.1 Caso general

A partir a ahora consideraremos que interesa aproximar una integral de la forma:



θ = E (h (X)) = h (x) f (x)dx

siendo X ∼ f , entonces, si x1 , x2 , . . . , xn i.i.d. X:

1∑
n
θ≈ h (xi )
n i=1

Por ejemplo, como en el ejercicio anterior se considera de una función de den-


sidad, se correspondería con el caso general de h(x) = x y f (x) = 4x3 para
0 < x < 1. La idea es que, en lugar de consderar una distribución uniforme, es
preferible generar más valores donde hay mayor “área” (ver Figura 9.1).
Los pasos serían simular x con densidad f y aproximar la integral por
mean(h(x)). En este caso podemos generar valores de la densidad objetivo
fácilmente mediante el método de inversión, ya que F (x) = x4 si 0 < x < 1. :
rfun <- function(nsim) runif(nsim)^(1/4) # Método de inversión
nsim <- 5000
set.seed(1)
x <- rfun(nsim)
# h <- function(x) x
# res <- mean(h(x)) # Aproximación por simulación
res <- mean(x)
res

## [1] 0.7967756
# error <- 2*sd(h(x))/sqrt(nsim)
error <- 2*sd(x)/sqrt(nsim)
error

## [1] 0.004728174
Ejercicio 9.2.

Aproximar: ∫ ∞
1 x2
ϕ(t) = √ e− 2 dx,
t 2π
para t = 4.5, empleando integración Monte Carlo (aproximación tradicional con
dominio infinito).
# h <- function(x) x > 4.5
# f <- function(x) dnorm(x)
set.seed(1)
178 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO

nsim <- 10^3


x <- rnorm(nsim)
mean(x > 4.5) # mean(h(x))

## [1] 0
pnorm(-4.5) # valor teórico P(X > 4.5)

## [1] 3.397673e-06
De esta forma es dificil que se generen valores (en este caso ninguno) en la región
que interesaría para la aproximación de la integral:
any(x > 4.5)

## [1] FALSE
Como ya se comentó anteriormente, sería preferible generar más valores donde
hay mayor “área”, pero en este caso f concentra la densidad en una región
que no resulta de utilidad. Por ese motivo puede ser preferible recurrir a una
densidad auxiliar que solvente este problema.

9.2 Muestreo por importancia


Para aproximar la integral:

θ = E (h (X)) = h (x) f (x)dx,

puede ser preferible generar observaciones de una densidad g que tenga una
forma similar al producto hf .
Si Y ∼ g:
∫ ∫
h (x) f (x)
θ= h (x) f (x)dx = g(x)dx = E (q (Y )) .
g(x)
h(x)f (x)
siendo q (x) = g(x) .

Si y1 , y2 , . . . , yn i.i.d. Y ∼ g:
1∑ 1∑
n n
θ≈ q (yi ) = w(yi )h (yi ) = θ̂g
n i=1 n i=1
f (x)
con w (x) = g(x) .

En este caso V ar(θ̂g ) = V ar (q (Y )) /n, pudiendo reducirse significativamente


respecto al método clásico si:
g(x) ∝ |h(x)| f (x),
aprox.
9.2. MUESTREO POR IMPORTANCIA 179

ya que en ese caso |q(x)| sería aproximadamente constante (puede demostrarse


fácilmente que la varianza es mínima si esa relación en exacta).
Para aplicar el TCL, la varianza del estimador θ̂g es finita si:
∫ 2 ( )
( 2 ) h (x) f 2 (x) f (X)
E q (Y ) = 2
dx = E h (X) < ∞.
g(x) g(X)

La idea básica es que si la densidad g tiene colas más pesadas que la densidad f
con mayor facilidad puede dar lugar a “simulaciones”
( ) con varianza finita (podría
emplearse en casos en los que no existe E h2 (X) ; ver Sección 4.1 en el Tema
4 de Análisis de resultados).
La distribución de los pesos w(yi ) debería ser homogénea para evitar datos
influyentes (inestabilidad).
Ejercicio 9.3.

Aproximar la integral del ejercicio anterior empleando muestreo por importan-


cia considerando como densidad auxiliar una exponencial de parámetro λ = 1
truncada en t:
g (x) = λe−λ(x−t) , x > t,
(emplear rexp(n)+t y dexp(y-t)). Comparar h(x)f (x) con g(x)f (4.5) y repre-
sentar gráficamente la aproximación en función de n.
curve(dnorm(x), 4.5, 6, ylab = "dnorm(x) y dexp(x-4.5)*k")
abline(v = 4.5)
abline(h = 0)
escala <- dnorm(4.5) # Reescalado para comparación...
curve(dexp(x - 4.5) * escala, add = TRUE, lty = 2)
1.5e−05
dnorm(x) y dexp(x−4.5)*k

1.0e−05
5.0e−06
0.0e+00

4.5 5.0 5.5 6.0

x
180 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO

Se generan los valores de la densidad auxiliar y se calculan los pesos:


set.seed(1)
nsim <- 10^3
y <- rexp(nsim) + 4.5 # Y ~ g
w <- dnorm(y)/dexp(y - 4.5)

La aproximación por simulación sería mean(w * h(y)):


# h(x) <- function(x) x > 4.5 # (1 si x > 4.5 => h(y) = 1)
mean(w) # w*h(y)

## [1] 3.144811e-06
pnorm(-4.5) # valor teórico

## [1] 3.397673e-06
plot(cumsum(w)/1:nsim, type = "l", ylab = "Aproximación", xlab = "Iteraciones")
abline(h = pnorm(-4.5), lty = 2)
5e−06
4e−06
Aproximación

3e−06
2e−06
1e−06

0 200 400 600 800 1000

Iteraciones

El error estandar de la aproximación sería sqrt(var(w * h(y))/nsim):


sqrt(var(w)/nsim) # sd(w*h(y))/sqrt(nsim)

## [1] 1.371154e-07
Empleando la aproximación tradicional:
est <- mean(rnorm(nsim) > 4.5)
est

## [1] 0
9.2. MUESTREO POR IMPORTANCIA 181

sqrt(est * (1 - est)/nsim)

## [1] 0

Ejercicio 9.4.

Aproximar P (2 < X < 6) siendo X ∼ Cauchy(0, 1) empleando muestreo por


importancia y considerando como densidad auxiliar la normal estandar Y ∼
N (0, 1). Representar gráficamente la aproximación y estudiar los pesos w(yi ).

Nota: En este caso van a aparecer problemas (la densidad auxiliar debería tener
colas más pesadas que la densidad objetivo; sería adecuado si intercambiaramos
las distribuciones objetivo y auxiliar, como en el ejercicio siguiente).

Se trata de aproximar pcauchy(6) - pcauchy(2), i.e. f(y) = dcauchy(y) y


h(y) = (y > 2) * (y < 6), empleando muestreo por importancia con g(y) =
dnorm(y).
nsim <- 10^5
set.seed(4321)
y <- rnorm(nsim)
w <- dcauchy(y)/dnorm(y) # w <- w/sum(w) si alguna es una cuasidensidad

La aproximación por simulación es mean(w(y) * h(y)):


mean(w * (y > 2) * (y < 6))

## [1] 0.09929348
pcauchy(6) - pcauchy(2) # Valor teórico

## [1] 0.09501516

Si se estudia la convergencia:
plot(cumsum(w * (y > 2) * (y < 6))/1:nsim, type = "l", ylab = "Aproximación", xlab = "Iteraciones
abline(h = pcauchy(6) - pcauchy(2), lty = 2)
182 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO

0.30
0.25
0.20
Aproximación

0.15
0.10
0.05
0.00

0e+00 2e+04 4e+04 6e+04 8e+04 1e+05

Iteraciones

Lo que indica es una mala elección de la densidad auxiliar…

La distribución de los pesos debería ser homogénea. Por ejemplo, si los reesca-
lamos para que su suma sea el número de valores generados, deberían tomar
valores en torno a uno:
boxplot(nsim * w/sum(w))
1200
1000
800
600
400
200
0
9.2. MUESTREO POR IMPORTANCIA 183

9.2.1 Remuestreo (del muestreo) por importancia

Cuando f y/o g son cuasi-densidades, para evitar calcular constantes normali-


zadoras, se emplea como aproximación:


n
w(yi )h (yi )
i=1
θ≈ ∑
n .
w(yi )
i=1

Adicionalmente, puede verse ∑que con un muestreo de {y1 , y2 , . . . , yn } ponderado


n
por w(yi ) (prob. = w(yi ) / i=1 w(yi ) ) se obtiene una simulación aproximada
de f (Sample importance resampling, Rubin, 1987).

Ejercicio 9.5.

Generar 1000 simulaciones de una distribución (aprox.) N (0, 1) mediante re-


muestreo del muestreo por importancia de 105 valores de una Cauchy(0, 1).

Se trata de simular una normal a partir de una Cauchy (Sampling Importance


Resampling). En este caso f(y) = dnorm(y) y g(y) = dcauchy(y), al revés
del ejercicio anterior…
# Densidad objetivo
# f <- dnorm # f <- function(x) ....

nsim <- 10^3


# El nº de simulaciones de la densidad auxiliar debe ser mucho mayor:
nsim2 <- 10^5
set.seed(4321)
y <- rcauchy(nsim2)
w <- dnorm(y)/dcauchy(y) # w <- w/sum(w) si alguna es una cuasidensidad

# Si se pidiera aproximar una integral


# h(y) = y si es la media # h <- function(y) y
# mean(w * h(y))

Sampling Importance Resampling:


rx <- sample(y, nsim, replace = TRUE, prob = w/sum(w))
hist(rx, freq = FALSE)
curve(dnorm, add = TRUE)
lines(density(rx), col ="red")
184 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO

Histogram of rx

0.3
Density

0.2
0.1
0.0

−4 −2 0 2

rx

Nota: Si f o g fuesen cuasidensidades y se pidiese aproximar la integral, habría


que reescalar los pesos: w <- f(y)/g(y); w <- w/sum(w), y la aproximación por
simulación sería sum(w * h(y)) y en el análisis de convergencia se emplearía
cumsum(w * h(y)) (sin dividir por el número de simulaciones).

9.3 Optimización Monte Carlo


Supongamos que estamos interesados en la minimización de una función:
minf (x).
x∈D

Hay una gran cantidad de algoritmos numéricos para resolver problemas de


optimización no lineal multidimensional, por ejemplo los basados en el método
de Newton-Raphson.
La idea original consiste en buscar los ceros de su primera derivada (o del gra-
diente) empleando una aproximación iterativa:
xi+1 = xi − [Hf (xi )]−1 ∇f (x =i ),
donde Hf (xi ) es el hessiano de la función (matriz de segundas derivadas) y
∇f (xi ) el gradiente (vector de primeras derivadas). Estos métodos normalmen-
te funcionan muy bien cuando la función objetivo no tiene mínimos locales (ideal
f cuadrática). Los resultados obtenidos pueden ser muy malos en caso contra-
rio (especialmente en el caso multidimensional) y dependen en gran medida del
punto inicial. Un ejemplo donde es habitual que aparezcan este tipo de proble-
mas es en la estimación por máxima verosimilitud (la función objetivo puede
ser multimodal).
9.3. OPTIMIZACIÓN MONTE CARLO 185

Ejercicio 9.6.

La mixtura de distribuciones normales:


1 3
N (µ1 , 1) + N (µ2 , 1),
4 4
tiene una función de verosimilitud asociada bimodal. Generar una muestra de
200 valores de esta distribución con µ1 = 0 y µ2 = 2.5, construir la correspon-
diente función de verosimilitud y representarla graficamente. Obtener la esti-
mación por máxima verosimilitud de los parámetros empleando la rutina nlm.
Muestra (simulación mixtura dos normales)
nsim <- 200
mu1 <- 0
mu2 <- 2.5
sd1 <- sd2 <- 1

set.seed(12345)
p.sim <- rbinom(nsim, 1, 0.25)
data <- rnorm(nsim, mu1*p.sim + mu2*(1-p.sim), sd1*p.sim + sd2*(1-p.sim))

hist(data, freq = FALSE, breaks = "FD", ylim = c(0, 0.3))


curve(0.25 * dnorm(x, mu1, sd1) + 0.75 * dnorm(x, mu2, sd2), add = TRUE)

Histogram of data
0.30
0.25
0.20
Density

0.15
0.10
0.05
0.00

−2 0 2 4 6

data

Logaritmo (negativo) de la función de verosimilitud (para la estimación de las


medias)
like <- function(mu)
-sum(log((0.25 * dnorm(data, mu[1], sd1) + 0.75 * dnorm(data, mu[2], sd2))))
# NOTA: Pueden aparecer NA/Inf por log(0)
186 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO

Si queremos capturar los valores en los que se evalúa esta función, podemos
proceder de forma similar a como se describe en el capítulo Function operators
del libro “Advanced R” de Hadley Wickham: Behavioural FOs leave the inputs
and outputs of a function unchanged, but add some extra behaviour.
tee <- function(f) {
function(...) {
input <- if(nargs() == 1) c(...) else list(...)
output <- f(...)
# Hacer algo ...
# ... con output e input
return(output)
}
}

En este caso queremos representar los puntos en los que el algoritmo de op-
timización evalúa la función objetivo (especialmente como evoluciona el valor
óptimo)
tee.optim2d <- function(f) {
best.f <- Inf # Suponemos que se va a minimizar (opción por defecto)
best.par <- c(NA, NA)
function(...) {
input <- c(...)
output <- f(...)
## Hacer algo ...
points(input[1], input[2], col = "lightgray")
if(best.f > output) {
lines(rbind(best.par, input), lwd = 2, col = "blue")
best.f <<- output
best.par <<- input
# points(best.par[1], best.par[2], col = "blue", pch = 20)
# cat("par = ", best.par, "value = ", best.f, "\n")
}
## ... con output e input
return(output)
}
}

Representar la superficie del logaritmo de la verosimilitud, los puntos iniciales


y las iteraciones en la optimización numérica con nlm:
mmu1 <- mmu2 <- seq(-2, 5, length = 128)
lli <- outer(mmu1, mmu2, function(x,y) apply(cbind(x,y), 1, like))

par(mar = c(4, 4, 1, 1))


9.3. OPTIMIZACIÓN MONTE CARLO 187

image(mmu1, mmu2, -lli, xlab = expression(mu[1]), ylab = expression(mu[2]))


contour(mmu1, mmu2, -lli, nlevels = 50, add = TRUE)

# Valores iniciales aleatorios


nstarts <- 5
set.seed(1)
starts <- matrix(runif(2*nstarts, -2, 5), nrow = nstarts)
points(starts, col = "blue", pch = 19)

# Minimización numérica con nlm


for (j in 1:nstarts){
# Normalmente llamaríamos a nlm(like, start)
res <- nlm(tee.optim2d(like), starts[j, ]) # nlm(like, starts[j, ])
points(res$estimate[1],res$estimate[2], pch = 19)
cat("par = ", res$estimate, "value =", res$minimum, "\n")
}
5

0 0 −550
−8
0 0
−70 −65 0

−1
−1350
−60

20
−1250

0
−1
−500 1 00 −1150

−1000 −1050
4

−450
−950
−900
−850
−800
−750
−700
−650
3

−600
2

−400 −400
µ2

−450
1

−500
−550
−600
−650
−700
−750
0

−800
−850
−900
−950
−1000
−1050
−1150 −1100 50
−6
−1

−1200
00
−1350 −1300 −7
−1
25

−1500 −14
0

50 00
−16
50 −8
50 00
−6 −7
−2

−550

−2 −1 0 1 2 3 4 5

µ1

## par = -0.03892511 2.494589 value = 361.5712


## par = -0.03892501 2.494589 value = 361.5712
## par = -0.03892507 2.494589 value = 361.5712
## par = 3.132201 0.9628536 value = 379.3739
## par = 20.51013 1.71201 value = 474.1414

9.3.1 Algoritmos de optimización Monte Carlo

Una alternativa sería tratar de generar valores aleatoriamente de forma que


las regiones donde la función objetivo es menor tuviesen mayor probabilidad y
188 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO

menor probabilidad las regiones donde la función objetivo es mayor. Por ejemplo,
se podría pensar en generar valores de acuerdo a una densidad (tranformación
Boltzman-Gibbs):
g(x) ∝ exp (−f (x)/T ) ,
donde T > 0 es un parámetro (denominado temperatura) seleccionado de forma
que se garantize la integrabilidad.
Entre los métodos de optimización Monte Carlo podríamos destacar:
• Métodos con gradiente aleatorio.
• Temple simulado.
• Algoritmos genéticos.
• Montecarlo EM.
• …

9.4 Temple simulado

Método inspirado en el templado de un metal (se calienta el metal a alta tempe-


ratura y se va enfriando lentamente). En cada paso se reemplaza la aproximación
actual por un valor aleatorio “cercano”, elegido con una probabilidad que de-
pende de la mejora en la función objetivo y de un parámetro T (denominado
temperatura) que disminuye gradualmente durante el proceso.
• Cuando la temperatura es grande los cambios son bastante probables en
cualquier dirección.
• Al ir disminuyendo la temperatura los cambios tienden a ser siempre “cues-
ta abajo”.
Al tener una probabilidad no nula de aceptar una modificación “cuesta arriba”
se trata de evitar quedar atrapado en un óptimo local.
9.4. TEMPLE SIMULADO 189

9.4.1 Algoritmo:

temp <- TEMP.INIT


place <- INIT.PLACEMENT()
cost.place <- COST(place)
while(temp < TEMP.FINAL) {
while(LOOP.CRITERION()) {
place.new <- PERTURB(place, temp)
cost.new <- COST(place.new)
cost.inc <- cost.new - cost.place
temp <- SCHEDULE(temp)
if ((cost.inc < 0) || (runif(1) > exp(-(cost.inc/temp)))) break
}
place <- place.new
cost.place <- cost.new
# temp <- SCHEDULE(temp)
}
COST <- function(place, ...) {...}
SCHEDULE <- function(temp, ...) {...}
INIT.PLACEMENT <- function(...) {...}
LOOP.CRITERION <- function(...) {...}

Adaptado de Premchand Akella (ppt).


Este algoritmo se puede ver como una adaptación del método de Metropolis-
Hastings que veremos más adelante (Tema 11 Introducción a los métodos de
cadenas de Markov Monte Carlo).

Ejercicio 9.7.

Repetir el ejercicio anterior empleando el algoritmo del temple simulado.


Minimización “SANN” con optim:
# Representar la superficie del logaritmo de la verosimilitud
image(mmu1, mmu2, -lli, xlab = expression(mu[1]), ylab = expression(mu[2]))
contour(mmu1, mmu2, -lli, nlevels = 50, add = TRUE)
points(starts, col = "blue", pch = 19)

set.seed(1)
for (j in 1:nstarts){
# Normalmente llamaríamos a optim(start, like, method = "SANN")
# Note that the "SANN" method depends critically on the settings of the control parameters.
# For "SANN" maxit gives the total number of function evaluations: there is no other stopping c
# Defaults to 10000.
res <- optim(starts[j, ], tee.optim2d(like), method = "SANN", control = list(temp = 100, maxit
points(res$par[1],res$par[2], pch = 19)
190 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO

cat("par = ", res$par, "value =", res$value, "\n")


}

5
0 0 0 −550
−80 −70 −65 0 −13
−60 50
−11
−500 50 −1200
−9
50 −1000 −1050

4
−450
−900
−800 −850
−750
−700
3 −650
−600
2

−400 −400
µ2

−450
1

−500
−550
−600
−650
−700
0

−750
−800
−850
−900
−950
−1000
−1100 −10
50
−1

−1150
−1300 −1250 00
−7
−1
20

−1500 −1
0

4 00
−165 00
0 00 50 −8
−2

−550 −6 −6

−2 −1 0 1 2 3 4 5

µ1

## par = 0.0002023461 2.473437 value = 361.6372


## par = -0.182735 2.45585 value = 362.0255
## par = -0.0281341 2.484467 value = 361.5801
## par = -0.03642928 2.488626 value = 361.5732
## par = 0.6814165 2.370026 value = 374.839

Alternativa: función basada en el algoritmo empleado en el ejemplo 5.9 del libro:


Robert y Casella, Introducing Monte Carlo Methods with R, Springer, 2010.
SA <- function(fun, pini, lower = -Inf, upper = Inf, tolerance = 10^(-4), factor = 1) {
temp <- scale <- iter <- dif <- 1
npar <- length(pini)
the <- matrix(pini, ncol = npar)
curfun <- hval <- fun(pini)
while (dif > tolerance) {
prop <- the[iter, ] + rnorm(npar) * scale[iter]
# Se decide si se acepta la propuesta
if (any(prop < lower) || any(prop > upper) ||
(temp[iter] * log(runif(1)) > -fun(prop) + curfun)) prop <- the[iter, ]
curfun <- fun(prop)
hval <- c(hval, curfun)
the <- rbind(the, prop)
iter <- iter + 1
temp <- c(temp, 1/log(iter + 1)) # Actualizar la temperatura
# Se controla el nº de perturbaciones aceptadas
9.4. TEMPLE SIMULADO 191

ace <- length(unique(the[(iter/2):iter, 1]))


if (ace == 1)
# si es muy pequeño se disminuye la escala de la perturbación
factor <- factor/10
if (2 * ace > iter)
# si es muy grande se aumenta
factor <- factor * 10
scale <- c(scale, max(2, factor * sqrt(temp[iter]))) # Actualizar la escala de la perturbaci
dif <- (iter < 100) + (ace < 2) + (max(hval) - max(hval[1:(iter/2)]))
}
list(theta = the, val = hval, ite = iter)
}

# Representar la superficie del logaritmo de la verosimilitud


image(mmu1, mmu2, -lli, xlab = expression(mu[1]), ylab = expression(mu[2]))
contour(mmu1, mmu2, -lli, nlevels = 50, add = TRUE)
points(starts, col = "blue", pch = 19)

set.seed(1)
for (j in 1:nstarts) {
sar <- SA(like, starts[j, ])
lines(sar$the[, 1], sar$the[, 2], lwd = 2, col = "blue")
points(sar$the[sar$it, 1], sar$the[sar$it, 2], pch = 19)
}
5

0 0 0 −550
−80 −70 −65 0 −13
−60 50
−11
−500 50 −1200
−9
50 −1000 −1050
4

−450
−900
−800 −850
−750
−700
−650
3

−600
2

−400 −400
µ2

−450
1

−500
−550
−600
−650
−700
0

−750
−800
−850
−900
−950
−1000
−1100 −10
50
−1

−1150
−1300 −1250 00
−7
−1
20

−1500 −1
0

40
−165 0 00
0 00 50 −8
−2

−550 −6 −6

−2 −1 0 1 2 3 4 5

µ1
192 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO

9.5 Algoritmos genéticos


Los algoritmos genéticos tratan de encontrar la mejor solución (entre un con-
junto de soluciones posibles) imitando los procesos de evolución biológica:
• Población: formada por n individuos xi codificados en cromosomas.
• f (xi ) ajuste/capacidad/adaptación del individuo xi .
• Selección: los individuos mejor adaptados tienen mayor probabilidad de
ser padres.
• Cruzamiento: los cromosomas de dos padres se combinan para generar
hijos.
• Mutación: modificación al azar del cromosoma de los hijos (variabilidad).
• Elitismo: el mejor individuo pasa a la siguiente generación.
Los paquetes de R DEOptim y gafit implementan algunos de estos tipos de
algoritmos.
Ejercicio 9.8.

Repetir el ejercicio anterior empleando la función DEOptim.


Optimización con algoritmo genético implementado en DEoptim:
require(DEoptim)

# Representar la superficie del logaritmo de la verosimilitud


image(mmu1, mmu2, -lli, xlab = expression(mu[1]), ylab = expression(mu[2]))
contour(mmu1, mmu2, -lli, nlevels = 50, add = TRUE)
# Estos algoritmos no requieren valores iniciales (los generan al azar en el rango)

lower <- c(-2, -2)


upper <- c(5, 5)
set.seed(1)
# DEoptim(like, lower, upper)
der <- DEoptim(tee.optim2d(like), lower, upper, DEoptim.control(itermax = 10))

## Iteration: 1 bestvalit: 373.132461 bestmemit: -0.764103 2.196961


## Iteration: 2 bestvalit: 367.580379 bestmemit: -0.430095 2.196961
## Iteration: 3 bestvalit: 367.580379 bestmemit: -0.430095 2.196961
## Iteration: 4 bestvalit: 367.580379 bestmemit: -0.430095 2.196961
## Iteration: 5 bestvalit: 361.906887 bestmemit: 0.058951 2.455186
## Iteration: 6 bestvalit: 361.906887 bestmemit: 0.058951 2.455186
## Iteration: 7 bestvalit: 361.906887 bestmemit: 0.058951 2.455186
## Iteration: 8 bestvalit: 361.657986 bestmemit: -0.064005 2.452184
## Iteration: 9 bestvalit: 361.657986 bestmemit: -0.064005 2.452184
## Iteration: 10 bestvalit: 361.657986 bestmemit: -0.064005 2.452184
9.5. ALGORITMOS GENÉTICOS 193

# Por defecto fija el tamaño de la población a NP = 10*npar = 20


# Puede ser mejor dejar el valor por defecto itermax = 200
points(der$optim$bestmem[1], der$optim$bestmem[2], pch = 19)
5

0 0 0 −550
−80 −70 −65 0 −13
−60 50
−11
−500 50 −1200
−9
50 −1000 −1050
4

−450
−900
−800 −850
−750
−700
−650
3

−600
2

−400 −400
µ2

−450
1

−500
−550
−600
−650
−700
0

−750
−800
−850
−900
−950
−1000
−1100 −10
50
−1

−1150
−1300 −1250 00
−7
−1
20

−1500 −1
0

40
−165 0 00
0 00 50 −8
−2

−550 −6 −6

−2 −1 0 1 2 3 4 5

µ1
194 CAPÍTULO 9. INTEGRACIÓN Y OPTIMIZACIÓN MONTECARLO
Capítulo 10

Técnicas de reducción de la
varianza

10.1 Reducción de la varianza

Éstas técnicas son aplicadas normalmente cuando se pretende ofrecer respuestas


lo más precisas posibles (con menor costo computacional) y principalmente sobre
cantidades medias.

• Supongamos que estamos interesados en aproximar la media de


un estadístico mediante simulación y no nos interesa aproximar su
varianza.

Existe un sinfín de técnicas encaminadas a reducir la varianza en un estudio de


simulación (respecto a una aproximación estandar). Algunas de ellas son:

• Muestreo por importancia.

• Variables antitéticas.

• Muestreo estratificado.

• Variables de control.

• Números aleatorios comunes.

• Métodos de remuestreo.

• Condicionamiento.

• …

195
196 CAPÍTULO 10. TÉCNICAS DE REDUCCIÓN DE LA VARIANZA

10.2 Variables antitéticas


Supongamos que pretendemos aproximar

θ = E (Z)

con V ar (Z) = σ 2 . Si generamos n pares (X1 , Y1 ) , ..., (Xn , Yn ) de X ∼ Y ∼ Z


con Cov (X, Y ) < 0, el estimador combinado tiene menor varianza:
( )
X +Y 1( ( ) ( ) ( ))
V ar = V ar X + V ar Y + 2Cov X, Y
2 4
σ2 1
= + Cov (X, Y )
2n 2n
σ2
= (1 + ρ (X, Y )) ,
2n
que el equivalente a una muestra unidimensional independiente con el mismo
número de observaciones 2n (con una reducción del −100ρ (X, Y ) %).

10.2.1 Ejemplo: Integración Monte Carlo

Para aproximar: ∫ 1
I= h (x) dx,
0
a partir de x1 , x2 , . . . , xn i.i.d. U (0, 1). Podemos emplear:
( )
h (U ) + h (1 − U )
I=E
2
1 ∑n
≈ (h (xi ) + h (1 − xi )) .
2n i=1

10.2.2 Generación de variables antitéticas

Cuando se utiliza el método de inversión resulta sencillo obtener pares de varia-


bles con correlación negativa:
• U ∼ U (0, 1) para simular X.
• 1 − U para simular la variable antitética Y .
En el caso general, si X = h (U1 , . . . , Ud ) y h es monótona puede verse (e.g. Ross,
1997) que Y = h (1 − U1 , . . . , 1 − Ud ) está negativamente correlada con X.
Si X ∼ N (µ, σ) puede tomarse como variable antitética

Y = 2µ − X
10.2. VARIABLES ANTITÉTICAS 197

En general esto es válido para cualquier variable simétrica repecto a un paráme-


tro µ. (e.g. X ∼ U (a, b) e Y = a + b − X).
Ejercicio 10.1. Variables antitéticas en integración Monte Carlo

Crear una función que implemente la técnica de variables antitéticas para apro-
ximar integrales del tipo:
∫ b
I= h (x) dx.
a
Emplearla para aproximar:
( ) ∫ 2
U (0,2) 1 x
E e = e dx ≈ 3.194,
0 2
y representar gráficamente la aproximación en función de n. Función objetivo:
a <- 0; b <- 2
ftn <- function(x) return(exp(x)/(b-a))
curve(ftn, a, b, ylim=c(0,4))
abline(h=0,lty=2)
abline(v=c(a,b),lty=2)
4
3
ftn(x)

2
1
0

0.0 0.5 1.0 1.5 2.0

Se trata de calcular la media de eU (0,2) :


teor <- (exp(b)-exp(a))/(b-a)
teor

## [1] 3.194528
Para la aproximación por integración Monte Carlo podemos emplear la función
del capítulo anterior:
198 CAPÍTULO 10. TÉCNICAS DE REDUCCIÓN DE LA VARIANZA

mc.integral <- function(ftn, a, b, n, plot=TRUE) {


fx <- sapply(runif(n, a, b), ftn)*(b-a)
if (plot) {
estint <- cumsum(fx)/(1:n)
esterr <- sqrt(cumsum((fx-estint)^2))/(1:n)
plot(estint, ylab="Media y rango de error", type="l", lwd= 2,
ylim=mean(fx)+2*c(-esterr[1],esterr[1]), xlab="Iteraciones")
abline(h = estint[n], lty=2)
lines(estint+2*esterr, lty = 3)
lines(estint-2*esterr, lty = 3)
return(list(valor=estint[n], error=2*esterr[n]))
} else return(list(valor=mean(fx), error=2*sd(fx)/sqrt(n)))
}

set.seed(54321)
res <- mc.integral(ftn, a, b, 500)
abline(h = teor)
4.5
4.0
Media y rango de error

3.5
3.0
2.5
2.0

0 100 200 300 400 500

Iteraciones

res

## $valor
## [1] 3.184612
##
## $error
## [1] 0.1619886

Para la integración Monte Carlo con variables antitéticas podríamos considerar:


10.2. VARIABLES ANTITÉTICAS 199

mc.integrala <- function(ftn, a, b, n, plot=TRUE,...) {


# n es el nº de evaluaciones de la función objetivo (para facilitar comparaciones, solo se gene
x <- runif(n%/%2, a, b)
# La siguiente línea solo para representar alternando
x <- as.numeric(matrix(c(x,a+b-x),nrow=2,byrow=TRUE))
# bastaría con emplear p.e. c(x,a+b-x)
fx <- sapply(x, ftn)*(b-a)
if (plot) {
estint <- cumsum(fx)/(1:n)
esterr <- sqrt(cumsum((fx-estint)^2))/(1:n)
plot(estint, ylab="Media y rango de error",type="l", lwd = 2,
ylim=mean(fx)+2*c(-esterr[1],esterr[1]),xlab="Iteraciones",...)
abline(h = estint[n], lty=2)
lines(estint+2*esterr, lty = 3)
lines(estint-2*esterr, lty = 3)
return(list(valor=estint[n],error=2*esterr[n]))
} else return(list(valor=mean(fx),error=2*sd(fx)/sqrt(n)))
}

set.seed(54321)
res <- mc.integrala(ftn, a, b, 500)
4.5
4.0
Media y rango de error

3.5
3.0
2.5
2.0

0 100 200 300 400 500

Iteraciones

res

## $valor
## [1] 3.222366
##
## $error
200 CAPÍTULO 10. TÉCNICAS DE REDUCCIÓN DE LA VARIANZA

## [1] 0.1641059

Pero aunque aparentemente converge antes, parece no haber una mejora en la


precisión de la aproximación. Si calculamos el porcentaje (estimado) de reduc-
ción del error:
100*(0.1619886-0.1641059)/0.1619886

## [1] -1.307067

El problema es que en este caso se está estimando mal la varianza (asumien-


do independencia). Hay que tener cuidado con las técnicas de reducción de la
varianza si uno de los objetivos de la simulación es precisamente estimar la varia-
bilidad. En este caso, una versión de la función anterior para integración Monte
Carlo con variables antitéticas, con aproximación del error bajo dependencia
podría ser:
mc.integrala2 <- function(ftn, a, b, n, plot = TRUE,...) {
# n es el nº de evaluaciones de la función objetivo (para facilitar comparaciones, so
x <- runif(n%/%2, a, b)
# La siguiente línea solo para representar alternando
x <- matrix(c(x,a+b-x),nrow=2,byrow=TRUE)
# bastaría con emplear p.e. c(x,a+b-x)
fx <- apply(x, 1, ftn)*(b-a)
corr <- cor(fx[,1], fx[,2])
fx <- as.numeric(fx)
return(list(valor=mean(fx), error=2*sd(fx)/sqrt(n)*sqrt(1+corr)))
}

set.seed(54321)
res <- mc.integrala2(ftn, a, b, 500)
res

## $valor
## [1] 3.222366
##
## $error
## [1] 0.05700069

Porcentaje estimado de reducción del error:


100*(0.1619886-0.05700069)/0.1619886

## [1] 64.81191

En este caso puede verse que la reducción teórica de la varianza es del 96.7%
10.3. ESTRATIFICACIÓN 201

10.3 Estratificación
Si se divide la población en estratos y se genera en cada uno un número de
observaciones proporcional a su tamaño (a la probabilidad de cada uno) nos
aseguramos de que se cubre el dominio de interés y se puede acelerar la conver-
gencia.
• Por ejemplo, para generar una muestra de tamaño n de una U (0, 1), se
pueden generar l = nk observaciones (1 ≤ k ≤ n) de la forma:
( )
(j − 1) j
Uj1 , . . . , Ujl ∼ U , para j = 1, ..., k.
k k
Si en el número de obsevaciones se tiene en cuenta la variabilidad en el estrato
se puede obtener una reducción significativa de la varianza.
Ejemplo 10.1. Muestreo estratificado de una exponencial (libro Ricardo)

Supóngase el siguiente problema (absolutamente artificial pero ilustrativo para


comprender esta técnica). Dada una muestra de tamaño 10 de una población
con distribución:
X ∼ exp (1) ,
se desea aproximar la media poblacional (es sobradamente conocido que es 1)
a partir de 10 simulaciones. Supongamos que para evitar que, por puro azar,
exista alguna zona en la que la exponencial toma valores, no representada en la
muestra simulada de 10 datos, se consideran tres estratos. Por ejemplo, el del
40% de valores menores, el siguiente 50% de valores (intermedios) y el 10% de
valores más grandes para esta distribución.
El algoritmo de inversión (optimizado) para simular una exp (1) es:
1. Generar U ∼ U (0, 1).
2. Hacer X = − ln U .
Dado que, en principio, simulando diez valores U1 , U2 , . . . , U10 ∼ U (0, 1), no
hay nada que nos garantice que las proporciones de los estratos son las deseadas
(aunque sí lo serán en media). Una forma de garantizar el que obtengamos 4, 5 y
1 valores, repectivamente, en cada uno de los tres estratos, consiste en simular:
• 4 valores de U [0.6, 1) para el primer estrato,
• 5 valores de U [0.1, 0.6) para el segundo y
• uno de U [0, 0.1) para el tercero.
Otra forma de proceder consistiría en rechazar valores de U que caigan en uno
de esos tres intervalos cuando el cupo de ese estrato esté ya lleno (lo cual no
sería computacionalmente eficiente).
El algoritmo con la estratificación propuesta sería como sigue:
202 CAPÍTULO 10. TÉCNICAS DE REDUCCIÓN DE LA VARIANZA

1. Para i = 1, 2, . . . , 10:

2. Generar Ui :

2a. Generar U ∼ U (0, 1).

2b. Si i ≤ 4 hacer Ui = 0.4 · U + 0.6.

2c. Si 4 < i ≤ 9 hacer Ui = 0.5 · U + 0.1.

2d. Si i = 10 hacer Ui = 0.1 · U .

3. Devolver Xi = − ln Ui .

No es difícil probar que:

• V ar (Xi ) = 0.0214644 si i = 1, 2, 3, 4,

• V ar (Xi ) = 0.229504 si i = 5, 6, 7, 8, 9 y

• V ar (X10 ) = 1.

Como consecuencia:

( ) 1 ∑
10
V ar X = 2 V ar (Xi ) = 0.022338
10 i=1

que es bastante menor que 1 (la varianza en el caso de muestreo aleatorio simple
no estratificado).

Ejercicio 10.2. Integración Monte Carlo con estratificación

Aproximar la integral anterior empleando la técnica de estratificación, consi-


derando k subintervalos regularmente espaciados en el intervalo [0, 2]. ¿Como
varía la reducción en la varianza dependiendo del valor de k?
mc.integrale <- function(ftn, a, b, n, k) {
# Integración Monte Carlo con estratificación
l <- n%/%k
int <- seq(a, b, len=k+1)
x <- runif(l*k, rep(int[-(k+1)], each=l), rep(int[-1], each=l))
# l uniformes en cada uno de los intervalos [(j-1)/k , j/k]
fx <- sapply(x, ftn)*(b-a)
return(list(valor=mean(fx), error=2*sd(fx)/sqrt(n))) # error mal calculado
}

set.seed(54321)
res <- mc.integral(ftn, a, b, 500)
abline(h = teor)
10.3. ESTRATIFICACIÓN 203

4.5
4.0
Media y rango de error

3.5
3.0
2.5
2.0

0 100 200 300 400 500

Iteraciones

res

## $valor
## [1] 3.184612
##
## $error
## [1] 0.1619886
set.seed(54321)
mc.integrale(ftn, a, b, 500, 50)

## $valor
## [1] 3.193338
##
## $error
## [1] 0.1597952
set.seed(54321)
mc.integrale(ftn, a, b, 500, 100)

## $valor
## [1] 3.193927
##
## $error
## [1] 0.1599089

De esta forma no se tiene en cuenta la variabilidad en el estrato. El tamaño de


las submuestras debería incrementarse hacia el extremo superior.

Ejercicio 10.3.
204 CAPÍTULO 10. TÉCNICAS DE REDUCCIÓN DE LA VARIANZA

Repetir el ejemplo anterior considerando intervalos regularmente espaciados en


escala exponencial.

10.4 Variables de control


En este caso se trata de sacar partido tanto a una covarianza positiva como
negativa. La idea básica es emplear una variable Y , con media conocida µY ,
para controlar la variable X (con media desconocida), de forma que ambas
variables estén “suficientemente” correlacionadas. La versión “controlada” de X
será:
X ∗ = X + α (Y − µY )
con E(X ∗ ) = E(X) = θ. Puede verse que V ar(X ∗ ) = V ar(X) + α2 V ar(Y ) +
2αCov(X, Y ) es mínima para:

Cov(X, Y )
α∗ = − ,
V ar(Y )
( )
con V ar(X ∗ ) = V ar(X) 1 − ρ2 (X, Y ) (lo que supone una reducción del
100ρ2 (X, Y ) %).
En la práctica normalmente α∗ no es conocida. Para estimarlo se puede realizar
ajuste lineal de X sobre Y (a partir de los datos simulados Xi e Yi , 1 ≤ i ≤ n):
SXY
• Si x̂ = β̂0 + β̂1 y es la recta ajustada, con β̂1 = y β̂0 = X − β̂1 Y , la
SY2
estimación sería:
α̂∗ = −β̂1

• Adicionalmente, para aproximar θ:


∗ ( )
θ̂ = X = X − β̂1 Y − µY
= β̂0 + β̂1 µY


• Si µY = 0 ⇒ θ̂ = X = β̂0 .

Ejercicio 10.4. Integración Monte Carlo con variables de control

Aproximar la integral anterior empleando la variable U ∼ U (0, 2) para controlar


la variable eU .
Se trata de calcular la media de exp(U(a, b)):
a <- 0; b <- 2
teor <- (exp(b)-exp(a))/(b-a)
teor
10.4. VARIABLES DE CONTROL 205

## [1] 3.194528
Aproximación clásica por simulación:
set.seed(54321)
nsim <- 1000
u <- runif(nsim, a, b)
expu <- exp(u)
mean(expu)

## [1] 3.182118
Con variable control:
plot(u, expu)
reg <- lm(expu ~ u)$coef
abline(reg, col='blue')
7
6
5
expu

4
3
2
1

0.0 0.5 1.0 1.5 2.0

# summary(lm(expu ~ u)) # R-squared: 0.9392


reg[1]+reg[2] # Coincidirá con la solución mean(expuc)

## (Intercept)
## 3.204933
Lo siguiente ya no sería necesario:
expuc <- expu - reg[2]*(u-1)
mean(expuc)

## [1] 3.204933
Estimación del porcentaje de reducción en la varianza:
206 CAPÍTULO 10. TÉCNICAS DE REDUCCIÓN DE LA VARIANZA

100*(var(expu)-var(expuc))/var(expu)

## [1] 93.91555

10.5 Números aleatorios comunes

Se trataría de una técnica básica del diseño de experimentos: realizar compa-


raciones homogéneas (bloquear). Por ejemplo cuando se diseña un experimento
para la comparación de la media de dos variables, se pueden emplear las deno-
minadas muestras apareadas, en lugar de muestras independientes.

Supóngamos que estamos interesados en las diferencias entre dos estrategias


(e.g. dos estimadores):

E (X) − E (Y ) = E (X − Y ) .

Para ello se generan dos secuencias X1 , X2 , . . ., Xn , e Y1 , Y2 , . . ., Yn y se calcula:

1∑
n
X −Y = (Xi − Yi )
n i=1

• Si las secuencias se generan de modo independiente:

( ) 1
V ar X − Y = (V ar (X) + V ar (Y ))
n

• Si se generar las secuencias empleando la misma semilla, los datos son


dependientes:
Cov (Xi , Yi ) > 0

y tendríamos que:

( ) 1 ∑
N
1
V ar X − Y = 2 V ar (Xi − Yi ) = V ar (Xi − Yi )
n i=1 n
1
= (V ar (Xi ) + V ar (Yi ) − 2Cov (Xi , Yi ))
n
1
≤ (V ar (Xi ) + V ar (Yi ))
n

En el capítulo de aplicaciones de la simulación se empleó esta técnica para


comparar distribuciones de estimadores…
10.6. EJERCICIOS FIN DE PRÁCTICA 207

10.6 Ejercicios fin de práctica


Ejercicio 10.5.

Aproximar mediante integración Monte Carlo (clásica) la media de una distri-


bución exponencial de parámetro 1/2:
∫ ∞
x −x
I= e 2 dx
0 2

y representar gráficamente la aproximación en función de n. Comparar los re-


sultados con los obtenidos empleando variables antitéticas, ¿se produce una
reducción en la varianza?
Nota: . Puede ser recomendable emplear el método de inversión para generar
las muestras (antitéticas) de la exponencial.
MC clásico:
nsim <- 1000
lambda <- 0.5
set.seed(1)
x <- - log(runif(nsim)) / lambda
# Aprox por MC da media
mean(x) # valor teor 1/lambda = 2

## [1] 1.97439
# Aprox da precisión
var(x)

## [1] 3.669456
MC con variables antitéticas:
# xa <-
# mean(xa) # Aprox por MC da media (valor teor 1/lambda = 2)
# var(xa) # Aprox da precisión supoñendo independencia
# corr <- cor(x1,x2)
# var(xa)*(1 + corr) # Estimación varianza supoñendo dependencia

Estimación del porcentaje de reducción en la varianza


# 100*(var(x) - var(xa))/var(x)
208 CAPÍTULO 10. TÉCNICAS DE REDUCCIÓN DE LA VARIANZA
Referencias

Bibliografía básica

Cao, R. (2002). Introducción a la simulación y a la teoría de colas. NetBiblo.


Gentle, J.E. (2003). Random number generation and Monte Carlo methods.
Springer‐Verlag.
Jones, O. et al. (2009). Introduction to Scientific Programming and Simulation
Using R. CRC.
Ripley, B.D. (1987). Stochastic Simulation. John Wiley & Sons.
Robert, C.P. y G. Casella (2010). Introducing Monte Carlo Methods with R.
Springer.
Ross, S.M. (1999).Simulación. Prentice Hall.
Suess, E.A. y Trumbo, B.E. (2010). Introduction to probability simulation and
Gibbs sampling with R. Springer.

Bibliografía complementaria

Libros

Azarang, M. R. y García Dunna, E. (1996). Simulación y análisis de modelos


estocásticos. McGraw-Hill.
Bratley, P., Fox, B.L. y Schrage L.E. (1987). A guide to simulation. Springer-
Verlag.
Devroye, L. (1986). Non-uniform random variate generation. Springer-Verlag.
Evans, M. y Swartz, T. (2000). Approximating integrals via Monte Carlo and
determinstic methods. Oxford University Press.

209
210 CAPÍTULO 10. TÉCNICAS DE REDUCCIÓN DE LA VARIANZA

Gentle, J.E. (1998). Random number generation and Monte Carlo methods.
Springer-Verlag.
Hörmann, W. et al. (2004). Automatic Nonuniform Random Variate Generation.
Springer.
Knuth, D.E. (1969). The Art of Computer Programming. Volume 2. Addison-
Wesley.
Knuth, D.E. (2002). The Art of Computer Programming. Volume 2, third edition,
ninth printing. Addison-Wesley.
Law, A.M. y Kelton, W.D. (1991). Simulation, modeling and analysis. McGraw-
Hill.
Moeschlin, O., Grycko, E., Pohl, C. y Steinert, F. (1998). Experimental stochas-
tics. Springer-Verlag.
Nelson, R. (1995). Probability, stochastic processes, and queueing theory: the
mathematics of computer performance modelling. Springer-Verlag.
Pardo, L. y Valdés, T. (1987). Simulación. Aplicaciones prácticas a la empresa.
Díaz de Santos.
Robert, C.P. y G. Casella (2004). Monte Carlo statistical methods. Springer.

Artículos

Demirhan, H. y Bitirim, N. (2016). CryptRndTest: an R package for testing the


cryptographic randomness. The R Journal, 8(1), 233-247.
Downham, D.Y. (1970). Algorithm AS 29: The runs up and down test. Journal
of the Royal Statistical Society. Series C (Applied Statistics), 19(2), 190-192.
L’Ecuyer, P. (1999). Good parameters and implementations for combined mul-
tiple recursive random number generators. Operations Research, 47, 159–164.
L’Ecuyer, P. y Simard, R. (2007). TestU01: A C library for empirical testing
of random number generators. ACM Transactions on Mathematical Software
(TOMS), 33(4), 1-40.
Marsaglia, G. y Tsang, W.W. (2002). Some difficult-to-pass tests of randomness.
Journal of Statistical Software, 7(3), 1-9.
Marsaglia, G., Zaman, A. y Tsang, W.W. (1990). Toward a universal random
number generator. Stat. Prob. Lett., 9(1), 35-39.
Matsumoto, M. y Nishimura, T. (1998). Mersenne Twister: A 623-dimensionally
equidistributed uniform pseudo-random number generator, ACM Transactions
on Modeling and Computer Simulation, 8, 3–30.
10.6. EJERCICIOS FIN DE PRÁCTICA 211

Park, S.K. y Miller , K.W. (1988). Random number generators: good ones are
hard to find. Communications of the ACM, 31(10), 1192-1201.
Park, S.K., Miller, K.W. y Stockmeyer, P.K. (1993). Technical correspondence.
Communications of the ACM, 36(7), 108-110.
212 CAPÍTULO 10. TÉCNICAS DE REDUCCIÓN DE LA VARIANZA
Apéndice A

Enlaces

Recursos para el aprendizaje de R


A continuación se muestran algunos recursos que pueden ser útiles para el apren-
dizaje de R y la obtención de ayuda (basados en el post https://rubenfcasal.
github.io/post/ayuda-y-recursos-para-el-aprendizaje-de-r, que puede estar más
actualizado).
Ayuda online:
• Ayuda en línea sobre funciones o paquetes: RDocumentation
• Buscador RSeek
• StackOverflow
Cursos: algunos cursos gratuitos:
• Coursera:
– Introducción a Data Science: Programación Estadística con R
– Mastering Software Development in R
• DataCamp:
– Introducción a R
• Stanford online:
– Statistical Learning
• Curso UCA: Introducción a R, R-commander y shiny
• Udacity: Data Analysis with R
• Swirl Courses: se pueden hacer cursos desde el propio R con el paquete
swirl.

213
214 APÉNDICE A. ENLACES

Para información sobre cursos en castellano se puede recurrir a la web de R-


Hispano en el apartado formación. Algunos de los cursos que aparecen en entra-
das antiguas son gratuitos. Ver: Cursos MOOC relacionados con R.
Libros
• Iniciación:
– 2011 - The Art of R Programming. A Tour of Statistical Software
Design, (No Starch Press)
– R for Data Science (online, online-castellano, O’Reilly)
– Hands-On Programming with R: Write Your Own Functions and
Simulations, by Garrett Grolemund (O’Reilly)
• Avanzados:
– 2008 - Software for Data Analysis: Programming with R - Chambers
(Springer)
– Advanced R by Hadley Wickham (online: 1ª ed, 2ª ed, Chapman &
Hall)
– R packages by Hadley Wickham (online, O’Reilly)
• Bookdown: el paquete bookdown de R permite escribir libros empleando
R Markdown y compartirlos. En https://bookdown.org está disponible
una selección de libros escritos con este paquete (un listado más completo
está disponible aquí). Algunos libros en este formato en castellano son:
– Técnicas de Remuestreo (disponible en el repositorio de GitHub ru-
benfcasal/book_remuestreo).
– Introducción al Análisis de Datos con R (disponible en el repositorio
de GitHub rubenfcasal/intror).
– Prácticas de Tecnologías de Gestión y Manipulación de Datos (dis-
ponible en el repositorio de GitHub gltaboada/tgdbook).
– Escritura de libros con bookdown (disponible en el repositorio de
GitHub rubenfcasal/bookdown_intro).
– R para profesionales de los datos: una introducción.
– Estadística Básica Edulcorada.
Material online: en la web se puede encontrar mucho material adicional, por
ejemplo:
• CRAN: Other R documentation
• RStudio:
– Online learning, Webinars
215

– tidyverse: dplyr, tibble, tidyr, stringr, readr.


– CheatSheets: RMarkdown, Shiny, dplyr, tidyr, stringr.
• Blogs en inglés:
– https://www.r-bloggers.com/
– https://www.littlemissdata.com/blog/rstudioconf2019
– RStudio: https://blog.rstudio.com
– Microsoft Revolutions: https://blog.revolutionanalytics.com
• Blogs en castellano:
– https://www.datanalytics.com
– http://oscarperpinan.github.io/R
– http://rubenfcasal.github.io
• Listas de correo:
– Listas de distribución de r-project.org: https://stat.ethz.ch/
mailman/listinfo
– Búsqueda en R-help: http://r.789695.n4.nabble.com/R-help-f789696.
html
– Búsqueda en R-help-es: https://r-help-es.r-project.narkive.com
https://grokbase.com/g/r/r-help-es
– Archivos de R-help-es: https://stat.ethz.ch/pipermail/r-help-es
216 APÉNDICE A. ENLACES
Apéndice B

Bondad de Ajuste y
Aleatoriedad

En los métodos clásicos de inferencia estadística es habitual asumir que los valo-
res observados X1 , . . . , Xn (o los errores de un modelo) consituyen una muestra
aleatoria simple de una variable aleatoria X. Se están asumiendo por tanto
dos hipótesis estructurales: la independencia (aleatoriedad) y la homogeneidad
(misma distribución) de las observaciones (o de los errores). Adicionalmente,
en inferencia paramétrica se supone que la distribución se ajusta a un modelo
paramétrico específico Fθ (x), siendo θ un parámetro que normalmente es desco-
nocido.
Uno de los objetivos de la inferencia no paramétrica es desarrollar herramientas
que permitan verificar el grado de cumplimiento de las hipótesis anteriores1 .
Los procedimientos habituales incluyen métodos descriptivos (principalmente
gráficos), contrastes de bondad de ajuste (también de homogeneidad o de datos
atípicos) y contrastes de aleatoriedad.
En este apéndice se describen brevemente algunos de los métodos clásicos, prin-
cipalmente con la idea de que pueden ser de utilidad para evaluar resultados de
simulación y para la construcción de modelos del sistema real (e.g. para mode-
lar variables que se tratarán como entradas del modelo general). Se empleará
principalmente el enfoque de la estadística no paramétrica, aunque también se
mostrarán algunas pequeñas diferencias entre su uso en inferencia y en simula-
ción.
Los métodos genéricos no son muy adecuados para evaluar generadores aleato-
rios (e.g. L’Ecuyer y Simard, 2007). La recomendación sería emplear baterías de
1 El otro objetivo de la inferencia estadística no paramétrica es desarrollar procedimientos

alternativos (métodos de distribución libre) que sean válidos cuando no se verifica alguna de
las hipótesis estructurales.

217
218 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

contrastes recientes, como las descritas en la Sección 3.2.2. No obstante, en la


última sección se describirán, únicamente con fines ilustrativos, algunos de los
primeros métodos diseñados específicamente para generadores aleatorios.

B.1 Métodos de bondad de ajuste


A partir de X1 , . . . , Xn m.a.s. de X con función de distribución F , interesa
realizar un contraste de la forma:
{
H0 : F = F0
H1 : F ̸= F0

En este caso interesará distinguir principalmente entre hipótesis nulas simples


(especifican un único modelo) y compuestas (especifican un conjunto o familia
de modelos). Por ejemplo:

H0 simple H0 compuesta
{ {
H0 : F = N (0, 1) H0 : F = N (µ, σ 2 )
H1 : F ̸= N (0, 1) H1 : F ̸= N (µ, σ 2 )

Entre los métodos gráficos habituales estarían: histograma, gráfico de la densi-


dad suavizada, gráfico de tallo y hojas, gráfico de la distribución empírica (o
versión suavizada) y gráficos P-P o Q-Q.
Entre los métodos de contrastes de hipótesis generales (H0 : F = F0 ) destaca-
rían las pruebas: Chi-cuadrado de Pearson, Kolmogorov-Smirnov, Cramer-von
Mises o Anderson-Darling. Además de los específicos de normalidad (H0 : F =
N (µ, σ 2 )): Kolmogorov-Smirnov-Lilliefors, Shapiro-Wilks y los de asimetría y
apuntamiento.

B.1.1 Histograma

Se agrupan los datos en intervalos Ik = [Lk−1 , Lk ) con k = 1, . . . , K y a cada


intervalo se le asocia un
∑valor (altura de la barra) igual a la frecuencia absoluta
n
de ese intervalo nk = i=1 1 (Xi ∈ [Lk−1 , Lk )), si la longitud de los intervalos
es constante, o proporcional a dicha frecuencia (de forma que el área coincida
con la frecuencia relativa y pueda ser comparado con una función de densidad):
ni
fˆn (x) =
n (Lk − Lk−1 )

Como ya se ha visto anteriormente, en R podemos generar este gráfico con la


función hist() del paquete base. Algunos de los principales parámetros (con
B.1. MÉTODOS DE BONDAD DE AJUSTE 219

los valores por defecto) son los siguientes:


hist(x, breaks = "Sturges", freq = NULL, plot = TRUE, ...)

• breaks: puede ser un valor numérico con el número de puntos de discreti-


zación, un vector con los puntos de discretización, una cadena de texto que
los determine (otras opciones son "Scott" y "FD"; en este caso llamará
internamente a la función nclass.xxx() donde xxx se corresponde con
la cadena de texto), o incluso una función personalizada que devuelva el
número o el vector de puntos de discretización.
• freq: lógico (TRUE por defecto si los puntos de discretización son equidis-
tantes), determina si en el gráfico se representan frecuencias o “densida-
des”.
• plot: lógico, se puede establecer a FALSE si no queremos generar el gráfico
y solo nos interesan el objeto con los resultados (que devuelve de forma
“invisible”, por ejemplo para discretizar los valores en intervalos).
Ejemplo:
datos <- c(22.56,22.33,24.58,23.14,19.03,26.76,18.33,23.10,
21.53,9.06,16.75,23.29,22.14,16.28,18.89,27.48,10.44,
26.86,27.27,18.74,19.88,15.76,30.77,21.16,24.26,22.90,
27.14,18.02,21.53,24.99,19.81,11.88,24.01,22.11,21.91,
14.35,11.14,9.93,20.22,17.73,19.05)
hist(datos, freq = FALSE)
curve(dnorm(x, mean(datos), sd(datos)), add = TRUE)

Histogram of datos
0.08
0.06
Density

0.04
0.02
0.00

5 10 15 20 25 30 35

datos

Si el número de valores es muy grande (por ejemplo en el caso de secuencias


aleatorias), nos puede interesar establecer la opción breaks = "FD" para au-
220 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

mentar el número de intervalos de discretización. En cualquier caso, como se


muestra en la Figura B.1, la convergencia del histograma a la densidad teóri-
ca se podría considerar bastante lenta. Alternativamente se podría considerar
una estimación suave de la densidad, por ejemplo empleando la estimación tipo
núcleo implementada en la función density().

n = 50 n = 500

0.6

0.6
0.4

0.4
0.2

0.2
0.0

0.0
−3 −2 −1 0 1 2 3 −3 −2 −1 0 1 2 3

n = 1000 n = 10000
0.6

0.6
0.4

0.4
0.2

0.2
0.0

0.0

−3 −2 −1 0 1 2 3 −3 −2 −1 0 1 2 3

Figura B.1: Convergencia del histograma a la densidad teórica.

B.1.2 Función de distribución empírica


∑n
La función de distribución empírica Fn (x) = n1 i=1 1 (Xi ≤ x) asigna a cada
número real x la frecuencia relativa de observaciones menores o iguales que x.
Para obtener las frecuencias relativas acumuladas, se ordena la muestra X(1) ≤
X(2) ≤ · · · ≤ X(n) y:

 0 si x < X(1)
Fn (x) = i
si X(i) ≤ x < X(i+1)
 n
1 si X(n) ≤ x

Ejemplo:
fn <- ecdf(datos)
curve(ecdf(datos)(x), xlim = extendrange(datos), type = 's',
ylab = 'distribution function', lwd = 2)
curve(pnorm(x, mean(datos), sd(datos)), add = TRUE)
B.1. MÉTODOS DE BONDAD DE AJUSTE 221

1.0
0.8
distribution function

0.6
0.4
0.2
0.0

10 15 20 25 30

Figura B.2: Comparación de la distribución empírica de los datos de ejemplo


con la función de distribución de la aproximación normal.

B.1.3 Gráficos P-P y Q-Q

El gráfico de probabilidad (o de probabilidad-probabilidad) es el gráfico de dis-


persión de:
{(F0 (xi ), Fn (xi )) : i = 1, · · · , n}

siendo Fn la función de distribución empírica y F0 la función de distribución bajo


H0 (con la que desea comparar, si la hipótesis nula es simple) o una estimación
bajo H0 (si la hipótesis nula es compuesta; e.g. si H0 : F = N (µ, σ 2 ), F̂0 función
de distribución de N (µ̂, σ̂ 2 )). Si H0 es cierta, la nube de puntos estará en torno
a la recta y = x (probabilidades observadas próximas a las esperadas bajo H0 ).

El gráfico Q-Q (cuantil-cuantil) es equivalente al anterior pero en la escala de


la variable:
{( ) }
qi , x(i) : i = 1, · · · , n

siendo x(i) los cuantiles observados y qi = F0−1 (pi ) los esperados2 bajo H0 .

Ejemplo:
qqnorm(datos)
qqline(datos)

2 Típicamente
{ (i−0.5)
}
pi = n
: i = 1, · · · , n .
222 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

Normal Q−Q Plot

30
25
Sample Quantiles

20
15
10

−2 −1 0 1 2

Theoretical Quantiles

require(car)
qqPlot(datos, "norm")
30
25
datos

20
15
10

38
10

−2 −1 0 1 2

norm quantiles

## [1] 10 38
B.1. MÉTODOS DE BONDAD DE AJUSTE 223

B.1.4 Contraste ji-cuadrado de Pearson

Se trata de un contraste de bondad de ajuste:


{
H0 : F = F0
H1 : F ̸= F0

desarrollado inicialmente para variables categóricas. En el caso general, podemos


pensar que los datos están agrupados en k clases: C1 , · · · , Ck . Por ejemplo, si
la variable es categórica o discreta, cada clase se puede corresponder con una
modalidad. Si la variable es continua habrá que categorizarla en intervalos.
Si la hipótesis nula es simple, cada clase tendrá asociada una probabilidad
pi = P (X ∈ Ci ) bajo H0 . Si por el contrario es compuesta, se trabajará con
una estimación de dicha probabilidad (y habrá que correguir la distribución
aproximada del estadístico del contraste).

Clases Discreta Continua H0 simple H0 compuesta


C1 x1 [L0 , L1 ) p1 p̂1
.. .. .. .. ..
. . . . .
Ck xk [Lk−1 , Lk ) p
∑ k p̂
∑ k
i pi = 1 i p̂i = 1

Se realizará un contraste equivalente:


{
H0 : Las probabilidades son correctas
H1 : Las probabilidades no son correctas

Si H0 es cierta, la frecuencia relativa fi de la clase Ci es una aproximación de


la probabilidad teórica, fi ≈ pi . Equivalentemente, las frecuencias observadas
ni = n · fi deberían ser próximas a las esperadas ei = n · pi bajo H0 , sugiriendo
el estadístico del contraste (Pearson, 1900):


k
(ni − ei )2
χ2 = ∼ χ2k−r−1 , si H0 cierta
i=1
ei aprox.

siendo k el número de clases y r el número de parámetros estimados (para


aproximar las probabilidades bajo H0 ).

ni
(ni −ei )2
Clases observadas pi bajo H0 ei bajo H0 ei
(n1 −e1 )2
C1 n1 p1 e1 e1
.. .. .. .. ..
. . . . .
224 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

ni
(ni −ei )2
Clases observadas pi bajo H0 ei bajo H0 ei
(nk −ek )2
Ck nk pk ek
∑ ∑ ∑ ek
∑k
χ2 = i=1 (ni −e
2
i)
Total i ni = n i pi = 1 i ei =n ei

Cuando H0 es cierta el estadístico tiende a tomar valores pequeños y grandes


cuando es falsa. Por tanto se rechaza H0 , para un nivel de significación α, si:

k
(ni − ei )2
≥ χ2k−r−1,1−α
i=1
ei

Si realizamos el contraste a partir del p-valor o nivel crítico:


( )
∑k
(ni − ei )2
p = P χk−r−1 ≥
2

i=1
ei

rechazaremos H0 si p ≤ α (y cuanto menor sea se rechazará con mayor seguridad)


y aceptaremos H0 si p > α (con mayor seguridad cuanto mayor sea).
Este método está implementado en la función chisq.test() para el caso dis-
creto (no corrige los grados de libertad). Ejemplo:
x <- trunc(5 * runif(100))
chisq.test(table(x)) # NOT 'chisq.test(x)'!

##
## Chi-squared test for given probabilities
##
## data: table(x)
## X-squared = 9.2, df = 4, p-value = 0.05629
La distribución exacta del estadístico del contraste es discreta (se podría aproxi-
mar por simulación, por ejemplo empleando los parámetros simulate.p.value
= TRUE y B = 2000 de la función chisq.test(); ver también el Ejercicio 7.2
de la Sección 7.7.3 para el caso del contraste chi-cuadrado de independencia).
Para que la aproximación continua χ2 sea válida:
• El tamaño muestral debe ser suficientemente grande (p.e. n > 30).
• La muestra debe ser una muestra aleatoria simple.
• Los parámetros deben estimarse (si es necesario) por máxima verosimili-
tud.
• Las frecuencias esperadas ei = n · pi deberían ser todas ≥ 5 (realmente
esta es una restricción conservadora, la aproximación puede ser adecuada
si no hay frecuencias esperadas inferiores a 1 y menos de un 20% inferiores
a 5).
B.1. MÉTODOS DE BONDAD DE AJUSTE 225

Si la frecuencia esperada de alguna clase es < 5, se suele agrupar con otra clase
(o con varias si no fuese suficiente con una) para obtener una frecuencia esperada
≥ 5:
• Cuando la variable es nominal (no hay una ordenación lógica) se suele
agrupar con la(s) que tiene(n) menor valor de ei .
• Si la variable es ordinal (o numérica) debe juntarse la que causó el proble-
ma con una de las adyacentes.
Si la variable de interés es continua, una forma de garantizar que ei ≥ 5 consiste
en tomar un número de intervalos k ≤ ⌊n/5⌋ y de forma que sean equiprobables
pi = 1/k, considerando los puntos críticos xi/k de la distribución bajo H0 .
Por ejemplo, se podría emplear la siguiente función (que imita a las incluídas
en R):
#-------------------------------------------------------------------------------
# chisq.test.cont(x, distribution, nclasses, output, nestpar,...)
#-------------------------------------------------------------------------------
# Realiza el test ji-cuadrado de bondad de ajuste para una distribución continua
# discretizando en intervalos equiprobables.
# Parámetros:
# distribution = "norm","unif",etc
# nclasses = floor(length(x)/5)
# output = TRUE
# nestpar = 0= nº de parámetros estimados
# ... = parámetros distribución
# Ejemplo:
# chisq.test.cont(x, distribution="norm", nestpar=2, mean=mean(x), sd=sqrt((nx-1)/nx)*sd(x))
#-------------------------------------------------------------------------------
chisq.test.cont <- function(x, distribution = "norm", nclasses = floor(length(x)/5),
output = TRUE, nestpar = 0, ...) {
# Funciones distribución
q.distrib <- eval(parse(text = paste("q", distribution, sep = "")))
d.distrib <- eval(parse(text = paste("d", distribution, sep = "")))
# Puntos de corte
q <- q.distrib((1:(nclasses - 1))/nclasses, ...)
tol <- sqrt(.Machine$double.eps)
xbreaks <- c(min(x) - tol, q, max(x) + tol)
# Gráficos y frecuencias
if (output) {
xhist <- hist(x, breaks = xbreaks, freq = FALSE, lty = 2, border = "grey50")
curve(d.distrib(x, ...), add = TRUE)
} else {
xhist <- hist(x, breaks = xbreaks, plot = FALSE)
}
# Cálculo estadístico y p-valor
226 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

O <- xhist$counts # Equivalente a table(cut(x, xbreaks)) pero más eficiente


E <- length(x)/nclasses
DNAME <- deparse(substitute(x))
METHOD <- "Pearson's Chi-squared test"
STATISTIC <- sum((O - E)^2/E)
names(STATISTIC) <- "X-squared"
PARAMETER <- nclasses - nestpar - 1
names(PARAMETER) <- "df"
PVAL <- pchisq(STATISTIC, PARAMETER, lower.tail = FALSE)
# Preparar resultados
classes <- format(xbreaks)
classes <- paste("(", classes[-(nclasses + 1)], ",", classes[-1], "]",
sep = "")
RESULTS <- list(classes = classes, observed = O, expected = E, residuals = (O -
E)/sqrt(E))
if (output) {
cat("\nPearson's Chi-squared test table\n")
print(as.data.frame(RESULTS))
}
if (any(E < 5))
warning("Chi-squared approximation may be incorrect")
structure(c(list(statistic = STATISTIC, parameter = PARAMETER, p.value = PVAL,
method = METHOD, data.name = DNAME), RESULTS), class = "htest")
}

Continuando con el ejemplo anterior, podríamos contrastar normalidad median-


te:
chisq.test.cont(datos, distribution = "norm", nestpar = 2, mean=mean(datos), sd=sd(dato
B.1. MÉTODOS DE BONDAD DE AJUSTE 227

Histogram of x

0.10
0.08
0.06
Density

0.04
0.02
0.00

10 15 20 25 30

##
## Pearson's Chi-squared test table
## classes observed expected residuals
## 1 ( 9.06000,14.49908] 6 5.125 0.3865103
## 2 (14.49908,16.94725] 3 5.125 -0.9386680
## 3 (16.94725,18.77800] 4 5.125 -0.4969419
## 4 (18.77800,20.41732] 6 5.125 0.3865103
## 5 (20.41732,22.05663] 4 5.125 -0.4969419
## 6 (22.05663,23.88739] 8 5.125 1.2699625
## 7 (23.88739,26.33556] 4 5.125 -0.4969419
## 8 (26.33556,30.77000] 6 5.125 0.3865103

##
## Pearson's Chi-squared test
##
## data: datos
## X-squared = 3.6829, df = 5, p-value = 0.5959

B.1.5 Contraste de Kolmogorov-Smirnov

Se trata de un contraste de bondad de ajuste diseñado para distribuciones con-


tinuas (similar a la prueba de Cramer-von Mises o a la de Anderson-Darling,
implementadas en el paquete goftest de R, que son en principio mejores). Se
basa en comparar la función de distribución F0 bajo H0 con la función de dis-
228 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

tribución empírica Fn :

Dn = sup |Fn (x) − F0 (x)|,


x
{ }
= max |Fn (X(i) ) − F0 (X(i) )|, |Fn (X(i−1) ) − F0 (X(i) )|
1≤i≤n

( ) i
Teniendo en cuenta que Fn X(i) = n:
{ }
i i−1
Dn = max − F0 (X(i) ), F0 (X(i) ) −
1≤i≤n n n
{ + −
}
= max Dn,i , Dn,i
1≤i≤n

Si H0 es simple y F0 es continua, la distribución del estadístico Dn bajo H0


no depende F0 (es de distribución libre). Esta distribución está tabulada (para
tamaños muestrales grandes se utiliza la aproximación asintótica). Se rechaza
H0 si el valor observado d del estadístico es significativamente grande:

p = P (Dn ≥ d) ≤ α.

Este método está implementado en la función ks.test() del paquete base de


R:
ks.test(x, y, ...)

donde x es un vector que contiene los datos, y es una función de distribución


(o una cadena de texto que la especifica; también puede ser otro vector de
datos para el contraste de dos muestras) y ... representa los parámetros de la
distribución.
Continuando con el ejemplo anterior, para contrastar H0 : F = N (20, 52 ) po-
dríamos emplear:
ks.test(datos, pnorm, mean = 20, sd = 5) # One-sample

##
## One-sample Kolmogorov-Smirnov test
##
## data: datos
## D = 0.13239, p-value = 0.4688
## alternative hypothesis: two-sided
Si H0 es compuesta, el procedimiento habitual es estimar los parámetros desco-
nocidos por máxima verosimilitud y emplear F̂0 en lugar de F0 . Sin embargo,
al proceder de esta forma es de esperar que F̂0 se aproxime más que F0 a la
distribución empírica, por lo que los cuantiles de la distribución de Dn pueden
ser demasiado conservativos (los p-valores tenderán a ser mayores de lo que de-
berían) y se tenderá a aceptar la hipótesis nula (puede ser preferible aproximar
B.2. DIAGNOSIS DE LA INDEPENDENCIA 229

el p-valor mediante simulación; como se muestra en el Ejercicio 8.5 de la Sección


8.3).
Para evitar este problema, en el caso de contrastar normalidad se desarrolló
el test de Lilliefors, implementado en la función lillie.test() del paquete
nortest (también hay versiones en este paquete para los métodos de Cramer-
von Mises y Anderson-Darling).
Por ejemplo:
ks.test(datos, pnorm, mean(datos), sd(datos)) # One-sample Kolmogorov-Smirnov test

##
## One-sample Kolmogorov-Smirnov test
##
## data: datos
## D = 0.097809, p-value = 0.8277
## alternative hypothesis: two-sided
library(nortest)
lillie.test(datos)

##
## Lilliefors (Kolmogorov-Smirnov) normality test
##
## data: datos
## D = 0.097809, p-value = 0.4162

B.2 Diagnosis de la independencia

Los métodos “clásicos” de inferencia estadística se basan en suponer que las


observaciones X1 , . . . , Xn son una muestra aleatoria simple (m.a.s.) de X. Por
tanto suponen que las observaciones son independientes (o los errores, en el caso
de un modelo de regresión).
• La ausencia de aleatoriedad es difícil de corregir y puede influir notable-
mente en el análisis estadístico.
• Si existe dependencia entre las observaciones muestrales (e.g. el conoci-
miento de Xi proporciona información sobre los valores de Xi+1 , Xi+2 ,
. . .), los métodos “clásicos” no serán en principio adecuados (pueden con-
ducir a conclusiones erróneas).
– Esto es debido principalmente a que introduce un sesgo en los esti-
madores de las varianzas (diseñados asumiendo independencia).
– Los correspondientes intervalos de confianza y contrastes de hipótesis
tendrán una confianza o una potencia distinta de la que deberían
230 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

(aunque las estimaciones de los parámetros pueden no verse muy


afectadas).

Si X1 e X2 son independientes (Cov(X1 , X2 ) = 0):

V ar(X1 + X2 ) = V ar(X1 ) + V ar(X2 )

En el caso general (dependencia):

V ar(X1 + X2 ) = V ar(X1 ) + V ar(X2 ) + 2Cov(X1 , X2 )

Típicamente Cov(X1 , X2 ) > 0 por lo que con los métodos “clásicos” (basados
en independencia) se suelen producir subestimaciones de las varianzas (IC más
estrechos y tendencia a rechazar H0 en contrastes).

Ejemplo: datos simulados

Consideramos un proceso temporal estacionario con dependencia exponencial (la


dependencia entre las observaciones depende del “salto” entre ellas; ver Ejemplo
7.4 en la Sección 7.3).
n <- 100 # Nº de observaciones
t <- seq(0, 1, length = n)
mu <- rep(0, n) # Media
# mu <- 0.25 + 0.5*t
# mu <- sin(2*pi*t)

# Matriz de covarianzas
t.dist <- as.matrix(dist(t))
t.cov <- exp(-t.dist)
# str(t.cov)
# num [1:100, 1:100] 1 0.99 0.98 0.97 0.96 ...

# Simulación de las observaciones


set.seed(1)
library(MASS)

z <- rnorm(n)
x1 <- mu + z # Datos independientes
x2 <- mvrnorm(1, mu, t.cov) # Datos dependientes

plot(t, mu, type="l", lwd = 2, ylim = c(-3,3), ylab = 'x')


lines(t, x1, col = 'blue')
lines(t, x2, col = 'red')
legend("bottomright", legend = c("Datos independientes", "Datos dependientes"), col = c
B.2. DIAGNOSIS DE LA INDEPENDENCIA 231

3
2
1
0
x

−1
−2

Datos independientes
Datos dependientes
−3

0.0 0.2 0.4 0.6 0.8 1.0

En el caso anterior la varianza es uno con ambos procesos. Las estimaciones


suponiendo independencia serían:
var(x1)

## [1] 0.8067621
var(x2)

## [1] 0.1108155
En el caso de datos dependientes se produce una clara subestimación de la
varianza

B.2.1 Métodos para detectar dependencia

Es de esperar que datos cercanos en el tiempo (o en el espacio) sean más pareci-


dos (dependientes) que datos más alejados, hablaríamos entonces de dependen-
cia temporal (espacial o espacio-temporal).
En esta sección nos centraremos en el caso de dependencia temporal (unidimen-
sional). Entre los métodos para detectar este tipo de dependencia destacaríamos:
• Gráficos:
– Secuencial / Dispersión frente al tiempo
– Dispersión retardado
– Correlograma
• Contrastes:
232 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

– Tests basados en rachas


– Test de Ljung-Box

B.2.2 Gráfico secuencial

El gráfico de dispersión {(i, Xi ) : i = 1, . . . , n} permite detectar la presencia de


un efecto temporal (en la tendencia o en la variabilidad).
• Es importante mantener/guardar el orden de recogida de los datos.
• Si existe una tendencia los datos no son homogéneos (debería tenerse en
cuenta la variable índice, o tiempo, como variable explicativa). Podría
indicar la presencia de un “efecto aprendizaje”.
• Comandos R: plot(as.ts(x))
Ejemplo:
old.par <- par(mfrow = c(1, 2))
plot(datos, type = 'l')
plot(as.ts(datos))
30

30
25

25
as.ts(datos)
datos

20

20
15

15
10

10

0 10 20 30 40 0 10 20 30 40

Index Time

Figura B.3: Ejemplos de gráficos secuenciales.

par(old.par)

Es habitual que este tipo de análisis se realice sobre los residuos de un modelo
de regresión (e.g. datos <- residuals(modelo))
Este gráfico también podría servir para detectar dependencia temporal:
• Valores próximos muy parecidos (valores grandes seguidos de grandes y
viceversa) indicarían una posible dependencia positiva.
• Valores próximos dispares (valores grandes seguidos de pequeños y vice-
versa) indicarían una posible dependencia negativa.
B.2. DIAGNOSIS DE LA INDEPENDENCIA 233

old.par <- par(mfrow = c(1, 3))


plot(x2, type = 'l', ylab = '', main = 'Dependencia positiva')
plot(x1, type = 'l', ylab = '', main = 'Independencia')
x3 <- x2 * c(1, -1)
plot(x3, type = 'l', ylab = '', main = 'Dependencia negativa')

Dependencia positiva Independencia Dependencia negativa

1.0
2
1.0

0.5
1
0.5

0.0
0

−0.5
−1
0.0

−1.0
−2

0 20 40 60 80 100 0 20 40 60 80 100 0 20 40 60 80 100

Index Index Index

par(old.par)

pero suele ser preferible emplear un gráfico de dispersión retardado.

B.2.3 Gráfico de dispersion retardado

El gráfico de dispersión {(Xi , Xi+1 ) : i = 1, . . . , n − 1} permite detectar depen-


dencias a un retardo (relaciones entre valores separados por un instante)

• Comando R:plot(x[-length(x)], x[-1], xlab = "X_t", ylab =


"X_t+1")
old.par <- par(mfrow = c(1, 3))
plot(x2[-length(x2)], x2[-1], xlab = "X_t", ylab = "X_t+1", main = 'Dependencia positiva')
plot(x1[-length(x1)], x1[-1], xlab = "X_t", ylab = "X_t+1", main = 'Independencia')
plot(x3[-length(x3)], x3[-1], xlab = "X_t", ylab = "X_t+1", main = 'Dependencia negativa')

Dependencia positiva Independencia Dependencia negativa


1.0
2
1.0

0.5
1
X_t+1

X_t+1

X_t+1

0.0
0.5

−0.5
−1
0.0

−1.0
−2

0.0 0.5 1.0 −2 −1 0 1 2 −1.0 −0.5 0.0 0.5 1.0

X_t X_t X_t

par(old.par)

Se puede generalizar al gráfico {(Xi , Xi+k ) : i = 1, . . . , n − k} que permite


detectar dependencias a k retardos (separadas k instantes).

Ejemplo
234 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

# Gráfico de dispersion retardado


plot(datos[-length(datos)], datos[-1], xlab = "X_t", ylab = "X_t+1")

30
25
X_t+1

20
15
10

10 15 20 25 30

X_t

El correspondiente coeficiente de correlación es una medida numérica del grado


de relación lineal (denominado autocorrelación de orden 1).
cor(datos[-length(datos)], datos[-1])

## [1] 0.01344127

Ejemplo: Calidad de un generador aleatorio

En el caso de una secuencia muy grande de número pseudoaleatorios (supuesta-


mente independientes), sería muy dificil distinguir un patrón a partir del gráfico
anterior. La recomendación en R sería utilizar puntos con color de relleno:
plot(u[-length(u)], u[-1], xlab="U_t", ylab="U_t+1", pch=21, bg="white")

Si se observa algún tipo de patrón indicaría dependencia (se podría considerar


como una versión descriptiva del denominado “Parking lot test”). Se puede
generalizar también a d-uplas (Xt+1 , Xt+2 , . . . , Xt+d ) (ver ejemplo del generador
RANDU en Figura 3.1 de la Sección 3.1).

B.2.4 El correlograma

Para estudiar si el grado de relación (lineal) entre Xi e Xi+k podemos utilizar


el coeficiente de correlación:
B.2. DIAGNOSIS DE LA INDEPENDENCIA 235

Buen generador Mal generador

1.0

1.0
0.8

0.8
0.6

0.6
U_t+1

U_t+1
0.4

0.4
0.2

0.2
0.0

0.0
0.0 0.2 0.4 0.6 0.8 1.0 0.0 0.2 0.4 0.6 0.8 1.0

U_t U_t

Figura B.4: Ejemplos de gráficos de dispensión retardados de dos secuencias de


longitud 10000.

Cov (Xi , Xi+k )


ρ (Xi , Xi+k ) =
σ (Xi ) σ (Xi+k )
• En el caso de datos homogéneos (estacionarios) la correlación sería función
únicamente del salto:
ρ (Xi , Xi+k ) ≡ ρ (k)
denominada función de autocorrelación simple (fas) o correlograma.
• Su estimador es el correlograma muestral:
∑n−k
(Xi − X)(Xi+k − X)
r(k) = i=1 ∑n
i=1 (Xi − X)
2

• Comando R:acf(x)
En caso de independencia es de esperar que las autocorrelaciones muestrales sean
próximas a cero (valores “grandes” indicarían dependencia positiva o negativa
según el signo).
old.par <- par(mfrow = c(1, 3))
acf(x1, main = 'Independencia')
acf(x2, main = 'Dependencia positiva')
acf(x3, main = 'Dependencia negativa')

Independencia Dependencia positiva Dependencia negativa


1.0

1.0

1.0
0.8

0.8

0.5
0.6

0.6
ACF

ACF

ACF
0.4

0.4

0.0
0.2

0.2

−0.5
−0.2

−0.2

−1.0

0 5 10 15 20 0 5 10 15 20 0 5 10 15 20

Lag Lag Lag


236 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

par(old.par)

Suponiendo normalidad e independencia, asintóticamente:


( )
1
r(k) ∼ N ρ(k),
aprox. n

• Si el tamaño muestral es grande, podríamos aceptar H0 : ρ (k) = 0 si:

2
|r(k)| < √
n

• En el gráfico de autocorrelaciones muestrales (también denominado co-


rrelograma) se representan las estimaciones r(k) de las autocorrelaciones
correspondientes a los primeros retardos (típicamente k < n/4) y las co-
rrespondientes bandas de confianza (para detectar dependencias significa-
tivas).

Ejemplo
acf(datos) # correlaciones

Series datos
1.0
0.8
0.6
0.4
ACF

0.2
0.0
−0.2

0 5 10 15

Lag

La función acf también permite estimar el covariograma3 .


covar <- acf(x2, type = "covariance")

3 En algunos campos, como en estadística espacial, en lugar del covariograma se suele em-

plear el semivariograma γ(k) = C(0) − C(k).


B.2. DIAGNOSIS DE LA INDEPENDENCIA 237

Series x2

0.10
0.08
0.06
ACF (cov)

0.04
0.02
0.00

0 5 10 15 20

Lag

B.2.5 Test de rachas

Permite contrastar si el orden de aparición de dos valores de una variable dicotó-


mica es aleatorio. Supongamos que X toma los valores + y − y que observamos
una muestra del tipo:

+ + + + − − − + + + − − + + + + + + − − −−

y nos interesa contrastar:


{
H0 : La muestra es aleatoria
H1 : La muestra no es aleatoria

Una racha es una secuencia de observaciones iguales (o similares):

+ + ++ − − − + + + −− + + + + ++ − − −−
| {z } | {z } | {z } |{z} | {z } | {z }
1 2 3 4 5 6

• Una muestra con “muchas” o “pocas” rachas sugeriría que la muestra no


es aleatoria (con dependencia negativa o positiva, respec.).
• Estadístico del contraste:

R = ”Número total de rachas en la muestra”

• Bajo la hipótesis nula de aleatoriedad:


( )
2n1 n2 2n1 n2 (2n1 n2 − n)
R ∼ N 1+ ,
aprox. n n2 (n − 1)
238 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

siendo n1 y n2 el nº de signos + y − en la muestra, respectivamente (n1 +


n2 = n). Para tamaños muéstrales pequeños (n < 40), esta aproximación
no es buena y conviene utilizar la distribución exacta (o utilizar corrección
por continuidad). Los valores críticos de esta distribución están tabulados.
Este contraste se emplea también para variables continuas, se fija un punto
de corte para dicotomizarlas. Normalmente se toma como punto de corte la
mediana.
• En este caso si k = n1 (≃ n2 ):
( )
k(k − 1)
R ∼ N k + 1,
aprox. 2k − 1

• Se rechaza la hipótesis nula de aleatoriedad si el número de rachas es


significativamente pequeño o grande.
• Si el tamaño muestral es grande, el p-valor será:
( )
R − E(R)

p ≃ 2P Z ≥ √
V ar(R)

• Comandos R: tseries::runs.test(as.factor(x > median(x)))


Ejemplo
library(tseries)
runs.test(as.factor(datos > median(datos)))

##
## Runs Test
##
## data: as.factor(datos > median(datos))
## Standard Normal = -0.4422, p-value = 0.6583
## alternative hypothesis: two.sided
Alternativamente, para evitar el cálculo del punto de corte (la mediana), reque-
rido para dicotomizar la variable continua, se podría emplear una modificación
de este contraste, el denominado test de rachas ascendentes y descendentes, en
el que se generan los valores + y − dependiendo de si el valor de la secuencia
es mayor o menor que el anterior (ver e.g. Downham, 1970). Este contraste es
más adecuado para generadores aleatorios.

B.2.6 El contraste de Ljung-Box

Es un test muy utilizado (en series de tiempo) para contrastar la hipótesis de


independencia. Se contrasta la hipótesis nula de que las primeras m autocorre-
B.3. CONTRASTES ESPECÍFICOS PARA GENERADORES ALEATORIOS239

laciones son cero: {


H0 : ρ1 = ρ2 = . . . = ρm = 0
H1 : ρi ̸= 0 para algún i
• Se elige un m tal que la estimación r(m) de ρm = ρ(m) sea “fiable” (e.g.
10 log10 n).
• El estadístico del contraste:

m
r(k)2
Q = n(n + 2) ∼ χ2m , si H0 es cierta.
n−k aprox.
k=1

• Se rechaza H0 si el valor observado es grande (Q ≥ χ2m,1−α ):


( )
p = P χ2m ≥ Q

• Comandos R:
Box.test(x, type=Ljung)
Box.test(x, lag, type=Ljung)

Ejemplo
Box.test(datos, type="Ljung") # Contrasta si la primera autocorrelación es nula

##
## Box-Ljung test
##
## data: datos
## X-squared = 0.0078317, df = 1, p-value = 0.9295
Box.test(datos, lag=5, type="Ljung") # Contrasta si las 5 primeras autocorrelaciones son nulas

##
## Box-Ljung test
##
## data: datos
## X-squared = 1.2556, df = 5, p-value = 0.9394
NOTA: Cuando se trabaja con residuos de un modelo lineal, para contrastar
que la primera autocorrelación es cero, es preferible emplear el test de Durbin-
Watson implementado en la función dwtest() del paquete lmtest.

B.3 Contrastes específicos para generadores


aleatorios
Los contrastes generales anteriores pueden ser muy poco adecuados para testear
generadores de números pseudoaleatorios (ver e.g. L’Ecuyer y Simard, 2007).
240 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

Por ese motivo se han desarrollado contrastes específicos, principalmente con el


objetivo de encontrar un generador con buenas propiedades criptográficas.

Muchos de estos contrastes están basados en la prueba chi-cuadrado y trabajan


con enteros en lugar de los valores uniformes. El procedimiento habitual consiste
en fijar un entero positivo K, y discretizar los valores uniformes U1 , U2 , . . . , Un ,
de la forma:
Xi = ⌊K · Ui ⌋ + 1,

donde ⌊u⌋ denota la parte entera de u. De esta forma se consigue una sucesión
de enteros aleatorios supuestamente independientes con distribución uniforme
en {1, . . . , K}.

En está sección se describirán algunos de los métodos tradicionales en este cam-


po con fines ilustrativos. Si realmente el objetivo es diagnosticar la calidad de
un generador, la recomendación sería emplear las baterías de contrastes más
recientes descritas en la Sección 3.2.2.

B.3.1 Contraste de frecuencias

Empleando la discretización anterior se simplifica notablemente el contraste chi-


cuadrado de bondad de ajuste a una uniforme, descrito en la Sección B.1.4 e
implementado en la función chisq.test.cont(). En este caso bastaría con con-
trastar la equiprobabilidad de la secuencia de enteros (empleando directamente
la función chisq.test()) y este método de denomina contraste de frecuencias
(frequency test). Por ejemplo:
set.seed(1)
u <- runif(1000)

k <- 10
x <- floor(k*u) + 1
# Test chi-cuadrado
f <- table(factor(x, levels = seq_len(k)))
chisq.test(f)

##
## Chi-squared test for given probabilities
##
## data: f
## X-squared = 10.26, df = 9, p-value = 0.3298
# Equivalente a
# source("Test Chi-cuadrado continua.R")
# chisq.test.cont(u, distribution = "unif", nclasses = k, output = FALSE, min = 0, max
B.3. CONTRASTES ESPECÍFICOS PARA GENERADORES ALEATORIOS241

B.3.2 Contraste de series

El contraste anterior se puede generalizar a contrastar la uniformidad de las


d-uplas (Xt+1 , Xt+2 , . . . , Xt+d ) con t = (i − 1)d, i = 1, . . . , m siendo m = ⌊n/d⌋.
La idea es que troceamos el hipercubo [0, 1]d en K d celdas equiprobables. Con-
siderando como categorías todos los posibles valores de las d-uplas, podemos
emplear el estadístico chi-cuadrado para medir la discrepancia entre las fre-
cuencias observadas en y las esperadas, iguales todas a Kmd . La elecciones más
frecuentes son d = 2 (contraste de pares seriados) y K = 8, 10 ó 20. Por ejemplo,
la función serial.test() del paquete randtoolbox implementa este contraste
para d = 2.
Para que la prueba chi-cuadrado sea fiable el valor de n debería ser grande en
comparación con el número de categorías K d (e.g. n ≥ 5dK d ). Si se considera
un valor d ≥ 3 puede ser necesario reducir considerablemente el valor de K para
evitar considerar demasiadas categorías. Alternativamente se podrían emplear
pruebas menos precisas como el contraste del poker o del coleccionista descritos
a continuación.

B.3.3 El contraste del poker

En el contrate del poker “clásico” se consideran conjuntos sucesivos de cinco


enteros y, para cada uno, se determina cuál de las siguientes posibilidades se da:
1. Un mismo entero se repite cinco veces (abreviadamente, AAAAA).
2. Un mismo entero se repite cuatro veces y otro distinto aparece una vez
(AAAAB).
3. Un entero se repite tres veces y otro distinto se repite dos (AAABB).
4. Un entero se repite tres veces y otros dos distintos aparecen una vez cada
uno (AAABC).
5. Un entero se repite dos veces, otro distinto se repite también dos veces y
un tercer entero diferente aparece una sóla vez (AABBC).
6. Un entero se repite dos veces y otros tres distintos aparecen una vez cada
uno (AABCD).
7. Los cinco enteros que aparecen son todos distintos (ABCDE).
Bajo las hipótesis de aleatoriedad y uniformidad, se pueden calcular las proba-
bilidades de estas modalidades. Por ejemplo para K = 10 obtendríamos:

P (AAAAA) = 0.0001, P (AAAAB) = 0.0045, P (AAABB) = 0.0090,


P (AAABC) = 0.0720, P (AABBC) = 0.1080, P (AABCD) = 0.5040,
P (ABCDE) = 0.3024.
242 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD

Es frecuente que las clases AAAAA y AAAAB se agrupen a la hora de aplicar


el test chi-cuadrado, ya que, en caso contrario, la restricción habitual ei ≥ 5
llevaría a que 0.0001 · n5 ≥ 5, es decir, n ≥ 250 000.
Es habitual simplificar el contraste anterior para facilitar su implementación
definiendo las categorías según el número de enteros distintos de entre los cinco
observados. Así obtendríamos:

P (1 entero diferente) = 0.0001, P (2 enteros diferentes) = 0.0135,


P (3 enteros diferentes) = 0.1800, P (4 enteros diferentes) = 0.5040,
P (5 enteros diferentes) = 0.3024,

procediendo también a agrupar las dos primeras modalidades.


En el caso general de considerar d-uplas (manos de d cartas con K posibilidades
cada una), la probabilidad de obtener c valores (cartas) diferentes es (e.g. Knuth,
2002, Sección 3.3.2, p. 64):

K!
P (C = c) = S(d, c),
(K − c)!K d

donde S(d, c) es el número de Stirling de segunda clase, definido como el número


de formas que existen de hacer una partición de un conjunto de d elementos en
c subconjuntos:
( )
1∑
c
c
S(d, c) = (−1)i (c − i)d .
c! i=0 i

Por ejemplo, la función poker.test() del paquete randtoolbox implementa


este contraste para el caso de d = K.

B.3.4 El contraste del coleccionista

Por simplicidad describiremos el caso de d = 1 (con K categorías). Considerando


la sucesión de enteros aleatorios se procede (como un coleccionista) a contabilizar
cuál es el número, Q, (aleatorio) de valores consecutivos hasta que se completa
la colección de todos los enteros entre 1 y K. Obviamente, bajo las hipótesis
de aleatoriedad y uniformidad, cada posible entero entre 1 y K tiene la misma
probabilidad de aparecer en cada generación y, por tanto, resulta posible calcular
la distribución de probabilidad de Q. De esta forma podemos utilizar los valores
calculados de las probabilidades

P (Q = K), P (Q = K + 1), . . . , P (Q = M − 1), P (Q ≥ M ),

para obtener las frecuencias esperadas de cada clase y confrontarlas con las ob-
servadas vía el estadístico chi-cuadrado (e.g. Knuth, 2002, Sección 3.3.2, p. 65).
B.3. CONTRASTES ESPECÍFICOS PARA GENERADORES ALEATORIOS243

Existen varias elecciones comunes de K y M . Tomando K = 5 con clases Q = 5,


Q = 6, . . ., Q = 19, Q ≥ 20, las probabilidades vendrían dadas por:

P (Q = 5) = 0.03840000, P (Q = 6) = 0.07680000,
P (Q = 7) = 0.09984000, P (Q = 8) = 0.10752000,
P (Q = 9) = 0.10450944, P (Q = 10) = 0.09547776,
P (Q = 11) = 0.08381645, P (Q = 12) = 0.07163904,
P (Q = 13) = 0.06011299, P (Q = 14) = 0.04979157,
P (Q = 15) = 0.04086200, P (Q = 16) = 0.03331007,
P (Q = 17) = 0.02702163, P (Q = 18) = 0.02184196,
P (Q = 19) = 0.01760857, P (Q ≥ 20) = 0.07144851.

Para K = 10 se podrían considerar las siguientes categorías (con sus correspon-


dientes probabilidades):

P (10 ≤ Q ≤ 19) = 0.17321155, P (20 ≤ Q ≤ 23) = 0.17492380,


P (24 ≤ Q ≤ 27) = 0.17150818, P (28 ≤ Q ≤ 32) = 0.17134210,
P (33 ≤ Q ≤ 39) = 0.15216056, P (Q ≥ 40) = 0.15685380.
244 APÉNDICE B. BONDAD DE AJUSTE Y ALEATORIEDAD
Apéndice C

Integración numérica

En muchos casos nos puede interesar la aproximación de una integral definida.


En estadística, además del caso de Inferencia Bayesiana (que se trató en el
Capítulo 11 empleando Integración Montecarlo y MCMC), nos puede interesar
por ejemplo aproximar mediante simulación el error cuadrático integrado medio
(MISE) de un estimador. En el caso de una densidad univariante sería de la
forma: { } ∫
M ISE fˆ = E (fˆ(x) − f (x))2 dx

Cuando el numero de dimensiones es pequeño, nos puede ineteresar emplear un


método numérico para aproximar este tipo de integrales.

C.1 Integración numérica unidimensional


Supongamos que nos interesa aproximar una integral de la forma:
∫ b
I= h(x)dx.
a
.
Consideraremos como ejemplo:
∫ 1
4
4x4 dx =
0 5
.
fun <- function(x) return(4 * x^4)
curve(fun, 0, 1)
abline(h = 0, lty = 2)
abline(v = c(0, 1), lty = 2)

245
246 APÉNDICE C. INTEGRACIÓN NUMÉRICA

4
3
fun(x)

2
1
0

0.0 0.2 0.4 0.6 0.8 1.0

C.1.1 Método del trapezoide

La regla de los trapecios es una forma de aproximar la integral utilizando n


trapecios. Si se consideran n subintervalos en [a, b] de longitud h = b−a
n (i.e.
n + 1 puntos regularmente espaciados cubriendo el dominio), y se aproxima
linealmente la función en cada subintervalo, se obtiene que:
∫ b
h
f (x) dx ≈ [f (a) + 2f (a + h) + 2f (a + 2h) + ... + f (b)]
a 2

trapezoid.vec <- function(f.vec, h = 0.01) {


# Integración numérica unidimensional entre a y b
# utilizando el método del trapezoide
# (se aproxima f linealmente en cada intervalo)
n <- length(f.vec)
return(h*(f.vec[1]/2 + sum(f.vec[2:(n-1)]) + f.vec[n]/2))
}

trapezoid <- function(fun, a = 0, b = 1, n = 100) {


# Integración numérica de fun (función unidimensional) entre a y b
# utilizando el método del trapezoide con n subdivisiones
# (se aproxima f linealmente en cada intervalo)
# Se asume a < b y n entero positivo
h <- (b-a)/n
x.vec <- seq(a, b, by = h)
f.vec <- sapply(x.vec, fun)
C.1. INTEGRACIÓN NUMÉRICA UNIDIMENSIONAL 247

return(trapezoid.vec(f.vec, h))
}

trapezoid(fun, 0, 1, 20)

## [1] 0.8033325
El error en esta aproximación se corresponde con:
(b − a)3 ′′
f (ξ),
12n2
para algún a ≤ ξ ≤ b (dependiendo del signo de la segunda derivada, i.e. de si la
función es cóncava o convexa, el error será negativo ó positivo). El error máximo
3
′′
absoluto es (b−a)
12n2 maxa≤ξ≤b |f (ξ)|. En el caso general multidimensional sería
O(n− d ).
2

C.1.2 Regla de Simpson

Se divide el intervalo n subintervalos de longitud h = b−a


n (con n par), consi-
derando n + 1 puntos regularmente espaciados xi = a + ih, para i = 0, 1, ..., n.
Aproximando de forma cuadrática la función en cada subintervalo [xj−1 , xj+1 ]
(considerando 3 puntos), se obtiene que:
∫ b [ ∑
(n/2)−1

n/2 ]
h
f (x) dx ≈ f (x0 ) + 2 f (x2j ) + 4 f (x2j−1 ) + f (xn ) ,
a 3 j=1 j=1

simpson <- function(fun, a, b, n = 100) {


# Integración numérica de fnt entre a y b
# utilizando la regla de Simpson con n subdivisiones
# (se aproxima fun de forma cuadrática en cada par de intervalos)
# fnt es una función de una sola variable
# Se asume a < b y n entero positivo par
n <- max(c(2*(n %/% 2), 4))
h <- (b-a)/n
x.vec1 <- seq(a+h, b-h, by = 2*h)
x.vec2 <- seq(a+2*h, b-2*h, by = 2*h)
f.vec1 <- sapply(x.vec1, fun)
f.vec2 <- sapply(x.vec2, fun)
return(h/3*(fun(a) + fun(b) + 4*sum(f.vec1) + 2*sum(f.vec2)))
# Una cota del error en valor absoluto es:
# h^4*(b-a)*max(c(f.vec1, fvec.2))^4/180.
}

simpson(fun, 0, 1, 20)
248 APÉNDICE C. INTEGRACIÓN NUMÉRICA

## [1] 0.8000033
El máximo error (en el caso unidimensional) viene dado por la expresión:

(b − a)5
(4)
max f (ξ) .
180n4 a≤ξ≤b

En el caso general multidimensional sería O(n− d ).


4

C.1.3 Cuadratura adaptativa

En lugar de evaluar la función en una rejilla regular (muestrear por igual el


dominio), puede interesar ir añadiendo puntos sólo en los lugares donde se mejore
la aproximación (en principio donde hay mayor área).
quadrature <- function(fun, a, b, tol=1e-8) {
# numerical integration using adaptive quadrature

simpson2 <- function(fun, a, b) {


# numerical integral using Simpson's rule
# assume a < b and n = 2
return((b-a)/6 * (fun(a) + 4*fun((a+b)/2) + fun(b)))
}

quadrature_internal <- function(S.old, fun, a, m, b, tol, level) {


level.max <- 100
if (level > level.max) {
cat ("recursion limit reached: singularity likely\n")
return (NULL)
}
S.left <- simpson2(fun, a, m)
S.right <- simpson2(fun, m, b)
S.new <- S.left + S.right
if (abs(S.new-S.old) > tol) {
S.left <- quadrature_internal(S.left, fun,
a, (a+m)/2, m, tol/2, level+1)
S.right <- quadrature_internal(S.right, fun,
m, (m+b)/2, b, tol/2, level+1)
S.new <- S.left + S.right
}
return(S.new)
}

level = 1
S.old <- (b-a) * (fun(a) + fun(b))/2
S.new <- quadrature_internal(S.old, fun,
C.2. INTEGRACIÓN NUMÉRICA BIDIMENSIONAL 249

a, (a+b)/2, b, tol, level+1)


return(S.new)
}

quadrature(fun, 0, 1)

## [1] 0.8
Fuente: r-blogger Guangchuang Yu

C.1.4 Comandos de R

integrate(fun, 0, 1) # Permite límites infinitos

## 0.8 with absolute error < 8.9e-15


## Cuidado: fun debe ser vectorial...

require(MASS)
area(fun, 0, 1)

## [1] 0.8000001

C.2 Integración numérica bidimensional


Supongamos que nos interesa aproximar una integral de la forma:
∫ bx ∫ by
I= f (x, y)dydx
ax ay

.
Consideraremos como ejemplo:
∫ ∫
1 1 ( )
x2 − y 2 dxdy = 0
−1 −1
.
f2d <- function(x,y) x^2 - y^2

Es habitual (especialmente en simulación) que la función se evalúe en una rejilla:


ax = -1
ay = -1
bx = 1
by = 1
250 APÉNDICE C. INTEGRACIÓN NUMÉRICA

nx = 21
ny = 21
x <- seq(ax, bx, length = nx)
y <- seq(ay, by, length = ny)
z <- outer(x, y, f2d)

hx <- x[2]-x[1]
hy <- y[2]-y[1]

C.2.1 Representación gráfica

Puede ser de utilidad las herramientas de los paquetes plot3D y plot3Drgl


(también se pueden utilizar las funciones spersp, simage, spoints y splot del
paquete npsp).
if(!require(plot3D)) stop('Required pakage `plot3D` not installed.')

# persp3D(z = z, x = x, y = y)

persp3D.f2d <- function(f2d, ax=-1, bx=1, ay=-1, by=1, nx=21, ny=21, ...) {
x <- seq(ax, bx, length = nx)
y <- seq(ay, by, length = ny)
hx <- x[2]-x[1]
hy <- y[2]-y[1]
z <- outer(x, y, f2d)
persp3D(x, y, z, ...)
}

persp3D.f2d(f2d, -1, 1, -1, 1, 50, 50, 1, ticktype = "detailed")


C.2. INTEGRACIÓN NUMÉRICA BIDIMENSIONAL 251

0.5

0.5

0.0
z

0.0
−0.5
1.0
−1.0
0.5
−0.5 −0.5
0.0
x0.0

y
−0.5
0.5

1.0−1.0

C.2.2 Método del trapezoide

Error O(n− d ).
2

trapezoid.mat <- function(z, hx, hy) {


# Integración numérica bidimensional
# utilizando el método del trapezoide (se aproxima f linealmente)
f.vec <- apply(z, 1, function(x) trapezoid.vec(x, hx))
return(trapezoid.vec(f.vec, hy))
}

# trapezoid.mat(z, hx, hy)

trapezoid.f2d <- function(f2d, ax=-1, bx=1, ay=-1, by=1, nx=21, ny=21) {


x <- seq(ax, bx, length = nx)
y <- seq(ay, by, length = ny)
hx <- x[2]-x[1]
hy <- y[2]-y[1]
z <- outer(x, y, f2d)
trapezoid.mat(z, hx, hy)
}

trapezoid.f2d(f2d, -1, 1, -1, 1, 101, 101)

## [1] -8.881784e-18
252 APÉNDICE C. INTEGRACIÓN NUMÉRICA

C.2.3 Comandos de R

Suponiendo que la función es vectorial, podemos emplear:


integrate( function(y) {
sapply(y, function(y) {
integrate(function(x) f2d(x,y), ax, bx)$value }) },
ay, by)

## -2.775558e-17 with absolute error < 1.1e-14


Si la función no es vectorial y solo admite parámetros escalares:
integrate(function(y) {
sapply(y, function(y) {
integrate(function(x) {
sapply(x, function(x) f2d(x,y)) }, ax, bx)$value }) },
ay, by)

Fuente: tolstoy.newcastle.edu.au.
Alternativamente se podría emplear la función adaptIntegrate del paquete
cubature.

También podría gustarte