Tema 12 Graficos

En este tema se bosquejan las posibilidades gráficas de R. En concreto vamos a describir algunas de las funciones que incluye el núcleo de R para trabajar con gráficos. Las funciones gráficas de R suelen ser genéricas y tienen un gran número de opciones. Por lo tanto, aquí nos limitaremos a comentar sus usos más comunes. Si estás interesado en conocer de manera exhaustiva el funcionamiento de alguna función consulta su ayuda.

12.1 La función plot

La función plot es una de las funciones más usadas para generar gráficos en R. Se trata de una función genérica, por lo que puede ser invocada para representar gráficamente distintos tipos de datos. La función plot es de nivel alto, es decir, genera un nuevo gráfico al ser invocada (las funciones de nivel bajo añaden información a un gráfico ya existente). En las siguientes secciones vamos a analizar algunas posibilidades de esta función.

12.1.1 Diagrama de dispersión

La función plot permite generar un diagrama de dispersión (scatter plot). Veamos un ejemplo:

plot(iris$Sepal.Width, iris$Sepal.Length)

En este caso se muestra la longitud de sépalo frente a la anchura de sépalo de las flores del data frame iris. Se han usado dos vectores para especificar los datos, pero también se permite especificar los datos mediante una lista con componentes x e y o mediante una matriz con dos columnas.

Veamos otro ejemplo en el que se crean los puntos (datos) a visualizar:

x <- c(1, 5, 3)
y <- c(4, 2, 8)
plot(x, y)

En este último ejemplo se visualizan los puntos (1,4), (5, 2) y (3, 8).

Si usamos un vector como fuente de datos, se muestra los valores del vector frente a sus índices:

(v <- sample(30, size = 10))
##  [1] 20 28 30 13 11  8 12 15  6 21
plot(v)

12.1.2 Serie temporal

Una serie temporal es una secuencia de datos medidos en determinados momentos, generalmente equidistantes en el tiempo, y ordenados cronológicamente. plot nos permite visualizar una serie temporal:

plot(UKDriverDeaths)

12.1.3 Visualizar una función

Podemos visualizar los valores de una función real de variable real si almacenamos en un vector una serie de valores de x (en orden creciente) y en otro vector sus correspondientes valores de y para la función. Por ejemplo, vamos a visualizar la función \(y = x^2 + 5\) para 7 valores equidistantes de x en el intervalo [-10, 10]:

x <- seq(-10, 10, length.out = 7)
y <- x^2 + 5
plot(x, y)

El parámetro type de plot permite visualizar los datos de distintas formas. Por defecto, este parámetro vale "p" y dibuja los puntos especificados, pero también se puede dibujar las líneas que unen los puntos, con "l", o líneas y puntos con "b" (hay más posibilidades):

plot(x, y, type = "l") # visualizamos las líneas que unen los puntos

plot(x, y, "o") # líneas y puntos superpuestos

Para visualizar una función con más precisión basta con especificar más puntos de la función:

x <- seq(-10, 10, length.out = 100)
y <- x^2 + 5
plot(x, y, type = "l")

La función curve es una alternativa a plot para visualizar la curva asociada a una función. Consulta su ayuda para ver todas las posibilidades:

curve(x^2 + 5, from = -10, to = 10)

12.1.4 Factores y diagramas de caja

En el caso de que le pasemos a plot un factor obtendremos un diagrama de barras con las frecuencias de las distintas categorías:

plot(iris$Species)

Podemos observar que existen 50 datos de las tres especies de lirio del conjunto de datos iris. Podríamos obtener un resultado similar con la función barplot:

barplot(table(iris$Species))

La función barplot también funciona con vectores:

barplot(c(1, 5, 2, 8))

Si usamos un factor y un vector numérico, se obtiene un diagrama de cajas (o de cajas y bigotes o box plot) por cada categoría del factor. Por ejemplo, veamos la distribución de la longitud de pétalo para las distintas categorías de lirios:

plot(iris$Species, iris$Petal.Length)

La función boxplot es una alternativa a plot para visualizar diagramas de cajas:

boxplot(Petal.Length ~ Species, data = iris)

boxplot también permite generar un diagrama de cajas con los elementos de un vector. Por ejemplo, veamos el diagrama de cajas de la longitud de pétalo de todas las flores de iris:

boxplot(iris$Petal.Length)

12.1.5 Diagramas de dispersión emparejados

Cuando plot recibe como parámetro un data frame genera diagramas de dispersión para todos los pares de variables en el data frame:

plot(iris[-5]) # se elimina de iris el factor con el tipo de flor

