R cuenta con un sistema de generación de gráficas poderoso y flexible. Para tener una pequeña idea acerca de las opciones disponibles en el programa base, podemos escribir el comando demo(graphics), aunque su capacidad para crear gráficos de alta calidad la proporcionan librerías adicionales, algunas de las cuales trabajaremos más adelante durante el desarrollo del curso. Su versatilidad es tan grande que resulta imposible detallar todas las posibilidades de R en términos de generación de representaciones gráficas, por lo que nos centraremos en las más habituales y/o versátiles.

plot()

Esta función gráfica ofrece muchas variantes, dependiendo del tipo de objeto al que se aplique (un vector, dos vectores de igual longitud, un data.frame e incluso otros objetos). Es lo que llamamos coerción: la salida depende de los valores que introduzcamos para los argumentos.

Seguramente el caso más simple corresponda a la representación de dos variables numéricas x e y, que nos devolverá su diagrama de dispersión. Por ejemplo

# Generamos n valores distribuidos uniformemente entre 1 y 10
n=100
v1=runif(n,0,10)
# Generamos los valores de y como una transformación lineal
# de los valores de x, más una componente aleatoria
v2=2*v1+rnorm(n)
plot(v1,v2)

Podemos personalizar el gráfico, modificando la forma de los puntos, asignando a pch1 el valor correspondiente al símbolo que queramos usar,

el tamaño de dichos símbolos, con cex (cuyo valor por defecto es 1),

y también su color, para lo que se dispone de múltiples opciones, confirmando la enorme versatilidad de R. Por ejemplo con los parámetros col y bg, usando los números del 1 al 8 según la escala:

con la función colors() con el valor numérico del color correspondiente:

el nombre del color2

##  [1] "white"         "aliceblue"     "antiquewhite"  "antiquewhite1"
##  [5] "antiquewhite2" "antiquewhite3" "antiquewhite4" "aquamarine"   
##  [9] "aquamarine1"   "aquamarine2"   "aquamarine3"   "aquamarine4"  
## [13] "azure"         "azure1"        "azure2"        "azure3"

el valor HEX del color, col = "#0000FF"3, o bien el valor RGB, haciendo uso de la función rgb, que toma la intensidad de colores rojo, verde y azul como argumentos. Su sintasxis es la siguiente:

rgb(red, green, blue, alpha, names=NULL, maxColorValue = 1)`

De forma predeterminada, estos valores se encuentran entre 0 y 1. Esta configuración predeterminada se puede cambiar con el argumento maxColorValue para tener valores entre 0 y el valor que especifiquemos (maxColorValue = 255, es el estándar para la representación del color RGB). Esta función también permite cambiar la transparencia del color, con el argumento alpha, que toma valores desde 0 (completamente transparente) hasta 1 (opaco).

Por ejemplo, vamos a usar círculos rojos rellenos en gris, con un tamaño un tercio inferior a los de la gráfica anterior. La nueva gráfica sería:

plot(v1,v2,pch=21,col="red",bg="gray",cex=2/3)

También podemos ponerle un título y/o un subtítulo y etiquetas a los ejes:

plot(v1,v2,pch=21,col="red",bg="gray",cex=2/3,
     main="Mi primer gráfico", xlab="x", ylab="y")

y darle formato al texto con family y font (en el siguiente ejemplo elegimos el tipo de letra Times New Roman4 y además lo destacamos en negrita):

plot(v1,v2,pch=21,col="red",bg="gray",cex=2/3,
     main="Mi primer gráfico", xlab="x", ylab="y",
     family="serif" , font=2)

R facilita la creación de gráficos más elaborados, añadiendo a los gráficos de alto nivel gráficos de bajo nivel. Por ejemplo, podríamos estar interesados en añadir la recta de regresión muestral, que ajusta por mínimos cuadrados nuestro conjunto de datos, representarla junto a ellos e incluir su expresión:

# Estimamos la recta de regresión muestral y guardamos la salida 
# en el objeto regresion, para poder utilizarla posteriormente
regresion<-lm(v2~v1)
# Representamos el conjunto de datos
plot(v1,v2,pch=21,col="red",bg="gray",cex=2/3,
     main="Mi primer gráfico", xlab="x", ylab="y",
     family="mono" , font=2)
# Añadimos la recta de regresión muestral
abline(regresion)
# Añadimos la expresión de la recta de regresión muestral
text(6,5,paste("y = ",round(coef(regresion)[2],2),
               "x +", round(coef(regresion)[1],2)))

Como ya hemos mencionado, la función plot devuelve distintos tipos de gráficos según los valores sobre los que la evaluemos. Por ejemplo, si convertimos la variable v1 en un factor (as.factor(trunc(v1))) muestra un diagrama de barras:

plot(as.factor(trunc(v1)))

Si la evaluamos sobre un único argumento numérico, lo representa como una secuencia:

plot(v1)

que podemos personalizar representando líneas en vez de puntos

plot(v1,type="l")

o ambos

plot(v1,type="b")

con el formato que nos parezca más adecuado para las líneas, gracias al argumento lty, según el nombre o código numérico:

plot(v1,type="b",
     lty=3, pch=16, col="red")

También nos permite representar una función. Por ejemplo, podemos representar \(f(x)=x \sin^2(x)\) en el intervalo \([0,1]\), añadiendo la expresión matemática como título:

# Definimos la función
f=function(x)
  x*sin(x)^2
# Representamos f(x) desde 0 hasta 10
plot(f,0,10, main=expression(f(x)==xsin(x)^2))

También podemos usar plotcomo función de bajo nivel (con add=TRUE) para representar varias funciones juntas:

# Definimos sendas funciones
f=function(x)
  x*sin(x)^2
g=function(x)
  2*x*sin(x)^2
# Las representamos desde 0 hasta 10
plot(f,0,10)
plot(g,0,10, add=TRUE, col="red")

El gráfico que aparece por defecto es claramente mejorable. Por ejemplo, podemos personalizar la región de dibujo, para ver las curvas completas, personalizando el valor de ylim

# Definimos las funciones
f=function(x)
  x*sin(x)^2
g=function(x)
  2*x*sin(x)^2
# Las representamos desde 0 hasta 10
plot(f,0,10, ylim=c(0,16))
plot(g,0,10, add=TRUE, col="red")

y añadir una leyenda para identificar las funciones

# Definimos las funciones
f=function(x)
  x*sin(x)^2
g=function(x)
  2*x*sin(x)^2
# Las representamos desde 0 hasta 10
plot(f,0,10, ylim=c(0,16), lty=2)
plot(g,0,10, add=TRUE, col="red")
legend("topleft", c("f(x)","g(x)"), 
       col = c("black","red"), lty = c(2, 1), 
       merge = TRUE, bg = "gray90")

Los gráficos son una herramienta muy potente para representar datos, cuyo principal objetivo suele ser mostrar de una manera visual y simple los posibles patrones presentes en ellos. Para conseguirlo debemos elegir el tipo adecuado, así como su formato. Una mala elección puede ocultar los patrones en vez de mostrarlos. Por ejemplo, supongamos que queremos representar la función \(\sin{i}\), para \(i=1, 2, ..., n\). Si lo hacemos utilizando puntos, con todas las opciones de forma por defecto

n <- 50
x <- 1:n
plot(x,sin(x))

no parece apreciarse ningún patrón. Si cambiamos su aspecto utilizando la misma escala para ambos ejes

n <- 50
x <- 1:n
plot(x,sin(x), asp=1)

el patrón existente sale a la luz. Otra opción podría ser representar líneas en vez de puntos:

n <- 50
x <- 1:n
plot(x,sin(x), type="l")


hist()

La forma habitual de representar un conjunto de datos numérico para observar su distribución es con un histograma. Podemos crearlo con la función hist(), que siempre nos pide como argumento un vector numérico x, mientras que el resto son opcionales (al tener asignados valores por defecto).

# Generamos un conjunto de datos con distribución normal 
x=rnorm(100)
# Representamos su distribución
hist(x)

Podemos modificar fácilmente su estilo añadiendo parámetros gráficos (algunos de los cuales ya conocemos y otros son propios de esta función como el número de intervalos breaks o el tipo de frecuencias representadas freq, absolutas o relativas)

Cambiar el tipo de frecuencias (absolutas por defecto), solo modifica la escala del eje vertical, pero no la forma del histograma:

# Generamos un conjunto de datos con distribución normal 
x=rnorm(100)
hist(x, freq=FALSE)

No ocurre lo mismo cuando forzamos el número de intervalos5 en los que agrupar los valores a representar:

hist(x, freq=FALSE, breaks=20,
     main="Histograma",ylab="Frecuencia relativa", col=rgb(0,0.5,0.5))

Como la forma del histograma depende de los intervalos, suele ser muy útil representar su densidad, para poder observar la forma de la distribución de la manera más precisa posible:

hist(x, freq=FALSE, breaks=20,
     main="Histograma",ylab="Frecuencia relativa", col=rgb(0,0.75,0.75))
lines(density(x),lty="dotted",lwd=2)

Otra cuestión interesante puede ser comparar el histograma con una distribución teórica, superponiéndola a éste. En el ejemplo con el que estamos trabajando, vamos a comparar con la curva de la densidad normal estándar (que es de la que hemos simulado los valores de x):

hist(x, freq=FALSE, breaks=20,
     main="Histograma",ylab="Frecuencia relativa", col=rgb(0,0.95,0.95))
curve(dnorm, lty="dotdash",lwd=3, add=TRUE)

Si la muestra fuese más grande, ambas distribuciones se parecerían mucho más:

# Generamos un conjunto de datos mucho más grande con distribución normal estándar
x=rnorm(10^6)
# Representamos el histograma de frecuencias relativas
hist(x, freq=FALSE, breaks=100, 
     main="Histograma",ylab="Frecuencia relativa")
# Añadimos la curva de la densidad normal estándar
curve(dnorm, add=TRUE, col="red", lwd=2)

Como con cualquier otra función, podemos consultar todas sus opciones con ?hist().

stem

Los diagramas de tallo y hojas son un tipo de representación clásica de la distribución de datos numéricos, similar a un histograma pero en formato de texto. Para construirlo, los datos se dividen en tallo (primer o primeros dígitos del número) y hoja (el resto de dígitos). En R se pueden crear con la función stem.

# Generamos un conjunto de datos con distribución normal estándar, no demasiado grande
x=rnorm(10^2)
# Representamos el diagrama de tallo y hojas
stem(x)
## 
##   The decimal point is at the |
## 
##   -1 | 9977
##   -1 | 4333222222111100
##   -0 | 98777766665555
##   -0 | 333322111111100
##    0 | 000011111222223333444
##    0 | 556666777889
##    1 | 0000011233344
##    1 | 6788
##    2 | 
##    2 | 5

boxplot()

Los boxplot, o diagrama de caja y bigotes, son representaciones gráficas que permiten resumir las principales características de los datos (acerca de su simetría, dispersión, posición y presencia de valores atípicos). La caja comienza en el primer cuartil y termina en el tercero (por lo que contendrá el 50% de los datos centrales), con una línea dentro para representar la mediana. A cada lado de la caja se dibuja un segmento con los datos más alejados, sin contar con los valores atípicos (que aparecen como círculos, caso de existir).

# La siguiente instrucción permite representar una matriz de gráficos
# En este caso, vamos a representar dos gráficos en una fila
par(mfrow=c(1,2))
x=rnorm(100)
# Primer boxplot
boxplot(x)
# Segundo boxplot (con los mismos datos, añadiendo un dato atípico)
boxplot(c(x,5))

Podemos representar los intervalos de confianza al 95% para la mediana, estableciendo el argumento notch como TRUE.

par(mfrow=c(1,2))
x=rnorm(100)
boxplot(x,notch=TRUE)
# Añadimos un dato atípico
boxplot(c(x,5),notch=TRUE)

Este tipo de gráficos resulta especialmente útil para comparar comportamientos6.

# Generamos sendas secuencias de valores de tres distribuciones
# como si provinieran de distintos procesos A, B y C
xA=rnorm(100,1,2)
xB=rnorm(100,3,1/2)
xC=runif(100,0,6)
datos=data.frame(x=c(xA,xB,xC), Proceso=rep(c("A","B","C"), c(100,100,100)))
boxplot(x~Proceso, data=datos,notch=TRUE)

Una limitación de este tipo de gráficos es que no están diseñados para detectar multimodalidad, por lo que puede ser recomendable combinarlos con el histograma o la curva de densidad 7. Veamos cómo hacerlo con un ejemplo donde concatenamos dos muestras que provienen de distribuciones normales con distintos parámetros:

xA=rnorm(1000,1,1/2)
xB=rnorm(1000,5,2)
x=c(xA,xB)
# Histograma
hist(x,probability=TRUE, 
     main="", xlab="",ylab="",axes=FALSE,
     col=rgb(0.5,0,0.5, alpha=0.5),breaks=40)
# Eje
axis(1)
# Densidad
lines(density(x), col="red", lwd=2)
# Boxplot
par(new=TRUE)
boxplot(x, horizontal=TRUE, axes=FALSE,
        col=rgb(0,0,0.15, alpha=0.25))

Otra alternativa para poner de manifiesto la distribución de los datos es agregar los puntos sobre los diagramas de caja. Podemos hacerlo con la función stripchart, especificando add=TRUE como argumento.

# Generamos sendas secuencias de valores de tres distribuciones
# como si provinieran de distintos procesos A, B y C
xA=rnorm(100,1,2)
xB=rnorm(100,3,1/2)
xC=runif(100,0,6)
datos=data.frame(x=c(xA,xB,xC), Proceso=rep(c("A","B","C"), c(100,100,100)))
boxplot(x~Proceso, data=datos)
stripchart(x~Proceso, data=datos, add=TRUE,
           vertical=TRUE, method="jitter", 
           pch=1, cex=1/2, col=2:4)


barplot() y pie()

Cuando una variable es cualitativa o numérica, pero toma pocos valores diferentes, es habitual representarla con un gráfico de barras o de sectores.

# Leemos el conjunto resumido de microdatos del INE sobre uso de TICs
# Si vas a ejecutar la línea siguiente, debes cambiar la ruta mostrada
# por la ruta donde esté alojado el conjunto de datos en tu ordenador
# Si estás en el mismo directorio, solo tendrás que escribir datosTICr
# (el nombre del archivo)
load("/Users/maribelparra/Library/Mobile Documents/com~apple~CloudDocs/GraficosR/Datos/datosTICr")
str(datosTIC)
## 'data.frame':    15027 obs. of  7 variables:
##  $ Edad        : num  98 71 51 81 76 76 72 59 71 83 ...
##  $ Sexo        : Factor w/ 2 levels "Hombre","Mujer": 2 2 2 2 1 1 1 1 2 2 ...
##  $ Nivel       : num  1 1 7 1 1 1 1 3 1 1 ...
##  $ NCompras    : num  NA NA NA NA NA NA NA NA NA NA ...
##  $ ValorCompras: num  NA NA NA NA NA NA NA NA NA NA ...
##  $ UsoInternet : Factor w/ 3 levels "Menos de 3 meses",..: NA NA 1 NA NA NA 1 1 NA NA ...
##  $ Confianza   : Factor w/ 3 levels "Poco o nada",..: NA NA 2 NA NA NA 1 1 NA NA ...
head(datosTIC)
# Diagrama de barras
barplot(table(datosTIC$Confianza),
        col=c("#CCFF99","#FFFF99","#9999FF"))

# Diagrama de sectores
pie(table(datosTIC$Confianza),
        col=c("#CCFF99","#FFFF99","#9999FF"))

Podemos incluir un segundo factor y representar un diagrama de barras apiladas de frecuencias absolutas:

x=table(datosTIC$Sexo, datosTIC$Confianza)
barplot(x,
        col=c("#CCFF99","#9999FF"))

Añadir una leyenda suele ayudarnos a entender mejor un gráfico

barplot(x,
        col=c("#CCFF99","#9999FF"))
legend("topright",legend=c("Hombre","Mujer"),
       fil=c("#CCFF99","#9999FF"), title="Sexo")

Si nos interesa representar frecuencias relativas en vez de frecuencias absolutas o recuentos, usaremos la función prop.table() pudiendo elegir si las calculamos por fila (1), por columna (2) o usando toda la tabla como grupo si no escribimos nada.

barplot(prop.table(x),
        col=c("#CCFF99","#9999FF"))

barplot(prop.table(x,margin=1),
        col=c("#CCFF99","#9999FF"))

barplot(prop.table(x,margin=2),
        col=c("#CCFF99","#9999FF"))


persp()

Con esta función genérica, R nos facilita la tarea de dibujar gráficos en perspectiva de una superficie sobre el plano x–y.

x <- seq(-20, 20, length.out = 51)
y <- x
f<- function(x,y) 
{
  r=sqrt(x^2+y^2)
  sin(r)/r
}
z <- outer(x, y, f)
persp(x, y, z)

Es sencillo modificar la perspectiva, para apreciar mejor la forma de la superficie

persp(x, y, z, theta = 30, phi = 30)

o el aspecto

persp(x, y, z, theta = 30, phi = 30, expand = 0.5, col = "lightblue")

EJEMPLOS ADICIONALES

1: La disposición de las pepitas de un girasol sigue un modelo matemático cuyas coordenadas polares para la semilla \(n-\)ésima son: \[ \begin{array}{l} r=n\\ \theta=\frac{137.51 \pi}{180}r \end{array} \] Representar 1000 pepitas con círculos.

n <- 1000
r <- 1:n 
theta <- 137.51*pi*r/180
# pasamos a coordenadas cartesianas
x <- cos(theta)*r
y <- sin(theta)*r
plot(x,y,asp=1)

2: Supongamos que se lanzan dos dados: uno negro y otro verde. Vamos a representar cómo varía la estimación de la probabilidad de que el dado negro tenga un valor mayor que el verde.

n=1000
negro <- sample(6, size = n, replace = TRUE)
verde <- sample(6, size = n, replace = TRUE)
p <- cumsum(negro>verde)/(1:n)
plot(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)

3: Simular puntos dentro de la región \([-4,4]\times[0,1/2]\) y colorear de forma diferente si están por debajo o por encima de la densidad normal estándar.

plot(dnorm, -4, 4, main="Campana de Gauss", 
     xlab="x", ylab="Densidad de la normal", 
     col="red", lwd=3)
points(x=c(-4,4), y=c(0,0), type="l") # dibuja eje X
# simulamos n puntos al azar en el recuadro
n=1000
x = runif(n=1000, min=-4, max=4) 
y = runif(n=1000, min=0, max=1/2) 
points(x, y) # representamos los puntos
color=ifelse(y<dnorm(x), "green", "red")
points(x, y, col=color) # pinto de nuevo con color

4: Vamos a obtener ejemplos de los diferentes tipos de gráficos que se generan dependiendo del tipo de datos que le pasemos como argumento:

par(mfrow = c(2, 3)) # Matriz de gráficos 2x3
# Generamos datos de distintos tipos
n=100
x <- rnorm (n)
y <- rnorm (n)
xt <- ts(matrix(x, nrow = n, ncol = 1),
         start = c(2013, 1), frequency = 12)
fechas <- seq(as.Date("2013/1/1"), by = "month", length = n)
factor <- factor(trunc(x))
f <- function(x) x^2
u=runif(n)
tabla=data.frame(v1=u, 
                 v2=u+rnorm(n,0,0.1), 
                 v3=u^2+rnorm(n,0,1/2))
plot(x, y, main = "Gráfico de dispersión")
plot(factor, main = "Diagrama de barras")
plot(factor, x, main = "Diagrama de cajas y bigotes")
plot(xt, main = "Serie temporal")
plot(fechas, x, main = "Gráfico basado en fechas")
plot(f, -5, 5, main = "Función")

par(mfrow = c(1, 1))
plot(tabla, main = "Matriz de correlaciones")

par(mfrow = c(1, 1))

  1. El valor de pch, en realidad puede ser cualquier símbolo.↩︎

  2. Para conocer el nombre de todos los colores disponibles se puede ejecutar la instrucción colors().↩︎

  3. Por ejemplo, col=1, col="white", col="#FFFFFF" son equivalentes.↩︎

  4. Es necesario que el tipo de letra elegido esté instalada en nuestro ordenador, porque en otro caso no tendría efecto. Las fuentes integradas son sans (Arial), serif (Times New Roman), mono (Courier) y symbol (Standard Symbols L). Es posible añadir otras con los paquetes extrafonty showtext.↩︎

  5. Los posibles valores de breaks son un vector con los puntos de corte (o función que los calcule), un valor numérico (en cuyo caso R lo redondea al valor más próximo que permita construir intervalos sencillos) o el nombre del algoritmo que los calcule ("Sturges", "Scott", "Freedman-Diaconis").↩︎

  6. Si los intervalos de confianza de dos diagramas no se solapan, significa que hay una fuerte evidencia estadística de que las medianas son diferentes.↩︎

  7. Una opción mejor son los diagramas de violín, que veremos más adelante en el curso.↩︎