Viendo el gráfico parece que la correlación lineal entre longitud y anchura de pétalo es alta. La función pairs es similar a este último uso de plot.

pairs(iris[-5])

La función coplot permite generar distintos diagramas de dispersión en función de un factor:

coplot(Petal.Width ~ Petal.Length | Species, iris)

12.2 Personalización de un gráfico

Existen muchas posibilidades para configurar el aspecto de un gráfico. En esta sección vamos a ver algunas. Observa este ejemplo:

x <- seq(0, 8*pi, by = 0.1)
plot(x, sin(x), type = "l", xlab = "x", ylab = "Seno", 
     main = "Función seno")

En este ejemplo se han usado textos para etiquetar los ejes del gráfico y se ha indicado un título. Es posible indicar el tipo de fuente y el color en que aparecen los textos.

Se puede especificar los límites de los ejes con los parámetros xlim e ylim y una malla de fondo con la función grid:

plot(x, sin(x), type = "l", xlab = "x", ylab = "Seno", 
     main = "Función seno", ylim = c(-1.5, 1.5))
grid()

12.2.1 Tipos de línea, puntos y colores

Existen opciones para especificar los colores con los que se dibuja o los tipos de puntos y líneas. Veamos algún ejemplo:

ang <- seq(0, 2*pi, length.out = 100)
plot(cos(ang), sin(ang), type = "l", col = "red", lty = "dashed",
     lwd = 3, asp = 1)
grid()

En el ejemplo se ha dibujado una circunferencia de color rojo (parámetro col), con líneas discontinuas (lty es el estilo de línea) y con una anchura de 3 unidades (parámetro lwd). El parámetro asp sirve para gestionar la proporción entre las distancias en los ejes x e y, si no se hubiera utilizado la circunferencia hubiera aparecido achatada.

Vamos a ver otro ejemplo con el estilo de los puntos:

ang <- seq(0, 2*pi, length.out = 16)
plot(cos(ang), sin(ang), col = "green", pch = 19, cex = 1.5, asp = 1)
grid()

En este caso el parámetro pch indica el tipo de punto y cex su tamaño (1.5 veces el tamaño por defecto).

La función colors devuelve un vector con los colores disponibles. El siguiente gráfico muestra los distintos tipos de punto que se pueden usar con el parámetro pch.

12.3 Funciones de nivel bajo

Las funciones de nivel bajo sirven para añadir información a un gráfico existente creado con una función de nivel alto, como plot. Vamos a estudiar alguna de ellas.

12.3.1 Funciones points y lines

points(x, y) y lines(x, y) añaden puntos o líneas conectadas, respectivamente, al gráfico actual. En estas funciones se puede usar el argumento type de plot, siendo su valor por defecto "p" para points y "l" para lines.

x <- seq(0, 8*pi, by = 0.1)
plot(x, sin(x), type = "l", col = "blue")
lines(x, cos(x), col = "red", lty = "dashed") # añade líneas con coseno
grid()

Veamos ahora un uso de points. Vamos a generar puntos aleatorios en el cuadrado de esquinas (-1, -1) y (1, 1) y vamos a dibujar con distinto color a los puntos a la izquierda y derecha del eje de ordenadas:

x <- runif(100, min = -1, max = 1)
y <- runif(100, min = -1, max = 1)
plot(x[x <= 0], y[x <= 0], pch = 19, col = "blue", xlim = c(-1, 1),
     ylim = c(-1, 1), xlab = "x", ylab = "y", asp = 1)
points(x[x > 0], y[x > 0], pch = 19, col = "red")

12.3.2 Función abline

La función abline sirve para añadir una línea a un gráfico. Esta función tiene varios parametros con nombre, que sirven para especificar de forma sencilla distintos tipos de líneas:

  • abline(a, b): línea de pendiente b y ordenada en el origen a
  • abline(h=y): línea horizontal
  • abline(v=x): línea vertical

Veamos un ejemplo:

# El siguiente plot crea un gráfico sin dibujar nada (type = "n")
plot(0, xlim = c(-5, 5), ylim = c(-3, 3), type = "n")
abline(1, 2, col = "red")     # línea y = 2x + 1
abline(h = 2, col = "green")  # línea y = 2
abline(v = 3, col = "violet") # línea x = 3
grid()

La función abline también permite dibujar la recta de regresión asociada a un modelo lineal:

plot(iris$Petal.Width, iris$Petal.Length)
modelo <- lm(Petal.Length ~ Petal.Width, data = iris)
abline(modelo, col = "red")

Como ejemplo adicional vamos a generar 20 muestras de tamaño 100 de una \(\mathcal{N}(2, 3)\) y vamos a visualizar los intervalos de confianza del 90% de la media de acuerdo a las muestras.

muestras <- matrix(rnorm(100*20, 2, 3), nrow = 20)
medias <- apply(muestras, 2, mean)
int_conf <- cbind(medias - qnorm(0.95)*3/sqrt(20),
                  medias + qnorm(0.95)*3/sqrt(20))
plot(c(min(int_conf), max(int_conf)), c(0, 21), type = "n",
     xlab = "Intervalo de confianza 90%", ylab = "muestra")
for (m in 1:20) {
  if (int_conf[m, 1] <= 2 && 2 <= int_conf[m, 2]) {
    lines(c(int_conf[m, 1],int_conf[m, 2]), c(m, m), col = "blue")
  } else {
    lines(c(int_conf[m, 1],int_conf[m, 2]), c(m, m), col = "red")
  }
}
abline(v = 2)

12.3.3 Función text

Esta función permite escribir texto en el gráfico:

# El siguiente plot crea un gráfico sin dibujar nada (type = "n")
plot(0, xlim = c(-5, 5), ylim = c(-3, 3), type = "n")
abline(h = 2, col = "green")  # línea y = 2
abline(v = 3, col = "violet") # línea x = 3
grid()
text(0, 2.3, "Línea horizontal")

La función text tiene parámetros para especificar el tipo de letra, justificación del texto, tamaño, etcétera.

12.3.4 Función polygon

Esta función dibuja un polígono, que puede ser rellenado con color y/o líneas opcionalmente:

plot(0, xlim = c(0, 1.5), ylim = c(0, 3), type = "n")
x <- c(0, 1, 1, 0.5, 0)
y <- c(0, 0, 1, 2, 1)
polygon(x, y, col = "yellow", border = "red")

La función polygon puede utilizarse para destacar una zona de dibujo. Por ejemplo, en el siguiente gráfico se dibujan 100 puntos aletorios uniformemente distribuidos en el cuadrado de esquinas (-1, -1) y (1, 1) y se usa la función polygon para destacar el primer cuadrante:

x <- runif(100, min = -1, max = 1)
y <- runif(100, min = -1, max = 1)
plot(x, y, pch = 19, asp = 1)
x <- c(0, 1, 1, 0)
y <- c(0, 0, 1, 1)
polygon(x, y, col = rgb(1, 0, 0, 0.2), border = NA)

Observa que para especificar el color del polígono se ha usado la función rgb, que permite especificar un color como una combinación de los colores red, green, blue, con el valor de cada color especificado como un valor real en el rango [0, 1], siendo 0 que no se usa y 1 que se usa totalmente. El último parámetro de rgb, que vale 0.2 en el ejemplo, es el nivel de transparencia. También hay que especificarlo en el rango [0, 1], significando 1 totalmente opaco.

12.3.5 Función legend

La función legend añade una leyenda al gráfico actual en un determinado lugar. Vamos a ver algún ejemplo:

x <- seq(0, 8*pi, by = 0.1)
plot(x, sin(x), type = "l", col = "blue")
lines(x, cos(x), col = "red", lty = "dashed") 
grid()
legend("topright", legend = c("seno", "coseno"), 
       col = c("blue", "red"), lty = c("solid", "dashed"))

Veamos otro ejemplo con un gráfico previo:

x <- runif(100, min = -1, max = 1)
y <- runif(100, min = -1, max = 1)
plot(x[x <= 0], y[x <= 0], pch = 19, col = "blue", xlim = c(-1, 1),
     ylim = c(-1, 1), asp = 1)
points(x[x > 0], y[x > 0], pch = 19, col = "red")
legend("topleft", legend = c("neg", "pos"), col = c("blue", "red"),
       pch = c(19, 19))

En un último ejemplo, más avanzado, mostramos un diagrama de dispersión de la longitud de pétalo, frente a su anchura para las flores del data frame iris. Los puntos se colorean según el factor Species.

plot(iris$Petal.Width, iris$Petal.Length, col = iris$Species, pch = 19)
legend("topleft", legend = levels(iris$Species), col = 1:3, pch = 19)

Los colores elegidos para dibujar los factores aparecen por orden en la salida de la función palette.

12.4 Histogramas: la función hist

La función hist produce un histograma del vector numérico que recibe como parámetro. El número de clases es seleccionado automáticamente, pero puede ser especificado, así como los límites de las clases. Por defecto se muestran las frecuencias, pero se puede seleccionar ver las frecuencias relativas.

hist(Nile)

Veamos ahora un histograma con 15 clases y frecuencias relativas:

hist(Nile, nclass = 15, probability = TRUE)

12.5 Diagramas de sectores: pie

Hemos visto que con barplot se puede obtener un gráfico de barras de los valores de una variable cualitativa. Con la función pie se puede obtener un diagrama de sectores.

barplot(table(iris$Species), main = "Distribución de tipos de lirios")

pie(table(iris$Species), main = "Distribución de tipos de lirios")

Es posible etiquetar cada sector. Por ejemplo, vamos a etiquetar cada tipo de lirio con su porcentaje de ocurrencias.

tabla <- prop.table(table(iris$Species))
pie(tabla, 
    main = "Distribución de tipos de lirios",
    labels = paste(names(tabla), round(tabla*100, 2), "%")
)

12.6 Función locator y par

En esta sección se comentan dos funciones que pueden resultar útiles. La primera es la función locator. Esta función sirve para obtener las coordenadas de ciertos puntos en el gráfico. Al ejecutarla usaremos el ratón para posicionar el cursor en las coordenadas que nos interesan y pulsaremos el botón izquierdo tantas veces como coordenadas queramos obtener. Esta función resulta útil para saber aproximadamente qué coordenadas usar en una llamada a funciones como text o legend en las que podemos especificar una posición del gráfico donde ubicar un elemento. Como ejemplo, prueba lo siguiente en la consola:

plot(0, xlim = c(0, 2), ylim = c(0, 2), type = "n")
locator(1)

A continuación sitúate en un punto del gráfico y pulsa el botón izquierdo del ratón.

La otra función es par, que sirve para especificar una serie de parámetros gráficos que se usan por defecto. Por ejemplo:

plot(Nile)

par(lwd = 5) # a partir de ahora el ancho de línea es 5 por defecto
plot(Nile)

Si se usa sin parámetros, par devuelve una lista con los valores actuales de los parámetros por defecto. Un uso muy común es hacer que varios gráficos aparezcan en la misma pantalla:

par(mfrow = c(2, 2))
plot(Nile)
curve(x^2, -2, 2)
curve(exp(x), 0, 1)
plot(iris$Species)

Si queremos restaurar el valor por defecto habrá que escribir par(mfrow = c(1, 1)).

12.7 Ejercicios

  1. La disposición de las pepitas de un girasol sigue un modelo matemático. La \(n\)-ésima semilla tiene coordenadas polares \(r=\sqrt{n}\) y \(\alpha= \frac{137.51\pi n}{180}\). Escribe un programa que solicite el número de pepitas y las dibuje como círculos.

  2. Representa la función logística simple:

    \[P(t) = \dfrac{1}{1+ e^{-t}} \]

    en el intervalo [-6, 6].

  3. Representa las funciones \(\sin(x)\) y \(2\sin(x)\) en un mismo gráfico, etiquetando cada una de las funciones.

  4. Genera 100 puntos aleatorios uniformemente distribuidos en el cuadrado de esquinas (-1, -1) y (1, 1). Dibuja los puntos de color negro. Dibuja de color rojo el punto más lejano al origen y une el origen y el punto con una línea roja. La distancia de un punto \((x, y)\) al origen se calcula como \(\sqrt{x^2 + y^2}\).

  5. Supongamos que en una habitación se reunen n personas al azar, con \(n \leq 365\), y ninguna de ellas ha nacido el 29 de febrero, ni hay gemelos ni mellizos. La probabilidad de que al menos dos personas cumplan años el mismo día es:

    \[ 1 - \frac{365}{365} \times \frac{364}{365} \times \frac{363}{365} \times \ldots \times \frac{365-n+1}{365} \]

    Realiza una función que dado n devuelva la citada probabilidad. Como dato de prueba con 23 personas la probabilidad es 0.507. Haz un gráfico en el que se muestre cómo evoluciona la probabilidad en función del número de personas que haya en la habitación. Destaca en el gráfico la probabilidad para \(n = 23\) (ver siguiente gráfico).

  6. Supongamos que se lanzan dos dados, uno negro y otro verde, ¿Cuál es la probabilidad de que el dado negro tenga un valor mayor que el verde? En este caso la respuesta es sencilla: \(\frac{15}{36}\), porque hay 36 posibles combinaciones de los valores de los dos dados y en 15 de ellas el dado negro es mayor que el verde. Sin embargo, hay situaciones en las que no es posible o fácil hacer estos cálculos. En dichas situaciones se puede recurrir a una simulación en la que se repite N veces el experimento aleatorio y se calcula la proporción de veces en la que el experimento tiene éxito (en nuestro caso el éxito equivale a que el dado negro vale más que el verde). Esa proporción es una aproximación a la probabilidad buscada. Cuanto mayor sea N mayor confianza tenemos en el valor de la aproximación. De hecho, cuando \(N \to \infty\) se obtiene la solución exacta. Haz una función que calcule una aproximación a la probabilidad de que el dado negro sea mayor que el verde. Se puede escribir una función vectorizada que no use ciclos. El parámetro de la función es el valor de N. Haz una gráfica en la que se refleje cómo la simulación converge a \(\frac{15}{36}\).

  7. Genera una matriz aleatoria con valores de 0 y 1. Por ejemplo:

    set.seed(10)
    (m <- matrix(sample(0:1, size = 16, replace = TRUE), nrow = 4))
    ##      [,1] [,2] [,3] [,4]
    ## [1,]    0    1    0    0
    ## [2,]    0    0    0    0
    ## [3,]    1    1    0    1
    ## [4,]    1    1    1    0

    Representa la matriz gráficamente con distintos símbolos para el 0 y el 1. Por ejemplo:

12.8 Soluciones a los ejercicios

# Semillas de girasoles
n <- as.integer(readline("Número de semillas: "))
radio <- 1:n
angulos <- 137.51 * pi * 1:n / 180
x <- cos(angulos) * radio
y <- sin(angulos) * radio
plot(x, y, asp = 1)
# Función logística simple
curve(1 / (1 + exp(-x)), -6, 6)
grid()

# Función logística simple
t <- seq(-6, 6, length.out = 100)
plot(t, 1 / (1 + exp(-t)), type = "l", xlab = "x", ylab = "y")
abline(h = 0.5, col = "red")
grid()

# Funciones seno
x <- seq(0, 8*pi, by = 0.1)
plot(x, 2*sin(x), type = "l", col = "blue")
lines(x, sin(x), col = "red", lty = "dashed") 
grid()
legend("topright", legend = c("2seno", "seno"), 
       col = c("blue", "red"), lty = c("solid", "dashed"))

# Punto más lejano al origen
n <- 100
x <- runif(n, min = -1, max = 1)
y <- runif(n, min = -1, max = 1)
p_index <- which.max(x^2 + y^2)
plot(x, y, pch = 19, asp = 1)
points(c(0, x[p_index]), c(0, y[p_index]), pch = 19, col = "red")
lines(c(0, x[p_index]), c(0, y[p_index]), pch = 19, col = "red")

# Probabilidad cumpleaños el mismo día
prob <- function(n) {
  producto <- 1
  for (f in (365-n+1):365)
    producto <- producto * f/365
  1 - producto
}
p <- sapply(1:80, prob)
plot(1:80, p, type = "l", col = "red", 
     xlab = "Número de personas",
     ylab = "Probabilidad",
     main = "Probabilidad del mismo día de nacimiento")
grid()
lines(c(0, 23, 23), c(rep(prob(23), 2), 0), col = "blue")

# Probabilidad cumpleaños el mismo día
prob <- function(n) {
  producto <- 1
  for (f in (365-n+1):365)
    producto <- producto * f/365
  1 - producto
}
p <- sapply(1:80, prob)
plot(1:80, p, type = "l", col = "red", 
     xlab = "Número de personas",
     ylab = "Probabilidad",
     main = "Probabilidad del mismo día de nacimiento")
grid()
lines(c(0, 23, 23), c(rep(prob(23), 2), 0), col = "blue")

# Convergencia simulación dado negro mayor que verde
prob <- function(n) {
 negro <- sample(6, size = n, replace = TRUE)
 verde <- sample(6, size = n, replace = TRUE)
 mean(negro > verde)
}
p <- sapply(1:750, prob)
plot(1:750, p, type = "l", col = "blue", 
    xlab = "Número de experimentos",
    ylab = "Probabilidad",
    main = "Convergencia de la simulación"
)
grid()
abline(h = 15/36, col = "red", lwd = 2)

# Representación gráfica de una matriz de ceros y unos
set.seed(10)
m <- matrix(sample(0:1, size = 16, replace = TRUE), nrow = 4)
plot(1:ncol(m), 1:nrow(m), type = "n", xlab = "Columna", ylab = "Fila")
for (f in 1:nrow(m)) {
  for (c in 1:ncol(m)) {
    if (m[f, c] == 1) {
       points(c, nrow(m)+1-f, pch = 19)
    } else {
       points(c, nrow(m)+1-f, pch = 1)
    }
  }
}